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

Merged
Amaury Levé (Evangelink) merged 5 commits into
microsoft:mainfrom
Evangelink:dev/amauryleve/aot-friendliness-mtp-mode
May 31, 2026
Merged

Make AOT-incompatible code paths a no-op at runtime in MTP/Native AOT mode#8688
Amaury Levé (Evangelink) merged 5 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>
CopilotAI review requested due to automatic review settings May 29, 2026 18:12

CopilotAI 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

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
FileDescription
src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.csUses AssemblyFileLocator to avoid direct Assembly.Location access in deployment filtering logic.
src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.csSkips resolver setup in AOT scenarios; replaces Assembly.Location usage with centralized helper/fallbacks.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/DataSerializationHelper.csThrows NotSupportedException when dynamic code isn’t supported (AOT) to prevent DataContract-based serialization usage.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/AssemblyFileLocator.csNew 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 threadsrc/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>
CopilotAI review requested due to automatic review settings May 29, 2026 20:02

CopilotAI 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.

Copilot's findings

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

Comment threadsrc/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>
@Evangelink

Copy link
Copy Markdown
MemberAuthor

Copilot resolve the merge conflicts in this pull request

CopilotAI review requested due to automatic review settings May 31, 2026 07:20

CopilotAI 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.

Copilot's findings

  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new

@Evangelink
Amaury Levé (Evangelink) merged commit 5212049 into microsoft:mainMay 31, 2026
23 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/aot-friendliness-mtp-mode branch May 31, 2026 18:12
Sign up for freeto 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

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

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

Merged
Amaury Levé (Evangelink) merged 5 commits into
microsoft:mainfrom
Evangelink:dev/amauryleve/aot-friendliness-mtp-mode
May 31, 2026
Merged

Make AOT-incompatible code paths a no-op at runtime in MTP/Native AOT mode#8688
Amaury Levé (Evangelink) merged 5 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>
CopilotAI review requested due to automatic review settings May 29, 2026 18:12

CopilotAI 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

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
FileDescription
src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.csUses AssemblyFileLocator to avoid direct Assembly.Location access in deployment filtering logic.
src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.csSkips resolver setup in AOT scenarios; replaces Assembly.Location usage with centralized helper/fallbacks.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/DataSerializationHelper.csThrows NotSupportedException when dynamic code isn’t supported (AOT) to prevent DataContract-based serialization usage.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/AssemblyFileLocator.csNew 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 threadsrc/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>
CopilotAI review requested due to automatic review settings May 29, 2026 20:02

CopilotAI 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.

Copilot's findings

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

Comment threadsrc/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>
@Evangelink

Copy link
Copy Markdown
MemberAuthor

Copilot resolve the merge conflicts in this pull request

CopilotAI review requested due to automatic review settings May 31, 2026 07:20

CopilotAI 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.

Copilot's findings

  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new

@Evangelink
Amaury Levé (Evangelink) merged commit 5212049 into microsoft:mainMay 31, 2026
23 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/aot-friendliness-mtp-mode branch May 31, 2026 18:12
Sign up for freeto 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

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

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

Merged
Amaury Levé (Evangelink) merged 5 commits into
microsoft:mainfrom
Evangelink:dev/amauryleve/aot-friendliness-mtp-mode
May 31, 2026
Merged

Make AOT-incompatible code paths a no-op at runtime in MTP/Native AOT mode#8688
Amaury Levé (Evangelink) merged 5 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>
CopilotAI review requested due to automatic review settings May 29, 2026 18:12

CopilotAI 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

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
FileDescription
src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.csUses AssemblyFileLocator to avoid direct Assembly.Location access in deployment filtering logic.
src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.csSkips resolver setup in AOT scenarios; replaces Assembly.Location usage with centralized helper/fallbacks.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/DataSerializationHelper.csThrows NotSupportedException when dynamic code isn’t supported (AOT) to prevent DataContract-based serialization usage.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/AssemblyFileLocator.csNew 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 threadsrc/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>
CopilotAI review requested due to automatic review settings May 29, 2026 20:02

CopilotAI 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.

Copilot's findings

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

Comment threadsrc/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>
@Evangelink

