-
-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathPesterInterface.ps1
More file actions
193 lines (166 loc) · 6.82 KB
/
PesterInterface.ps1
File metadata and controls
193 lines (166 loc) · 6.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
using namespace System.Collections
using namespace System.Collections.Generic
using namespace Pester
[CmdletBinding(PositionalBinding = $false)]
param(
#Path(s) to search for tests. Paths can also contain line numbers (e.g. /path/to/file:25)
[Parameter(ValueFromRemainingArguments)][String[]]$Path = $PWD,
#Only return the test information, don't actually run them. Also returns minimal output
[Switch]$Discovery,
#Only load the functions but don't execute anything. Used for testing.
[Parameter(DontShow)][Switch]$LoadFunctionsOnly,
#If specified, emit the output objects as a flattened json to the specified named pipe handle. Used for IPC to the extension host.
#If this value is the special value 'stdout' or undefined, then the output object is written to stdout.
[String]$PipeName,
#The verbosity to pass to the system
[String]$Verbosity,
#If specified, the shim will write to a temporary file at Pipename path and this script will output what would have been written to the stream. Useful for testing.
[Switch]$DryRun,
#An optional custom path to the Pester module.
[String]$CustomModulePath,
#Include ANSI characters in output
[Switch]$IncludeAnsi,
#Specify the path to a PesterConfiguration.psd1 file. The script will also look for a PesterConfiguration.psd1 in the current working directory.
[String]$ConfigurationPath
)
[Console]::OutputEncoding = [Text.Encoding]::UTF8
try {
$modulePath = if ($CustomModulePath) { Resolve-Path $CustomModulePath -ErrorAction Stop } else { 'Pester' }
Import-Module -Name $modulePath -MinimumVersion '5.2.0' -ErrorAction Stop
} catch {
if ($PSItem.FullyQualifiedErrorId -ne 'Modules_ModuleWithVersionNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand') { throw }
throw [NotSupportedException]'Pester 5.2.0 or greater is required to use the Pester Tests extension but was not found on your system. Please install the latest version of Pester from the PowerShell Gallery.'
}
if ($psversiontable.psversion -ge '7.2.0') {
if ($IncludeAnsi) {
$PSStyle.OutputRendering = 'ANSI'
} else {
$PSStyle.OutputRendering = 'PlainText'
}
}
if ($PSVersionTable.PSEdition -eq 'Desktop') {
#Defaults to inquire
$DebugPreference = 'continue'
}
Write-Debug "Home: $env:HOME"
Write-Debug "PSModulePath: $env:PSModulePath"
Write-Debug "OutputRendering: $($PSStyle.OutputRendering)"
filter Import-PrivateModule ([Parameter(ValueFromPipeline)][string]$Path) {
<#
.SYNOPSIS
This function imports a module from a file into a private variable and does not expose it via Get-Module.
.NOTES
Thanks to @SeeminglyScience for the inspiration
#>
#We dont use namespaces here to keep things portable
$absolutePath = Resolve-Path $Path -ErrorAction Stop
[Management.Automation.Language.Token[]]$tokens = $null
[Management.Automation.Language.ParseError[]]$errors = $null
[Management.Automation.Language.ScriptBlockAst]$scriptBlockAST = [Management.Automation.Language.Parser]::ParseFile($absolutePath, [ref]$tokens, [ref]$errors)
if ($errors) {
$errors | ForEach-Object { Write-Error $_.Message }
return
}
return [psmoduleinfo]::new($scriptBlockAst.GetScriptBlock())
}
function Register-PesterPlugin ([hashtable]$PluginConfiguration) {
<#
.SYNOPSIS
Utilizes a private Pester API to register the plugin.
#>
$Pester = (Get-Command Invoke-Pester -ErrorAction Stop).Module
& $Pester {
param($PluginConfiguration)
if ($null -ne $SCRIPT:additionalPlugins -and $testAdapterPlugin.Name -in $SCRIPT:additionalPlugins.Name) {
Write-Debug "PesterInterface: $($testAdapterPlugin.Name) is already registered. Skipping..."
return
}
if ($null -eq $SCRIPT:additionalPlugins) {
$SCRIPT:additionalPlugins = @()
}
$testAdapterPlugin = New-PluginObject @PluginConfiguration
$SCRIPT:additionalPlugins += $testAdapterPlugin
} $PluginConfiguration
}
function Unregister-PesterPlugin ([hashtable]$PluginConfiguration) {
<#
.SYNOPSIS
Utilizes a private Pester API to unregister the plugin.
#>
$Pester = (Get-Command Invoke-Pester -ErrorAction Stop).Module
& $Pester {
param($PluginConfiguration)
if (-not $SCRIPT:additionalPlugins) {
Write-Debug 'PesterInterface: No plugins are registered. Skipping...'
return
}
$plugin = $SCRIPT:additionalPlugins | Where-Object Name -EQ $PluginConfiguration.Name
if (-not $plugin) {
Write-Debug "PesterInterface: $($PluginConfiguration.Name) is not registered. Skipping..."
return
}
$SCRIPT:additionalPlugins = $SCRIPT:additionalPlugins | Where-Object Name -NE $PluginConfiguration.Name
} $PluginConfiguration
}
#endregion Functions
#Main Function
function Invoke-Main {
$pluginModule = Import-PrivateModule $PSScriptRoot/PesterTestPlugin.psm1
$configArgs = @{
Discovery = $Discovery
PipeName = $PipeName
DryRun = $DryRun
}
#This syntax may seem strange but it allows us to inject state into the plugin.
$plugin = & $pluginModule {
param($externalConfigArgs) New-PesterTestAdapterPluginConfiguration @externalConfigArgs
} $configArgs
try {
Register-PesterPlugin $plugin
# These should be unique which is why we use a hashset
[HashSet[string]]$paths = @()
[HashSet[string]]$lines = @()
# Including both the path and the line speeds up the script by limiting the discovery surface
# Specifying just the line will still scan all files
$Path.foreach{
if ($PSItem -match '(?<Path>.+?):(?<Line>\d+)$') {
[void]$paths.Add($matches['Path'])
[void]$lines.Add($PSItem)
} else {
[void]$paths.Add($PSItem)
}
}
[PesterConfiguration]$config = if ($PesterPreference) {
Write-Debug "$PesterPreference Detected, using for base configuration"
$pesterPreference
} elseif ($ConfigurationPath) {
Write-Debug "-ConfigurationPath $ConfigurationPath was specified, looking for configuration"
$resolvedConfigPath = Resolve-Path $ConfigurationPath -ErrorAction Stop
Write-Debug "Pester Configuration found at $ConfigurationPath, using as base configuration."
Import-PowerShellDataFile $resolvedConfigPath -ErrorAction Stop
} elseif (Test-Path './PesterConfiguration.psd1') {
Write-Debug "PesterConfiguration.psd1 found in test root directory $cwd, using as base configuration."
Import-PowerShellDataFile './PesterConfiguration.psd1' -ErrorAction Stop
} else {
New-PesterConfiguration
}
$config.Run.SkipRun = [bool]$Discovery
$config.Run.PassThru = $true
#If Verbosity is $null it will use PesterPreference
if ($Discovery) { $config.Output.Verbosity = 'None' }
elseif ($Verbosity) { $config.Output.Verbosity = $Verbosity }
if ($paths.Count) {
$config.Run.Path = [string[]]$paths #Cast to string array is required or it will error
}
if ($lines.Count) {
$config.Filter.Line = [string[]]$lines #Cast to string array is required or it will error
}
Invoke-Pester -Configuration $config | Out-Null
} catch {
throw
} finally {
Unregister-PesterPlugin $plugin
}
}
#Run Main function
if (-not $LoadFunctionsOnly) { Invoke-Main }