Port improvements to cloning of DscResource.Tests

This commit is contained in:
Mike Hendrickson
2019-06-03 10:30:44 -07:00
parent 0d429cd06d
commit 6bbd5e23ac
2 changed files with 252 additions and 83 deletions
+8
View File
@@ -2,6 +2,14 @@
## Unreleased
* Ports fixes for the following issues:
[Issue #505](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/505)
[Issue #590](https://github.com/PowerShell/xPSDesiredStateConfiguration/issues/590)
Changes to test helper Enter-DscResourceTestEnvironment so that it only
updates DSCResource.Tests when it is longer than 120 minutes since
it was last pulled. This is to improve performance of test execution
and reduce the likelihood of connectivity issues caused by inability to
pull DSCResource.Tests.
* Fixes issue where MsiPackage Integration tests fail if the test HttpListener
fails to start. Moves the test HttpListener objects to dynamically assigned,
higher numbered ports to avoid conflicts with other services, and also checks
+244 -83
View File
@@ -10,6 +10,18 @@ Set-StrictMode -Version 'Latest'
#>
$script:appVeyorAdministratorCredential = $null
<#
This is the name of the magic file that will be written to the .git folder
in DSCResource.Tests to determine the last time it was updated.
#>
$script:dscResourceTestsMagicFile = 'DSC_LAST_FETCH'
<#
This is the number of minutes after which the DSCResource.Tests
will be updated.
#>
$script:dscResourceTestsRefreshAfterMinutes = 120
<#
String data for unit test names to be used with the generic test functions.
Maps command names to the appropriate test names to insert when checking that
@@ -65,24 +77,26 @@ Test-Path = test that the path {0} exists
#>
function Get-TestName
{
[OutputType([String])]
[OutputType([System.String])]
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[String]
[System.String]
$Command,
[Boolean]
[Parameter()]
[System.Boolean]
$IsCalled = $true,
[String]
[Parameter()]
[System.String]
$Custom = ''
)
$testName = ''
if (-not [String]::IsNullOrEmpty($Custom))
if (-not [System.String]::IsNullOrEmpty($Custom))
{
$testName = ($testStrings.$Command -f $Custom)
}
@@ -115,7 +129,8 @@ function Invoke-ExpectedMocksAreCalledTest
[CmdletBinding()]
param
(
[Hashtable[]]
[Parameter()]
[System.Collections.Hashtable[]]
$MocksCalled
)
@@ -171,41 +186,46 @@ function Invoke-ExpectedMocksAreCalledTest
The string that should be used to create the name of the test that checks for
the correct error being thrown.
#>
function Invoke-GenericUnitTest {
function Invoke-GenericUnitTest
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[ScriptBlock]
[System.Management.Automation.ScriptBlock]
$Function,
[Parameter(Mandatory = $true)]
[Hashtable]
[System.Collections.Hashtable]
$FunctionParameters,
[Hashtable[]]
[Parameter()]
[System.Collections.Hashtable[]]
$MocksCalled,
[Boolean]
[Parameter()]
[System.Boolean]
$ShouldThrow = $false,
[String]
[Parameter()]
[System.String]
$ErrorMessage = '',
[String]
[Parameter()]
[System.String]
$ErrorTestName = ''
)
if ($ShouldThrow)
{
It "Should throw an error for $ErrorTestName" {
{ $null = $($Function.Invoke($FunctionParameters)) } | Should Throw $ErrorMessage
{ $null = $($Function.Invoke($FunctionParameters)) } | Should -Throw -ExpectedMessage $ErrorMessage
}
}
else
{
It 'Should not throw' {
{ $null = $($Function.Invoke($FunctionParameters)) } | Should Not Throw
{ $null = $($Function.Invoke($FunctionParameters)) } | Should -Not -Throw
}
}
@@ -239,19 +259,20 @@ function Invoke-GetTargetResourceUnitTest
param
(
[Parameter(Mandatory = $true)]
[Hashtable]
[System.Collections.Hashtable]
$GetTargetResourceParameters,
[Hashtable[]]
[Parameter()]
[System.Collections.Hashtable[]]
$MocksCalled,
[Parameter(Mandatory = $true)]
[Hashtable]
[System.Collections.Hashtable]
$ExpectedReturnValue
)
It 'Should not throw' {
{ $null = Get-TargetResource @GetTargetResourceParameters } | Should Not Throw
{ $null = Get-TargetResource @GetTargetResourceParameters } | Should -Not -Throw
}
Invoke-ExpectedMocksAreCalledTest -MocksCalled $MocksCalled
@@ -259,17 +280,17 @@ function Invoke-GetTargetResourceUnitTest
$getTargetResourceResult = Get-TargetResource @GetTargetResourceParameters
It 'Should return a Hashtable' {
$getTargetResourceResult -is [Hashtable] | Should Be $true
$getTargetResourceResult -is [System.Collections.Hashtable] | Should -Be $true
}
It "Should return a Hashtable with $($ExpectedReturnValue.Keys.Count) properties" {
$getTargetResourceResult.Keys.Count | Should Be $ExpectedReturnValue.Keys.Count
$getTargetResourceResult.Keys.Count | Should -Be $ExpectedReturnValue.Keys.Count
}
foreach ($key in $ExpectedReturnValue.Keys)
{
It "Should return a Hashtable with the $key property as $($ExpectedReturnValue.$key)" {
$getTargetResourceResult.$key | Should Be $ExpectedReturnValue.$key
$getTargetResourceResult.$key | Should -Be $ExpectedReturnValue.$key
}
}
}
@@ -302,37 +323,42 @@ function Invoke-GetTargetResourceUnitTest
The string that should be used to create the name of the test that checks for
the correct error being thrown.
#>
function Invoke-SetTargetResourceUnitTest {
function Invoke-SetTargetResourceUnitTest
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[Hashtable]
[System.Collections.Hashtable]
$SetTargetResourceParameters,
[Hashtable[]]
[Parameter()]
[System.Collections.Hashtable[]]
$MocksCalled,
[Boolean]
[Parameter()]
[System.Boolean]
$ShouldThrow = $false,
[String]
[Parameter()]
[System.String]
$ErrorMessage = '',
[String]
[Parameter()]
[System.String]
$ErrorTestName = ''
)
if ($ShouldThrow)
{
It "Should throw an error for $ErrorTestName" {
{ $null = Set-TargetResource @SetTargetResourceParameters } | Should Throw $ErrorMessage
{ $null = Set-TargetResource @SetTargetResourceParameters } | Should -Throw -ExpectedMessage $ErrorMessage
}
}
else
{
It 'Should not throw' {
{ $null = Set-TargetResource @SetTargetResourceParameters } | Should Not Throw
{ $null = Set-TargetResource @SetTargetResourceParameters } | Should -Not -Throw
}
}
@@ -366,19 +392,20 @@ function Invoke-TestTargetResourceUnitTest
param
(
[Parameter(Mandatory = $true)]
[Hashtable]
[System.Collections.Hashtable]
$TestTargetResourceParameters,
[Hashtable[]]
[Parameter()]
[System.Collections.Hashtable[]]
$MocksCalled,
[Parameter(Mandatory = $true)]
[Boolean]
[System.Boolean]
$ExpectedReturnValue
)
It 'Should not throw' {
{ $null = Test-TargetResource @TestTargetResourceParameters } | Should Not Throw
{ $null = Test-TargetResource @TestTargetResourceParameters } | Should -Not -Throw
}
Invoke-ExpectedMocksAreCalledTest -MocksCalled $MocksCalled
@@ -386,10 +413,12 @@ function Invoke-TestTargetResourceUnitTest
$testTargetResourceResult = Test-TargetResource @TestTargetResourceParameters
It "Should return $ExpectedReturnValue" {
$testTargetResourceResult | Should Be $ExpectedReturnValue
$testTargetResourceResult | Should -Be $ExpectedReturnValue
}
}
<#
.SYNOPSIS
Tests that the Get-TargetResource method of a DSC Resource is not null, can be converted to a hashtable, and has the correct properties.
@@ -408,16 +437,17 @@ function Test-GetTargetResourceResult
(
[Parameter(Mandatory = $true)]
[ValidateNotNull()]
[Hashtable]
[System.Collections.Hashtable]
$GetTargetResourceResult,
[String[]]
[Parameter()]
[System.String[]]
$GetTargetResourceResultProperties
)
foreach ($property in $GetTargetResourceResultProperties)
{
$GetTargetResourceResult[$property] | Should Not Be $null
$GetTargetResourceResult[$property] | Should -Not -Be $null
}
}
@@ -430,13 +460,13 @@ function Test-GetTargetResourceResult
#>
function Test-IsLocalMachine
{
[OutputType([Boolean])]
[OutputType([System.Boolean])]
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String]
[System.String]
$Scope
)
@@ -467,7 +497,6 @@ function Test-IsLocalMachine
NOTE: This is likely overkill; consider removing it.
#>
$networkAdapters = @(Get-CimInstance Win32_NetworkAdapterConfiguration)
foreach ($networkAdapter in $networkAdapters)
{
if ($null -ne $networkAdapter.IPAddress)
@@ -500,23 +529,24 @@ function Test-IsLocalMachine
#>
function Wait-ScriptBlockReturnTrue
{
[OutputType([Boolean])]
[OutputType([System.Boolean])]
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[ScriptBlock]
[System.Management.Automation.ScriptBlock]
$ScriptBlock,
[Int]
[Parameter()]
[System.Int32]
$TimeoutSeconds = 5
)
$startTime = [DateTime]::Now
$startTime = [System.DateTime]::Now
$invokeScriptBlockResult = $false
while (-not $invokeScriptBlockResult -and (([DateTime]::Now - $startTime).TotalSeconds -lt $TimeoutSeconds))
while (-not $invokeScriptBlockResult -and (([System.DateTime]::Now - $startTime).TotalSeconds -lt $TimeoutSeconds))
{
$invokeScriptBlockResult = $ScriptBlock.Invoke()
Start-Sleep -Seconds 1
@@ -534,12 +564,12 @@ function Wait-ScriptBlockReturnTrue
#>
function Test-IsFileLocked
{
[OutputType([Boolean])]
[OutputType([System.Boolean])]
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[String]
[System.String]
$Path
)
@@ -550,7 +580,7 @@ function Test-IsFileLocked
try
{
$content = Get-Content -Path $Path
Get-Content -Path $Path | Out-Null
return $false
}
catch
@@ -577,15 +607,16 @@ function Test-IsFileLocked
#>
function Test-SetTargetResourceWithWhatIf
{
[OutputType([Boolean])]
[OutputType([System.Boolean])]
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[Hashtable]
[System.Collections.Hashtable]
$Parameters,
[String[]]
[Parameter()]
[System.String[]]
$ExpectedOutput
)
@@ -609,7 +640,7 @@ function Test-SetTargetResourceWithWhatIf
Wait-ScriptBlockReturnTrue -ScriptBlock {-not (Test-IsFileLocked -Path $transcriptPath)}
$transcriptContent = Get-Content -Path $transcriptPath -Raw
$transcriptContent | Should Not Be $null
$transcriptContent | Should -Not -Be $null
$regexString = '\*+[^\*]*\*+'
@@ -627,13 +658,13 @@ function Test-SetTargetResourceWithWhatIf
if ($null -eq $ExpectedOutput -or $ExpectedOutput.Count -eq 0)
{
[String]::IsNullOrEmpty($transcriptContent) | Should Be $true
[System.String]::IsNullOrEmpty($transcriptContent) | Should -Be $true
}
else
{
foreach ($expectedOutputPiece in $ExpectedOutput)
{
$transcriptContent.Contains($expectedOutputPiece) | Should Be $true
$transcriptContent.Contains($expectedOutputPiece) | Should -Be $true
}
}
}
@@ -707,51 +738,33 @@ function Get-AppVeyorAdministratorCredential
#>
function Enter-DscResourceTestEnvironment
{
[OutputType([Hashtable])]
[OutputType([System.Collections.Hashtable])]
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String]
[System.String]
$DscResourceModuleName,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String]
[System.String]
$DscResourceName,
[Parameter(Mandatory = $true)]
[ValidateSet('Unit', 'Integration')]
[String]
[System.String]
$TestType
)
$testsFolderPath = Split-Path -Path $PSScriptRoot -Parent
$moduleRootPath = Split-Path -Path $testsFolderPath -Parent
$moduleRootPath = Split-Path -Path $PSScriptRoot -Parent
$dscResourceTestsPath = Join-Path -Path $moduleRootPath -ChildPath 'DSCResource.Tests'
$testHelperFilePath = Join-Path -Path $dscResourceTestsPath -ChildPath 'TestHelper.psm1'
if (-not (Test-Path -Path $dscResourceTestsPath))
if (Test-DscResourceTestsNeedsInstallOrUpdate)
{
Push-Location $moduleRootPath
git clone 'https://github.com/PowerShell/DscResource.Tests' --quiet
Pop-Location
}
else
{
$gitInstalled = $null -ne (Get-Command -Name 'git' -ErrorAction 'SilentlyContinue')
if ($gitInstalled)
{
Push-Location $dscResourceTestsPath
git pull origin dev --quiet
Pop-Location
}
else
{
Write-Verbose -Message 'Git not installed. Leaving current DSCResource.Tests as is.'
}
Install-DscResourceTestsModule
}
Import-Module -Name $testHelperFilePath
@@ -762,6 +775,155 @@ function Enter-DscResourceTestEnvironment
-TestType $TestType
}
<#
.SYNOPSIS
Tests to see if DSCResource.Tests needs to be downloaded
or updated.
.DESCRIPTION
This function returns true if the DSCResource.Tests
content needs to be downloaded or updated.
A magic file in the .Git folder of DSCResource.Tests
is used to determine if the repository needs to be
updated.
If the last write time of the magic file is over a
specified number of minutes old then this will cause
the function to return true.
.PARAMETER RefreshAfterMinutes
The number of minutes old the magic file should be
before requiring an update. Defaults to the value
defined in $script:dscResourceTestsRefreshAfterMinutes
#>
function Test-DscResourceTestsNeedsInstallOrUpdate
{
[OutputType([System.Boolean])]
[CmdletBinding()]
param
(
[Parameter()]
[System.Int32]
$RefreshAfterMinutes = $script:dscResourceTestsRefreshAfterMinutes
)
$moduleRootPath = Split-Path -Path $PSScriptRoot -Parent
$dscResourceTestsPath = Join-Path -Path $moduleRootPath -ChildPath 'DSCResource.Tests'
if (Test-Path -Path $dscResourceTestsPath)
{
$magicFilePath = Get-DscResourceTestsMagicFilePath -DscResourceTestsPath $DscResourceTestsPath
if (Test-Path -Path $magicFilePath)
{
$magicFileLastWriteTime = (Get-Item -Path $magicFilePath).LastWriteTime
$magicFileAge = New-TimeSpan -End (Get-Date) -Start $magicFileLastWriteTime
if ($magicFileAge.Minutes -lt $RefreshAfterMinutes)
{
Write-Debug -Message ('DSCResource.Tests was last updated {0} minutes ago. Update not required.' -f $magicFileAge.Minutes)
return $false
}
else
{
Write-Verbose -Message ('DSCResource.Tests was last updated {0} minutes ago. Update required.' -f $magicFileAge.Minutes) -Verbose
}
}
}
return $true
}
<#
.SYNOPSIS
Installs the DSCResource.Tests content.
.DESCRIPTION
This function uses Git to install or update the
DSCResource.Tests repository.
It will then create or update the magic file in
the .git folder in the DSCResource.Tests folder.
If Git is not installed and the DSCResource.Tests
folder is not available then an exception will
be thrown.
If the DSCResource.Tests folder does exist but
Git is not installed then a warning will be
displayed and the repository will not be pulled.
#>
function Install-DscResourceTestsModule
{
[CmdletBinding()]
param
(
)
$moduleRootPath = Split-Path -Path $PSScriptRoot -Parent
$dscResourceTestsPath = Join-Path -Path $moduleRootPath -ChildPath 'DSCResource.Tests'
$gitInstalled = $null -ne (Get-Command -Name 'git' -ErrorAction 'SilentlyContinue')
$writeMagicFile = $false
if (Test-Path -Path $dscResourceTestsPath)
{
if ($gitInstalled)
{
Push-Location -Path $dscResourceTestsPath
Write-Verbose -Message 'Updating DSCResource.Tests.' -Verbose
& git @('pull','origin','dev','--quiet')
$writeMagicFile = $true
Pop-Location
}
else
{
Write-Warning -Message 'Git not installed. DSCResource.Tests will not be updated.'
}
}
else
{
if (-not $gitInstalled)
{
throw 'Git not installed. Can not pull DSCResource.Tests.'
}
Push-Location -Path $moduleRootPath
Write-Verbose -Message 'Cloning DSCResource.Tests.' -Verbose
& git @('clone','https://github.com/PowerShell/DscResource.Tests','--quiet')
$writeMagicFile = $true
Pop-Location
}
if ($writeMagicFile)
{
# Write the magic file
$magicFilePath = Get-DscResourceTestsMagicFilePath -DscResourceTestsPath $DscResourceTestsPath
$null = Set-Content -Path $magicFilePath -Value (Get-Date) -Force
}
}
<#
.SYNOPSIS
Gets the full path of the magic file used to
determine the last date/time the DSCResource.Tests
folder was updated.
.PARAMETER DscResourceTestsPath
The path to the folder that contains DSCResource.Tests.
#>
function Get-DscResourceTestsMagicFilePath
{
param
(
[Parameter(Mandatory = $true)]
[System.String]
$DscResourceTestsPath
)
return $DscResourceTestsPath | Join-Path -ChildPath '.git' | Join-Path -ChildPath $script:dscResourceTestsMagicFile
}
<#
.SYNOPSIS
Exits the specified DSC Resource test environment.
@@ -776,12 +938,11 @@ function Exit-DscResourceTestEnvironment
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[Hashtable]
[System.Collections.Hashtable]
$TestEnvironment
)
$testsFolderPath = Split-Path -Path $PSScriptRoot -Parent
$moduleRootPath = Split-Path -Path $testsFolderPath -Parent
$moduleRootPath = Split-Path -Path $PSScriptRoot -Parent
$dscResourceTestsPath = Join-Path -Path $moduleRootPath -ChildPath 'DSCResource.Tests'
$testHelperFilePath = Join-Path -Path $dscResourceTestsPath -ChildPath 'TestHelper.psm1'