Skip to content

Make AOT-incompatible code paths a no-op at runtime in MTP/Native AOT mode#8688

Open
Evangelink wants to merge 4 commits into
microsoft:mainfrom
Evangelink:dev/amauryleve/aot-friendliness-mtp-mode
Open

Make AOT-incompatible code paths a no-op at runtime in MTP/Native AOT mode#8688
Evangelink wants to merge 4 commits into
microsoft:mainfrom
Evangelink:dev/amauryleve/aot-friendliness-mtp-mode

Conversation

@Evangelink
Copy link
Copy Markdown
Member

What

Two targeted runtime improvements that reduce the trim/AOT warning surface for MSTest.TestAdapter when consumers enable trimming or Native AOT through Microsoft.Testing.Platform (MTP):

1. AssemblyFileLocator — single place to handle Assembly.Location

New internal static helper in MSTestAdapter.PlatformServices/Helpers/AssemblyFileLocator.cs with three members:

  • TryGetLocation(Assembly) -> string? — returns null when Assembly.Location is empty (single-file / Native AOT case).
  • GetDirectoryOrAppContextBase(Assembly) — falls back to AppContext.BaseDirectory.
  • GetFileNameOrSimpleName(Assembly) — falls back to <simpleName>.dll.

Each member carries a single [UnconditionalSuppressMessage("SingleFile", "IL3000")] with a meaningful justification. This removes the #pragma warning disable IL3000 blocks scattered across call sites:

  • TestSourceHost.SetContext (WIN_UI branch)
  • TestSourceHost.GetAppBaseAsPerPlatform (NETFRAMEWORK)
  • TestSourceHost.GetResolutionPaths (both NET && !WINDOWS_UWP and NETFRAMEWORK)
  • DeploymentUtilityBase.Deploy

2. Skip AssemblyResolver and DataSerializationHelper work in Native AOT

Gate the relevant code on RuntimeFeature.IsDynamicCodeSupported — the established AOT-detection signal already used by TestApplication and AppInsightsTelemetryProviderExtensions in this codebase:

  • TestSourceHost.SetupHost (NET && !WINDOWS_UWP) no longer constructs AssemblyResolver when running under NAOT. Its AssemblyLoadContext.Resolving hook can never fire in NAOT anyway. The trimmer can now drop GetResolutionPaths and the whole resolver class.
  • DataSerializationHelper.Serialize / Deserialize throw NotSupportedException under NAOT with a clear message pointing users to MTP mode (where TestMethod.ActualData is used in-process). In practice these methods are not reached in MTP at runtime — discovery sets ActualData directly. The throw is a safety net plus a trimmer hint.

The trimmer treats RuntimeFeature.IsDynamicCodeSupported as a constant false substitution during NAOT publish, so the gated branches and their transitive dependencies are statically removed from the published binary.

Why

PR #8586 enables an integration test that publishes MSTest with PublishAot=true + TreatWarningsAsErrors=true. The pragma-to-attribute PR (#8686) silences the warnings; this PR goes further by making the code actually safe / no-op at runtime in NAOT, so consumers using MSTest in MTP-only mode get a smaller and more correct published binary.

Independence from #8686

This PR branches from main and is independent of #8686. The two PRs touch TestSourceHost.cs and DataSerializationHelper.cs in different ways, so expect merge conflicts; whichever lands first will need the other rebased.

