Skip to content

Commit 1d32b42

Browse files
committed
Add tests to improve test coverage
1 parent 2b67a4e commit 1d32b42

13 files changed

Lines changed: 2138 additions & 65 deletions

‎Build.build.ps1‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ param(
1616
# Add the clean task before the default build
1717
[switch]$Clean,
1818

19-
# A minimum code coverage percentage to accept as a double: 0.85
20-
[double]$RequiredCodeCoverage = 0.85,
19+
# A minimum code coverage percentage to accept as a double: 0.80 for 80%
20+
[double]$RequiredCodeCoverage = 0.80,
2121

2222
# Which projects to build
2323
[Alias("Projects")]

‎Source/Public/Add-Parameter.ps1‎

Lines changed: 63 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,39 +11,34 @@ function Add-Parameter {
1111
1212
Note that THIS generator does not add parameters to script files directly, but only to functions defined in the InputObject.
1313
.EXAMPLE
14-
$Boilerplate = {
15-
param(
16-
# The Foreground Color (name, #rrggbb, etc)
17-
[Alias('Fg')]
18-
[PoshCode.Pansies.RgbColor]$ForegroundColor,
19-
20-
# The Background Color (name, #rrggbb, etc)
21-
[Alias('Bg')]
22-
[PoshCode.Pansies.RgbColor]$BackgroundColor
23-
)
24-
$ForegroundColor.ToVt() + $BackgroundColor.ToVt($true) + (
25-
Use-OriginalBlock
26-
) +"`e[0m" # Reset colors
27-
}
28-
14+
# The normal way to use Add-Parameter is to set it in the build manifest for your module,
15+
# to add a common set of parameters to certain functions in the module by wildcard name.
16+
#
17+
# Set the Generator to Add-Parameter and pass a file to copy parameters from as `boilerplate`
18+
# Finally, pass the names of the functions you want to copy them to:
19+
@{
20+
ModuleManifest = "./source/TerminalBlocks.psd1"
21+
Generators = @(
22+
@{ Generator = "Add-Parameter"; Boilerplate = "NewTerminalBlock.ps1"; Function = "Show-*", "New-TerminalBlock" }
23+
)
24+
}
25+
.EXAMPLE
26+
# You can also use it through Invoke-ScriptGenerator to add parameters to a function in the wild!
27+
# The BoilerPlate must have a param block with parameters, whether it's a string, script, file path, function, or scriptblock ...
28+
# Remember, Add-Parameter will not replace parameters, so if a parameter name already exists, it will not be touched.
2929
30-
$Source = {
3130
function Show-Date {
3231
param(
3332
# The text to display
3433
[string]$Format
3534
)
3635
Get-Date -Format $Format
3736
}
38-
}
39-
40-
Invoke-ScriptGenerator $Source -Generator Add-Parameter -Parameters @{ FunctionName = "*"; Boilerplate = $Boilerplate } -OutVariable Source
4137
42-
function Show-Date {
38+
${function:Show-Date} = Invoke-ScriptGenerator ${function:Show-Date} -Generator Add-Parameter -Parameters @{
39+
FunctionName = "*";
40+
Boilerplate = {
4341
param(
44-
# The text to display
45-
[string]$Format,
46-
4742
# The Foreground Color (name, #rrggbb, etc)
4843
[Alias('Fg')]
4944
[PoshCode.Pansies.RgbColor]$ForegroundColor,
@@ -52,14 +47,33 @@ function Add-Parameter {
5247
[Alias('Bg')]
5348
[PoshCode.Pansies.RgbColor]$BackgroundColor
5449
)
55-
Get-Date -Format $Format
50+
}
5651
}
52+
53+
Get-Command Show-Date | Format-List
54+
55+
Name : Show-Date
56+
CommandType : Function
57+
Definition :
58+
param(
59+
# The text to display
60+
[string]$Format,
61+
62+
# The Foreground Color (name, #rrggbb, etc)
63+
[Alias('Fg')]
64+
[PoshCode.Pansies.RgbColor]$ForegroundColor,
65+
66+
# The Background Color (name, #rrggbb, etc)
67+
[Alias('Bg')]
68+
[PoshCode.Pansies.RgbColor]$BackgroundColor
69+
)
70+
Get-Date -Format $Format
5771
#>
58-
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
59-
<#RuleId#>'PSReviewUnusedParameter',
60-
<#ParameterName#>'FunctionName',
61-
Justification = 'This parameter IS used, the rule does not understand scopes'
62-
)]
72+
# [Diagnostics.CodeAnalysis.SuppressMessageAttribute(
73+
# <#RuleId#>'PSReviewUnusedParameter',
74+
# <#ParameterName#>'FunctionName',
75+
# Justification = 'This parameter IS used, the rule does not understand scopes'
76+
# )]
6377
[CmdletBinding()]
6478
[OutputType([TextReplacement])]
6579
param(
@@ -143,33 +157,44 @@ function Add-Parameter {
143157

144158
[AstVisitAction] VisitFunctionDefinition([FunctionDefinitionAst]$ast) {
145159
if (!$ast.Where($this.FunctionFilter)) {
160+
Write-Debug "Skipping function $($ast.Name) because it does not match the filter"
146161
return [AstVisitAction]::SkipChildren
147162
}
163+
Write-Debug "Processing function $($ast.Name)"
164+
return [AstVisitAction]::Continue
165+
}
148166

149-
[ParameterExtractor]$ExistingParameters = $ast
167+
[AstVisitAction] VisitParamBlock([ParamBlockAst]$ast) {
168+
169+
# [ParameterExtractor]$ExistingParameters = $ast
170+
$InsertOffset = if ($Ast.Parameters) {
171+
$ast.Parameters[-1].Extent.EndOffset
172+
} else {
173+
$ast.Extent.EndOffset - 1
174+
}
150175

151-
Write-Debug "Existing parameters in $($ast.Name): $($ExistingParameters.Parameters.Name -join ', ')"
176+
Write-Debug "Existing parameters: $($Ast.Parameters.Name -join ', ')"
152177
$global:ParameterSource = $this.ParameterSource
153-
$Additional = $this.ParameterSource.Parameters.Where{ $_.Name -notin $ExistingParameters.Parameters.Name }
178+
$Additional = $this.ParameterSource.Parameters.Where{ $_.Name -notin ([string[]]$Ast.Parameters.Name) }
154179
Write-Debug "Additional parameters from boilerplate: $($Additional.Count)"
155180
if (($Text = $Additional.Text -join ",`n`n")) {
156-
Write-Debug "Adding parameters to $($ast.Name): $($Additional.Name -join ', ')"
181+
Write-Debug "Adding parameters: $($Additional.Name -join ', ')"
157182
$this.Replacements.Add(@{
158-
StartOffset = $ExistingParameters.InsertOffset
159-
EndOffset = $ExistingParameters.InsertOffset
160-
Text = if ($ExistingParameters.Parameters.Count -gt 0) {
183+
StartOffset = $InsertOffset
184+
EndOffset = $InsertOffset
185+
Text = if ($Ast.Parameters.Count -gt 0) {
161186
",`n`n" + $Text
162187
} else {
163188
"`n" + $Text
164189
}
165-
})
190+
})
166191
}
167192
return [AstVisitAction]::SkipChildren
168193
}
169194
}
170195
}
171196
process {
172-
# Write-Debug "Add-Parameter $($InputObject.Extent.File ?? ( "L:" + $InputObject.Extent.StartLineNumber + ".." + $InputObject.Extent.EndLineNumber + " C:" + $InputObject.Extent.StartColumnNumber + ".." + $InputObject.Extent.EndColumnNumber)) $FunctionName $Boilerplate"
197+
Write-Debug "Add-Parameter $($InputObject.Extent.File -ne "scriptblock" ? $InputObject.Extent.File : ( "L:" + $InputObject.Extent.StartLineNumber + ".." + $InputObject.Extent.EndLineNumber + " C:" + $InputObject.Extent.StartColumnNumber + ".." + $InputObject.Extent.EndColumnNumber)) $FunctionName $Boilerplate"
173198

174199
$Generator = [ParameterGenerator]@{
175200
FunctionFilter = { $Func = $_; $FunctionName.ForEach({ $Func.Name -like $_ }) -contains $true }.GetNewClosure()

‎Source/Public/ConvertTo-Script.ps1‎

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,32 @@ function ConvertTo-Script {
66
.SYNOPSIS
77
A Script Generator which converts a module to a script
88
.DESCRIPTION
9-
ConvertTo-Script takes a script module (which may include dotnet assemblies),
10-
and generates a script file with the module and libraries embedded and ready to run.
11-
12-
You should provide the name of a function from the module that will be invoked when the script is run.
9+
ConvertTo-Script takes a script module (which may include assemblies), and the name of a function in that
10+
module, and generates a script file with the same name as the function -- that actually contains the full
11+
module (including assemblies), so you can share it as a single-file script.
12+
13+
It actually copies over the param block and any comments in it, so you get full help for the script.
14+
15+
NOTE: This Script Generator, only generates NEW FILES, so it does not output any TextReplacements.
16+
.EXAMPLE
17+
# The normal way to use ConvertTo-Script is to set it in the build manifest for your module, so you get a
18+
# module, and a script that you can run directly (and upload to the gallery as a script) at the same time.
19+
20+
# Set the Generator to ConvertTo-Script, and pass the function name you want to turn into a script
21+
# You can also pass a GUID to use for the script if you want to make sure future versions use the same ID
22+
@{
23+
ModuleManifest = "./Source/ModuleBuilder.psd1"
24+
CopyDirectories = @('en-US')
25+
Generators = @(
26+
@{ Generator = "ConvertTo-Script"; Function = "Build-Module"; GUID = '6b8e5f3a-2c1d-4e7b-9a4f-1c3e5d7b9a2f' }
27+
)
28+
}
1329
14-
In this way you can package any module into a script which invokes a specific command in that module.
30+
# Now, when we build the module with Build-Module it will generate a Build-Module.ps1 script in the output directory!
31+
.EXAMPLE
32+
# You can also use it through Invoke-ScriptGenerator if you just want to convert an existing function in a module to a script without building it:
1533
16-
This function never outputs any TextReplacements.
34+
$null = Invoke-ScriptGenerator -Path "ModuleBuilder/3.2.0/ModuleBuilder.psm1" -Generator "ConvertTo-Script" -Parameters @{ Function = "Build-Module" }
1735
#>
1836
[CmdletBinding()]
1937
[OutputType([TextReplacement])]
@@ -44,6 +62,10 @@ function ConvertTo-Script {
4462
[string]$FunctionName
4563
[CommentHelpInfo]$HelpInfo
4664

65+
ParamBlockExtractor([string]$FunctionName) {
66+
$this.FunctionName = $FunctionName
67+
}
68+
4769
[AstVisitAction] VisitParamBlock([ParamBlockAst]$ast) {
4870
if ($ast.Parameters) {
4971
$this.ParamBlock = @(@($ast.Attributes.Extent.Text) + $ast.Extent.Text) -join "`n"
@@ -63,9 +85,7 @@ function ConvertTo-Script {
6385
}
6486
process {
6587
Write-Debug " ENTER: ConvertTo-Script PROCESS $Path $FunctionName"
66-
$Visitor = [ParamBlockExtractor]@{
67-
FunctionName = $FunctionName
68-
}
88+
$Visitor = [ParamBlockExtractor]::new($FunctionName)
6989
$ScriptModule.Visit($Visitor)
7090

7191
Write-Debug " Parse Module Manifest: $Path"

‎Source/Public/Merge-ScriptBlock.ps1‎

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,16 +111,17 @@ filter Merge-ScriptBlock {
111111
} else {
112112
$StartOffset = $Extent.StartOffset
113113
}
114-
Write-Debug "Adding Boilerplate for $($Ast.BlockKind)"
114+
115+
# Write-Debug "Adding Boilerplate for $($Ast.BlockKind)"
115116
$this.Replacements.Add(@{
116-
StartOffset = $StartOffset
117-
EndOffset = $Extent.EndOffset
118-
# We end up having to normalize the template to be named blocks...
119-
# Just in case the InputObject has named blocks
120-
Text = "$($Ast.BlockKind) {`n" +
121-
$this.GetExtentText($Template).Replace("Use-OriginalBlock", $this.GetExtentText($Ast)) +
122-
"`n}"
123-
})
117+
StartOffset = $StartOffset
118+
EndOffset = $Extent.EndOffset
119+
# We end up having to normalize the template to be named blocks...
120+
# Just in case the InputObject has named blocks
121+
Text = "$($Ast.BlockKind) {`n" +
122+
$this.GetExtentText($Template).Replace("Use-OriginalBlock", $this.GetExtentText($Ast)) +
123+
"`n}"
124+
})
124125
}
125126
}
126127

@@ -129,7 +130,8 @@ filter Merge-ScriptBlock {
129130
if (!$Ast.Where($this.FunctionFilter)) {
130131
return [AstVisitAction]::SkipChildren
131132
}
132-
Write-Debug "Merging $($Ast.Name) with boilerplate"
133+
134+
# Write-Debug "Merging $($Ast.Name) with boilerplate"
133135
$this.Replace($this.BeginBlockTemplate, $Ast.Body.BeginBlock)
134136
$this.Replace($this.ProcessBlockTemplate, $Ast.Body.ProcessBlock)
135137
$this.Replace($this.EndBlockTemplate, $Ast.Body.EndBlock)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#requires -Module ModuleBuilder
2+
. $PSScriptRoot\..\Convert-FolderSeparator.ps1
3+
4+
Describe "When we use Generators" -Tag Integration {
5+
BeforeAll {
6+
$Output = Build-Module $PSScriptRoot\Source2\build.psd1 -Passthru
7+
$Module = [IO.Path]::ChangeExtension($Output.Path, "psm1")
8+
$Metadata = Import-Metadata $Output.Path
9+
}
10+
11+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
@{
22
Path = "Source2.psd1"
33
OutputDirectory = "..\Result2"
4+
Generators = @(
5+
@{ Generator = "ConvertTo-Script"; Function = "Set-Source"; GUID = '6b8e5f3a-2c1d-4e7b-9a4f-1c3e5d7b9a2f' }
6+
)
47
}

‎Tests/Public/Add-Parameter.Tests.ps1‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#requires -Module ModuleBuilder
2-
31
Describe "Add-Parameter" {
42
Context "Example 1. Adding parameters to functions" {
53
It "Adds boilerplate parameters to functions" {
@@ -30,11 +28,19 @@ Describe "Add-Parameter" {
3028
param()
3129
[Environment]::UserName
3230
}
31+
32+
function Get-Date {
33+
param(
34+
# The Date Format String
35+
[string]$Format = "o"
36+
)
37+
[DateTime]::Now.ToString($Format)
38+
}
3339
}
3440

3541
# Use Invoke-ScriptGenerator instead of calling Add-Parameter directly, to get the result of the transformation as text
3642
$result = Invoke-ScriptGenerator -Code $source -Generator Add-Parameter -Parameters @{
37-
FunctionName = "*"
43+
FunctionName = "Show-*"
3844
Boilerplate = $boilerplate
3945
}
4046

@@ -65,6 +71,16 @@ Describe "Add-Parameter" {
6571
$showUserName | Should -Not -BeNullOrEmpty
6672
$showUserName.Body.ParamBlock.Parameters.Name.VariablePath.UserPath
6773
| Should -Be @('ForegroundColor', 'BackgroundColor')
74+
75+
# Get-Date Should not be modified, since it does not match the FunctionName filter
76+
$getDate = $Ast.Find({
77+
param($node)
78+
$node -is [System.Management.Automation.Language.FunctionDefinitionAst] -and
79+
$node.Name -eq 'Get-Date'
80+
}, $true)
81+
$getDate | Should -Not -BeNullOrEmpty
82+
$getDate.Body.ParamBlock.Parameters.Name.VariablePath.UserPath
83+
| Should -Be @('Format')
6884
}
6985
}
7086
}

‎Tests/Public/Build-Module.Tests.ps1‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#requires -Module ModuleBuilder
21
Describe "Build-Module" {
32
BeforeAll {
43
. $PSScriptRoot\..\Convert-FolderSeparator.ps1

‎Tests/Public/ConvertFrom-SourceLineNumber.Tests.ps1‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#requires -Module ModuleBuilder
21
Describe "ConvertFrom-SourceLineNumber" {
32
# use the integration test code
43
BeforeAll {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Describe "ConvertTo-Script" {
2+
Context "Example 1. Extracting a function from a module" {
3+
BeforeAll {
4+
$ModuleBuilder = Get-Module ModuleBuilder | Select-Object -First 1
5+
$ModuleBuilder.Path | Should -Exist
6+
7+
$BuildModuleCommand = Get-Command Build-Module
8+
$BuildModuleCommand | Should -Not -BeNullOrEmpty
9+
10+
$ScriptFile = $ModuleBuilder.Path | Split-Path | Join-Path -ChildPath "Build-Module.ps1"
11+
$ScriptFile | Should -Not -Exist
12+
}
13+
AfterAll {
14+
Remove-Item $ScriptFile
15+
}
16+
17+
It "Generates a script for the specified function" {
18+
$global:DebugPreference = "Continue"
19+
$null = Invoke-ScriptGenerator -Path $ModuleBuilder.Path -Generator "ConvertTo-Script" -Parameters @{ FunctionName = "Build-Module" }
20+
21+
$global:DebugPreference = "SilentlyContinue"
22+
$ScriptFile | Should -Exist
23+
$ScriptFileCommand = Get-Command $ScriptFile
24+
$ScriptFileCommand.Parameters.Keys | Sort-Object | Should -Be ($BuildModuleCommand.Parameters.Keys | Sort-Object)
25+
}
26+
}
27+
}

0 commit comments

Comments
 (0)