Copy link
Copy Markdown
MemberAuthor

Copilot resolve the merge conflicts in this pull request

CopilotAI review requested due to automatic review settings May 31, 2026 07:20

CopilotAI 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.

Copilot's findings

  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new

@Evangelink
Amaury Levé (Evangelink) merged commit 5212049 into microsoft:mainMay 31, 2026
23 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/aot-friendliness-mtp-mode branch May 31, 2026 18:12
Sign up for freeto 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

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

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

Merged
Amaury Levé (Evangelink) merged 5 commits into
microsoft:mainfrom
Evangelink:dev/amauryleve/aot-friendliness-mtp-mode
May 31, 2026
Merged

Make AOT-incompatible code paths a no-op at runtime in MTP/Native AOT mode#8688
Amaury Levé (Evangelink) merged 5 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>
CopilotAI review requested due to automatic review settings May 29, 2026 18:12

CopilotAI 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

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
FileDescription
src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.csUses AssemblyFileLocator to avoid direct Assembly.Location access in deployment filtering logic.
src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.csSkips resolver setup in AOT scenarios; replaces Assembly.Location usage with centralized helper/fallbacks.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/DataSerializationHelper.csThrows NotSupportedException when dynamic code isn’t supported (AOT) to prevent DataContract-based serialization usage.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/AssemblyFileLocator.csNew 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 threadsrc/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>
CopilotAI review requested due to automatic review settings May 29, 2026 20:02

CopilotAI 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.

Copilot's findings

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

Comment threadsrc/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>
@Evangelink

Copy link
Copy Markdown
MemberAuthor

Copilot resolve the merge conflicts in this pull request

CopilotAI review requested due to automatic review settings May 31, 2026 07:20

CopilotAI 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.

Copilot's findings

  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new

@Evangelink
Amaury Levé (Evangelink) merged commit 5212049 into microsoft:mainMay 31, 2026
23 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/aot-friendliness-mtp-mode branch May 31, 2026 18:12
Sign up for freeto 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

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

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

Merged
Amaury Levé (Evangelink) merged 5 commits into
microsoft:mainfrom
Evangelink:dev/amauryleve/aot-friendliness-mtp-mode
May 31, 2026
Merged

Make AOT-incompatible code paths a no-op at runtime in MTP/Native AOT mode#8688
Amaury Levé (Evangelink) merged 5 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>
CopilotAI review requested due to automatic review settings May 29, 2026 18:12

CopilotAI 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

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
FileDescription
src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.csUses AssemblyFileLocator to avoid direct Assembly.Location access in deployment filtering logic.
src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.csSkips resolver setup in AOT scenarios; replaces Assembly.Location usage with centralized helper/fallbacks.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/DataSerializationHelper.csThrows NotSupportedException when dynamic code isn’t supported (AOT) to prevent DataContract-based serialization usage.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/AssemblyFileLocator.csNew 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 threadsrc/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>
CopilotAI review requested due to automatic review settings May 29, 2026 20:02

CopilotAI 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.

Copilot's findings

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

Comment threadsrc/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>
@Evangelink

Copy link
Copy Markdown
MemberAuthor

Copilot resolve the merge conflicts in this pull request

CopilotAI review requested due to automatic review settings May 31, 2026 07:20

CopilotAI 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.

Copilot's findings

  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new

@Evangelink
Amaury Levé (Evangelink) merged commit 5212049 into microsoft:mainMay 31, 2026
23 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/aot-friendliness-mtp-mode branch May 31, 2026 18:12
Sign up for freeto 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

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

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

Merged
Amaury Levé (Evangelink) merged 5 commits into
microsoft:mainfrom
Evangelink:dev/amauryleve/aot-friendliness-mtp-mode
May 31, 2026
Merged

Make AOT-incompatible code paths a no-op at runtime in MTP/Native AOT mode#8688
Amaury Levé (Evangelink) merged 5 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>
CopilotAI review requested due to automatic review settings May 29, 2026 18:12

