Skip to content

Fix intermittent AssemblyLoading_MultipleVersions_ExactAndGreaterMatch(LoadStream) failure#83044

Merged
jaredpar merged 1 commit intomainfrom
copilot/fix-analyzer-assembly-loader-tests
Apr 3, 2026
Merged

Fix intermittent AssemblyLoading_MultipleVersions_ExactAndGreaterMatch(LoadStream) failure#83044
jaredpar merged 1 commit intomainfrom
copilot/fix-analyzer-assembly-loader-tests

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 2, 2026

InvokeUtil.Exec snapshots CompilerLoadContext.Assemblies before running the test, then asserts the set is unchanged afterward. AssertEx has static fields that lazily initialize DiffPlex on first use; when that first use fell between the snapshot and the final comparison, DiffPlex (and Microsoft.Win32.Registry as a netstandard2.0 compat shim on .NET 10) appeared only in the post-test list, failing the SetEqual check. Only LoadStream was affected because the other variants ran in process contexts where a prior test had already triggered AssertEx initialization.

Changes

  • InvokeUtil.cs: Call RuntimeHelpers.RunClassConstructor(typeof(AssertEx).TypeHandle) before taking the compilerContextAssemblies snapshot, ensuring DiffPlex and any compat-shim assemblies it pulls in are already present in both sides of the comparison.
// Before (snapshot taken before AssertEx/DiffPlex are initialized)
var loader = new AnalyzerAssemblyLoader(...);
var compilerContextAssemblies = loader.CompilerLoadContext.Assemblies.SelectAsArray(...);

// After (force initialization first so the snapshot includes infrastructure assemblies)
var loader = new AnalyzerAssemblyLoader(...);
RuntimeHelpers.RunClassConstructor(typeof(AssertEx).TypeHandle);
var compilerContextAssemblies = loader.CompilerLoadContext.Assemblies.SelectAsArray(...);
Microsoft Reviewers: Open in CodeFlow

…okeUtil.Exec

Agent-Logs-Url: https://github.com/dotnet/roslyn/sessions/673c28d8-d3e5-490d-b4e2-caff66247118

Co-authored-by: jaredpar <146967+jaredpar@users.noreply.github.com>
@jaredpar jaredpar marked this pull request as ready for review April 2, 2026 22:43
@jaredpar jaredpar requested a review from a team as a code owner April 2, 2026 22:43
@jaredpar
Copy link
Copy Markdown
Member

jaredpar commented Apr 2, 2026

@RikkiGibson, @333fred, @jjonescz PTAL

In another PR I hit a flaky failure in this test method. Decided to see what CCA could do with it and gave it the following prompt:

When running our unit tests on .NET core we hit the following failure in CI:

AnalyzerAssemblyLoaderTests.AssemblyLoading_MultipleVersions_ExactAndGreaterMatch(kind: LoadStream)
The information on the failure is

Code
The failure is an AssertEx.SetEqual assertion. The diff shows 2 unexpected assemblies loaded in the LoadStream variant (marked 
++>):

 1. DiffPlex, Version=1.7.2.0 — loaded early in the list
 2. Microsoft.Win32.Registry, Version=10.0.0.0 — also unexpectedly present

The LoadDirect and ShadowLoad variants of the same test passed — only LoadStream failed.
This failure is in InvokeUtil.cs:line at line 75.

This indicates that some code in our unit tests is loading DiffPlex and Microsoft.Win32.Registry during that test execution. What could be causing that to happen?

Was really curious if it could reason out a race condition around AssemblyLoadContext. Turns out it can.

@jaredpar jaredpar merged commit f6b7abf into main Apr 3, 2026
26 checks passed
@jaredpar jaredpar deleted the copilot/fix-analyzer-assembly-loader-tests branch April 3, 2026 00:16
@dotnet-policy-service dotnet-policy-service Bot added this to the Next milestone Apr 3, 2026
Copy link
Copy Markdown
Member

@RikkiGibson RikkiGibson left a comment

Choose a reason for hiding this comment

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

LGTM, just had another conversation going on while I was in the middle of looking up AssertEx.cs.

@RikkiGibson
Copy link
Copy Markdown
Member

heads up that I saw this again for a build on commit 93224f2. I branched this off of main today so I think I have the fix from this PR.

