Tests/Unit/MSFT_xArchive.TestHelper.psm1

<#
    .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 -LiteralPath $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