Skip to content

Invoke build tools directly in sample tests instead of Invoke-Expression - #726

Merged
Nikola Metulev (nmetulev) merged 9 commits into
mainfrom
azchohfi-sample-tests-direct-invocation
Aug 12, 2026
Merged

Nikola Metulev (nmetulev) merged 9 commits into
mainfrom
azchohfi-sample-tests-direct-invocation

Conversation

@azchohfi

Copy link
Copy Markdown
Collaborator

What

The sample Pester tests invoked dotnet, npm, npx, and cargo by composing a command string and handing it to Invoke-Expression:

Invoke-Expression "dotnet build -c Debug -r $rid"

Invoke-WinappCommand in SampleTestHelpers.psm1 did the same for the CLI, including interpolating a resolved project path into the dotnet run fallback. Temp directory paths flow through these strings, so a path containing a quote or other parser-significant character would be re-interpreted as syntax rather than passed through as data.

How

Call the executables directly, so arguments never round-trip through the parser.

Invoke-WinappCommand keeps its existing single -Arguments string parameter, so all 71 call sites are unchanged. A small ConvertTo-ArgumentList helper splits that string into discrete arguments — honoring single and double quotes — and the result is splatted onto the resolved executable:

$argList = ConvertTo-ArgumentList -Arguments $Arguments
$output = & $exe @argList

Changing every call site to pass arrays would have been the alternative, but it touches 71 lines across 14 files for no additional safety, since the tokenizer reproduces the same splitting the parser was doing.

Validation

All 15 sample scripts parse clean. The tokenizer was checked against the argument shapes actually used in these tests, including quoted paths with spaces and quoted values containing =:

'pack "C:\my path\out" --manifest Package.appxmanifest --cert devcert.pfx'
  -> [pack] [C:\my path\out] [--manifest] [Package.appxmanifest] [--cert] [devcert.pfx]

'cert generate --publisher "CN=Sparse Guide" --if-exists skip'
  -> [cert] [generate] [--publisher] [CN=Sparse Guide] [--if-exists] [skip]

Test-only change; no product code is touched.

The sample Pester tests ran dotnet, npm, npx, and cargo by composing a command
string and passing it to Invoke-Expression, and Invoke-WinappCommand did the
same for the CLI itself. Temp directory paths flow into those strings, so a path
containing a quote or other parser-significant character would be re-interpreted
as syntax rather than passed through as data.

Call the executables directly. Invoke-WinappCommand keeps its single -Arguments
string parameter so all 71 call sites are unchanged; a small ConvertTo-ArgumentList
tokenizer splits that string into discrete arguments, honoring single and double
quotes, and the result is splatted onto the resolved executable.
Copilot AI balanced review requested due to automatic review settings August 11, 2026 18:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Removes Invoke-Expression from sample tests and invokes build tools directly.

Changes:

  • Directly invokes dotnet, npm, npx, and cargo.
  • Adds argument tokenization for Invoke-WinappCommand.
  • Passes npm package paths directly.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
samples/SampleTestHelpers.psm1 Reworks CLI and npm invocation.
samples/dotnet-app/test.Tests.ps1 Directly invokes dotnet.
samples/electron/test.Tests.ps1 Directly invokes npm and npx tools.
samples/rust-app/test.Tests.ps1 Directly invokes Cargo.
samples/sparse-app/test.Tests.ps1 Directly invokes dotnet.
samples/tauri-app/test.Tests.ps1 Directly invokes npm and Cargo.
samples/winui-app/test.Tests.ps1 Directly invokes dotnet.
samples/winui-solution/test.Tests.ps1 Directly invokes dotnet.
samples/winui-unpackaged-app/test.Tests.ps1 Directly invokes dotnet.
samples/wpf-app/test.Tests.ps1 Directly invokes dotnet.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +82 to +85
} elseif ($ch -eq '"' -or $ch -eq "'") {
$quote = $ch
$hasContent = $true
} elseif ([char]::IsWhiteSpace($ch)) {
@github-actions

github-actions Bot commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

Build Metrics Report

Binary Sizes

Artifact Baseline Current Delta
CLI (ARM64) 38.62 MB 38.62 MB ✅ 0.0 KB (0.00%)
CLI (x64) 38.73 MB 38.73 MB ✅ 0.0 KB (0.00%)
MSIX (ARM64) N/A 16.02 MB N/A
MSIX (x64) N/A 17.01 MB N/A
NPM Package N/A 33.42 MB N/A
NuGet Package N/A 33.46 MB N/A

Test Results

✅ 4555 passed, 5 skipped out of 4560 tests in 584.7s (+1 test, -133.7s vs. baseline)

Test Coverage

✅ 89.1% line coverage, 82.4% branch coverage · ✅ no change vs. baseline

CLI Startup Time

46ms median (x64, winapp --version) · ⚠️ +12ms vs. baseline

Try This Build

Installs the MSIX for your architecture, replacing any previously installed build. Needs the GitHub CLI — the command offers to install it and sign you in if it is missing.

& ([scriptblock]::Create((irm https://raw.githubusercontent.com/microsoft/winappCli/main/scripts/winapp-pr.ps1))) 726
Switching between builds often?

Put the tool on your PATH once:

& ([scriptblock]::Create((irm https://raw.githubusercontent.com/microsoft/winappCli/main/scripts/winapp-pr.ps1))) -AddToPath

Then this build is just:

winapp-pr 726

Run winapp-pr with no arguments to pick from a list of open PRs.


Updated 2026-08-12 18:53:16 UTC · commit 614d1a5 · workflow run

PowerShell unrolls a single-element array on output, so a one-token command
such as "restore" came back as a String rather than String[]. Splatting a
scalar string passes it one character at a time, turning `winapp restore` into
`winapp r e s t o r e`, which failed with a non-zero exit.

This only affected callers that resolve winapp from PATH; the npx branch
prepends an element and so always had an array, which is why cpp-app and
flutter-app failed while electron passed.

Return with a unary comma so the array survives the pipeline.
ConvertTo-ArgumentList carries the whole risk of this change and nothing tested
it directly, which is how the single-element unrolling defect reached CI. The
sample matrix catches it, but only after a full run.

Add scripts/tests/SampleTestHelpers.Tests.ps1 covering the return shape, the
splat behavior that actually broke, and the argument forms the 71 call sites
use. Point the existing scripts Pester run at the scripts/tests directory rather
than the single MS Learn file so anything added there runs too.
@azchohfi
Alexandre Zollinger Chohfi (azchohfi) marked this pull request as ready for review August 12, 2026 04:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants