diff --git a/scripts/tests/PSModule/SourceCode.Tests.ps1 b/scripts/tests/PSModule/SourceCode.Tests.ps1 index d9e95e5..ffb2a9e 100644 --- a/scripts/tests/PSModule/SourceCode.Tests.ps1 +++ b/scripts/tests/PSModule/SourceCode.Tests.ps1 @@ -2,106 +2,134 @@ 'PSReviewUnusedParameter', '', Justification = 'Parameters are used in the test.' )] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseDeclaredVarsMoreThanAssignments', 'functionBearingPublicFiles', + Justification = 'Variables are used in the test.' +)] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute( + 'PSUseDeclaredVarsMoreThanAssignments', 'functionBearingFiles', + Justification = 'Variables are used in the test.' +)] [CmdLetBinding()] Param( + # The path to the 'src' folder of the repo. [Parameter(Mandatory)] [string] $Path, + # The path to the 'tests' folder of the repo. [Parameter(Mandatory)] [string] $TestsPath ) BeforeAll { $scriptFiles = Get-ChildItem -Path $Path -Include *.psm1, *.ps1 -Recurse -File - LogGroup 'Script files:' { + LogGroup "Found $($scriptFiles.Count) script files in [$Path]" { $scriptFiles | ForEach-Object { - Write-Verbose " - $($_.FullName)" + Write-Verbose " - $($_.FullName)" -Verbose } } $functionsPath = Join-Path -Path $Path -ChildPath 'functions' - # $privateFunctionsPath = Join-Path -Path $functionsPath -ChildPath 'private' + $functionFiles = (Test-Path -Path $functionsPath) ? (Get-ChildItem -Path $functionsPath -File -Filter '*.ps1' -Recurse) : $null + + LogGroup "Found $($functionFiles.Count) function files in [$functionsPath]" { + $functionFiles | ForEach-Object { + Write-Verbose " - $($_.FullName)" -Verbose + } + } + $privateFunctionsPath = Join-Path -Path $functionsPath -ChildPath 'private' + $privateFunctionFiles = (Test-Path -Path $privateFunctionsPath) ? + (Get-ChildItem -Path $privateFunctionsPath -File -Filter '*.ps1' -Recurse) : $null + LogGroup "Found $($privateFunctionFiles.Count) private function files in [$privateFunctionsPath]" { + $privateFunctionFiles | ForEach-Object { + Write-Verbose " - $($_.FullName)" -Verbose + } + } $publicFunctionsPath = Join-Path -Path $functionsPath -ChildPath 'public' - # $variablesPath = Join-Path -Path $Path -ChildPath 'variables' - # $privateVariablesPath = Join-Path -Path $variablesPath -ChildPath 'private' - # $publicVariablesPath = Join-Path -Path $variablesPath -ChildPath 'public' - $functionFiles = (Test-Path -Path $functionsPath) ? (Get-ChildItem -Path $functionsPath -Filter '*.ps1' -File) : $null $publicFunctionFiles = (Test-Path -Path $publicFunctionsPath) ? (Get-ChildItem -Path $publicFunctionsPath -File -Filter '*.ps1' -Recurse) : $null - - Write-Verbose "Found $($scriptFiles.Count) script files in [$Path]" - Write-Verbose "Found $($functionFiles.Count) function files in [$functionsPath]" - Write-Verbose "Found $($publicFunctionFiles.Count) public function files in [$publicFunctionsPath]" -} - -Describe 'PSModule - SourceCode tests' { - Context 'function/filter' { - It 'Should contain one function or filter' { - $issues = @('') - $functionFiles | ForEach-Object { - $filePath = $_.FullName - $relativePath = $filePath.Replace($Path, '').Trim('\').Trim('/') - $Ast = [System.Management.Automation.Language.Parser]::ParseFile($filePath, [ref]$null, [ref]$null) - $tokens = $Ast.FindAll( { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } , $true ) - if ($tokens.count -ne 1) { - $issues += " - $relativePath - $($tokens.Name)" - } - } - $issues -join [Environment]::NewLine | - Should -BeNullOrEmpty -Because 'the script should contain one function or filter' + LogGroup "Found $($publicFunctionFiles.Count) public function files in [$publicFunctionsPath]" { + $publicFunctionFiles | ForEach-Object { + Write-Verbose " - $($_.FullName)" -Verbose } - - It 'Should have matching filename and function/filter name' { - $issues = @('') - $functionFiles | ForEach-Object { - $filePath = $_.FullName - $fileName = $_.BaseName - $relativePath = $filePath.Replace($Path, '').Trim('\').Trim('/') - $Ast = [System.Management.Automation.Language.Parser]::ParseFile($filePath, [ref]$null, [ref]$null) - $tokens = $Ast.FindAll( { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } , $true ) - if ($tokens.Name -ne $fileName) { - $issues += " - $relativePath - $($tokens.Name)" - } - } - $issues -join [Environment]::NewLine | - Should -BeNullOrEmpty -Because 'the script files should be called the same as the function they contain' + } + $variablesPath = Join-Path -Path $Path -ChildPath 'variables' + $variableFiles = (Test-Path -Path $variablesPath) ? (Get-ChildItem -Path $variablesPath -File -Filter '*.ps1' -Recurse) : $null + LogGroup "Found $($variableFiles.Count) variable files in [$variablesPath]" { + $variableFiles | ForEach-Object { + Write-Verbose " - $($_.FullName)" -Verbose } + } + $privateVariablesPath = Join-Path -Path $variablesPath -ChildPath 'private' + $privateVariableFiles = (Test-Path -Path $privateVariablesPath) ? + (Get-ChildItem -Path $privateVariablesPath -File -Filter '*.ps1' -Recurse) : $null + LogGroup "Found $($privateVariableFiles.Count) private variable files in [$privateVariablesPath]" { + $privateVariableFiles | ForEach-Object { + Write-Verbose " - $($_.FullName)" -Verbose + } + } + $publicVariablesPath = Join-Path -Path $variablesPath -ChildPath 'public' + $publicVariableFiles = (Test-Path -Path $publicVariablesPath) ? + (Get-ChildItem -Path $publicVariablesPath -File -Filter '*.ps1' -Recurse) : $null + LogGroup "Found $($publicVariableFiles.Count) public variable files in [$publicVariablesPath]" { + $publicVariableFiles | ForEach-Object { + Write-Verbose " - $($_.FullName)" -Verbose + } + } + $classPath = Join-Path -Path $Path -ChildPath 'classes' + $classFiles = (Test-Path -Path $classPath) ? (Get-ChildItem -Path $classPath -File -Filter '*.ps1' -Recurse) : $null + LogGroup "Found $($classFiles.Count) class files in [$classPath]" { + $classFiles | ForEach-Object { + Write-Verbose " - $($_.FullName)" -Verbose + } + } + $privateClassPath = Join-Path -Path $classPath -ChildPath 'private' + $privateClassFiles = (Test-Path -Path $privateClassPath) ? + (Get-ChildItem -Path $privateClassPath -File -Filter '*.ps1' -Recurse) : $null + LogGroup "Found $($privateClassFiles.Count) private class files in [$privateClassPath]" { + $privateClassFiles | ForEach-Object { + Write-Verbose " - $($_.FullName)" -Verbose + } + } + $publicClassPath = Join-Path -Path $classPath -ChildPath 'public' + $publicClassFiles = (Test-Path -Path $publicClassPath) ? + (Get-ChildItem -Path $publicClassPath -File -Filter '*.ps1' -Recurse) : $null + LogGroup "Found $($publicClassFiles.Count) public class files in [$publicClassPath]" { + $publicClassFiles | ForEach-Object { + Write-Verbose " - $($_.FullName)" -Verbose + } + } +} - It 'All public functions/filters have tests' { +Describe 'PSModule - SourceCode tests' { + Context 'General tests' { + It "Should use '[System.Environment]::ProcessorCount' instead of '`$env:NUMBER_OF_PROCESSORS' (ID: NumberOfProcessors)" { $issues = @('') - - $testFiles = Get-ChildItem -Path $TestsPath -Recurse -File -Filter '*.ps1' - $functionsInTestFiles = $testFiles | ForEach-Object { - $ast = [System.Management.Automation.Language.Parser]::ParseFile($_.FullName, [ref]$null, [ref]$null) - $ast.FindAll( - { - param($node) - $node -is [System.Management.Automation.Language.CommandAst] -and - $node.GetCommandName() -ne $null - }, - $true - ) | ForEach-Object { - $_.GetCommandName() - } | Sort-Object -Unique - } - - $publicFunctionFiles | ForEach-Object { - $filePath = $_.FullName - $relativePath = $filePath.Replace($Path, '').Trim('\').Trim('/') - $Ast = [System.Management.Automation.Language.Parser]::ParseFile($filePath, [ref]$null, [ref]$null) - $tokens = $Ast.FindAll( { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } , $true ) - $functionName = $tokens.Name - if ($functionsInTestFiles -notcontains $functionName) { - $issues += " - $relativePath - $functionName" + $scriptFiles | ForEach-Object { + Select-String -Path $_.FullName -Pattern '\$env:NUMBER_OF_PROCESSORS' -AllMatches | ForEach-Object { + $filePath = $_.FullName + $relativePath = $filePath.Replace($Path, '').Trim('\').Trim('/') + $skipTest = Select-String -Path $filePath -Pattern '#SkipTest:NumberOfProcessors:(?.+)' -AllMatches + if ($skipTest.Matches.Count -gt 0) { + $skipReason = $skipTest.Matches.Groups | Where-Object { $_.Name -eq 'Reason' } | Select-Object -ExpandProperty Value + Write-GitHubWarning -Message " - $relativePath - $skipReason" -Title 'Skipping NumberOfProcessors test' + continue + } + $issues += " - $($_.Path):L$($_.LineNumber)" } } $issues -join [Environment]::NewLine | - Should -BeNullOrEmpty -Because 'a test should exist for each of the functions in the module' + Should -BeNullOrEmpty -Because 'the script should use [System.Environment]::ProcessorCount instead of $env:NUMBER_OF_PROCESSORS' } - - It "Should not contain '-Verbose' unless it is disabled using ':`$false' qualifier after it" { + It "Should not contain '-Verbose' unless it is disabled using ':`$false' qualifier after it (ID: Verbose)" { $issues = @('') $scriptFiles | ForEach-Object { $filePath = $_.FullName $relativePath = $filePath.Replace($Path, '').Trim('\').Trim('/') + $skipTest = Select-String -Path $filePath -Pattern '#SkipTest:Verbose:(?.+)' -AllMatches + if ($skipTest.Matches.Count -gt 0) { + $skipReason = $skipTest.Matches.Groups | Where-Object { $_.Name -eq 'Reason' } | Select-Object -ExpandProperty Value + Write-GitHubWarning -Message " - $relativePath - $skipReason" -Title 'Skipping Verbose test' + continue + } Select-String -Path $filePath -Pattern '\s(-Verbose(?::\$true)?)\b(?!:\$false)' -AllMatches | ForEach-Object { $issues += " - $relativePath`:L$($_.LineNumber) - $($_.Line)" } @@ -109,12 +137,17 @@ Describe 'PSModule - SourceCode tests' { $issues -join [Environment]::NewLine | Should -BeNullOrEmpty -Because "the script should not contain '-Verbose' unless it is disabled using ':`$false' qualifier after it." } - - It "Should use '`$null = ...' instead of '... | Out-Null'" { + It "Should use '`$null = ...' instead of '... | Out-Null' (ID: OutNull)" { $issues = @('') $scriptFiles | ForEach-Object { $filePath = $_.FullName $relativePath = $filePath.Replace($Path, '').Trim('\').Trim('/') + $skipTest = Select-String -Path $filePath -Pattern '#SkipTest:OutNull:(?.+)' -AllMatches + if ($skipTest.Matches.Count -gt 0) { + $skipReason = $skipTest.Matches.Groups | Where-Object { $_.Name -eq 'Reason' } | Select-Object -ExpandProperty Value + Write-GitHubWarning -Message " - $relativePath - $skipReason" -Title 'Skipping OutNull test' + continue + } Select-String -Path $filePath -Pattern 'Out-Null' -AllMatches | ForEach-Object { $issues += " - $relativePath`:L$($_.LineNumber) - $($_.Line)" } @@ -122,12 +155,17 @@ Describe 'PSModule - SourceCode tests' { $issues -join [Environment]::NewLine | Should -BeNullOrEmpty -Because "the script should use '`$null = ...' instead of '... | Out-Null'" } - - It 'Should not use ternary operations for compatability reasons' -Skip { + It 'Should not use ternary operations for compatability reasons (ID: NoTernary)' -Skip { $issues = @('') $scriptFiles | ForEach-Object { $filePath = $_.FullName $relativePath = $filePath.Replace($Path, '').Trim('\').Trim('/') + $skipTest = Select-String -Path $filePath -Pattern '#SkipTest:NoTernary:(?.+)' -AllMatches + if ($skipTest.Matches.Count -gt 0) { + $skipReason = $skipTest.Matches.Groups | Where-Object { $_.Name -eq 'Reason' } | Select-Object -ExpandProperty Value + Write-GitHubWarning -Message " - $relativePath - $skipReason" -Title 'Skipping NoTernary test' + continue + } Select-String -Path $filePath -Pattern '(?.+)' -AllMatches + if ($skipTest.Matches.Count -gt 0) { + $skipReason = $skipTest.Matches.Groups | Where-Object { $_.Name -eq 'Reason' } | Select-Object -ExpandProperty Value + Write-GitHubWarning -Message " - $relativePath - $skipReason" -Title 'Skipping LowercaseKeywords test' + continue + } $errors = $null $tokens = $null [System.Management.Automation.Language.Parser]::ParseFile($FilePath, [ref]$tokens, [ref]$errors) @@ -157,81 +199,183 @@ Describe 'PSModule - SourceCode tests' { } $issues -join [Environment]::NewLine | Should -BeNullOrEmpty -Because 'all powershell keywords should be lowercase' } + } - # It 'comment based doc block start is indented with 4 spaces' {} - # It 'comment based doc is indented with 8 spaces' {} - # It 'has synopsis for all functions' {} - # It 'has description for all functions' {} - # It 'has examples for all functions' {} + Context 'classes' { + } - It 'Should have [CmdletBinding()] attribute' { - $issues = @('') - $functionFiles | ForEach-Object { - $found = $false - $filePath = $_.FullName - $relativePath = $filePath.Replace($Path, '').Trim('\').Trim('/') - $scriptAst = [System.Management.Automation.Language.Parser]::ParseFile($filePath, [ref]$null, [ref]$null) - $tokens = $scriptAst.FindAll({ $true }, $true) - foreach ($token in $tokens) { - if ($token.TypeName.Name -eq 'CmdletBinding') { - $found = $true + Context 'functions' { + Context 'Generic' { + BeforeAll { + $functionBearingFiles = $functionFiles | Where-Object { + $Ast = [System.Management.Automation.Language.Parser]::ParseFile($_.FullName, [ref]$null, [ref]$null) + $tokens = $Ast.FindAll( { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } , $true ) + $tokens.count -ne 0 + } + } + # It 'has synopsis for all functions' {} + # It 'has description for all functions' {} + # It 'has examples for all functions' {} + # It 'comment based doc is indented with 8 spaces' {} + # It 'boolean parameters in CmdletBinding() attribute are written without assignments' {} + # I.e. [CmdletBinding(ShouldProcess)] instead of [CmdletBinding(ShouldProcess = $true)] + # It 'has [OutputType()] attribute' {} + # Parameters + # It 'comment based doc block start is indented with 4 spaces' {} + # It 'has parameter description for all functions' {} + # It 'parameters have [Parameter()] attribute' {} + # It 'boolean parameters to the [Parameter()] attribute are written without assignments' {} + # I.e. [Parameter(Mandatory)] instead of [Parameter(Mandatory = $true)] + # It 'datatype for parameters are written on the same line as the parameter name' {} + # It 'datatype for parameters and parameter name are separated by a single space' {} + # It 'parameters are separated by a blank line' {} + It 'Should contain one function or filter (ID: FunctionCount)' { + $issues = @('') + $functionBearingFiles | ForEach-Object { + $filePath = $_.FullName + $relativePath = $filePath.Replace($Path, '').Trim('\').Trim('/') + $skipTest = Select-String -Path $filePath -Pattern '#SkipTest:FunctionCount:(?.+)' -AllMatches + if ($skipTest.Matches.Count -gt 0) { + $skipReason = $skipTest.Matches.Groups | Where-Object { $_.Name -eq 'Reason' } | Select-Object -ExpandProperty Value + Write-GitHubWarning -Message " - $relativePath - $skipReason" -Title 'Skipping FunctionCount test' + continue + } + $Ast = [System.Management.Automation.Language.Parser]::ParseFile($filePath, [ref]$null, [ref]$null) + $tokens = $Ast.FindAll( { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } , $true ) + if ($tokens.count -ne 1) { + $issues += " - $relativePath - $($tokens.Name)" } } - if (-not $found) { - $issues += " - $relativePath" + $issues -join [Environment]::NewLine | + Should -BeNullOrEmpty -Because 'the script should contain one function or filter' + } + It 'Should have matching filename and function/filter name (ID: FunctionName)' { + $issues = @('') + $functionBearingFiles | ForEach-Object { + $filePath = $_.FullName + $fileName = $_.BaseName + $relativePath = $filePath.Replace($Path, '').Trim('\').Trim('/') + $skipTest = Select-String -Path $filePath -Pattern '#SkipTest:FunctionName:(?.+)' -AllMatches + if ($skipTest.Matches.Count -gt 0) { + $skipReason = $skipTest.Matches.Groups | Where-Object { $_.Name -eq 'Reason' } | Select-Object -ExpandProperty Value + Write-GitHubWarning -Message " - $relativePath - $skipReason" -Title 'Skipping FunctionName test' + continue + } + $Ast = [System.Management.Automation.Language.Parser]::ParseFile($filePath, [ref]$null, [ref]$null) + $tokens = $Ast.FindAll( { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } , $true ) + if ($tokens.Name -ne $fileName) { + $issues += " - $relativePath - $($tokens.Name)" + } } + $issues -join [Environment]::NewLine | + Should -BeNullOrEmpty -Because 'the script files should be called the same as the function they contain' } - $issues -join [Environment]::NewLine | - Should -BeNullOrEmpty -Because 'the script should have [CmdletBinding()] attribute' - } - - It 'Should have a param() block' { - $issues = @('') - $functionFiles | ForEach-Object { - $found = $false - $filePath = $_.FullName - $relativePath = $filePath.Replace($Path, '').Trim('\').Trim('/') - $scriptAst = [System.Management.Automation.Language.Parser]::ParseFile($filePath, [ref]$null, [ref]$null) - $tokens = $scriptAst.FindAll({ $args[0] -is [System.Management.Automation.Language.ParamBlockAst] }, $true) - foreach ($token in $tokens) { - if ($token.count -eq 1) { - $found = $true + It 'Should have [CmdletBinding()] attribute (ID: CmdletBinding)' { + $issues = @('') + $functionBearingFiles | ForEach-Object { + $found = $false + $filePath = $_.FullName + $relativePath = $filePath.Replace($Path, '').Trim('\').Trim('/') + $skipTest = Select-String -Path $filePath -Pattern '#SkipTest:CmdletBinding:(?.+)' -AllMatches + if ($skipTest.Matches.Count -gt 0) { + $skipReason = $skipTest.Matches.Groups | Where-Object { $_.Name -eq 'Reason' } | Select-Object -ExpandProperty Value + Write-GitHubWarning -Message " - $relativePath - $skipReason" -Title 'Skipping CmdletBinding test' + continue + } + $scriptAst = [System.Management.Automation.Language.Parser]::ParseFile($filePath, [ref]$null, [ref]$null) + $tokens = $scriptAst.FindAll({ $true }, $true) + foreach ($token in $tokens) { + if ($token.TypeName.Name -eq 'CmdletBinding') { + $found = $true + } + } + if (-not $found) { + $issues += " - $relativePath" } } - if (-not $found) { - $issues += " - $relativePath" + $issues -join [Environment]::NewLine | + Should -BeNullOrEmpty -Because 'the script should have [CmdletBinding()] attribute' + } + It 'Should have a param() block (ID: ParamBlock)' { + $issues = @('') + $functionBearingFiles | ForEach-Object { + $found = $false + $filePath = $_.FullName + $relativePath = $filePath.Replace($Path, '').Trim('\').Trim('/') + $skipTest = Select-String -Path $filePath -Pattern '#SkipTest:ParamBlock:(?.+)' -AllMatches + if ($skipTest.Matches.Count -gt 0) { + $skipReason = $skipTest.Matches.Groups | Where-Object { $_.Name -eq 'Reason' } | Select-Object -ExpandProperty Value + Write-GitHubWarning -Message " - $relativePath - $skipReason" -Title 'Skipping ParamBlock test' + continue + } + $scriptAst = [System.Management.Automation.Language.Parser]::ParseFile($filePath, [ref]$null, [ref]$null) + $tokens = $scriptAst.FindAll({ $args[0] -is [System.Management.Automation.Language.ParamBlockAst] }, $true) + foreach ($token in $tokens) { + if ($token.count -eq 1) { + $found = $true + } + } + if (-not $found) { + $issues += " - $relativePath" + } } + $issues -join [Environment]::NewLine | + Should -BeNullOrEmpty -Because 'the script should have a param() block' } - $issues -join [Environment]::NewLine | - Should -BeNullOrEmpty -Because 'the script should have a param() block' } + Context 'public functions' { + BeforeAll { + $functionBearingPublicFiles = $publicFunctionFiles | Where-Object { + $Ast = [System.Management.Automation.Language.Parser]::ParseFile($_.FullName, [ref]$null, [ref]$null) + $tokens = $Ast.FindAll( { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } , $true ) + $tokens.count -ne 0 + } + } + It 'All public functions/filters have tests (ID: FunctionTest)' { + $issues = @('') - # It 'boolean parameters in CmdletBinding() attribute are written without assignments' {} - # I.e. [CmdletBinding(ShouldProcess)] instead of [CmdletBinding(ShouldProcess = $true)] - # It 'has [OutputType()] attribute' {} - } - - Context 'Parameter design' { - # It 'has parameter description for all functions' {} - # It 'parameters have [Parameter()] attribute' {} - # It 'boolean parameters to the [Parameter()] attribute are written without assignments' {} - # I.e. [Parameter(Mandatory)] instead of [Parameter(Mandatory = $true)] - # It 'datatype for parameters are written on the same line as the parameter name' {} - # It 'datatype for parameters and parameter name are separated by a single space' {} - # It 'parameters are separated by a blank line' {} - } + # Get commands used in tests from the files in 'tests' folder. + $testFiles = Get-ChildItem -Path $TestsPath -Recurse -File -Filter '*.ps1' + $functionsInTestFiles = $testFiles | ForEach-Object { + $ast = [System.Management.Automation.Language.Parser]::ParseFile($_.FullName, [ref]$null, [ref]$null) + $ast.FindAll( + { + param($node) + $node -is [System.Management.Automation.Language.CommandAst] -and + $node.GetCommandName() -ne $null + }, + $true + ) | ForEach-Object { + $_.GetCommandName() + } | Sort-Object -Unique + } - Context 'Compatability checks' { - It "Should use '[System.Environment]::ProcessorCount' instead of '`$env:NUMBER_OF_PROCESSORS'" { - $issues = @('') - $scriptFiles | ForEach-Object { - Select-String -Path $_.FullName -Pattern '\$env:NUMBER_OF_PROCESSORS' -AllMatches | ForEach-Object { - $issues += " - $($_.Path):L$($_.LineNumber)" + # Get all the functions in the public function files and check if they have a test. + $functionBearingPublicFiles | ForEach-Object { + $filePath = $_.FullName + $relativePath = $filePath.Replace($Path, '').Trim('\').Trim('/') + $skipTest = Select-String -Path $filePath -Pattern '#SkipTest:FunctionTest:(?.+)' -AllMatches + if ($skipTest.Matches.Count -gt 0) { + $skipReason = $skipTest.Matches.Groups | Where-Object { $_.Name -eq 'Reason' } | Select-Object -ExpandProperty Value + Write-GitHubWarning -Message " - $relativePath - $skipReason" -Title 'Skipping FunctionTest test' + continue + } + $Ast = [System.Management.Automation.Language.Parser]::ParseFile($filePath, [ref]$null, [ref]$null) + $tokens = $Ast.FindAll( { $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] } , $true ) + $functionName = $tokens.Name + # If the file contains a function and the function name is not in the test files, add it as an issue. + if ($functionName.count -eq 1 -and $functionsInTestFiles -notcontains $functionName) { + $issues += " - $relativePath - $functionName" + } } + $issues -join [Environment]::NewLine | + Should -BeNullOrEmpty -Because 'a test should exist for each of the functions in the module' } - $issues -join [Environment]::NewLine | - Should -BeNullOrEmpty -Because 'the script should use [System.Environment]::ProcessorCount instead of $env:NUMBER_OF_PROCESSORS' } + Context 'private functions' {} + } + + Context 'variables' { } Context 'Module manifest' { diff --git a/tests/src/functions/public/New-PSModuleTest.ps1 b/tests/src/functions/public/New-PSModuleTest.ps1 index baab620..8fc0641 100644 --- a/tests/src/functions/public/New-PSModuleTest.ps1 +++ b/tests/src/functions/public/New-PSModuleTest.ps1 @@ -1,5 +1,6 @@ #Requires -Modules @{ModuleName='PSSemVer'; ModuleVersion='1.0'} - +#SkipTest:FunctionTest:Difficult to test due to the nature of the function. +#SkipTest:Verbose:Just want to test that a function can have multiple skips. function New-PSModuleTest { <# .SYNOPSIS @@ -28,11 +29,10 @@ function New-PSModuleTest { [string] $Name ) Write-Debug "Debug message" + Write-Verbose "Verbose message" -Verbose Write-Output "Hello, $Name!" } New-Alias New-PSModuleTestAlias3 New-PSModuleTest New-Alias -Name New-PSModuleTestAlias4 -Value New-PSModuleTest - - Set-Alias New-PSModuleTestAlias5 New-PSModuleTest diff --git a/tests/src/functions/public/Test-PSModuleTest.ps1 b/tests/src/functions/public/Test-PSModuleTest.ps1 index 26be2b9..4056e2f 100644 --- a/tests/src/functions/public/Test-PSModuleTest.ps1 +++ b/tests/src/functions/public/Test-PSModuleTest.ps1 @@ -1,4 +1,5 @@ -function Test-PSModuleTest { +#SkipTest:Verbose:Just want to test that a function can have multiple skips. +function Test-PSModuleTest { <# .SYNOPSIS Performs tests on a module. @@ -15,4 +16,5 @@ [string] $Name ) Write-Output "Hello, $Name!" + Write-Verbose 'Verbose message' -Verbose } diff --git a/tests/src/functions/public/completers.ps1 b/tests/src/functions/public/completers.ps1 new file mode 100644 index 0000000..6b1adbb --- /dev/null +++ b/tests/src/functions/public/completers.ps1 @@ -0,0 +1,8 @@ +Register-ArgumentCompleter -CommandName New-PSModuleTest -ParameterName Name -ScriptBlock { + param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) + $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters + + 'Alice', 'Bob', 'Charlie' | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + } +} diff --git a/tests/srcWithManifest/functions/public/New-PSModuleTest.ps1 b/tests/srcWithManifest/functions/public/New-PSModuleTest.ps1 index d4e6e26..8fc0641 100644 --- a/tests/srcWithManifest/functions/public/New-PSModuleTest.ps1 +++ b/tests/srcWithManifest/functions/public/New-PSModuleTest.ps1 @@ -1,5 +1,6 @@ #Requires -Modules @{ModuleName='PSSemVer'; ModuleVersion='1.0'} - +#SkipTest:FunctionTest:Difficult to test due to the nature of the function. +#SkipTest:Verbose:Just want to test that a function can have multiple skips. function New-PSModuleTest { <# .SYNOPSIS @@ -27,11 +28,11 @@ function New-PSModuleTest { [Parameter(Mandatory)] [string] $Name ) + Write-Debug "Debug message" + Write-Verbose "Verbose message" -Verbose Write-Output "Hello, $Name!" } New-Alias New-PSModuleTestAlias3 New-PSModuleTest New-Alias -Name New-PSModuleTestAlias4 -Value New-PSModuleTest - - Set-Alias New-PSModuleTestAlias5 New-PSModuleTest diff --git a/tests/srcWithManifest/functions/public/Test-PSModuleTest.ps1 b/tests/srcWithManifest/functions/public/Test-PSModuleTest.ps1 index 26be2b9..4056e2f 100644 --- a/tests/srcWithManifest/functions/public/Test-PSModuleTest.ps1 +++ b/tests/srcWithManifest/functions/public/Test-PSModuleTest.ps1 @@ -1,4 +1,5 @@ -function Test-PSModuleTest { +#SkipTest:Verbose:Just want to test that a function can have multiple skips. +function Test-PSModuleTest { <# .SYNOPSIS Performs tests on a module. @@ -15,4 +16,5 @@ [string] $Name ) Write-Output "Hello, $Name!" + Write-Verbose 'Verbose message' -Verbose } diff --git a/tests/tests/PSModuleTest.Tests.ps1 b/tests/tests/PSModuleTest.Tests.ps1 index d329e70..00b3cde 100644 --- a/tests/tests/PSModuleTest.Tests.ps1 +++ b/tests/tests/PSModuleTest.Tests.ps1 @@ -2,9 +2,6 @@ It 'Function: Get-PSModuleTest' { Get-PSModuleTest -Name 'World' | Should -Be 'Hello, World!' } - It 'Function: New-PSModuleTest' { - New-PSModuleTest -Name 'World' | Should -Be 'Hello, World!' - } It 'Function: Set-PSModuleTest' { Set-PSModuleTest -Name 'World' | Should -Be 'Hello, World!' }