Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Issue with Modules Koans #399

Merged
merged 14 commits into from Jul 5, 2020
32 changes: 10 additions & 22 deletions .vscode/launch.json
Expand Up @@ -5,37 +5,19 @@
"version": "0.2.0",
"configurations": [
{
"name": "PowerShell Launch Current File",
"name": "PowerShell Launch Script",
"type": "PowerShell",
"request": "launch",
"script": "${file}",
"args": [],
"cwd": "${file}"
},
{
"name": "PowerShell Launch Current File in Temporary Console",
"type": "PowerShell",
"request": "launch",
"script": "${file}",
"args": [],
"cwd": "${file}",
"createTemporaryIntegratedConsole": true
"script": "./Start-DebugSession.ps1",
"cwd": "${workspaceFolder}"
},
{
"name": "PowerShell Launch Current File w/Args Prompt",
"name": "PowerShell: Launch Current File",
"type": "PowerShell",
"request": "launch",
"script": "${file}",
"args": [
"${command:SpecifyScriptArgs}"
],
"cwd": "${file}"
},
{
"name": "PowerShell Attach to Host Process",
"type": "PowerShell",
"request": "attach"
},
{
"name": "PowerShell Interactive Session",
"type": "PowerShell",
Expand All @@ -47,6 +29,12 @@
"type": "PowerShell",
"request": "attach",
"processId": "current"
},
{
"name": "PowerShell Attach to Host Process",
"type": "PowerShell",
"request": "attach",
"runspaceId": 1
}
]
}
48 changes: 40 additions & 8 deletions PSKoans/Private/Invoke-Koan.ps1
Expand Up @@ -27,25 +27,57 @@
)
end {
try {
$Requirements = [System.Management.Automation.Language.Parser]::ParseFile(
$ParameterSplat.Script,
[ref]$null,
[ref]$null
).Ast.ScriptRequirements

$Script = {
param( $Params )
param( $Params, $RequiredModules, $PSKoansPath, $PSModulePath )

[System.Collections.Generic.HashSet[string]] $ModulePaths = @(
$PSModulePath -split [System.IO.Path]::PathSeparator
$env:PSModulePath -split [System.IO.Path]::PathSeparator
)

$env:PSModulePath = $ModulePaths -join [System.IO.Path]::PathSeparator

Get-Module $PSKoansPath -ListAvailable | Import-Module
foreach ($module in $RequiredModules) {
Import-Module $module
}

. ([scriptblock]::Create('using module PSKoans'))
Invoke-Pester @Params
}

$Thread = [powershell]::Create()
$Thread.AddScript($Script) > $null
$Thread.AddArgument($ParameterSplat) > $null
$Runspace = [powershell]::Create()
$Runspace.AddScript($Script) > $null
$Runspace.AddParameter('Params', $ParameterSplat) > $null
$Runspace.AddParameter('PSKoansPath', $MyInvocation.MyCommand.Module.ModuleBase) > $null
$Runspace.AddParameter('PSModulePath', $env:PSModulePath) > $null

$Status = $Thread.BeginInvoke()
if ($Requirements.RequiredModules) {
$Runspace.AddParameter('RequiredModules', $Requirements.RequiredModules)
}

$Status = $Runspace.BeginInvoke()

do { Start-Sleep -Milliseconds 1 } until ($Status.IsCompleted)

$Thread.EndInvoke($Status)
$Result = $Runspace.EndInvoke($Status)

if ($Runspace.HadErrors) {
# These will be errors outside the test itself; better propagate them upwards.
foreach ($errorItem in $Runspace.Streams.Error) {
$PSCmdlet.WriteError($errorItem)
}
}

$Result
}
finally {
$Thread.Dispose()
$Runspace.Dispose()
}
}
}
30 changes: 28 additions & 2 deletions PSKoans/Public/Get-Karma.ps1
Expand Up @@ -37,7 +37,7 @@
'ListKoans' {
Get-PSKoan @GetParams
}
"Default" {
default {
Write-Verbose 'Sorting koans...'
try {
$SortedKoanList = Get-PSKoan @GetParams
Expand All @@ -46,6 +46,17 @@
$PSCmdlet.ThrowTerminatingError($_)
}

foreach ($item in $IncludeModule) {
if ($SortedKoanList.Where{ $_.Module -notlike $item }.Count -eq 0) {
$warningString = @(
"Koans for a module name matching '$item' were not found in your user directory."
"Please run Update-PSKoan -Module $item to ensure those modules are present."
) -join ' '

Write-Warning $warningString
}
}

Write-Verbose "Koan files retrieved: $($SortedKoanList.Count)"
Write-Verbose 'Counting koans...'
[int]$TotalKoans = $SortedKoanList | Measure-Koan
Expand All @@ -68,9 +79,24 @@
$PSCmdlet.ThrowTerminatingError( (New-PSKoanErrorRecord @ErrorDetails) )
}

if ($Module) {
# No koans were found because modules aren't copied by default.
Update-PSKoan -Module $Module -Confirm:$false
Get-Karma @PSBoundParameters

return
}

# Something's wrong; possibly a koan folder from older versions, or a folder exists but has no files
$Modules = if ($IncludeModule) {
@{ IncludeModule = $IncludeModule }
}
else {
@{ }
}

Write-Warning 'No koans found in your koan directory. Initiating full reset...'
Update-PSKoan -Confirm:$false
Update-PSKoan -Confirm:$false @Modules
Get-Karma @PSBoundParameters # Re-call ourselves with the same parameters

return # Skip the rest of the function
Expand Down
24 changes: 15 additions & 9 deletions PSKoans/Public/Get-PSKoan.ps1
Expand Up @@ -45,25 +45,31 @@ function Get-PSKoan {
'Module' { Join-Path -Path $script:ModuleRoot -ChildPath 'Koans' }
}

if ($pscmdlet.ParameterSetName -eq 'ListModules') {
$moduleList = Join-Path -Path $ParentPath -ChildPath 'Modules' |
Get-ChildItem -Directory |
Select-Object -ExpandProperty Name
if ($PSCmdlet.ParameterSetName -eq 'ListModules') {
$modulesPath = Join-Path -Path $ParentPath -ChildPath 'Modules'

return $moduleList
if (Test-Path $modulesPath) {
$modulesPath |
Get-ChildItem -Directory |
Select-Object -ExpandProperty Name
}

return
}

$KoanDirectories = switch ($pscmdlet.ParameterSetName) {
$KoanDirectories = switch ($PSCmdlet.ParameterSetName) {
'IncludeModule' {
$Module = $IncludeModule
Get-ChildItem $ParentPath -Exclude Modules -Directory
}
{ $Module } {
$ModuleRegex = ConvertFrom-WildcardPattern -Pattern $Module

Join-Path -Path $ParentPath -ChildPath 'Modules' |
Get-ChildItem -Directory |
Where-Object { $_.Name -match $ModuleRegex }
$modulesPath = Join-Path -Path $ParentPath -ChildPath 'Modules'
if (Test-Path $modulesPath) {
Get-ChildItem $modulesPath -Directory |
Where-Object { $_.Name -match $ModuleRegex }
}
}
}

Expand Down
31 changes: 23 additions & 8 deletions PSKoans/Public/Show-Karma.ps1
Expand Up @@ -5,32 +5,43 @@ function Show-Karma {
[Alias('Invoke-PSKoans', 'Test-Koans', 'Get-Enlightenment', 'Meditate', 'Clear-Path', 'Measure-Karma')]
param(
[Parameter(ParameterSetName = 'ListKoans')]
[Parameter(ParameterSetName = 'ListKoans-ModuleOnly')]
[Parameter(ParameterSetName = 'ListKoans-IncludeModule')]
[Parameter(ParameterSetName = 'ModuleOnly')]
[Parameter(ParameterSetName = 'IncludeModule')]
[Parameter(ParameterSetName = 'OpenFile')]
[Parameter(ParameterSetName = 'OpenFile-ModuleOnly')]
[Parameter(ParameterSetName = 'OpenFile-IncludeModule')]
[Parameter(ParameterSetName = 'Default')]
[Alias('Koan', 'File')]
[SupportsWildcards()]
[string[]]
$Topic,

[Parameter(Mandatory, ParameterSetName = 'ModuleOnly')]
[Parameter(ParameterSetName = 'ListKoans')]
[Parameter(Mandatory, ParameterSetName = 'ListKoans-ModuleOnly')]
[Parameter(Mandatory, ParameterSetName = 'OpenFile-ModuleOnly')]
[SupportsWildcards()]
[string[]]
$Module,

[Parameter(Mandatory, ParameterSetName = 'IncludeModule')]
[Parameter(Mandatory, ParameterSetName = 'ListKoans-IncludeModule')]
[Parameter(Mandatory, ParameterSetName = 'OpenFile-IncludeModule')]
[SupportsWildcards()]
[string[]]
$IncludeModule,

[Parameter(Mandatory, ParameterSetName = 'ListKoans')]
[Parameter(Mandatory, ParameterSetName = 'ListKoans-ModuleOnly')]
[Parameter(Mandatory, ParameterSetName = 'ListKoans-IncludeModule')]
[Alias('ListKoans', 'ListTopics')]
[switch]
$List,

[Parameter(Mandatory, ParameterSetName = 'OpenFile')]
[Parameter(Mandatory, ParameterSetName = 'OpenFile-ModuleOnly')]
[Parameter(Mandatory, ParameterSetName = 'OpenFile-IncludeModule')]
[Alias('Meditate')]
[switch]
$Contemplate,
Expand All @@ -55,13 +66,13 @@ function Show-Karma {

$GetParams = @{ }
switch ($PSCmdlet.ParameterSetName) {
'IncludeModule' { $GetParams['IncludeModule'] = $IncludeModule }
'ModuleOnly' { $GetParams['Module'] = $Module }
{ $_ -match 'IncludeModule$' } { $GetParams['IncludeModule'] = $IncludeModule }
{ $_ -match 'ModuleOnly$' } { $GetParams['Module'] = $Module }
{ $PSBoundParameters.ContainsKey('Topic') } { $GetParams['Topic'] = $Topic }
}

switch ($PSCmdlet.ParameterSetName) {
'ListKoans' {
{ $_ -match '^ListKoans' } {
Get-PSKoan @GetParams
}
'OpenFolder' {
Expand All @@ -86,7 +97,7 @@ function Show-Karma {
$KoanLocation | Invoke-Item
}
}
'OpenFile' {
{ $_ -match '^OpenFile' } {
# If there is no cached data, we need to call Get-Karma to populate it
if (-not $script:CurrentTopic -or ($Topic -and $script:CurrentTopic.Name -notlike $Topic)) {
try {
Expand All @@ -99,7 +110,12 @@ function Show-Karma {
}

$Editor = Get-PSKoanSetting -Name Editor
$FilePath = (Get-PSKoan -Topic $script:CurrentTopic.Name -Scope User).Path
$KoanParams = @{
Topic = $script:CurrentTopic.Name
IncludeModule = @( $Module; $IncludeModule )
Scope = 'User'
}
$FilePath = (Get-PSKoan @KoanParams).Path
$LineNumber = $script:CurrentTopic.CurrentLine

$Arguments = switch ($Editor) {
Expand Down Expand Up @@ -140,8 +156,7 @@ function Show-Karma {
try {
Get-Karma @GetParams |
Format-Custom @FormatParams |
Out-String |
Write-Host
Out-Host
}
catch {
$PSCmdlet.ThrowTerminatingError($_)
Expand Down
11 changes: 11 additions & 0 deletions Start-DebugSession.ps1
@@ -0,0 +1,11 @@
$env:PSModulePath = $(
@(
$env:PSModulePath -split [System.IO.Path]::PathSeparator
$PSScriptRoot
) | Select-Object -Unique
) -join [System.IO.Path]::PathSeparator

& $PSScriptRoot/PSKoans.ezformat.ps1
Import-Module $PSScriptRoot/PSKoans

### Enter code to test below
16 changes: 8 additions & 8 deletions Tests/Functions/Public/Show-Karma.Tests.ps1
Expand Up @@ -25,7 +25,7 @@ Describe 'Show-Karma' {

Context 'Default Behaviour' {
BeforeAll {
Mock Write-Host { }
Mock Out-Host { }
Mock Get-Karma -ModuleName 'PSKoans' {
[PSCustomObject]@{
PSTypeName = 'PSKoans.Result'
Expand All @@ -50,7 +50,7 @@ Describe 'Show-Karma' {
}

It 'should write the formatted output to host' {
Assert-MockCalled Write-Host
Assert-MockCalled Out-Host
}

It 'should call Get-Karma to examine the koans' {
Expand All @@ -60,7 +60,7 @@ Describe 'Show-Karma' {

Context 'With All Koans Completed' {
BeforeAll {
Mock Write-Host { }
Mock Out-Host { }
Mock Get-Karma -ModuleName 'PSKoans' {
[PSCustomObject]@{
PSTypeName = 'PSKoans.CompleteResult'
Expand All @@ -80,7 +80,7 @@ Describe 'Show-Karma' {
Context 'With -ClearScreen Switch' {
BeforeAll {
Mock Clear-Host { }
Mock Write-Host { }
Mock Out-Host { }
Mock Get-Karma -ModuleName 'PSKoans' {
[PSCustomObject]@{
PSTypeName = 'PSKoans.Result'
Expand Down Expand Up @@ -109,7 +109,7 @@ Describe 'Show-Karma' {
}

It 'should display the rendered output' {
Assert-MockCalled Write-Host
Assert-MockCalled Out-Host
}

It 'should Invoke-Pester on each of the koans' {
Expand All @@ -119,7 +119,7 @@ Describe 'Show-Karma' {

Context 'With Nonexistent Koans Folder / No Koans Found' {
BeforeAll {
Mock Write-Host { }
Mock Out-Host { }
Mock Get-PSKoan -ModuleName 'PSKoans' { }
Mock Update-PSKoan -ModuleName 'PSKoans' { throw 'Prevent recursion' }
Mock Write-Warning
Expand Down Expand Up @@ -174,7 +174,7 @@ Describe 'Show-Karma' {

Context 'With -Topic Parameter' {
BeforeAll {
Mock Write-Host { }
Mock Out-Host { }
Mock Get-Karma -MockWith {
[PSCustomObject]@{
PSTypeName = 'PSKoans.Result'
Expand Down Expand Up @@ -204,7 +204,7 @@ Describe 'Show-Karma' {
Context 'With All Koans in a Single Topic Completed' {
BeforeAll {
Mock Format-Custom { $InputObject.Complete }
Mock Write-Host { }
Mock Out-Host { }
Mock Get-Karma -ModuleName 'PSKoans' {
[PSCustomObject]@{
PSTypeName = 'PSKoans.CompleteResult'
Expand Down