Skip to content

Commit 3552bdb

Browse files
committed
Port changes from version 3.22.0 to V4
1 parent 4a47d89 commit 3552bdb

File tree

8 files changed

+94
-17
lines changed

8 files changed

+94
-17
lines changed

src/NUnitCommon/nunit.agent.core/Drivers/NUnitFrameworkApi2018.cs

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
using System.Collections.Generic;
55
using System.IO;
66
using System.Reflection;
7-
7+
using NUnit.Common;
88
#if NETCOREAPP
9+
using System.Runtime.Loader;
910
using NUnit.Engine.Internal;
1011
#endif
1112

@@ -40,10 +41,12 @@ public class NUnitFrameworkApi2018 : NUnitFrameworkApi
4041
private Type? _frameworkControllerType;
4142

4243
#if NETCOREAPP
43-
private TestAssemblyLoadContext? _assemblyLoadContext;
44+
private AssemblyLoadContext? _assemblyLoadContext;
45+
private TestAssemblyResolver? _testAssemblyResolver;
46+
private Assembly? _testAssembly;
4447
private Assembly? _frameworkAssembly;
4548

46-
internal List<ResolutionStrategy>? ResolutionStrategies => _assemblyLoadContext?.ResolutionStrategies;
49+
internal List<ResolutionStrategy>? ResolutionStrategies => _testAssemblyResolver?.ResolutionStrategies;
4750
#endif
4851

