Skip to content

Commit fcaaa8e

Browse files
committed
added custom assertions for testing zip archives
1 parent b5e23c2 commit fcaaa8e

File tree

4 files changed

+190
-107
lines changed

4 files changed

+190
-107
lines changed
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
function Should-BeZipArchiveOnlyContaining {
2+
<#
3+
.SYNOPSIS
4+
Checks if a zip archive contains the entries $ExpectedValue
5+
.EXAMPLE
6+
"C:\Users\<user>\archive.zip" | Should -BeZipArchiveContaining @("file1.txt")
7+
8+
Checks if archive.zip only contains file1.txt
9+
#>
10+
11+
[CmdletBinding()]
12+
Param (
13+
[string] $ActualValue,
14+
[string[]] $ExpectedValue,
15+
[switch] $Negate,
16+
[string] $Because,
17+
[switch] $LiteralPath,
18+
$CallerSessionState
19+
)
20+
21+
# ActualValue is supposed to be a path to an archive
22+
# It could be a path to a custom PSDrive, so it needes to be converted
23+
if ($LiteralPath) {
24+
$ActualValue = Convert-Path -LiteralPath $ActualValue
25+
} else {
26+
$ActualValue = Convert-Path -Path $ActualValue
27+
}
28+
29+
30+
# Ensure ActualValue is a valid path
31+
if ($LiteralPath) {
32+
$testPathResult = Test-Path -LiteralPath $ActualValue
33+
} else {
34+
$testPathResult = Test-Path -Path $ActualValue
35+
}
36+
37+
# Don't continue processing if ActualValue is not an actual path
38+
# Determine if the assertion succeeded or failed and then return
39+
if (-not $testPathResult) {
40+
$succeeded = $Negate
41+
if (-not $succeeded) {
42+
$failureMessage = "The path ${ActualValue} does not exist"
43+
}
44+
return [pscustomobject]@{
45+
Succeeded = $succeeded
46+
FailureMessage = $failureMessage
47+
}
48+
}
49+
50+
# Get 7-zip to list the contents of the archive
51+
$output = 7z.exe l $ActualValue -ba
52+
53+
# Check if the output is null
54+
if ($null -eq $output) {
55+
if ($null -eq $ExpectedValue -or $ExpectedValue.Length -eq 0) {
56+
$succeeded = -not $Negate
57+
} else {
58+
$succeeded = $Negate
59+
}
60+
61+
if (-not $succeeded) {
62+
$failureMessage = "Archive {0} contains nothing, but it was expected to contain something"
63+
}
64+
65+
return [pscustomobject]@{
66+
Succeeded = $succeeded
67+
FailureMessage = $failureMessage
68+
}
69+
}
70+
71+
# Filter the output line by line
72+
$lines = $output -split [System.Environment]::NewLine
73+
74+
# Stores the entry names
75+
$entryNames = @()
76+
77+
# Go through each line and split it by whitespace
78+
foreach ($line in $lines) {
79+
$lineComponents = $line -split " +"
80+
81+
# Example of some lines:
82+
#2022-08-05 15:54:04 D.... 0 0 SourceDir
83+
#2022-08-05 15:54:04 ..... 11 11 SourceDir/Sample-1.txt
84+
85+
# First component is date
86+
# 2nd component is time
87+
# 3rd componnent is attributes
88+
# 4th component is size
89+
# 5th component is compressed size
90+
# 6th component is entry name
91+
92+
$entryName = $lineComponents[$lineComponents.Length - 1]
93+
94+
# Since 7zip does not show trailing forwardslash for directories, we need to check the attributes to see if it starts with 'D'
95+
# If so, it means the entry is a directory and we should append a forwardslash to the entry name
96+
97+
if ($lineComponents[2].StartsWith('D')) {
98+
$entryName += '/'
99+
}
100+
101+
# Replace backslashes to forwardslashes
102+
$dirSeperatorChar = [System.IO.Path]::DirectorySeparatorChar
103+
$entryName = $entryName.Replace($dirSeperatorChar, "/")
104+
105+
$entryNames += $entryName
106+
}
107+
108+
$itemsNotInArchive = @()
109+
110+
# Go through each item in ExpectedValue and ensure it is in entryNames
111+
foreach ($expectedItem in $ExpectedValue) {
112+
if ($entryNames -notcontains $expectedItem) {
113+
$itemsNotInArchive += $expectedItem
114+
}
115+
}
116+
117+
if ($itemsNotInArchive.Length -gt 0 -and -not $Negate) {
118+
# Create a comma-seperated string from $itemsNotInEnryName
119+
$commaSeperatedItemsNotInArchive = $itemsNotInArchive -join ","
120+
$failureMessage = "'$ActualValue' does not contain $commaSeperatedItemsNotInArchive $(if($Because) { "because $Because"})."
121+
$succeeded = $false
122+
}
123+
124+
# Ensure the length of $entryNames is equal to that of $ExpectedValue
125+
if ($null -eq $succeeded -and $entryNames.Length -ne $ExpectedValue.Length -and -not $Negate) {
126+
$failureMessage = "${ActualValue} does not contain the same number of items as ${ExpectedValue -join ""} (expected ${ExpectedValue.Length} entries but found ${entryNames.Length}) $(if($Because) { "because $Because"})."
127+
$succeeded = $false
128+
}
129+
130+
if ($null -eq $succeeded) {
131+
$succeeded = -not $Negate
132+
if (-not $succeeded) {
133+
$failureMessage = "Expected ${ActualValue} to not contain the entries ${ExpectedValue -join ""} only $(if($Because) { "because $Because"})."
134+
}
135+
}
136+
137+
$ObjProperties = @{
138+
Succeeded = $succeeded
139+
FailureMessage = $failureMessage
140+
}
141+
return New-Object PSObject -Property $ObjProperties
142+
}
143+
144+
Add-ShouldOperator -Name BeZipArchiveOnlyContaining -InternalName 'Should-BeZipArchiveOnlyContaining' -Test ${function:Should-BeZipArchiveOnlyContaining}

