diff --git a/DscResource.DocumentationHelper/MofHelper.psm1 b/DscResource.DocumentationHelper/MofHelper.psm1 index f48dd949..ed74a4f9 100644 --- a/DscResource.DocumentationHelper/MofHelper.psm1 +++ b/DscResource.DocumentationHelper/MofHelper.psm1 @@ -42,17 +42,17 @@ function Get-MofSchemaObject $currentComment = "" $currentlyInCommentBlock = $false $partialLine = $null - foreach($textLine in $contents) + foreach ($textLine in $contents) { if ($textLine.StartsWith("/*")) { $currentlyInCommentBlock = $true } - elseif($textLine.StartsWith("*/")) + elseif ($textLine.StartsWith("*/")) { $currentlyInCommentBlock = $false } - elseif($currentlyInCommentBlock -eq $true) + elseif ($currentlyInCommentBlock -eq $true) { # Ignore lines in comment blocks } @@ -103,7 +103,7 @@ function Get-MofSchemaObject } else { - if($partialLine) + if ($partialLine) { [string] $currentLine = $partialLine + $textLine $partialLine = $null @@ -130,7 +130,7 @@ function Get-MofSchemaObject $metadataObjects = @() # Does this assume that the metadata is on the same line? - if($length -gt 0) + if ($length -gt 0) { $metadata = $currentLine.Substring($start, $end - $start) $metadataObjects = $metadata.Split(",") diff --git a/DscResource.Tests.psd1 b/DscResource.Tests.psd1 index e81f02cf..e8ed18bd 100644 --- a/DscResource.Tests.psd1 +++ b/DscResource.Tests.psd1 @@ -12,7 +12,7 @@ CompanyName = 'Microsoft Corporation' # Copyright statement for this module - Copyright = '(c) 2017 Microsoft Corporation. All rights reserved.' + Copyright = '(c) 2018 Microsoft Corporation. All rights reserved.' # Description of the functionality provided by this module Description = 'Module for common meta tests and other shared functions for PowerShell DSC resources repositories.' diff --git a/Meta.Tests.ps1 b/Meta.Tests.ps1 index 6fb721db..f6f0d230 100644 --- a/Meta.Tests.ps1 +++ b/Meta.Tests.ps1 @@ -43,7 +43,7 @@ $dscResourcesFolderFilePath = Join-Path -Path $moduleRootFilePath -ChildPath 'Ds $repoRootPath = $moduleRootFilePath $repoRootPathFound = $false while (-not $repoRootPathFound ` - -and -not ([String]::IsNullOrEmpty((Split-Path -Path $repoRootPath -Parent)))) + -and -not ([String]::IsNullOrEmpty((Split-Path -Path $repoRootPath -Parent)))) { if (Get-ChildItem -Path $repoRootPath -Filter '.git' -Directory -Force) { @@ -58,8 +58,8 @@ while (-not $repoRootPathFound ` if (-not $repoRootPathFound) { Write-Warning -Message ('The root folder of the DSC Resource repository could ' + ` - 'not be located. This may prevent some markdown files from being checked for ' + ` - 'errors. Please ensure this repository has been cloned using Git.') + 'not be located. This may prevent some markdown files from being checked for ' + ` + 'errors. Please ensure this repository has been cloned using Git.') $repoRootPath = $moduleRootFilePath } @@ -170,18 +170,19 @@ Describe 'Common Tests - File Formatting' { $markdownFileExtensions = @('.md') $markdownFiles = $textFiles | - Where-Object { $markdownFileExtensions -contains $_.Extension } + Where-Object { $markdownFileExtensions -contains $_.Extension } foreach ($markdownFile in $markdownFiles) { $filePathOutputName = Get-RelativePathFromModuleRoot ` - -FilePath $markdownFile.FullName ` - -ModuleRootFilePath $moduleRootFilePath + -FilePath $markdownFile.FullName ` + -ModuleRootFilePath $moduleRootFilePath It ('Markdown file ''{0}'' should not have Byte Order Mark (BOM)' -f $filePathOutputName) { $markdownFileHasBom = Test-FileHasByteOrderMark -FilePath $markdownFile.FullName - if ($markdownFileHasBom) { + if ($markdownFileHasBom) + { Write-Warning -Message "$filePathOutputName contain Byte Order Mark (BOM). Use fixer function 'ConvertTo-ASCII'." } @@ -203,14 +204,15 @@ Describe 'Common Tests - Validate Script Files' -Tag 'Script' { foreach ($scriptFile in $scriptFiles) { $filePathOutputName = Get-RelativePathFromModuleRoot ` - -FilePath $scriptFile.FullName ` - -ModuleRootFilePath $moduleRootFilePath + -FilePath $scriptFile.FullName ` + -ModuleRootFilePath $moduleRootFilePath Context $filePathOutputName { It ('Script file ''{0}'' should not have Byte Order Mark (BOM)' -f $filePathOutputName) -Skip:(!$optIn) { $scriptFileHasBom = Test-FileHasByteOrderMark -FilePath $scriptFile.FullName - if ($scriptFileHasBom) { + if ($scriptFileHasBom) + { Write-Warning -Message "$filePathOutputName contain Byte Order Mark (BOM). Use fixer function 'ConvertTo-ASCII'." } @@ -226,8 +228,8 @@ Describe 'Common Tests - .psm1 File Parsing' { foreach ($psm1File in $psm1Files) { $filePathOutputName = Get-RelativePathFromModuleRoot ` - -FilePath $psm1File.FullName ` - -ModuleRootFilePath $moduleRootFilePath + -FilePath $psm1File.FullName ` + -ModuleRootFilePath $moduleRootFilePath Context $filePathOutputName { It ('Module file ''{0}'' should not contain parse errors' -f $filePathOutputName) { @@ -257,14 +259,15 @@ Describe 'Common Tests - Validate Module Files' -Tag 'Module' { foreach ($moduleFile in $moduleFiles) { $filePathOutputName = Get-RelativePathFromModuleRoot ` - -FilePath $moduleFile.FullName ` - -ModuleRootFilePath $moduleRootFilePath + -FilePath $moduleFile.FullName ` + -ModuleRootFilePath $moduleRootFilePath Context $filePathOutputName { It ('Module file ''{0}'' should not have Byte Order Mark (BOM)' -f $filePathOutputName) -Skip:(!$optIn) { $moduleFileHasBom = Test-FileHasByteOrderMark -FilePath $moduleFile.FullName - if ($moduleFileHasBom) { + if ($moduleFileHasBom) + { Write-Warning -Message "$filePathOutputName contain Byte Order Mark (BOM). Use fixer function 'ConvertTo-ASCII'." } @@ -342,14 +345,17 @@ Describe 'Common Tests - Script Resource Schema Validation' { <# PSSA = PS Script Analyzer - Only the first and last tests here will pass/fail correctly at the moment. The other 3 tests - will currently always pass, but print warnings based on the problems they find. - These automatic passes are here to give contributors time to fix the PSSA - problems before we turn on these tests. These 'automatic passes' should be removed - along with the first test (which is replaced by the following 3) around Jan-Feb - 2017. - Issue #161 has been raised to adddress this: - https://github.com/PowerShell/DscResource.Tests/issues/161 + + The following PSSA tests will always fail if any violations are found: + - Common Tests - Error-Level Script Analyzer Rules + - Common Tests - Custom Script Analyzer Rules + + The following PSSA tests will only fail if a violation is found and + a matching option is found in the opt-in file. + - Common Tests - Required Script Analyzer Rules + - Common Tests - Flagged Script Analyzer Rules + - Common Tests - New Error-Level Script Analyzer Rules + - Common Tests - Custom Script Analyzer Rules #> Describe 'Common Tests - PS Script Analyzer on Resource Files' { @@ -412,9 +418,9 @@ Describe 'Common Tests - PS Script Analyzer on Resource Files' { foreach ($dscResourcesPsm1File in $dscResourcesPsm1Files) { $invokeScriptAnalyzerParameters = @{ - Path = $dscResourcesPsm1File.FullName - ErrorAction = 'SilentlyContinue' - Recurse = $true + Path = $dscResourcesPsm1File.FullName + ErrorAction = 'SilentlyContinue' + Recurse = $true } Context $dscResourcesPsm1File.Name { @@ -453,14 +459,14 @@ Describe 'Common Tests - PS Script Analyzer on Resource Files' { Write-Warning -Message 'For instructions on how to run PSScriptAnalyzer on your own machine, please go to https://github.com/powershell/PSScriptAnalyzer' } - <# - Automatically passing this test since it may break several resource modules at the moment. - Automatic pass to be removed Jan-Feb 2017. - Issue #161 has been raised to adddress this: - https://github.com/PowerShell/DscResource.Tests/issues/161 - #> - $requiredPssaRulesOutput = $null - $requiredPssaRulesOutput | Should Be $null + if ($null -ne $requiredPssaRulesOutput -and (Get-OptInStatus -OptIns $optIns -Name 'Common Tests - Required Script Analyzer Rules')) + { + <# + If opted into 'Common Tests - Required Script Analyzer Rules' then + test that there were no violations + #> + $requiredPssaRulesOutput | Should Be $null + } } It 'Should pass all flagged PS Script Analyzer rules' { @@ -479,14 +485,14 @@ Describe 'Common Tests - PS Script Analyzer on Resource Files' { Write-Warning -Message 'For instructions on how to run PSScriptAnalyzer on your own machine, please go to https://github.com/powershell/PSScriptAnalyzer' } - <# - Automatically passing this test since it may break several resource modules at the moment. - Automatic pass to be removed Jan-Feb 2017. - Issue #161 has been raised to adddress this: - https://github.com/PowerShell/DscResource.Tests/issues/161 - #> - $flaggedPssaRulesOutput = $null - $flaggedPssaRulesOutput | Should Be $null + if ($null -ne $flaggedPssaRulesOutput -and (Get-OptInStatus -OptIns $optIns -Name 'Common Tests - Flagged Script Analyzer Rules')) + { + <# + If opted into 'Common Tests - Flagged Script Analyzer Rules' then + test that there were no violations + #> + $flaggedPssaRulesOutput | Should Be $null + } } It 'Should pass any recently-added, error-level PS Script Analyzer rules' { @@ -507,14 +513,14 @@ Describe 'Common Tests - PS Script Analyzer on Resource Files' { Write-Warning -Message 'For instructions on how to run PSScriptAnalyzer on your own machine, please go to https://github.com/powershell/PSScriptAnalyzer' } - <# - Automatically passing this test since it may break several resource modules at the moment. - Automatic pass to be removed Jan-Feb 2017. - Issue #161 has been raised to adddress this: - https://github.com/PowerShell/DscResource.Tests/issues/161 - #> - $newErrorPssaRulesOutput = $null - $newErrorPssaRulesOutput | Should Be $null + if ($null -ne $newErrorPssaRulesOutput -and (Get-OptInStatus -OptIns $optIns -Name 'Common Tests - New Error-Level Script Analyzer Rules')) + { + <# + If opted into 'Common Tests - New Error-Level Script Analyzer Rules' then + test that there were no violations + #> + $newErrorPssaRulesOutput | Should Be $null + } } It 'Should not suppress any required PS Script Analyzer rules' { @@ -555,14 +561,14 @@ Describe 'Common Tests - PS Script Analyzer on Resource Files' { Write-Warning -Message 'For instructions on how to run PSScriptAnalyzer on your own machine, please go to https://github.com/powershell/PSScriptAnalyzer' } - <# - Automatically passing this test since it may break several resource modules at the moment. - Automatic pass to be removed Jan-Feb 2017. - Issue #161 has been raised to adddress this: - https://github.com/PowerShell/DscResource.Tests/issues/161 - #> - $customPssaRulesOutput = $null - $customPssaRulesOutput | Should Be $null + if ($null -ne $customPssaRulesOutput -and (Get-OptInStatus -OptIns $optIns -Name 'Common Tests - Custom Script Analyzer Rules')) + { + <# + If opted into 'Common Tests - Custom Script Analyzer Rules' then + test that there were no violations + #> + $customPssaRulesOutput | Should Be $null + } } } } @@ -598,10 +604,10 @@ Describe 'Common Tests - Validate Example Files' -Tag 'Examples' { # Copies all module files into the destination module folder. Copy-Item -Path (Join-Path -Path $moduleRootFilePath -ChildPath '*') ` - -Destination $powershellModulePath ` - -Exclude @('node_modules','.*') ` - -Recurse ` - -Force + -Destination $powershellModulePath ` + -Exclude @('node_modules', '.*') ` + -Recurse ` + -Force } $exampleFile = Get-ChildItem -Path (Join-Path -Path $moduleRootFilePath -ChildPath 'Examples') -Filter '*.ps1' -Recurse @@ -617,7 +623,7 @@ Describe 'Common Tests - Validate Example Files' -Tag 'Examples' { $mockConfigurationData = @{ AllNodes = @( @{ - NodeName = 'localhost' + NodeName = 'localhost' PSDscAllowPlainTextPassword = $true } ) @@ -639,30 +645,30 @@ Describe 'Common Tests - Validate Example Files' -Tag 'Examples' { $exampleCommand = Get-Command -Name Example -ErrorAction SilentlyContinue if ($exampleCommand) { - $params = @{} + $params = @{} - # Each credential parameter in the Example function is assigned the mocked credential. 'PsDscRunAsCredential' is not assigned because that broke the example. - $credentialParameterToMockCredentialFor = $exampleCommand.Parameters.Keys | Where-Object { - $_ -like '*Account' ` + # Each credential parameter in the Example function is assigned the mocked credential. 'PsDscRunAsCredential' is not assigned because that broke the example. + $credentialParameterToMockCredentialFor = $exampleCommand.Parameters.Keys | Where-Object { + $_ -like '*Account' ` -or ($_ -like '*Credential' -and $_ -ne 'PsDscRunAsCredential') ` -or $_ -like '*Passphrase' - } + } - foreach ($currentParameter in $credentialParameterToMockCredentialFor) - { - $params.Add($currentParameter, $mockCredential) - } + foreach ($currentParameter in $credentialParameterToMockCredentialFor) + { + $params.Add($currentParameter, $mockCredential) + } - <# + <# If there is a $ConfigurationData variable that was dot-sources. Then use that as the configuration data instead of the mocked confgiuration data. #> - if (Get-Item -Path variable:ConfigurationData -ErrorAction SilentlyContinue) - { - $mockConfigurationData = $ConfigurationData - } + if (Get-Item -Path variable:ConfigurationData -ErrorAction SilentlyContinue) + { + $mockConfigurationData = $ConfigurationData + } - Example @params -ConfigurationData $mockConfigurationData -OutputPath 'TestDrive:\' -ErrorAction Continue -WarningAction SilentlyContinue | Out-Null + Example @params -ConfigurationData $mockConfigurationData -OutputPath 'TestDrive:\' -ErrorAction Continue -WarningAction SilentlyContinue | Out-Null } else { @@ -688,8 +694,8 @@ Describe 'Common Tests - Validate Example Files' -Tag 'Examples' { # Restore the load of the module to ensure future tests have access to it Import-Module -Name (Join-Path -Path $moduleRootFilePath ` - -ChildPath "$moduleName.psd1") ` - -Global + -ChildPath "$moduleName.psd1") ` + -Global } } } @@ -700,11 +706,11 @@ Describe 'Common Tests - Validate Markdown Files' -Tag 'Markdown' { if (Get-Command -Name 'npm' -ErrorAction SilentlyContinue) { $npmParametersForStartProcess = @{ - FilePath = 'npm' - ArgumentList = '' + FilePath = 'npm' + ArgumentList = '' WorkingDirectory = $PSScriptRoot - Wait = $true - WindowStyle = 'Hidden' + Wait = $true + WindowStyle = 'Hidden' } Context 'When installing markdown validation dependencies' { @@ -806,9 +812,9 @@ Describe 'Common Tests - Validate Markdown Files' -Tag 'Markdown' { catch [System.Exception] { Write-Warning -Message ("Unable to run gulp to test markdown files. Please " + ` - "be sure that you have installed nodejs and have " + ` - "run 'npm install -g gulp' in order to have this " + ` - "text execute.") + "be sure that you have installed nodejs and have " + ` + "run 'npm install -g gulp' in order to have this " + ` + "text execute.") } if ($optIn) @@ -871,7 +877,7 @@ Describe 'Common Tests - Validate Markdown Files' -Tag 'Markdown' { { # Remove folder node_modules that npm created. $npmNodeModulesPath = (Join-Path -Path $PSScriptRoot -ChildPath 'node_modules') - if( Test-Path -Path $npmNodeModulesPath) + if ( Test-Path -Path $npmNodeModulesPath) { Remove-Item -Path $npmNodeModulesPath -Recurse -Force } @@ -882,7 +888,7 @@ Describe 'Common Tests - Validate Markdown Files' -Tag 'Markdown' { else { Write-Warning -Message ("Unable to run gulp to test markdown files. Please " + ` - "be sure that you have installed nodejs and npm in order " + ` - "to have this text execute.") + "be sure that you have installed nodejs and npm in order " + ` + "to have this text execute.") } } diff --git a/README.md b/README.md index b5b15f94..566009bb 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,26 @@ New tests may run but only produce errors. Once you fix the test, please copy `.MetaTestOptIn.json` from this repo to the root of your repo. If there is any new problem in the area, this will cause the tests to fail, not just warn. +The following opt-in flags are available: + +* **Common Tests - Validate Module Files**: run tests to validate module files + have correct BOM. +* **Common Tests - Validate Markdown Files**: run tests to validate markdown + files do not violate markdown rules. Markdown rules can be suppressed in + .markdownlint.json file. +* **Common Tests - Validate Example Files**: run tests to validate that examples + can be compiled without error. +* **Common Tests - Validate Script Files**: run tests to validate script files + have correct BOM. +* **Common Tests - Required Script Analyzer Rules**: fail tests if any required + script analyzer rules are violated. +* **Common Tests - Flagged Script Analyzer Rules**: fail tests if any flagged + script analyzer rules are violated. +* **Common Tests - New Error-Level Script Analyzer Rules**: fail tests if any + new error-level script analyzer rules are violated. +* **Common Tests - Custom Script Analyzer Rules**: fail tests if any + custom script analyzer rules are violated. + ### Using AppVeyor.psm1 with eXperiemental DSC Resources An example ```AppVeyor.yml``` file used in an 'experimental' DSC Resource where the @@ -580,6 +600,11 @@ These are the artifacts that differ when running tests using a container. so that it is possible to change the color of the text that is written. * Added module DscResource.Container which contain logic to handle the container testing when unit tests are run in a Docker Windows container. +* Added Get-OptInStatus function to enable retrieving of an opt-in status + by name. This is required for implementation of PSSA opt-in rules where + the describe block contains multiple opt-ins in a single block. +* Added new opt-in flags to allow enforcement of script analyzer rules ([issue #161](https://github.com/PowerShell/DscResource.Tests/issues/161)) +* Updated year in DscResources.Tests.psd1 manifest to 2018. ### 0.2.0.0 diff --git a/TestHelper.psm1 b/TestHelper.psm1 index 1c0ebfe1..04f9ddde 100644 --- a/TestHelper.psm1 +++ b/TestHelper.psm1 @@ -902,12 +902,14 @@ function Get-PesterDescribeOptInStatus { param ( - [string[]]$OptIns + [Parameter(Mandatory = $true)] + [System.String[]] + $OptIns ) $describeName = Get-PesterDescribeName $optIn = $OptIns -icontains $describeName - if(!$optIn) + if (-not $optIn) { $message = @" Describe $describeName will not fail unless you opt-in. @@ -925,21 +927,64 @@ of the repo in the following format: <# .SYNOPSIS - Gets the value of the Name parameter for the specified command in the stack + Gets the opt-in status of an option with the specified name. Writes + a warning if the test is not opted-in. + + .PARAMETER OptIns + An array of what is opted-in. + + .PARAMETER Name + The name of the opt-in option to check the status of. +#> +function Get-OptInStatus +{ + param + ( + [Parameter(Mandatory = $true)] + [System.String[]] + $OptIns, + + [Parameter(Mandatory = $true)] + [System.String] + $Name + ) + + $optIn = $OptIns -icontains $Name + if (-not $optIn) + { + $message = @" +$Name will not fail unless you opt-in. +To opt-in, create a '.MetaTestOptIn.json' at the root +of the repo in the following format: +[ + "$Name" +] +"@ + Write-Warning -Message $message + } + + return $optIn +} + +<# + .SYNOPSIS + Gets the value of the Name parameter for the specified command in the stack. .PARAMETER Command - The name of the command to find the Name parameter for + The name of the command to find the Name parameter for. #> function Get-CommandNameParameterValue { - Param( - [Parameter(Mandatory=$true)] - [string] $Command + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Command ) - $commandStackItem = (Get-PSCallStack).Where{$_.Command -eq $Command} + $commandStackItem = (Get-PSCallStack).Where{ $_.Command -eq $Command } $commandArgumentNameValues = $commandStackItem.Arguments.TrimStart('{',' ').TrimEnd('}',' ') -split '\s*,\s*' - $nameParameterValue = ($commandArgumentNameValues.Where{ $_ -like 'name=*'} -split '=')[-1] + $nameParameterValue = ($commandArgumentNameValues.Where{ $_ -like 'name=*' } -split '=')[-1] return $nameParameterValue } @@ -960,17 +1005,21 @@ function Get-CommandNameParameterValue will return C:\Users\foo\Documents\WindowsPowerShell\Modules #> -function Get-PSModulePathItem { - param( - [Parameter(Mandatory, Position = 0)] - [string] $Prefix +function Get-PSModulePathItem +{ + param + ( + [Parameter(Mandatory = $true, Position = 0)] + [System.String] + $Prefix ) $item = $env:PSModulePath.Split(';') | Where-Object -FilterScript { $_ -like "$Prefix*" } | Select-Object -First 1 - if (!$item) { + if (-not $item) + { Write-Error -Message "Cannot find the requested item in the PowerShell module path.`n`$env:PSModulePath = $env:PSModulePath" } @@ -1534,6 +1583,7 @@ Export-ModuleMember -Function @( 'Reset-DSC', 'Install-NugetExe', 'Get-PesterDescribeOptInStatus', + 'Get-OptInStatus', 'Get-UserProfilePSModulePathItem', 'Get-PSHomePSModulePathItem', 'Test-FileHasByteOrderMark', diff --git a/Tests/Unit/TestHelper.Tests.ps1 b/Tests/Unit/TestHelper.Tests.ps1 index d886aee2..269caa80 100644 --- a/Tests/Unit/TestHelper.Tests.ps1 +++ b/Tests/Unit/TestHelper.Tests.ps1 @@ -405,6 +405,22 @@ InModuleScope $script:ModuleName { } } + Describe 'TestHelper\Get-OptInStatus' { + Context 'When querying for the status of an opted-in test' { + It 'Should return $true when querying for the it status of an opted-in test' { + $result = Get-OptInStatus -OptIns @('Opt-In') -Name 'Opt-In' + $result | Should -Be $true + } + } + + Context 'When querying for the status of an opted-out test' { + It 'Should return $false when querying for the it of an opted-out test' { + $result = Get-OptInStatus -OptIns @('Opt-Out') -Name 'Opt-In' + $result | Should -Be $false + } + } + } + Describe 'TestHelper\Install-NuGetExe' { Context 'When downloading NuGet' { BeforeAll {