CopilotAI 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

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
FileDescription
src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.csUses AssemblyFileLocator to avoid direct Assembly.Location access in deployment filtering logic.
src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.csSkips resolver setup in AOT scenarios; replaces Assembly.Location usage with centralized helper/fallbacks.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/DataSerializationHelper.csThrows NotSupportedException when dynamic code isn’t supported (AOT) to prevent DataContract-based serialization usage.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/AssemblyFileLocator.csNew 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 threadsrc/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>
CopilotAI review requested due to automatic review settings May 29, 2026 20:02

CopilotAI 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.

Copilot's findings

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

Comment threadsrc/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>
@Evangelink

Copy link
Copy Markdown
MemberAuthor

Copilot resolve the merge conflicts in this pull request

CopilotAI review requested due to automatic review settings May 31, 2026 07:20

CopilotAI 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.

Copilot's findings

  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new

@Evangelink
Amaury Levé (Evangelink) merged commit 5212049 into microsoft:mainMay 31, 2026
23 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/aot-friendliness-mtp-mode branch May 31, 2026 18:12
Sign up for freeto 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

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

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

Merged
Amaury Levé (Evangelink) merged 5 commits into
microsoft:mainfrom
Evangelink:dev/amauryleve/aot-friendliness-mtp-mode
May 31, 2026
Merged

Make AOT-incompatible code paths a no-op at runtime in MTP/Native AOT mode#8688
Amaury Levé (Evangelink) merged 5 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>
CopilotAI review requested due to automatic review settings May 29, 2026 18:12

CopilotAI 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

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
FileDescription
src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.csUses AssemblyFileLocator to avoid direct Assembly.Location access in deployment filtering logic.
src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.csSkips resolver setup in AOT scenarios; replaces Assembly.Location usage with centralized helper/fallbacks.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/DataSerializationHelper.csThrows NotSupportedException when dynamic code isn’t supported (AOT) to prevent DataContract-based serialization usage.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/AssemblyFileLocator.csNew 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 threadsrc/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>
CopilotAI review requested due to automatic review settings May 29, 2026 20:02

CopilotAI 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.

Copilot's findings

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

Comment threadsrc/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>
@Evangelink

Copy link
Copy Markdown
MemberAuthor

Copilot resolve the merge conflicts in this pull request

CopilotAI review requested due to automatic review settings May 31, 2026 07:20

CopilotAI 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.

Copilot's findings

  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new

@Evangelink
Amaury Levé (Evangelink) merged commit 5212049 into microsoft:mainMay 31, 2026
23 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/aot-friendliness-mtp-mode branch May 31, 2026 18:12
Sign up for freeto 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

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

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

Merged
Amaury Levé (Evangelink) merged 5 commits into
microsoft:mainfrom
Evangelink:dev/amauryleve/aot-friendliness-mtp-mode
May 31, 2026
Merged

Make AOT-incompatible code paths a no-op at runtime in MTP/Native AOT mode#8688
Amaury Levé (Evangelink) merged 5 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>
CopilotAI review requested due to automatic review settings May 29, 2026 18:12

CopilotAI 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

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
FileDescription
src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.csUses AssemblyFileLocator to avoid direct Assembly.Location access in deployment filtering logic.
src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.csSkips resolver setup in AOT scenarios; replaces Assembly.Location usage with centralized helper/fallbacks.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/DataSerializationHelper.csThrows NotSupportedException when dynamic code isn’t supported (AOT) to prevent DataContract-based serialization usage.
src/Adapter/MSTestAdapter.PlatformServices/Helpers/AssemblyFileLocator.csNew 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 threadsrc/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>
CopilotAI review requested due to automatic review settings May 29, 2026 20:02

CopilotAI 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.

Copilot's findings

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

Comment threadsrc/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>
@Evangelink

Copy link
Copy Markdown
MemberAuthor

Copilot resolve the merge conflicts in this pull request

CopilotAI review requested due to automatic review settings May 31, 2026 07:20

CopilotAI 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.

Copilot's findings

  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new

@Evangelink
Amaury Levé (Evangelink) merged commit 5212049 into microsoft:mainMay 31, 2026
23 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/aot-friendliness-mtp-mode branch May 31, 2026 18:12
Sign up for freeto 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

@Evangelink