diff --git a/PSCMake/Common/CMake.ps1 b/PSCMake/Common/CMake.ps1 index 9e77ca9..08e259a 100644 --- a/PSCMake/Common/CMake.ps1 +++ b/PSCMake/Common/CMake.ps1 @@ -37,22 +37,6 @@ $CMakeCandidates = @( } ) -<# - .Synopsis - Invokes CMake. - - .Description - A function wrapping calls to CMake, allowing the calls to be mocked for testing. -#> -function InvokeCMake { - param( - [string] $CMakePath, - [string[]] $Arguments - ) - Write-Verbose "CMake Arguments: $Arguments" - & $CMakePath @Arguments -} - <# .Synopsis Finds the root of the CMake build - the current or ancestral folder containing a 'CMakePresets.json' file. diff --git a/PSCMake/PSCMake.psm1 b/PSCMake/PSCMake.psm1 index 4fcb884..331861b 100644 --- a/PSCMake/PSCMake.psm1 +++ b/PSCMake/PSCMake.psm1 @@ -30,6 +30,22 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot/Common/Common.ps1 . $PSScriptRoot/Common/Ninja.ps1 +<# + .Synopsis + Invokes an executable. + + .Description + A function wrapping calls to '&', allowing the calls to be mocked for testing. +#> +function InvokeExecutable { + param( + [string] $Path, + [string[]] $Arguments + ) + Write-Verbose "Invoking: $Path $Arguments" + & $Path @Arguments +} + <# .Synopsis An argument-completer for `Build-CMakeBuild`'s `-Preset` parameter. @@ -129,7 +145,7 @@ function BuildTargetsCompleter { <# .Synopsis - An argument-completer for `Build-CMakeBuild`'s `-Targets` parameter. + An argument-completer for `Invoke-CMakeOutput`'s `-Target` parameter. #> function ExecutableTargetsCompleter { param( @@ -210,7 +226,7 @@ function ConfigureCMake { } ) - InvokeCMake $CMake $CMakeArguments + InvokeExecutable $CMake $CMakeArguments if ($LASTEXITCODE -ne 0) { Write-Error "Configuration failed. Command line: '$($CMake.Source)' $($CMakeArguments -join ' ')" } @@ -410,7 +426,7 @@ function Build-CMakeBuild { ) $StartTime = [datetime]::Now - InvokeCMake $CMake $CMakeArguments + InvokeExecutable $CMake $CMakeArguments if ($LASTEXITCODE -ne 0) { Write-Error "Build failed. Command line: '$($CMake.Source)' $($CMakeArguments -join ' ')" } @@ -563,7 +579,7 @@ function Invoke-CMakeOutput { Write-Output "Running: $TargetPath $Arguments" Write-Output '----' - & $TargetPath @Arguments + InvokeExecutable $TargetPath $Arguments } Register-ArgumentCompleter -CommandName Invoke-CMakeOutput -ParameterName Preset -ScriptBlock $function:BuildPresetsCompleter diff --git a/Tests/Build-CMakeBuild.Tests.ps1 b/Tests/Build-CMakeBuild.Tests.ps1 index d886001..57b8e49 100644 --- a/Tests/Build-CMakeBuild.Tests.ps1 +++ b/Tests/Build-CMakeBuild.Tests.ps1 @@ -16,7 +16,7 @@ BeforeAll { # Mock subsequent calls to invoke CMake so that we don't actually try to build anything. $script:CMakeCalls = @() - Mock -ModuleName PSCMake InvokeCMake { + Mock -ModuleName PSCMake InvokeExecutable { param( [string] $CMakePath, [string[]] $Arguments diff --git a/Tests/Configure-CMakeBuild.Tests.ps1 b/Tests/Configure-CMakeBuild.Tests.ps1 index c635c95..a74ad90 100644 --- a/Tests/Configure-CMakeBuild.Tests.ps1 +++ b/Tests/Configure-CMakeBuild.Tests.ps1 @@ -15,11 +15,12 @@ BeforeAll { Import-Module -Force $PSScriptRoot/../PSCMake/PSCMake.psd1 -DisableNameChecking # Mock subsequent calls to invoke CMake so that we don't actually try to build anything. - Mock -ModuleName PSCMake InvokeCMake { + Mock -ModuleName PSCMake InvokeExecutable { param( [string] $CMakePath, [string[]] $Arguments ) + $null = $CMakePath $script:CMakeCalls += , $Arguments } } diff --git a/Tests/ExecutableTargetsCompleter.Tests.ps1 b/Tests/ExecutableTargetsCompleter.Tests.ps1 new file mode 100644 index 0000000..1950d66 --- /dev/null +++ b/Tests/ExecutableTargetsCompleter.Tests.ps1 @@ -0,0 +1,25 @@ +#Requires -PSEdition Core + +BeforeAll { + . $PSScriptRoot/TestUtilities.ps1 + . $PSScriptRoot/ReferenceBuild.ps1 + + $Properties = PrepareReferenceBuild + + $CMake = "$env:ProgramFiles/CMake/bin/cmake.exe" + & $CMake @Properties + + Import-Module -Force $PSScriptRoot/../PSCMake/PSCMake.psd1 -DisableNameChecking +} + +Describe 'ExecutableTargetsCompleter' { + It 'Returns the executable targets' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + $Completions = Get-CommandCompletion "Invoke-CMakeOutput -Target " + $Completions.CompletionMatches.CompletionText | Should -Be @( + 'SubDirectoryOther_Executable', + 'SubDirectory_Executable' + ) + } + } +} diff --git a/Tests/Invoke-CMakeOutput.Tests.ps1 b/Tests/Invoke-CMakeOutput.Tests.ps1 new file mode 100644 index 0000000..c4c3b22 --- /dev/null +++ b/Tests/Invoke-CMakeOutput.Tests.ps1 @@ -0,0 +1,81 @@ +#Requires -PSEdition Core + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +BeforeAll { + . $PSScriptRoot/TestUtilities.ps1 + . $PSScriptRoot/ReferenceBuild.ps1 + + $Properties = PrepareReferenceBuild + + $CMake = "$env:ProgramFiles/CMake/bin/cmake.exe" + & $CMake @Properties + + Import-Module -Force $PSScriptRoot/../PSCMake/PSCMake.psd1 -DisableNameChecking + + # Mock subsequent calls to invoke CMake so that we don't actually try to build anything. + Mock -ModuleName PSCMake InvokeExecutable { + param( + [string] $Path, + [string[]] $Arguments + ) + $script:ExecutableCalls += [PSCustomObject]@{Path = $Path; Arguments = $Arguments} + } +} + +Describe 'Invoke-CMakeOutput' { + BeforeEach { + $script:ExecutableCalls = @() + } + + It 'Builds then invokes a specified target' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Invoke-CMakeOutput -Target SubDirectory_Executable + + $script:ExecutableCalls | Should -HaveCount 2 + $script:ExecutableCalls[0].Arguments | Should -Be @('--build', '--preset', 'windows-x64', '--target', 'SubDirectory_Executable') + $script:ExecutableCalls[1].Arguments | Should -BeNullOrEmpty + $script:ExecutableCalls[1].Path | Should -Be @("$PSScriptRoot\ReferenceBuild\__output\windows-x64\SubDirectory\Debug\SubDirectory_Executable.exe") + } + } + + It 'Builds then invokes a specified target - configuration specified' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Invoke-CMakeOutput -Target SubDirectory_Executable -Configuration Release + + $script:ExecutableCalls | Should -HaveCount 2 + $script:ExecutableCalls[0].Arguments | Should -Be @('--build', '--preset', 'windows-x64', '--config', 'Release', '--target', 'SubDirectory_Executable') + $script:ExecutableCalls[1].Arguments | Should -BeNullOrEmpty + $script:ExecutableCalls[1].Path | Should -Be @("$PSScriptRoot\ReferenceBuild\__output\windows-x64\SubDirectory\Release\SubDirectory_Executable.exe") + } + } + + It 'Passes extra parameters to the invocation' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + Invoke-CMakeOutput -Target SubDirectory_Executable chunky bacon + + $script:ExecutableCalls | Should -HaveCount 2 + $script:ExecutableCalls[0].Arguments | Should -Be @('--build', '--preset', 'windows-x64', '--target', 'SubDirectory_Executable') + $script:ExecutableCalls[1].Arguments | Should -Be @('chunky', 'bacon') + $script:ExecutableCalls[1].Path | Should -Be @("$PSScriptRoot\ReferenceBuild\__output\windows-x64\SubDirectory\Debug\SubDirectory_Executable.exe") + } + } + + It 'Runs a single in-scope executable' { + Using-Location "$PSScriptRoot/ReferenceBuild/SubDirectory" { + Invoke-CMakeOutput + + $script:ExecutableCalls | Should -HaveCount 2 + $script:ExecutableCalls[0].Arguments | Should -Be @('--build', '--preset', 'windows-x64', '--target', 'SubDirectory_Executable') + $script:ExecutableCalls[1].Arguments | Should -BeNullOrEmpty + $script:ExecutableCalls[1].Path | Should -Be @("$PSScriptRoot\ReferenceBuild\__output\windows-x64\SubDirectory\Debug\SubDirectory_Executable.exe") + } + } + + It 'Fails with multiple executable targets in scope' { + Using-Location "$PSScriptRoot/ReferenceBuild" { + { Invoke-CMakeOutput } | Should -Throw "Multiple executable scoped targets match. Specify a target explicitly: SubDirectoryOther_Executable SubDirectory_Executable" + } + } +}