4952
private string? _testAssemblyPath;
@@ -67,6 +70,10 @@ public string Load(string testAssemblyPath, IDictionary<string, object> settings
6770
_testAssemblyPath = Path.GetFullPath(testAssemblyPath);
6871
var idPrefix = _driverId + "-";
6972

73+
bool useDefaultAssemblyLoadContext = false;
74+
if (settings.TryGetValue(SettingDefinitions.UseDefaultAssemblyLoadContext, out var val))
75+
useDefaultAssemblyLoadContext = (bool)val;
76+
7077
#if NETFRAMEWORK
7178
try
7279
{
@@ -95,12 +102,46 @@ public string Load(string testAssemblyPath, IDictionary<string, object> settings
95102
var controllerAssembly = _frameworkControllerType?.Assembly?.GetName();
96103
log.Debug($"Controller assembly is {controllerAssembly}");
97104
#else
98-
_assemblyLoadContext = new TestAssemblyLoadContext(testAssemblyPath);
105+
try
106+
{
107+
_testAssembly = AssemblyHelper.FindLoadedAssemblyByPath(_testAssemblyPath);
108+
109+
if (_testAssembly is not null)
110+
{
111+
_assemblyLoadContext = AssemblyLoadContext.GetLoadContext(_testAssembly);
112+
log.Debug($" Already loaded in context {_assemblyLoadContext}");
113+
}
114+
else
115+
{
116+
_assemblyLoadContext = useDefaultAssemblyLoadContext
117+
? AssemblyLoadContext.Default
118+
: new AssemblyLoadContext(Path.GetFileNameWithoutExtension(testAssemblyPath));
119+
_testAssembly = _assemblyLoadContext.LoadFromAssemblyPath(testAssemblyPath);
120+
log.Debug($" Loaded into new context {_assemblyLoadContext}");
121+
}
122+
123+
_testAssemblyResolver = new TestAssemblyResolver(_assemblyLoadContext.ShouldNotBeNull(), testAssemblyPath);
124+
}
125+
catch (Exception e)
126+
{
127+
var msg = $"Failed to load test assembly {testAssemblyPath}";
128+
log.Error(msg);
129+
throw new NUnitEngineException(msg, e);
130+
}
131+
log.Debug($"Loaded {testAssemblyPath}");
99132

100-
var testAssembly = LoadAssembly(testAssemblyPath);
101-
_frameworkAssembly = LoadAssembly(_nunitRef);
133+
try
134+
{
135+
_frameworkAssembly = LoadAssembly(_nunitRef);
136+
}
137+
catch (Exception e)
138+
{
139+
log.Error($"{FAILED_TO_LOAD_NUNIT}\r\n{e}");
140+
throw new NUnitEngineException(FAILED_TO_LOAD_NUNIT, e);
141+
}
142+
log.Debug("Loaded nunit.framework");
102143

103-
_frameworkController = CreateInstance(CONTROLLER_TYPE, testAssembly, idPrefix, settings);
144+
_frameworkController = CreateInstance(CONTROLLER_TYPE, _testAssembly, idPrefix, settings);
104145
if (_frameworkController is null)
105146
{
106147
log.Error(INVALID_FRAMEWORK_MESSAGE);

src/NUnitCommon/nunit.agent.core/TestAssemblyLoadContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt
22

3-
#if NETCOREAPP3_1_OR_GREATER
3+
#if NETCOREAPP3_1_OR_GREATER && false
44

55
using System.Reflection;
66
using System.Runtime.InteropServices;

src/NUnitCommon/nunit.agent.core/TestAssemblyResolver.cs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,20 @@ internal sealed class TestAssemblyResolver : IDisposable
2525
private static readonly Logger log = InternalTrace.GetLogger(typeof(TestAssemblyResolver));
2626

2727
private readonly AssemblyLoadContext _loadContext;
28+
private readonly string _basePath;
29+
private readonly AssemblyDependencyResolver _assemblyDependencyResolver;
2830

2931
// Our Strategies for resolving references
3032
internal List<ResolutionStrategy> ResolutionStrategies = new List<ResolutionStrategy>();
3133

3234
public TestAssemblyResolver(AssemblyLoadContext loadContext, string testAssemblyPath)
3335
{
3436
_loadContext = loadContext;
37+
_basePath = Path.GetDirectoryName(testAssemblyPath).ShouldNotBeNull();
38+
_assemblyDependencyResolver = new AssemblyDependencyResolver(testAssemblyPath);
39+
#if NET8_0_OR_GREATER
40+
AppContext.SetData("APP_CONTEXT_BASE_DIRECTORY", _basePath);
41+
#endif
3542

3643
InitializeResolutionStrategies(loadContext, testAssemblyPath);
3744

@@ -76,15 +83,21 @@ public void Dispose()
7683
_loadContext.Resolving -= OnResolving;
7784
}
7885

79-
public Assembly? Resolve(AssemblyLoadContext context, AssemblyName assemblyName)
80-
{
81-
return OnResolving(context, assemblyName);
82-
}
83-
8486
private Assembly? OnResolving(AssemblyLoadContext loadContext, AssemblyName assemblyName)
8587
{
8688
Guard.ArgumentNotNull(loadContext);
8789

90+
var runtimeResolverPath = _assemblyDependencyResolver.ResolveAssemblyToPath(assemblyName);
91+
if (!string.IsNullOrEmpty(runtimeResolverPath) && File.Exists(runtimeResolverPath))
92+
{
93+
var loadedAssembly = _loadContext.LoadFromAssemblyPath(runtimeResolverPath);
94+
if (loadedAssembly is not null)
95+
{
96+
log.Info($"Assembly {assemblyName} ({loadedAssembly}) is loaded using the deps.json info");
97+
return loadedAssembly;
98+
}
99+
}
100+
88101
foreach (var strategy in ResolutionStrategies)
89102
{
90103
strategy.Calls++;

src/NUnitCommon/nunit.agent.core/nunit.agent.core.csproj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,10 @@
2727
</ItemGroup>
2828

2929
<ItemGroup Condition="'$(TargetFramework)'=='netcoreapp3.1' OR '$(TargetFramework)'=='net6.0'">
30-
<PackageReference Include="Microsoft.Win32.Registry" Version="4.3.0" />
3130
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="3.1.0" />
3231
</ItemGroup>
3332

3433
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
35-
<PackageReference Include="Microsoft.Win32.Registry" Version="4.3.0" />
3634
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.2" />
3735
</ItemGroup>
3836

src/NUnitCommon/nunit.common/AssemblyHelper.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
using System;
44
using System.IO;
5+
using System.Linq;
56
using System.Reflection;
67
namespace NUnit
78
{
@@ -76,5 +77,17 @@ internal static string GetAssemblyPathFromCodeBase(string codeBase)
7677
return codeBase.Substring(start);
7778
}
7879
#endif
80+
81+
// For assemblies already loaded by MTP or by some other means
82+
public static Assembly? FindLoadedAssemblyByPath(string assemblyPath)
83+
{
84+
var full = Path.GetFullPath(assemblyPath);
85+
86+
return AppDomain.CurrentDomain.GetAssemblies()
87+
.FirstOrDefault(a =>
88+
!a.IsDynamic &&
89+
!string.IsNullOrEmpty(a.Location) &&
90+
StringComparer.OrdinalIgnoreCase.Equals(Path.GetFullPath(a.Location), full));
91+
}
7992
}
8093
}

src/NUnitCommon/nunit.common/DotNet.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,8 @@ internal static bool FindBestRuntime(Version targetVersion, IEnumerable<RuntimeI
152152

153153
foreach (var candidate in availableRuntimes)
154154
{
155-
if (candidate.Version >= targetVersion)
155+
if (candidate.Version.Major > targetVersion.Major ||
156+
candidate.Version.Major == targetVersion.Major && candidate.Version.Minor >= candidate.Version.Minor)
156157
if (bestRuntime is null || candidate.Version.Major == bestRuntime.Version.Major)
157158
bestRuntime = candidate;
158159
}

src/NUnitConsole/nunit4-console.tests/nunit4-console.tests.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
<ItemGroup>
1717
<PackageReference Include="NUnit" Version="4.3.2" />
1818
<PackageReference Include="NUnitLite" Version="4.3.2" />
19-
<PackageReference Include="System.ComponentModel.TypeConverter" Version="4.3.0" />
2019
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
2120
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
2221
<PackageReference Include="NSubstitute" Version="4.3.0" />

src/NUnitEngine/nunit.engine.api/SettingDefinitions.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,18 @@ static SettingDefinitions()
256256
/// </summary>
257257
public static SettingDefinition<string> ImageTargetFrameworkName { get; } = new(nameof(ImageTargetFrameworkName), string.Empty);
258258

259+
/// <summary>
260+
/// Set this to true to force use of the default assembly load context for the
261+
/// test assembly and in resolving all dependencies rather than creating and
262+
/// using a separate instance of AssemblyLoadContext.
263+
/// </summary>
264+
/// <remarks>
265+
/// This is provided for use by the NUnit3 VS Adapter and may not work if used
266+
/// outside of that context. It must be set in the top-level package via the
267+
/// AddSetting method so that the same value is passed to all subpackages.
268+
/// </remarks>
269+
public const string UseDefaultAssemblyLoadContext = "UseDefaultAssemblyLoadContext";
270+
259271
#endregion
260272

261273
#region Settings Used by the NUnit Framework

0 commit comments

Comments
 (0)