Tests/Unit/MSFT_xArchive.TestHelper.psm1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
<#
    .SYNOPSIS
    Converts a hashtable to a file structure.
 
    .PARAMETER ParentPath
    The path to the directory that should contain the given file structure.
 
    .PARAMETER ZipFileStructure
    The hashtable defining the zip file structure
    Hashtables are directories.
    Strings are files with the key as the name of the file and the value as the contents.
#>

function ConvertTo-FileStructure
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $ParentPath,

        [Parameter(Mandatory = $true)]
        [ValidateNotNull()]
        [Hashtable]
        $ZipFileStructure
    )

    foreach ($key in $ZipFileStructure.Keys)
    {
        if ($ZipFileStructure[$key] -is [Hashtable])
        {
            $newDirectoryPath = Join-Path -Path $ParentPath -ChildPath $key
            New-Item -Path $newDirectoryPath -ItemType Directory | Out-Null
            ConvertTo-FileStructure -ParentPath $newDirectoryPath -ZipFileStructure $ZipFileStructure[$key]
        }
        elseif ($ZipFileStructure[$key] -is [String])
        {
            $newFilePath = Join-Path -Path $ParentPath -ChildPath $key
            New-Item -Path $newFilePath -ItemType File | Out-Null
            Set-Content -Path $newFilePath -Value $ZipFileStructure[$key]
        }
        else
        {
            throw "Zip file structure must be made of strings and Hashtables. Found a different type."
        }
    }
}

<#
    .SYNOPSIS
    Creates a new zip file with the given name from a hashtable describing the file structure.
 
    .PARAMETER Name
    The name of the zip file to create
 
    .PARAMETER ZipFileStructure
    The hashtable defining the zip file structure
    Hashtables are directories.
    Strings are files with the key as the name of the file and the value as the contents.
#>

function New-ZipFileFromHashtable
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $ParentPath,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $Name,

        [Parameter(Mandatory = $true)]
        [ValidateNotNull()]
        [Hashtable]
        $ZipFileStructure
    )

    $expandedZipPath = Join-Path -Path $ParentPath -ChildPath $Name
    New-Item -Path $expandedZipPath -ItemType Directory | Out-Null

    ConvertTo-FileStructure -ParentPath $expandedZipPath -ZipFileStructure $ZipFileStructure

    $compressedZipPath = Join-Path -Path $ParentPath -ChildPath "$Name.zip"
    [System.IO.Compression.ZipFile]::CreateFromDirectory($expandedZipPath, $compressedZipPath, 'NoCompression', $false)
    return $compressedZipPath
}

<#
    .SYNOPSIS
        Tests if two file structures are the same.
        Uses Pester.
 
    .PARAMETER SourcePath
        The path to the source file to test against.
 
    .PARAMETER DestinationPath
        The path the to destination file to test.
 
    .PARAMETER CheckLastWriteTime
        Indicates that the last write times should match.
 
    .PARAMETER CheckCreationTime
        Indicates that the creation times should match.
 
    .PARAMETER CheckContents
        Indicates that the contents of the file structures should match.
#>

function Test-FileStructuresMatch
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String] $SourcePath,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String] $DestinationPath,

        [Switch] $CheckLastWriteTime,

        [Switch] $CheckCreationTime,

        [Switch] $CheckContents
    )

    $sourcePathLength = $SourcePath.Length
    $destinationPathLength = $DestinationPath.Length

    $destinationContents = @{}
    $destinationChildItems = Get-ChildItem -Path $DestinationPath -Recurse

    foreach ($destinationChildItem in $destinationChildItems)
    {
        $destinationContents[$destinationChildItem.FullName.Substring($destinationPathLength)] = $destinationChildItem
    }

    $sourceChildItems = Get-ChildItem -Path $SourcePath -Recurse

    foreach ($sourceChildItem in $sourceChildItems)
    {
        $sourceChildItemName = $sourceChildItem.FullName.Substring($sourcePathLength)
        $destinationChildItem = $destinationContents[$sourceChildItemName]

        $destinationChildItem | Should Not Be $null
        $destinationChildItem.GetType() | Should Be $sourceChildItem.GetType()

        if ($destinationChildItem.GetType() -eq [System.IO.FileInfo])
        {
            if ($CheckLastWriteTime)
            {
                $sourceChildItem.LastWriteTime | Should Be $destinationChildItem.LastWriteTime
            }

            if ($CheckCreationTime)
            {
                $sourceChildItem.CreationTime | Should Be $destinationChildItem.CreationTime
            }

            if ($CheckContents)
            {
                $sourceStream = $null
                $destinationStream = $null

                try
                {
                    $sourceStream = $sourceChildItem.Open()
                    $destinationStream = $destinationChildItem.Open()

                    $sourceFileContents = $sourceStream.Read()
                    $destinationFileContents = $destinationStream.Read()

                    $sourceFileContentsLength = $sourceFileContents.Length

                    $destinationFileContents.Length | Should Be $sourceFileContentsLength

                    for ($fileIndex = 0; $fileIndex -lt $sourceFileContentsLength; $fileIndex++)
                    {
                        $sourceFileContents[$fileIndex] | Should Be $destinationFileContents[$fileIndex]
                    }
                }
                finally
                {
                    if ($null -ne $sourceStream)
                    {
                        $sourceStream.Dispose()
                    }

                    if ($null -ne $destinationStream)
                    {
                        $destinationStream.Dispose()
                    }
                }
            }
        }
    }
}

Export-ModuleMember -Function `
    New-ZipFileFromHashtable, `
    Test-FileStructuresMatch