Tests/Compress-Archive.Tests.ps1

Lines changed: 31 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,17 @@
11
# Copyright (c) Microsoft Corporation.
22
# Licensed under the MIT License.
33

4+
BeforeDiscovery {
5+
# Loads and registers custom assertion. Ignores usage of unapproved verb with -DisableNameChecking
6+
Import-Module "$PSScriptRoot/Assertions/Should-BeZipArchiveOnlyContaining.psm1" -DisableNameChecking
7+
}
8+
49
Describe("Microsoft.PowerShell.Archive tests") {
510
BeforeAll {
611

712
$originalProgressPref = $ProgressPreference
813
$ProgressPreference = "SilentlyContinue"
914
$originalPSModulePath = $env:PSModulePath
10-
11-
# Add compression assemblies
12-
function Add-CompressionAssemblies {
13-
Add-Type -AssemblyName System.IO.Compression
14-
if ($psedition -eq "Core")
15-
{
16-
Add-Type -AssemblyName System.IO.Compression.ZipFile
17-
}
18-
else
19-
{
20-
Add-Type -AssemblyName System.IO.Compression.FileSystem
21-
}
22-
}
23-
24-
Add-CompressionAssemblies
25-
26-
# Used for validating an archive's contents
27-
function Test-ZipArchive {
28-
param
29-
(
30-
[string] $archivePath,
31-
[string[]] $expectedEntries,
32-
[switch] $Literal
33-
)
34-
35-
try
36-
{
37-
if ($Literal) {
38-
$archivePath = Convert-Path -LiteralPath $archivePath
39-
} else {
40-
$archivePath = Convert-Path -Path $archivePath
41-
}
42-
43-
44-
$archiveFileStreamArgs = @($archivePath, [System.IO.FileMode]::Open)
45-
$archiveFileStream = New-Object -TypeName System.IO.FileStream -ArgumentList $archiveFileStreamArgs
46-
47-
$zipArchiveArgs = @($archiveFileStream, [System.IO.Compression.ZipArchiveMode]::Read, $false)
48-
$zipArchive = New-Object -TypeName System.IO.Compression.ZipArchive -ArgumentList $zipArchiveArgs
49-
50-
$actualEntryCount = $zipArchive.Entries.Count
51-
$actualEntryCount | Should -Be $expectedEntries.Length
52-
53-
# Get a list of entry names in the zip archive
54-
$archiveEntries = @()
55-
ForEach ($archiveEntry in $zipArchive.Entries) {
56-
$archiveEntries += $archiveEntry.FullName
57-
}
58-
59-
# Ensure each entry in the archive is in the list of expected entries
60-
ForEach ($expectedEntry in $expectedEntries) {
61-
$expectedEntry | Should -BeIn $archiveEntries
62-
}
63-
64-
}
65-
finally
66-
{
67-
if ($null -ne $zipArchive) { $zipArchive.Dispose()}
68-
if ($null -ne $archiveFileStream) { $archiveFileStream.Dispose() }
69-
}
70-
}
71-
72-
# This function gets a list of a directories descendants formatted as archive entries
73-
function Get-Descendants {
74-
param (
75-
[string] $Path
76-
)
77-
78-
79-
# Get the folder name
80-
$folderName = Split-Path -Path $Path -Leaf
81-
82-
# Get descendents
83-
$descendants = Get-ChildItem -Path $Path -Recurse -Name
84-
85-
$output = @()
86-
87-
# Prefix each descendant name with folder name
88-
foreach ($name in $descendants) {
89-
$output += ($folderName + '/' + $name).Replace([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar)
90-
}
91-
92-
return $output
93-
}
9415
}
9516

9617
AfterAll {
@@ -325,12 +246,11 @@
325246
}
326247
}
327248

328-
It "-WriteMode Create works" -Tag this2 {
249+
It "-WriteMode Create works" -Tag td1 {
329250
$sourcePath = "TestDrive:/SourceDir"
330251
$destinationPath = "TestDrive:/archive1.zip"
331252
Compress-Archive -Path $sourcePath -DestinationPath $destinationPath
332-
Test-Path $destinationPath
333-
Test-ZipArchive $destinationPath @('SourceDir/', 'SourceDir/Sample-1.txt')
253+
$destinationPath | Should -BeZipArchiveOnlyContaining @('SourceDir/', 'SourceDir/Sample-1.txt')
334254
}
335255
}
336256

@@ -354,26 +274,21 @@
354274
$sourcePath = "TestDrive:/SourceDir/ChildDir-1/Sample-2.txt"
355275
$destinationPath = "TestDrive:/archive1.zip"
356276
Compress-Archive -Path $sourcePath -DestinationPath $destinationPath
357-
$destinationPath | Should -Exist
358-
Test-ZipArchive $destinationPath @('Sample-2.txt')
277+
$destinationPath | Should -BeZipArchiveOnlyContaining @('Sample-2.txt')
359278
}
360279

