tasks/Invoke-Pester.pester.build.ps1
Param ( # Project path [string]$ProjectPath = (property ProjectPath $BuildRoot), # Base directory of all output (default to 'output') [string]$OutputDirectory = (property OutputDirectory (Join-Path $BuildRoot 'output')), [string] $ProjectName = (property ProjectName $( (Get-ChildItem $BuildRoot\*\*.psd1 | Where-Object { ($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and $(try { Test-ModuleManifest $_.FullName -ErrorAction Stop }catch{$false}) } ).BaseName ) ), [string] $ModuleVersion = (property ModuleVersion $( try { (gitversion | ConvertFrom-Json -ErrorAction Stop).InformationalVersion } catch { Write-Verbose "Error attempting to use GitVersion $($_)" '' } )), [string]$PesterOutputFolder = (property PesterOutputFolder 'testResults'), [string]$PesterOutputFormat = (property PesterOutputFormat 'NUnitXml'), [string[]]$PesterScript = (property PesterScript 'tests', (Join-Path $ProjectName 'tests')), [string[]]$PesterTag = (property PesterTag @()), [string[]]$PesterExcludeTag = (property PesterExcludeTag @()), [int]$CodeCoverageThreshold = (property CodeCoverageThreshold 100), # Build Configuration object $BuildInfo = (property BuildInfo @{}) ) # Synopsis: Making sure the Module meets some quality standard (help, tests) task Invoke_pester_tests { "`tProject Path = $ProjectPath" "`tProject Name = $ProjectName" "`tTests = $($PesterScript -join ', ')" "`tTags = $($PesterTag -join ', ')" "`tExclude Tags = $($PesterExcludeTag -join ', ')" "`tModuleVersion = $ModuleVersion" if (!(Split-path -isAbsolute $OutputDirectory)) { $OutputDirectory = Join-Path -Path $ProjectPath -ChildPath $OutputDirectory Write-Build Yellow "Absolute path to Output Directory is $OutputDirectory" } if (!(Split-path -isAbsolute $PesterOutputFolder)) { $PesterOutputFolder = Join-Path $OutputDirectory $PesterOutputFolder } if (!(Test-Path $PesterOutputFolder)) { Write-Build Yellow "Creating folder $PesterOutputFolder" $null = New-Item -ItemType Directory -force $PesterOutputFolder -ErrorAction Stop } if ([String]::IsNullOrEmpty($ModuleVersion)) { $ModuleInfo = Import-PowerShellDataFile "$OutputDirectory/$ProjectName/*/$ProjectName.psd1" -ErrorAction Stop if ($PreReleaseTag = $ModuleInfo.PrivateData.PSData.Prerelease) { $ModuleVersion = $ModuleInfo.ModuleVersion + "-" + $PreReleaseTag } else { $ModuleVersion = $ModuleInfo.ModuleVersion } } else { $ModuleVersion, $BuildMetadata = $ModuleVersion -split '\+', 2 $ModuleVersionFolder, $PreReleaseTag = $ModuleVersion -split '\-', 2 } $os = if($isWindows -or $PSVersionTable.PSVersion.Major -le 5) { 'Windows' } elseif($isMacOS) { 'MacOS' } else { 'Linux' } $PSVersion = 'PSv.{0}' -f $PSVersionTable.PSVersion $PesterOutputFileFileName = "{0}_v{1}.{2}.{3}.xml" -f $ProjectName, $ModuleVersion, $os, $PSVersion $PesterOutputFullPath = Join-Path $PesterOutputFolder "$($PesterOutputFormat)_$PesterOutputFileFileName" $moduleUnderTest = Import-Module $ProjectName -PassThru $ExcludeFromCodeCoverage = @('tasks','PlasterTemplate') $CodeCoverageFiles = (Get-ChildItem -Path $moduleUnderTest.ModuleBase -Include *.psm1, *.ps1 -Recurse).Where{ $result = $true foreach ($ExclPath in $ExcludeFromCodeCoverage) { if (!(Split-Path -IsAbsolute $ExclPath)) { $ExclPath = Join-Path $moduleUnderTest.ModuleBase $ExclPath } if ($_.FullName -Match ([regex]::Escape($ExclPath))) { $result = $false } } $result } $PesterParams = @{ OutputFormat = $PesterOutputFormat OutputFile = $PesterOutputFullPath PassThru = $true CodeCoverageOutputFileFormat = 'JaCoCo' CodeCoverage = $CodeCoverageFiles CodeCoverageOutputFile = (Join-Path $PesterOutputFolder "CodeCov_$PesterOutputFileFileName") #ExcludeTag = 'FunctionalQuality', 'TestQuality', 'helpQuality' } if ($PesterExcludeTag) { $PesterParams.Add('ExcludeTag',$PesterExcludeTag) } if ($PesterTag) { $PesterParams.Add('Tag',$PesterTag) } # Test folders is specified, do not run invoke-pester against $BuildRoot if ($PesterScript.count -gt 0) { $PesterParams.Add('Script', @()) foreach ($TestFolder in $PesterScript) { if (!(Split-path -isAbsolute $TestFolder)) { $TestFolder = Join-Path $ProjectPath $TestFolder } # The Absolute path to this folder exists, adding to the list of pester scripts to run if (Test-Path $TestFolder) { $PesterParams.Script += $TestFolder } } } $script:TestResults = Invoke-Pester @PesterParams -Verbose $PesterResultObjectCliXml = Join-Path $PesterOutputFolder "PesterObject_$PesterOutputFileFileName" $null = $script:TestResults | Export-CliXml -Path $PesterResultObjectCliXml -Force } # Synopsis: This task ensures the build job fails if the test aren't successful. task Fail_Build_if_Pester_Tests_failed -If ($CodeCoverageThreshold -ne 0) { "Asserting that no test failed" if (!(Split-path -isAbsolute $OutputDirectory)) { $OutputDirectory = Join-Path -Path $ProjectPath -ChildPath $OutputDirectory Write-Build Yellow "Absolute path to Output Directory is $OutputDirectory" } if (!(Split-path -isAbsolute $PesterOutputFolder)) { $PesterOutputFolder = Join-Path $OutputDirectory $PesterOutputFolder } $os = if($isWindows -or $PSVersionTable.PSVersion.Major -le 5) { 'Windows' } elseif($isMacOS) { 'MacOS' } else { 'Linux' } if ([String]::IsNullOrEmpty($ModuleVersion)) { $ModuleInfo = Import-PowerShellDataFile "$OutputDirectory/$ProjectName/*/$ProjectName.psd1" -ErrorAction Stop if($PreReleaseTag = $ModuleInfo.PrivateData.PSData.Prerelease) { $ModuleVersion = $ModuleInfo.ModuleVersion + "-" + $PreReleaseTag } else { $ModuleVersion = $ModuleInfo.ModuleVersion } } else { $ModuleVersion, $BuildMetadata = $ModuleVersion -split '\+', 2 $ModuleVersionFolder, $PreReleaseTag = $ModuleVersion -split '\-', 2 } $PSVersion = 'PSv.{0}' -f $PSVersionTable.PSVersion $PesterOutputFileFileName = "{0}_v{1}.{2}.{3}.xml" -f $ProjectName, $ModuleVersion, $os, $PSVersion $PesterResultObjectClixml = Join-Path $PesterOutputFolder "PesterObject_$PesterOutputFileFileName" Write-Build White "`tPester Output Object = $PesterResultObjectClixml" if (-Not (Test-Path $PesterResultObjectClixml)) { if ( $CodeCoverageThreshold -eq 0 ) { Write-Build Green "Pester run and Coverage bypassed. No Pester output found but allowed." return } else { Throw "No command were tested. Threshold of $CodeCoverageThreshold % not met" } } else { $PesterObject = Import-Clixml -Path $PesterResultObjectClixml -ErrorAction Stop assert ($PesterObject.FailedCount -eq 0) ('Failed {0} tests. Aborting Build' -f $PesterObject.FailedCount) } } # Synopsis: Fails the build if the code coverage is under predefined threshold task Pester_if_Code_Coverage_Under_Threshold { if (!(Split-path -isAbsolute $OutputDirectory)) { $OutputDirectory = Join-Path -Path $ProjectPath -ChildPath $OutputDirectory Write-Build Yellow "Absolute path to Output Directory is $OutputDirectory" } if (!(Split-path -isAbsolute $PesterOutputFolder)) { $PesterOutputFolder = Join-Path $OutputDirectory $PesterOutputFolder } $os = if($isWindows -or $PSVersionTable.PSVersion.Major -le 5) { 'Windows' } elseif($isMacOS) { 'MacOS' } else { 'Linux' } if ([String]::IsNullOrEmpty($ModuleVersion)) { $ModuleInfo = Import-PowerShellDataFile "$OutputDirectory/$ProjectName/*/$ProjectName.psd1" -ErrorAction Stop if($PreReleaseTag = $ModuleInfo.PrivateData.PSData.Prerelease) { $ModuleVersion = $ModuleInfo.ModuleVersion + "-" + $PreReleaseTag } else { $ModuleVersion = $ModuleInfo.ModuleVersion } } else { $ModuleVersion, $BuildMetadata = $ModuleVersion -split '\+', 2 $ModuleVersionFolder, $PreReleaseTag = $ModuleVersion -split '\-', 2 } $PSVersion = 'PSv.{0}' -f $PSVersionTable.PSVersion $PesterOutputFileFileName = "{0}_v{1}.{2}.{3}.xml" -f $ProjectName, $ModuleVersion, $os, $PSVersion $PesterResultObjectClixml = Join-Path $PesterOutputFolder "PesterObject_$PesterOutputFileFileName" Write-Build White "`tPester Output Object = $PesterResultObjectClixml" if (-Not (Test-Path $PesterResultObjectClixml)) { if ( $CodeCoverageThreshold -eq 0 ) { Write-Build Green "Pester run and Coverage bypassed. No Pester output found but allowed." return } else { Throw "No command were tested. Threshold of $CodeCoverageThreshold % not met" } } else { $PesterObject = Import-Clixml -Path $PesterResultObjectClixml } if ($PesterObject.CodeCoverage.NumberOfCommandsAnalyzed) { $coverage = $PesterObject.CodeCoverage.NumberOfCommandsExecuted / $PesterObject.CodeCoverage.NumberOfCommandsAnalyzed if ($coverage -lt $CodeCoverageThreshold / 100) { Throw "The Code Coverage FAILURE: ($($Coverage*100) %) is under the threshold of $CodeCoverageThreshold %." } else { Write-Build Green "Code Coverage SUCCESS with value of $($coverage*100) %" } } } # Synopsis: Uploading Unit Test results to AppVeyor task Upload_Test_Results_To_AppVeyor -If {(property BuildSystem 'unknown') -eq 'AppVeyor'} { if (!(Split-path -isAbsolute $OutputDirectory)) { $OutputDirectory = Join-Path -Path $ProjectPath -ChildPath $OutputDirectory Write-Build Yellow "Absolute path to Output Directory is $OutputDirectory" } if (!(Split-path -isAbsolute $PesterOutputFolder)) { $PesterOutputFolder = Join-Path $OutputDirectory $PesterOutputFolder } if (!(Test-Path $PesterOutputFolder)) { Write-Build Yellow "Creating folder $PesterOutputFolder" $null = New-Item -ItemType Directory -force $PesterOutputFolder -ErrorAction Stop } $os = if($isWindows -or $PSVersionTable.PSVersion.Major -le 5) { 'Windows' } elseif($isMacOS) { 'MacOS' } else { 'Linux' } if ([String]::IsNullOrEmpty($ModuleVersion)) { $ModuleInfo = Import-PowerShellDataFile "$OutputDirectory/$ProjectName/*/$ProjectName.psd1" -ErrorAction Stop if ($PreReleaseTag = $ModuleInfo.PrivateData.PSData.Prerelease) { $ModuleVersion = $ModuleInfo.ModuleVersion + "-" + $PreReleaseTag } else { $ModuleVersion = $ModuleInfo.ModuleVersion } } else { $ModuleVersion, $BuildMetadata = $ModuleVersion -split '\+', 2 $ModuleVersionFolder, $PreReleaseTag = $ModuleVersion -split '\-', 2 } $PSVersion = 'PSv.{0}' -f $PSVersionTable.PSVersion $PesterOutputFileFileName = "{0}_v{1}.{2}.{3}.xml" -f $ProjectName, $ModuleVersion, $os, $PSVersion $PesterOutputFullPath = Join-Path $PesterOutputFolder "$($PesterOutputFormat)_$PesterOutputFileFileName" $TestResultFile = Get-Item $PesterOutputFullPath -ErrorAction Ignore if($TestResultFile) { Write-Build Green " Uploading test results $TestResultFile to Appveyor" $TestResultFile | Add-TestResultToAppveyor Write-Build Green " Upload Complete" } } # Synopsis: Meta task that runs Quality Tests, and fails if they're not successful task Pester_Tests_Stop_On_Fail Invoke_pester_tests, Upload_Test_Results_To_AppVeyor, Fail_Build_if_Pester_Tests_failed |