Helix log: https://helixr1107v0xdeko0k025g8.blob.core.windows.net/dotnet-roslyn-refs-pull-83185-merge-acddef87dfd04007b0/workitem_4.Attempt.2/1/console.bc7e4310.log?helixlogtype=result&skoid=8eda00af-b5ec-4be9-b69b-0919a2338892&sktid=72f988bf-86f1-41af-91ab-2d7cd011db47&skt=2026-04-15T23%3A58%3A33Z&ske=2026-04-16T00%3A58%3A33Z&sks=b&skv=2026-02-06&sv=2026-02-06&se=2026-04-16T00%3A58%3A33Z&sr=b&sp=rl&sig=vIktUJ6cbaMvBzn3sdbyZGSo7352aixhyisHX%2BotTSc%3D.

[xUnit.net 00:00:17.64]     Microsoft.CodeAnalysis.UnitTests.AnalyzerAssemblyLoaderTests.PathResolver_MultipleResolvers_CanIntercept_ReturningNull(kind: ShadowLoad) [FAIL]
<trimmed for brevity>
[xUnit.net 00:00:17.65]       Differences:
[xUnit.net 00:00:17.65]       ++> Microsoft.Win32.Registry, Version=10.0.0.0, Culture=neutral, 
[xUnit.net 00:00:17.65]       Stack Trace:
[xUnit.net 00:00:17.65]         /_/src/Compilers/Test/Core/Assert/AssertEx.cs(483,0): at Roslyn.Test.Utilities.AssertEx.SetEqual[T](IEnumerable`1 expected, IEnumerable`1 actual, IEqualityComparer`1 comparer, String message, String itemSeparator, Func`2 itemInspector)
[xUnit.net 00:00:17.65]         /_/src/Compilers/Test/Core/Assert/AssertEx.cs(394,0): at Roslyn.Test.Utilities.AssertEx.SetEqual(IEnumerable`1 expected, IEnumerable`1 actual, IEqualityComparer`1 comparer, String message, String itemSeparator, Func`2 itemInspector)
[xUnit.net 00:00:17.65]         /_/src/Compilers/Core/CodeAnalysisTest/InvokeUtil.cs(84,0): at Microsoft.CodeAnalysis.UnitTests.InvokeUtil.Exec(ITestOutputHelper testOutputHelper, ImmutableArray`1 pathResolvers, ImmutableArray`1 assemblyResolvers, AssemblyLoadTestFixture fixture, AnalyzerTestKind kind, String typeName, String methodName, Object state)
[xUnit.net 00:00:17.65]         /_/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs(122,0): at Microsoft.CodeAnalysis.UnitTests.AnalyzerAssemblyLoaderTests.Run(AnalyzerTestKind kind, Object state, MethodInfo method, ImmutableArray`1 pathResolvers, ImmutableArray`1 assemblyResolvers)
[xUnit.net 00:00:17.65]         /_/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs(94,0): at Microsoft.CodeAnalysis.UnitTests.AnalyzerAssemblyLoaderTests.Run(AnalyzerTestKind kind, Action`2 testAction, ImmutableArray`1 pathResolvers, ImmutableArray`1 assemblyResolvers)
[xUnit.net 00:00:17.65]         /_/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs(1751,0): at Microsoft.CodeAnalysis.UnitTests.AnalyzerAssemblyLoaderTests.PathResolver_MultipleResolvers_CanIntercept_ReturningNull(AnalyzerTestKind kind)
[xUnit.net 00:00:17.65]            at InvokeStub_AnalyzerAssemblyLoaderTests.PathResolver_MultipleResolvers_CanIntercept_ReturningNull(Object, Span`1)
[xUnit.net 00:00:17.65]            at System.Reflection.MethodBaseInvoker.InvokeWithOneArg(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

I have a branch on the side where I'm trying to solve. Have to focus on other things for now though.

I am wondering if we should, instead of asserting that entire state of the world remains exactly the same before/after, just ensure that assemblies that are "part of the fixture" are not leaking in. As it seems like any work occurring ambiently in the process could perhaps cause new assemblies to be loaded in this context or the default context over time.

@AlekseyTs
Copy link
Copy Markdown
Contributor

AlekseyTs commented Apr 16, 2026

heads up that I saw this again for a build on commit

I think this test should do its work in isolation, i.e. in an environment where other tests cannot interfere. And it should not be snapshotting assemblies loaded in the current test process. Perhaps it should execute the scenario in a separate process.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants