Skip to content

Commit

Permalink
Reuse AssemblyLoadTestFixture across test runs
Browse files Browse the repository at this point in the history
This also has the added benefit that the entire .NET Core set of tests
run in less than one second. Previously it close to half a second per
test.

This will hopefully alleviate the pain for the runtime issue we're
hittnig running `AssemblyLoadContext` tests in CI. This reduces the
amount of compilations on this code path which seem to be the trigger
for the failure.

- dotnet#66621
- dotnet/runtime#81108
  • Loading branch information
jaredpar committed Jan 31, 2023
1 parent 437d4cc commit 655789f
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -172,17 +172,21 @@ public void Exec(ITestOutputHelper testOutputHelper, bool shadowLoad, string typ
/// Long term this is something that needs to be addressed. Tracked by https://github.com/dotnet/roslyn/issues/66532
///
/// </remarks>
[Collection(AssemblyLoadTestFixtureCollection.Name)]
public sealed class AnalyzerAssemblyLoaderTests : TestBase
{
public ITestOutputHelper TestOutputHelper { get; }

public AnalyzerAssemblyLoaderTests(ITestOutputHelper testOutputHelper)
#if NETCOREAPP

public AssemblyLoadTestFixture TestFixture { get; }

public AnalyzerAssemblyLoaderTests(ITestOutputHelper testOutputHelper, AssemblyLoadTestFixture testFixture)
{
TestOutputHelper = testOutputHelper;
TestFixture = testFixture;
}

#if NETCOREAPP

private void Run(bool shadowLoad, Action<AnalyzerAssemblyLoader, AssemblyLoadTestFixture> testAction, [CallerMemberName] string? memberName = null) =>
Run(
shadowLoad,
Expand All @@ -199,10 +203,9 @@ private void Run(
var alc = new AssemblyLoadContext($"Test {memberName}", isCollectible: true);
try
{
using var fixture = new AssemblyLoadTestFixture();
prepLoadContextAction(alc, fixture);
prepLoadContextAction(alc, TestFixture);
var util = new InvokeUtil();
util.Exec(TestOutputHelper, alc, fixture, shadowLoad, testAction.Method.DeclaringType!.FullName!, testAction.Method.Name);
util.Exec(TestOutputHelper, alc, TestFixture, shadowLoad, testAction.Method.DeclaringType!.FullName!, testAction.Method.Name);
}
finally
{
Expand All @@ -212,6 +215,13 @@ private void Run(

#else

// The AsesmblyLoadTestFixture collection can't be taken advantage of on .NET Framework because
// it can't be efficiently marhshaled through AppDomains. Have to ignore it here.
public AnalyzerAssemblyLoaderTests(ITestOutputHelper testOutputHelper, AssemblyLoadTestFixture _)
{
TestOutputHelper = testOutputHelper;
}

private void Run(
bool shadowLoad,
Action<AnalyzerAssemblyLoader, AssemblyLoadTestFixture> testAction,
Expand Down Expand Up @@ -359,16 +369,19 @@ public void AssemblyLoading_OverwriteBeforeLoad(bool shadowLoad)
{
Run(shadowLoad, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) =>
{
loader.AddDependencyLocation(testFixture.Delta1.Path);
testFixture.Delta1.WriteAllBytes(testFixture.Delta2.ReadAllBytes());
var assembly = loader.LoadFromPath(testFixture.Delta1.Path);
using var temp = new TempRoot();
var tempDir = temp.CreateDirectory();
var delta1Copy = tempDir.CreateDirectory("a").CreateFile("Delta.dll").CopyContentFrom(testFixture.Delta1.Path);
loader.AddDependencyLocation(delta1Copy.Path);
delta1Copy.WriteAllBytes(testFixture.Delta2.ReadAllBytes());
var assembly = loader.LoadFromPath(delta1Copy.Path);

var name = AssemblyName.GetAssemblyName(testFixture.Delta2.Path);
Assert.Equal(name.FullName, assembly.GetName().FullName);

VerifyDependencyAssemblies(
loader,
testFixture.Delta1.Path);
delta1Copy.Path);
});
}

Expand Down Expand Up @@ -1226,7 +1239,7 @@ public void AssemblyLoading_NativeDependency(bool shadowLoad)
var analyzer = analyzerAssembly.CreateInstance("Class1")!;
var result = analyzer.GetType().GetMethod("GetFileAttributes")!.Invoke(analyzer, new[] { testFixture.AnalyzerWithNativeDependency.Path });
Assert.NotEqual(INVALID_FILE_ATTRIBUTES, result);
Assert.Equal(FileAttributes.Archive, (FileAttributes)result!);
Assert.Equal(FileAttributes.Archive | FileAttributes.ReadOnly, (FileAttributes)result!);
});
}

Expand Down
8 changes: 8 additions & 0 deletions src/Compilers/Test/Core/AssemblyLoadTestFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using Basic.Reference.Assemblies;
using Microsoft.CodeAnalysis;
Expand Down Expand Up @@ -477,6 +478,13 @@ private static TempFile GenerateDll(string assemblyName, TempDirectory directory

var tempFile = directory.CreateFile($"{assemblyName}.dll");
tempFile.WriteAllBytes(analyzerDependencyCompilation.EmitToArray());

// Mark the file as read only to prevent mutations. The output of this type is frequently used across
// unit tests boundaries. Need a guardrail to make sure one test doesn't pollute the output of
// another test.
var fileInfo = new FileInfo(tempFile.Path);
fileInfo.Attributes |= FileAttributes.ReadOnly;

return tempFile;
}

Expand Down

0 comments on commit 655789f

Please sign in to comment.