Test-Command.ps1
function Test-Command { <# .Synopsis Test-Command checks commands for consistency. .Description Test-Command checks commands for consistency. Test-Command run a series of static analysis rules on your script, and helps you see if there's anything to improve. It will not run any script, just look at the information about the script, like it's help, command metadata, or the script content itself. .Example Get-Module ScriptCop | Test-Command .Example Get-Command -Type Cmdlet | Test-Command .Example Get-Command Get-Command | Test-Command .Link about_ScriptCop_rules #> [CmdletBinding(DefaultParameterSetName='Command')] param( # The command or module to test. If the object is not a module info or command # info, it will not work. [Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=0,ParameterSetName='Command')] [ValidateScript({ if ($_ -is [Management.Automation.CommandInfo]) { return $true } if ($_ -is [Management.Automation.PSModuleInfo]) { return $true } if ($_ -is [IO.FileInfo]) { return $true } if ($_ -is [string]) { return $true } throw "Input must either be a command, a module, a file, or a name" })] $Command, # A script block containing functions, for instance: function foo {}. # Script Blocks that do not contain functions will be ignored. [Parameter(Mandatory=$true,ParameterSetName='ScriptBlock',Position=0)] [ScriptBlock] $ScriptBlock, # The scriptcop 'patrol' (list of rules) to run #|MaxLength 255 #|Options Get-ScriptCopPatrol | Select-Object -ExpandProperty Name [string]$Patrol, # The name of the rule to run #|Options Get-ScriptCopRule | Select-Object -ExpandProperty Name [String[]]$Rule, # Rules to avoid running. #|Options Get-ScriptCopRule | Select-Object -ExpandProperty Name [String[]]$ExcludedRule ) begin { Set-StrictMode -Off $CommandMetaData = @() $ModuleMetaData = @() function WriteScriptCopError{ param([switch]$IsModuleError) if ($ScriptCopError) { foreach ($e in $ScriptCopError) { if (-not $e) { continue } $result = New-Object PSObject -Property @{ Rule = if ($IsModuleError) { $Module } else { $testCmd } Problem = $e ItemWithProblem = if ($IsModuleError) { $ModuleInfo} else { $CommandInfo } } $result.psObject.TypeNames.Add("ScriptCopError") $result } } } $progressId = Get-Random } process { Write-Progress "Collecting Commands" "$command " -Id $progressId if ($psCmdlet.ParameterSetName -eq 'Command') { if ($command -is [string]) { $cmds = @(Get-Command $command -ErrorAction Silentlycontinue) } elseif ($command -is [Management.Automation.PSModuleInfo]) { $cmds = @($command.ExportedFunctions.Values) + $command.ExportedCmdlets.Values $ModuleMetaData += $command } elseif ($command -is [Management.Automation.CommandInfo]) { $cmds = @($command) } elseif ($command -is [IO.FileInfo]) { $cmds = @(Get-Command $command.FullName) } } elseif ($psBoundParameters.Scriptblock) { $functionOnly = Get-FunctionFromScript -ScriptBlock $psBoundParameters.ScriptBlock $cmds = @() foreach ($f in $functionOnly) { . ([ScriptBlock]::Create($f)) $matched = $f -match "function ((\w+-\w+)|(\w+))" if ($matched -and $matches[1]) { $cmds+=Get-Command $matches[1] } } } if ($cmds) { Write-Progress "Collecting Command Details" " " -Id $progressId $c = 0 foreach ($cmd in $cmds) { $c++ $perc = $c * 100 / $cmds.Count Write-Progress "Collecting Command Details" "$cmd" -PercentComplete $perc -Id $progressId $help = $cmd | Get-Help $CommandMetaData += @{ Command = $cmd Function = $(if ($cmd -is [Management.Automation.FunctionInfo]) { $cmd }) Application = $(if ($cmd -is [Management.Automation.ApplicationInfo]) { $cmd }) ExternalScript = $(if ($cmd -is [Management.Automation.ExternalScriptInfo]) { $cmd }) Cmdlet = $(if ($cmd -is [Management.Automation.CmdletInfo]) { $cmd }) Help = $(if ($help -and $help -isnot [string]) { $help }) Tokens = $( if ($cmd -is [Management.Automation.FunctionInfo]) { [Management.Automation.PSParser]::Tokenize($cmd.scriptblock,[ref]$null) } elseif ($cmd -is [Management.Automation.ExternalScriptInfo]) { [Management.Automation.PSParser]::Tokenize($cmd.scriptcontents,[ref]$null) } ) Text = $( if ($cmd -is [Management.Automation.FunctionInfo]) { "function $($cmd.Name) { $($cmd.definition) }" } elseif ($cmd -is [Management.Automation.ExternalScriptInfo]) { $cmd.scriptcontents } ) } } Write-Progress "Collecting Command Details" " " -ID $progressId } } end { Write-Progress 'Filtering Rules' ' ' -Id $progressId $currentRules = @{} + $script:ScriptCopRules $RuleNameMatch = { $Rule -contains $_.Name -or $Rule -contains $_.Name.Replace(".ps1","") } if ($Rule) { $currentRules.TestCommandInfo = @($currentRules.TestCommandInfo | Where-Object $RuleNameMatch) $currentRules.TestCmdletInfo = @($currentRules.TestCmdletInfo | Where-Object $RuleNameMatch) $currentRules.TestScriptInfo = @($currentRules.TestScriptInfo | Where-Object $RuleNameMatch) $currentRules.TestFunctionInfo = @($currentRules.TestFunctionInfo | Where-Object $RuleNameMatch) $currentRules.TestApplicationInfo= @($currentRules.TestApplicationInfo | Where-Object $RuleNameMatch) $currentRules.TestModuleInfo = @($currentRules.TestModuleInfo | Where-Object $RuleNameMatch) $currentRules.TestScriptToken = @($currentRules.TestScriptToken | Where-Object $RuleNameMatch) $currentRules.TestHelpContent = @($currentRules.TestHelpContent | Where-Object $RuleNameMatch) } $ExcludedRuleNotMatch = { $ExcludedRule -notcontains $_.Name -and $ExcludedRule -notcontains $_.Name.Replace(".ps1","") } if ($ExcludedRule) { $currentRules.TestCommandInfo = @($currentRules.TestCommandInfo | Where-Object $ExcludedRuleNotMatch) $currentRules.TestCmdletInfo = @($currentRules.TestCmdletInfo | Where-Object $ExcludedRuleNotMatch) $currentRules.TestScriptInfo = @($currentRules.TestScriptInfo | Where-Object $ExcludedRuleNotMatch) $currentRules.TestFunctionInfo = @($currentRules.TestFunctionInfo | Where-Object $ExcludedRuleNotMatch) $currentRules.TestApplicationInfo= @($currentRules.TestApplicationInfo | Where-Object $ExcludedRuleNotMatch) $currentRules.TestModuleInfo = @($currentRules.TestModuleInfo | Where-Object $ExcludedRuleNotMatch) $currentRules.TestScriptToken = @($currentRules.TestScriptToken | Where-Object $ExcludedRuleNotMatch) $currentRules.TestHelpContent = @($currentRules.TestHelpContent | Where-Object $ExcludedRuleNotMatch) } if ($patrol) { $commandRules = Get-ScriptCopPatrol -Name $patrol | Select-Object -ExpandProperty CommandRule -ErrorAction SilentlyContinue $moduleRules = Get-ScriptCopPatrol -Name $patrol | Select-Object -ExpandProperty ModuleRule -ErrorAction SilentlyContinue $patrolCommandMatch = $RuleNameMatch = { $commandRules -contains $_.Name -or $commandRules -contains $_.Name.Replace(".ps1","") } $patrolModuleMatch = $RuleNameMatch = { $moduleRules -contains $_.Name -or $moduleRules -contains $_.Name.Replace(".ps1","") } $currentRules.TestCommandInfo = @($currentRules.TestCommandInfo | Where-Object $patrolCommandMatch) $currentRules.TestCmdletInfo = @($currentRules.TestCmdletInfo | Where-Object $patrolCommandMatch) $currentRules.TestScriptInfo = @($currentRules.TestScriptInfo | Where-Object $patrolCommandMatch) $currentRules.TestFunctionInfo = @($currentRules.TestFunctionInfo | Where-Object $patrolCommandMatch) $currentRules.TestApplicationInfo= @($currentRules.TestApplicationInfo | Where-Object $patrolCommandMatch) $currentRules.TestModuleInfo = @($currentRules.TestModuleInfo | Where-Object $patrolModuleMatch) $currentRules.TestScriptToken = @($currentRules.TestScriptToken | Where-Object $patrolCommandMatch) $currentRules.TestHelpContent = @($currentRules.TestHelpContent | Where-Object $patrolCommandMatch) } Write-Progress "Running ScriptCop" "Validating Modules" -Id $ProgressId if ($currentRules.TestModuleInfo) { $c = 0 $ruleCount = @($currentRules.TestModuleInfo).Count foreach ($module in $currentRules.TestModuleInfo){ $c++ $perc = $c * 100 / $ruleCount Write-Progress "Running ScriptCop" "Validating Modules - $($module.Name)" -PercentComplete $perc -Id $ProgressId if ($scriptCopError) {$scriptCopError = $null } $ModuleMetaData | ForEach-Object { $moduleInfo = $_ $null = $_ | & $Module -ErrorAction SilentlyContinue -ErrorVariable ScriptCopError WriteScriptCopError -IsModuleError } } } #region Validating Commands Write-Progress "Running ScriptCop" "Validating Command Metadata" -Id $ProgressId if ($currentRules.TestCommandInfo) { $c = 0 $ruleCount = @($currentRules.TestCommandInfo).Count foreach ($testCmd in $currentRules.TestCommandInfo){ $c++ $perc = $c * 100 / $RuleCount Write-Progress "Running ScriptCop" "Validating Command Metadata - $($testCmd.Name)" -Id $ProgressId -PercentComplete $perc if ($scriptCopError) {$scriptCopError = $null } $CommandMetaData | Where-Object { $_.Command } | ForEach-Object { $commandInfo = $_.Command $null = $commandInfo | & $testCmd -ErrorAction SilentlyContinue -ErrorVariable ScriptCopError WriteScriptCopError } } } #endregion Validating Commands #region Validating Cmdlets Write-Progress "Running ScriptCop" "Validating Cmdlet Metadata" -Id $ProgressId if ($currentRules.TestCmdletInfo) { $c = 0 $ruleCount = @($currentRules.TestCmdletInfo).Count foreach ($testCmd in $currentRules.TestCmdletInfo){ $c++ $perc = $c * 100 / $ruleCount Write-Progress "Running ScriptCop" "Validating Cmdlet Metadata - $($testCmd.Name)" -Id $ProgressId -PercentComplete $perc if ($scriptCopError) {$scriptCopError = $null } $CmdletMetaData | Where-Object { $_.Cmdlet } | ForEach-Object { $commandInfo = $_.Cmdlet $null = $commandInfo | & $testCmd -ErrorAction SilentlyContinue -ErrorVariable ScriptCopError WriteScriptCopError } } } #endregion Validating Cmdlets #region Validating Functions Write-Progress "Running ScriptCop" "Validating Functions" -Id $ProgressId if ($currentRules.TestFunctionInfo) { $c = 0 $ruleCount = @($currentRules.TestFunctionInfo).Count foreach ($testCmd in $currentRules.TestFunctionInfo){ $c++ $perc = $c * 100 / $ruleCount Write-Progress "Running ScriptCop" "Validating Function Metadata - $($testCmd.Name)" -Id $ProgressId -PercentComplete $perc if ($scriptCopError) {$scriptCopError = $null } $CommandMetaData | Where-Object { $_.Function } | ForEach-Object { $commandInfo = $_.Function $null = $commandInfo | & $testCmd -ErrorAction SilentlyContinue -ErrorVariable ScriptCopError WriteScriptCopError } } } #endregion Validating Functions #region Validating Applications Write-Progress "Running ScriptCop" "Validating Applications Metadata" -Id $ProgressId if ($currentRules.TestApplicationInfo) { $c = 0 $ruleCount = @($currentRules.TestApplicationInfo).Count foreach ($testCmd in $currentRules.TestApplicationInfo){ $c++ $perc = $c * 100 / $ruleCount Write-Progress "Running ScriptCop" "Validating Applications Metadata - $($testCmd.Name)" -Id $ProgressId -PercentComplete $perc if ($scriptCopError) {$scriptCopError = $null } $CommandMetaData | Where-Object { $_.Application } | ForEach-Object { $commandInfo = $_.Application $null = $commandInfo | & $testCmd -ErrorAction SilentlyContinue -ErrorVariable ScriptCopError WriteScriptCopError } } } #endregion Validating Applications #region Validating Scripts Write-Progress "Running ScriptCop" "Validating Script Metadata" -Id $ProgressId if ($currentRules.TestScriptInfo) { $c = 0 $ruleCount = @($currentRules.TestScriptInfo).Count foreach ($testCmd in $currentRules.TestScriptInfo){ $c++ $perc = $c * 100 / $ruleCount Write-Progress "Running ScriptCop" "Validating Script Metadata - $($testCmd.Name)" -Id $ProgressId -PercentComplete $perc if ($scriptCopError) {$scriptCopError = $null } $CommandMetaData | Where-Object { $_.Script } | ForEach-Object { $commandInfo = $_.Script $null = $commandInfo | & $testCmd -ErrorAction SilentlyContinue -ErrorVariable ScriptCopError WriteScriptCopError } } } #endregion Validating Scripts #region Validating Help Write-Progress "Running ScriptCop" "Validating Help" -Id $ProgressId if ($currentRules.TestHelpContent) { $c = 0 $ruleCount = @($currentRules.TestHelpContent).Count foreach ($testCmd in $currentRules.TestHelpContent){ $c++ $perc = $c * 100 / $ruleCount Write-Progress "Running ScriptCop" "Validating Help - $($testCmd.Name)" -Id $ProgressId -PercentComplete $perc if ($scriptCopError) {$scriptCopError = $null } $CommandMetaData | ForEach-Object { $commandInfo = $_.Command $null = & $testCmd -HelpCommand $_.Command -HelpContent $_.Help -ErrorAction SilentlyContinue -ErrorVariable ScriptCopError WriteScriptCopError } } } #endregion Validating Help #region Validating Tokens Write-Progress "Running ScriptCop" "Validating Tokens" -Id $ProgressId if ($currentRules.TestScriptToken) { $c = 0 $ruleCount = @($currentRules.TestScriptToken).Count foreach ($testCmd in $currentRules.TestScriptToken){ $c++ $perc = $c * 100 / $ruleCount Write-Progress "Running ScriptCop" "Validating Tokens - $($testCmd.Name)" -Id $ProgressId -PercentComplete $perc if ($scriptCopError) {$scriptCopError = $null } $CommandMetaData | ForEach-Object { $commandInfo = $_.Command if ($_.Tokens -and $_.Text) { $null = & $testCmd -ScriptTokenCommand $_.Command -ScriptToken $_.Tokens -ScriptText "$($_.Text)" -ErrorAction SilentlyContinue -ErrorVariable ScriptCopError WriteScriptCopError } } } } #endregion Validating Tokens } } |