Add packaged-app identity/AUMID groundwork and Windows gating to PackagedApp extension#9914
Conversation
The PackagedApp launcher's only real launch path was CopyDirectory + Process.Start, which cannot launch a packaged (MSIX) WinUI/UWP app; VSTest gets registration + AUMID activation from VS-internal deployment components (IVsAppContainerProjectDeploy2, IDeploymentServicesProvider) that are not redistributable. This adds the parts that need no VS-internal API: - AppxManifestInfo: parses AppxManifest.xml and computes PackageFamilyName + Application User Model ID using the public Windows publisher-hash algorithm (SHA-256 of the UTF-16LE publisher, first 8 bytes, Crockford base32). Pure managed, cross-platform, unit-tested against the well-known 8wekyb3d8bbwe vector. - IsEnabledAsync now gates on OperatingSystem.IsWindows(), so on non-Windows the launcher is never registered and the platform keeps its default in-process path (instead of being forced onto the controller/deploy path). - LaunchTestHostAsync now detects a packaged layout (AppxManifest.xml) and fails fast with an actionable message including the AUMID activation would use, referencing #2784, instead of silently starting an executable that cannot host the run. Non-packaged (loose-layout) hosts keep the existing Process.Start path. Registration (PackageManager.RegisterPackageByUriAsync) + AUMID activation (IApplicationActivationManager) and the sandbox connect-back transport remain a follow-up (#2784). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c5c7d634-e822-4475-9312-a9dde92753f6
There was a problem hiding this comment.
Pull request overview
This PR adds groundwork in the Microsoft.Testing.Extensions.PackagedApp extension to recognize packaged (MSIX) layouts by parsing AppxManifest.xml to compute PFN/AUMID, gates the extension to Windows-only registration, and fails fast with an actionable error when a packaged layout is detected (since AUMID activation isn’t implemented yet).
Changes:
- Add
AppxManifestInfoto parseAppxManifest.xmland computePackageFamilyNameandAppUserModelId(AUMID), plus unit tests for the publisher-hash algorithm and manifest parsing. - Gate
PackagedAppTestHostLauncher.IsEnabledAsync()toOperatingSystem.IsWindows()and reject packaged layouts (manifest present) with a localized, actionable error. - Update resources (
.resx+.xlf) and internal API tracking; expose internals to the unit test assembly.
Show a summary per file
| File | Description |
|---|---|
| test/UnitTests/Microsoft.Testing.Extensions.UnitTests/Microsoft.Testing.Extensions.UnitTests.csproj | Conditionally reference the PackagedApp extension only for .NETCoreApp TFMs. |
| test/UnitTests/Microsoft.Testing.Extensions.UnitTests/AppxManifestInfoTests.cs | New unit tests covering publisher-id hashing and manifest parsing edge cases. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/AppxManifestInfo.cs | New manifest parser + PFN/AUMID computation. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/PackagedAppTestHostLauncher.cs | Windows gating + fail-fast rejection for packaged layouts; updated doc remarks. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/Resources/ExtensionResources.resx | Add new localized strings for invalid manifest and unsupported packaged launch. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/Resources/xlf/ExtensionResources.cs.xlf | Add new trans-units for the new resource keys. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/Resources/xlf/ExtensionResources.de.xlf | Add new trans-units for the new resource keys. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/Resources/xlf/ExtensionResources.es.xlf | Add new trans-units for the new resource keys. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/Resources/xlf/ExtensionResources.fr.xlf | Add new trans-units for the new resource keys. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/Resources/xlf/ExtensionResources.it.xlf | Add new trans-units for the new resource keys. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/Resources/xlf/ExtensionResources.ja.xlf | Add new trans-units for the new resource keys. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/Resources/xlf/ExtensionResources.ko.xlf | Add new trans-units for the new resource keys. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/Resources/xlf/ExtensionResources.pl.xlf | Add new trans-units for the new resource keys. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/Resources/xlf/ExtensionResources.pt-BR.xlf | Add new trans-units for the new resource keys. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/Resources/xlf/ExtensionResources.ru.xlf | Add new trans-units for the new resource keys. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/Resources/xlf/ExtensionResources.tr.xlf | Add new trans-units for the new resource keys. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/Resources/xlf/ExtensionResources.zh-Hans.xlf | Add new trans-units for the new resource keys. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/Resources/xlf/ExtensionResources.zh-Hant.xlf | Add new trans-units for the new resource keys. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/Microsoft.Testing.Extensions.PackagedApp.csproj | Add InternalsVisibleTo for the unit test assembly. |
| src/Platform/Microsoft.Testing.Extensions.PackagedApp/InternalAPI/InternalAPI.Unshipped.txt | Track newly introduced internal API surface for AppxManifestInfo. |
Review details
- Files reviewed: 20/20 changed files
- Comments generated: 3
- Review effort level: Low
This comment has been minimized.
This comment has been minimized.
Regenerated via UpdateXlf. The last localized check-in added translations for 'GlobalTestFixtureShouldBeValidClassLayout' (fr) and 'SlowTestStillRunning' (ru) that XliffTasks rejects (placeholder mismatch), breaking the build with "xlf is out-of-date with resx". UpdateXlf resets those two units to state="new" so the build passes; they will be re-translated on the next localization pass. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 4350f662-0e5d-4e05-b156-1ef619da3b9e
Responds to Copilot review feedback on the PackagedApp extension: - Replace TryReadFromLayout (a Try* method that could still throw on parse/IO errors) with GetManifestPath, a truly non-throwing existence probe that returns the manifest path or null; the launcher now does an explicit ReadFromManifest when a packaged layout is detected. - Add PackagedAppTestHostLauncherTests: IsEnabledAsync is enabled only on Windows (runs on both Windows and non-Windows CI legs), and LaunchTestHostAsync fails fast on a packaged layout with an actionable message carrying the computed AUMID (or package family name when no Application is declared). - Update AppxManifestInfoTests to cover GetManifestPath (including that it never parses). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c5c7d634-e822-4475-9312-a9dde92753f6
This comment has been minimized.
This comment has been minimized.
Adds PackagedAppLaunchNotSupportedTests, an end-to-end acceptance test that bakes an AppxManifest.xml (Name=Contoso.MyTestApp, Publisher=CN=Contoso, Application Id=App) next to a real Microsoft.Testing.Platform test host that calls AddPackagedAppDeployment(). Running the host exercises the full controller/launch path: the PackagedApp launcher detects the packaged layout and throws instead of Process.Start-ing it. The asset catches the exception and returns a sentinel exit code, so the test deterministically asserts the failure and that the message stays actionable (contains the computed AUMID Contoso.MyTestApp_h91ms92gdsmmt!App and the #2784 tracking URL). The manifest is baked into an immutable per-fixture asset (not mutated at test time), so it is parallel-safe. Windows-only, matching the launcher's OS gating; on other OSes the launcher stays disabled and the layout is never inspected. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c5c7d634-e822-4475-9312-a9dde92753f6
|
Added end-to-end acceptance coverage for the packaged-layout fail-fast in |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
…criptions - AppxManifestInfo now parses all <Application> entries and adds ResolveApplication(executableFileName) that disambiguates by executable and rejects ambiguous matches, instead of always selecting the first application. The launcher reports the AUMID of the app matching the requested test host. - Update the extension description, NuGet PackageDescription, and PACKAGE.md to distinguish the supported non-packaged (loose-layout) path from the unsupported packaged (MSIX) path pending #2784; regenerate xlf. - Update InternalAPI and unit tests accordingly. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a7e0a98b-132a-4c4d-9d3a-4764c1504e05
…g issue - Add UTF-8 BOM to the PackagedApp .cs files in the diff to match the repo convention (.editorconfig requires utf-8-bom for *.cs). - Point the packaged-app 'not implemented yet' references to the dedicated tracker #9933 instead of #2784 (which tracks unpackaged WinUI and states MSIX already works). - Update the AddPackagedAppDeployment XML docs to state only non-packaged (loose-layout) hosts are launched today and packaged (MSIX) layouts are rejected. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a7e0a98b-132a-4c4d-9d3a-4764c1504e05
Update TestingPlatformBuilderHook and the test host handle summaries to state only non-packaged (loose-layout) hosts are launched today and packaged (MSIX) layouts are rejected (tracked by #9933). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a7e0a98b-132a-4c4d-9d3a-4764c1504e05
This comment has been minimized.
This comment has been minimized.
Application/@eXecutable in an MSIX manifest can be package-root-relative (e.g. bin\\host.exe), so the manifest may live in a parent of the executable's directory. Add AppxManifestInfo.FindManifestPath, which walks up from the executable's directory to the nearest AppxManifest.xml, and use it in the launcher so such valid layouts are still detected instead of falling through to Process.Start. ResolveApplication now matches on the executable file name so a subdirectory-qualified Executable still resolves. Added unit tests. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a7e0a98b-132a-4c4d-9d3a-4764c1504e05
This comment has been minimized.
This comment has been minimized.
UWP apps always require package identity and an AppxManifest.xml, so they are inherently packaged and are rejected by the launcher today. Only genuinely unpackaged hosts (for example, unpackaged WinUI) use the implemented copy-and-launch path. Update PACKAGE.md, the NuGet PackageDescription, the extension resource description, and the public XML docs (PackagedAppExtensions, TestingPlatformBuilderHook, the launcher, and the handle) so the supported path no longer advertises UWP, while UWP/packaged WinUI stay described as the rejected/planned MSIX path. Regenerated xlf. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a7e0a98b-132a-4c4d-9d3a-4764c1504e05
This comment has been minimized.
This comment has been minimized.
🧪 Test quality grade — PR #9914
This advisory comment was generated automatically. Grades are heuristic Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "awmgmcpg"See Network Configuration for more information.
|
Summary
Follow-up to the discussion under #9908 about
Microsoft.Testing.Extensions.PackagedApp. Today the launcher's only real launch path isCopyDirectory+Process.Start, which cannot launch a packaged (MSIX) WinUI/UWP app. VSTest gets loose-layout registration and AUMID activation from Visual-Studio-internal deployment components (IVsAppContainerProjectDeploy2,IDeploymentServicesProvider.GetAppLauncher(), MEF-composed from the VS install) that are not redistributable, so a standalone MTP extension cannot depend on them.This PR lands the parts of that flow that need no VS-internal API, plus makes the extension honest about what it can do today. It does not add any public API surface:
AppxManifestInfo/AppxApplicationInfoare internal helpers (tracked inInternalAPI.Unshipped.txt).Changes
AppxManifestInfo(new, internal): parsesAppxManifest.xmland computes the PackageFamilyName and, per declared<Application>, the Application User Model ID ({PackageFamilyName}!{AppId}) using the public Windows publisher-hash algorithm — SHA-256 of the UTF-16LEPublisher, first 8 bytes, Crockford base32. Pure managed and cross-platform; it is the managed, VS-independent equivalent of VS's internaldeploymentFile.AppUserModelId. A package may declare several applications, soResolveApplication(executableFileName)selects the one whoseExecutablematches the requested test host and rejects ambiguous requests instead of defaulting to the first entry.IsEnabledAsync()now returnsOperatingSystem.IsWindows(). On non-Windows the launcher is never registered, so the platform keeps its default in-process path instead of being forced onto the controller/deploy path by a no-op launcher.LaunchTestHostAsyncdetects a packaged layout (anAppxManifest.xmlat the executable's directory or a parent package root) and throws an actionable error — including the AUMID of the application matching the requested executable and a pointer to Implement packaged (MSIX) app registration and AUMID activation in Microsoft.Testing.Extensions.PackagedApp #9933 — instead of silentlyProcess.Starting an executable that can't host the run. Non-packaged (loose-layout) hosts keep the existing behavior unchanged.PackageDescription,PACKAGE.md, and the public XML docs now describe the supported path as launching a non-packaged (loose-layout) Windows host (for example, unpackaged WinUI), and describe genuinely packaged (MSIX) apps — UWP or packaged WinUI — as the rejected/planned path pending Implement packaged (MSIX) app registration and AUMID activation in Microsoft.Testing.Extensions.PackagedApp #9933. (UWP always requires package identity and anAppxManifest.xml, so it is inherently packaged and is not part of the supported path today.)Still a follow-up (not in this PR — #9933)
Actual
PackageManager.RegisterPackageByUriAsyncregistration,IApplicationActivationManagerAUMID activation, and the AppContainer connect-back transport (the platform passes the controller IPC pipe name via an environment variable, which an activated packaged process does not inherit) remain to be done. (#2784 tracks the distinct unpackaged WinUI scenario and is not the tracker for this work.)Verification
Microsoft.Testing.Extensions.PackagedAppbuilds with 0 warnings / 0 errors (incl.-p:TreatWarningsAsErrors=true); internal API and xlf updated.AppxManifestInfoTestsandPackagedAppTestHostLauncherTests(including multi-application resolution and subdirectory-executable package-root detection) discovered and passing.