You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add Windows unpackaged device tests on Helix alongside packaged tests (dotnet#33702)
## Summary
Adds Windows **unpackaged** (self-contained EXE) device test support on
Helix alongside the existing **packaged** (MSIX) tests. Previously,
unpackaged tests were disabled due to Windows App SDK bootstrap failures
(exit code `0xC000027B`) on Helix workers that lack the pre-installed
runtime.
## What changed
### WindowsAppSDKSelfContained for unpackaged builds
- Created shared
`src/Microsoft.Maui.TestUtils.DeviceTests.Runners.props` with
`WindowsAppSDKSelfContained=true`, conditioned on
`_MauiDeviceTestUnpackaged=true` (set by Cake during unpackaged builds
only)
- Each device test csproj imports it via `<Import
Project="$(MauiSrcDirectory)Microsoft.Maui.TestUtils.DeviceTests.Runners.props"
/>`
- This bundles the Windows App SDK runtime with the EXE so Helix workers
don't need it pre-installed
- The property does **not** apply to packaged (MSIX) builds or propagate
to library dependencies
> **Why not `Directory.Build.props`?** We tried centralizing via
`Directory.Build.props` files in each device test folder, but importing
the root `Directory.Build.props` chain pulls in Arcade SDK which
redirects `BaseOutputPath` → `artifacts/bin/`, which also redirects
`AppxPackageDir` — so `AppPackages/` no longer appears at the project
directory where the zip step expects it, breaking packaged (MSIX)
builds. The direct `<Import>` from csproj avoids this while still
centralizing the property in one shared file (mirrors the existing
`Runners.targets` pattern).
### Pipeline changes (`stage-device-tests.yml`)
- Build both unpackaged and packaged for each project (unpackaged first,
output saved, then packaged)
- Zip archives now contain both `AppPackages/` (MSIX) and `Unpackaged/`
(EXE) folders
- Helix submission creates two work items per project: `{name}-packaged`
and `{name}-unpackaged`
- Proper `##vso` error/warning annotations for missing artifacts
> **Why build unpackaged first?** Both builds use `dotnet publish` on
the same project. The packaged build writes MSIX to
`{ProjectDir}/AppPackages/` while the unpackaged build writes to
`artifacts/bin/.../publish/`. Building unpackaged first and saving the
publish output ensures both outputs survive for the zip step.
### Helix test runner (`run-windows-devicetests.cmd`)
- New script that handles both modes:
- **Packaged**: installs certificate, MSIX dependencies, and app package
via `Add-AppxPackage`
- **Unpackaged**: runs the self-contained EXE directly
- Installs Windows App SDK runtime on Helix workers
- Category-based test execution with configurable timeout
### Cake build script (`windows.cake`)
- Passes `_MauiDeviceTestUnpackaged=true` and `WindowsPackageType=None`
for unpackaged builds
- Passes certificate thumbprint and `SelfContained=True` for packaged
builds
## Files changed
| File | Change |
|------|--------|
| `src/Microsoft.Maui.TestUtils.DeviceTests.Runners.props` | **New** —
shared `WindowsAppSDKSelfContained` property |
| `eng/pipelines/arcade/stage-device-tests.yml` | Build orchestration,
zip creation, Helix submission |
| `eng/devices/windows.cake` | Cake build properties for
packaged/unpackaged |
| `eng/devices/run-windows-devicetests.cmd` | **New** — Helix test
runner script |
| `eng/helix_xharness.proj` | Helix work item definitions |
| 5 × `*.DeviceTests.csproj` | `<Import>` of shared `Runners.props` |
## Test results (Build 1297857)
All 10 Windows Helix work items passed:
| Project | Packaged | Unpackaged |
|---------|----------|------------|
| Controls.DeviceTests | ✅ | ✅ |
| Core.DeviceTests | ✅ | ✅ |
| Essentials.DeviceTests | ✅ | ✅ |
| Graphics.DeviceTests | ✅ | ✅ |
| MauiBlazorWebView.DeviceTests | ✅ | ✅ |
Verified via Helix console logs that packaged tests install and launch
MSIX, while unpackaged tests run the EXE directly — both code paths are
exercised.
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Write-Host "##vso[task.logissue type=error]Microsoft.ui.xaml.dll missing from unpackaged output for $($project.Name)! Tests will crash with 0xC000027B."
551
+
}
493
552
} else {
494
-
Write-Host "Warning: $appPackagesDir not found for $($project.Name)"
553
+
Write-Host "##vso[task.logissue type=warning]Unpackaged output not found for $($project.Name)"
Copy file name to clipboardExpand all lines: src/BlazorWebView/tests/DeviceTests/MauiBlazorWebView.DeviceTests.csproj
+2Lines changed: 2 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -15,6 +15,8 @@
15
15
<RuntimeIdentifierCondition="$(TargetFramework.Contains('-maccatalyst')) and '$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)' == 'arm64'">maccatalyst-arm64</RuntimeIdentifier>
0 commit comments