361280
It "Validate that an empty folder can be compressed" {
362281
$sourcePath = "TestDrive:/EmptyDir"
363282
$destinationPath = "TestDrive:/archive2.zip"
364283
Compress-Archive -Path $sourcePath -DestinationPath $destinationPath
365-
$destinationPath | Should -Exist
366-
Test-ZipArchive $destinationPath @('EmptyDir/')
284+
$destinationPath | Should -BeZipArchiveOnlyContaining @('EmptyDir/')
367285
}
368286

369287
It "Validate a folder containing files, non-empty folders, and empty folders can be compressed" {
370288
$sourcePath = "TestDrive:/SourceDir"
371289
$destinationPath = "TestDrive:/archive3.zip"
372290
Compress-Archive -Path $sourcePath -DestinationPath $destinationPath
373-
$destinationPath | Should -Exist
374-
$contents = Get-Descendants -Path $sourcePath
375-
$contents += "SourceDir/"
376-
Test-ZipArchive $destinationPath $contents
291+
$destinationPath | Should -BeZipArchiveOnlyContaining @('SourceDir/', 'SourceDir/ChildDir-1/', 'SourceDir/ChildDir-2/', 'SourceDir/ChildEmptyDir/', 'SourceDir/Sample-1.txt', 'SourceDir/ChildDir-1/Sample-2.txt', 'SourceDir/ChildDir-2/Sample-3.txt')
377292
}
378293
}
379294