Validation

  • dotnet build src/Adapter/MSTestAdapter.PlatformServices/MSTestAdapter.PlatformServices.csproj -c Release -f net8.0 — 0 warnings / 0 errors.
  • dotnet build ... -f net462 — 0 warnings / 0 errors (the new NAOT guards are #if NET-fenced so netfx is unaffected).

The same acceptance test added in #8686 (Publish_WithTestAdapter_DoesNotSurfaceWarningsFromSuppressedSources) would benefit from these changes by removing additional warnings from the TestSourceHost and DeploymentUtilityBase surfaces, and by letting the trimmer drop the AssemblyResolver entirely.

… mode

This change introduces two targeted improvements that reduce the trim/AOT
warning surface for the MSTest adapter when consumers enable
`PublishTrimmed=true` or `PublishAot=true` and pull MSTest in via MTP.

1. Centralize `Assembly.Location` handling in a new internal
   `AssemblyFileLocator` helper. Previously, every call site that needed
   the path or directory of an on-disk assembly carried its own
   `#pragma warning disable IL3000` (or relied on an IL3000 attribute
   suppression) and re-implemented the single-file/Native AOT fallback to
   `AppContext.BaseDirectory` or the assembly simple name. The helper
   gives one place to suppress IL3000 with a meaningful justification and
   one place to maintain the fallback logic. Updated call sites:
   - `TestSourceHost.SetContext` (WIN_UI branch),
   - `TestSourceHost.GetAppBaseAsPerPlatform` (NETFRAMEWORK),
   - `TestSourceHost.GetResolutionPaths` (NET, !WINDOWS_UWP and NETFRAMEWORK),
   - `DeploymentUtilityBase.Deploy` (filtering the adapter assembly out
     of the deployment loop).

2. Skip the AssemblyResolver registration and `DataSerializationHelper`
   work at runtime in Native AOT. `RuntimeFeature.IsDynamicCodeSupported`
   is the established AOT-detection signal in this codebase (already used by
   `TestApplication` and `AppInsightsTelemetryProviderExtensions`). When
   it is `false`:
   - `TestSourceHost.SetupHost` no longer constructs `AssemblyResolver`
     (its `AssemblyLoadContext.Resolving` hook can never fire under NAOT).
     This also lets the trimmer drop `GetResolutionPaths` and the resolver
     class from the published binary.
   - `DataSerializationHelper.Serialize` / `Deserialize` throw a
     descriptive `NotSupportedException` instead of attempting
     `DataContractJsonSerializer`-based serialization (which requires
     dynamic code). In MTP mode the in-process `TestMethod.ActualData` is
     used for parameterized arguments and these methods are not reached at
     runtime; the throw is a safety net plus a trimmer hint.

The trimmer treats `RuntimeFeature.IsDynamicCodeSupported` as a constant
`false` substitution during NAOT publish, so the gated branches (and the
unreferenced `AssemblyResolver` / DataContract code paths) are statically
removed from the published binary.

This PR is independent of microsoft#8686 (which is the pragma-to-attribute
conversion for the same warnings) but the two changes overlap on
`TestSourceHost.cs` and `DataSerializationHelper.cs`; expect a merge
conflict between them.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 29, 2026 18:12
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Reduces the trim/Native AOT warning surface (and runtime reachability) of AOT-incompatible adapter code paths when MSTest is used via Microsoft.Testing.Platform (MTP), by centralizing Assembly.Location handling and gating unsupported behaviors at runtime.

Changes:

  • Added AssemblyFileLocator to centralize Assembly.Location access (single-file/NAOT-safe fallbacks + IL3000 suppression).
  • Updated TestSourceHost and DeploymentUtilityBase to use AssemblyFileLocator instead of direct Assembly.Location access / IL3000 pragmas.
  • Gated AssemblyResolver creation and DataSerializationHelper.Serialize/Deserialize on RuntimeFeature.IsDynamicCodeSupported, turning AOT-incompatible paths into no-ops/throws in AOT scenarios.
Show a summary per file
File Description
src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs Uses AssemblyFileLocator to avoid direct Assembly.Location access in deployment filtering logic.
src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs Skips resolver setup in AOT scenarios; replaces Assembly.Location usage with centralized helper/fallbacks.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/DataSerializationHelper.cs Throws NotSupportedException when dynamic code isn’t supported (AOT) to prevent DataContract-based serialization usage.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/AssemblyFileLocator.cs New helper encapsulating Assembly.Location access and AOT/single-file fallbacks with IL3000 suppressions.

Copilot's findings

  • Files reviewed: 4/4 changed files
  • Comments generated: 3

Comment thread src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs Outdated
Amaury Levé and others added 2 commits May 29, 2026 21:18
…e AOT"

RuntimeFeature.IsDynamicCodeSupported is false for any AOT-like runtime
without a JIT or interpreter (Native AOT, Mono iOS AOT, Blazor WebAssembly
AOT, …), not exclusively Native AOT. Update doc comments, exception
messages, and the in-source rationale in TestSourceHost to describe the
actual condition being checked.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…n netfx/uap

`RuntimeFeature.IsDynamicCodeSupported` is .NET Core/5+ only, so the
`<see cref=...>` references in the XML docs fail to resolve when the
project builds for net462 and uap10.0.16299. Switch to `<c>...</c>`
literals to keep the documentation readable on all target frameworks
without requiring conditional compilation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 29, 2026 20:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 4/4 changed files
  • Comments generated: 2

Comment thread src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs Outdated
Comment thread src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs Outdated
Both comments referenced 'source gen mode', but the actual code paths check

for Assembly.Location being empty (single-file / Native AOT scenarios).

Reworded to describe the real behavior to avoid confusing future maintainers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

2 participants