@@ -515,27 +430,27 @@
515430
$sourcePath = "TestDrive:/SourceDir"
516431
$destinationPath = "TestDrive:/EmptyDirectory"
517432

518-
(Get-Item $destinationPath) -is [System.IO.DirectoryInfo] | Should -Be $true
433+
# Ensure $destinationPath is a directory
434+
Test-Path $destinationPath -PathType Container | Should -Be $true
435+
519436
Compress-Archive -Path $sourcePath -DestinationPath $destinationPath -WriteMode Overwrite
520437

521-
# Ensure $destiationPath is now a file
522-
$destinationPathInfo = Get-Item $destinationPath
523-
$destinationPathInfo -is [System.IO.DirectoryInfo] | Should -Be $false
524-
$destinationPathInfo -is [System.IO.FileInfo] | Should -Be $true
438+
# Ensure $destinationPath is now a file
439+
Test-Path $destinationPath -PathType Leaf | Should -Be $true
525440
}
526441

527442
It "Overwrites an archive that already exists" {
528443
$destinationPath = "TestDrive:/archive.zip"
529444

530-
# Get the entries of the original zip archive
531-
Test-ZipArchive $destinationPath @("Sample-1.txt")
445+
# Ensure the original archive contains Sample-1.txt
446+
$destinationPath | Should -BeZipArchiveOnlyContaining @("Sample-1.txt")
532447

533448
# Overwrite the archive
534449
$sourcePath = "TestDrive:/Sample-2.txt"
535450
Compress-Archive -Path $sourcePath -DestinationPath "TestDrive:/archive.zip" -WriteMode Overwrite
536451

537452
# Ensure the original entries and different than the new entries
538-
Test-ZipArchive $destinationPath @("Sample-2.txt")
453+
$destinationPath | Should -BeZipArchiveOnlyContaining @("Sample-2.txt")
539454
}
540455
}
541456

@@ -620,9 +535,20 @@
620535
$destinationPath = "TestDrive:/archive[2.zip"
621536

622537
Compress-Archive -Path $sourcePath -DestinationPath $destinationPath
623-
Test-Path -LiteralPath $destinationPath | Should -Be $true
624-
Test-ZipArchive $destinationPath @("SourceDir/", "SourceDir/Sample-1.txt") -Literal
538+
$destinationPath | Should -BeZipArchiveOnlyContaining @("SourceDir/", "SourceDir/Sample-1.txt") -LiteralPath
625539
Remove-Item -LiteralPath $destinationPath
626540
}
627541
}
542+
543+
Context "test" -Tag lol {
544+
BeforeAll {
545+
$content = "Some Data"
546+
$content | Out-File -FilePath TestDrive:/Sample-1.txt
547+
Compress-Archive -Path TestDrive:/Sample-1.txt -DestinationPath TestDrive:/archive1.zip
548+
}
549+
550+
It "test custom assetion" {
551+
"${TestDrive}/archive1.zip" | Should -BeZipArchiveOnlyContaining @("Sample-1.txt")
552+
}
553+
}
628554
}

Tests/Install-7Zip.ps1

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
# This script will download and install 7-zip
5+
6+
function Install-7zip {
7+
8+
Param (
9+
[string] OS
10+
)
11+
12+
13+
}

src/ZipArchive.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ void IArchive.AddFileSystemEntry(ArchiveAddition addition)
6363
if (entryInArchive == null)
6464
{
6565
// Ensure addition.entryName has '/' at the end
66-
if (!addition.EntryName.EndsWith(ZipArchiveDirectoryPathTerminator))
66+
if (!entryName.EndsWith(ZipArchiveDirectoryPathTerminator))
6767
{
68-
addition.EntryName += ZipArchiveDirectoryPathTerminator;
68+
entryName += ZipArchiveDirectoryPathTerminator;
6969
}
7070

7171
_zipArchive.CreateEntry(entryName);

0 commit comments

Comments
 (0)