Skip to content

Commit 3853e85

Browse files
committed
Further changes from issue 1779
1 parent f1fa31b commit 3853e85

2 files changed

Lines changed: 217 additions & 145 deletions

File tree

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

Lines changed: 118 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using System.Runtime.Loader;
1616
using NUnit.Common;
1717
using TestCentric.Metadata;
18+
using System.Runtime.CompilerServices;
1819

1920
namespace NUnit.Engine.Internal
2021
{
@@ -59,17 +60,17 @@ private void InitializeResolutionStrategies(AssemblyLoadContext loadContext, str
5960
ResolutionStrategies = new List<ResolutionStrategy>();
6061

6162
if (tryWindowsDesktopFirst)
62-
ResolutionStrategies.Add(new WindowsDesktopStrategy());
63+
ResolutionStrategies.Add(new WindowsDesktopStrategy(false));
6364
if (tryAspNetCoreFirst)
64-
ResolutionStrategies.Add(new AspNetCoreStrategy());
65+
ResolutionStrategies.Add(new AspNetCoreStrategy(false));
6566

6667
ResolutionStrategies.Add(new TrustedPlatformAssembliesStrategy());
6768
ResolutionStrategies.Add(new RuntimeLibrariesStrategy(loadContext, testAssemblyPath));
6869

6970
if (!tryWindowsDesktopFirst)
70-
ResolutionStrategies.Add(new WindowsDesktopStrategy());
71+
ResolutionStrategies.Add(new WindowsDesktopStrategy(false));
7172
if (!tryAspNetCoreFirst)
72-
ResolutionStrategies.Add(new AspNetCoreStrategy());
73+
ResolutionStrategies.Add(new AspNetCoreStrategy(false));
7374
}
7475

7576
public void Dispose()
@@ -93,173 +94,162 @@ public void Dispose()
9394
log.Info("Cannot resolve assembly '{0}'", assemblyName);
9495
return null;
9596
}
97+
}
9698

97-
#region Nested ResolutionStrategy Classes
99+
#region ResolutionStrategy Classes
98100

99-
public abstract class ResolutionStrategy
101+
public abstract class ResolutionStrategy
102+
{
103+
public abstract bool TryToResolve(
104+
AssemblyLoadContext loadContext, AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? loadedAssembly);
105+
}
106+
107+
public class TrustedPlatformAssembliesStrategy : ResolutionStrategy
108+
{
109+
private static readonly Logger log = InternalTrace.GetLogger(typeof(TrustedPlatformAssembliesStrategy));
110+
public override bool TryToResolve(
111+
AssemblyLoadContext loadContext, AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? loadedAssembly)
100112
{
101-
public abstract bool TryToResolve(
102-
AssemblyLoadContext loadContext, AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? loadedAssembly);
113+
return TryLoadFromTrustedPlatformAssemblies(loadContext, assemblyName, out loadedAssembly);
103114
}
104115

105-
public class TrustedPlatformAssembliesStrategy : ResolutionStrategy
116+
private static bool TryLoadFromTrustedPlatformAssemblies(
117+
AssemblyLoadContext loadContext, AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? loadedAssembly)
106118
{
107-
public override bool TryToResolve(
108-
AssemblyLoadContext loadContext, AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? loadedAssembly)
119+
// https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/default-probing
120+
loadedAssembly = null;
121+
var trustedAssemblies = System.AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES") as string;
122+
if (string.IsNullOrEmpty(trustedAssemblies))
109123
{
110-
return TryLoadFromTrustedPlatformAssemblies(loadContext, assemblyName, out loadedAssembly);
124+
return false;
111125
}
112126

113-
private static bool TryLoadFromTrustedPlatformAssemblies(
114-
AssemblyLoadContext loadContext, AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? loadedAssembly)
127+
var separator = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ";" : ":";
128+
foreach (var assemblyPath in trustedAssemblies.Split(separator))
115129
{
116-
// https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/default-probing
117-
loadedAssembly = null;
118-
var trustedAssemblies = System.AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES") as string;
119-
if (string.IsNullOrEmpty(trustedAssemblies))
120-
{
121-
return false;
122-
}
123-
124-
var separator = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ";" : ":";
125-
foreach (var assemblyPath in trustedAssemblies.Split(separator))
130+
var fileName = Path.GetFileNameWithoutExtension(assemblyPath);
131+
if (FileMatchesAssembly(fileName) && File.Exists(assemblyPath))
126132
{
127-
var fileName = Path.GetFileNameWithoutExtension(assemblyPath);
128-
if (FileMatchesAssembly(fileName) && File.Exists(assemblyPath))
129-
{
130-
loadedAssembly = loadContext.LoadFromAssemblyPath(assemblyPath);
131-
log.Info("'{0}' assembly is loaded from trusted path '{1}'", assemblyPath, loadedAssembly.Location);
133+
loadedAssembly = loadContext.LoadFromAssemblyPath(assemblyPath);
134+
log.Info("'{0}' assembly is loaded from trusted path '{1}'", assemblyPath, loadedAssembly.Location);
132135

133-
return true;
134-
}
136+
return true;
135137
}
138+
}
136139

137-
return false;
140+
return false;
138141

139-
bool FileMatchesAssembly(string fileName) =>
140-
string.Equals(fileName, assemblyName.Name, StringComparison.InvariantCultureIgnoreCase);
141-
}
142+
bool FileMatchesAssembly(string fileName) =>
143+
string.Equals(fileName, assemblyName.Name, StringComparison.InvariantCultureIgnoreCase);
142144
}
145+
}
143146

144-
public class RuntimeLibrariesStrategy : ResolutionStrategy
147+
public class RuntimeLibrariesStrategy : ResolutionStrategy
148+
{
149+
private static readonly Logger log = InternalTrace.GetLogger(typeof(RuntimeLibrariesStrategy));
150+
151+
private DependencyContext? _dependencyContext;
152+
private readonly CompositeCompilationAssemblyResolver _assemblyResolver;
153+
154+
public RuntimeLibrariesStrategy(AssemblyLoadContext loadContext, string testAssemblyPath)
145155
{
146-
private DependencyContext? _dependencyContext;
147-
private readonly CompositeCompilationAssemblyResolver _assemblyResolver;
156+
_dependencyContext = DependencyContext.Load(loadContext.LoadFromAssemblyPath(testAssemblyPath));
157+
158+
_assemblyResolver = new CompositeCompilationAssemblyResolver(
159+
[
160+
new AppBaseCompilationAssemblyResolver(Path.GetDirectoryName(testAssemblyPath)!),
161+
new ReferenceAssemblyPathResolver(),
162+
new PackageCompilationAssemblyResolver()
163+
]);
164+
}
148165

149-
public RuntimeLibrariesStrategy(AssemblyLoadContext loadContext, string testAssemblyPath)
166+
public override bool TryToResolve(
167+
AssemblyLoadContext loadContext, AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? loadedAssembly)
168+
{
169+
if (_dependencyContext is null)
150170
{
151-
_dependencyContext = DependencyContext.Load(loadContext.LoadFromAssemblyPath(testAssemblyPath));
152-
153-
_assemblyResolver = new CompositeCompilationAssemblyResolver(new ICompilationAssemblyResolver[]
154-
{
155-
new AppBaseCompilationAssemblyResolver(Path.GetDirectoryName(testAssemblyPath)!),
156-
new ReferenceAssemblyPathResolver(),
157-
new PackageCompilationAssemblyResolver()
158-
});
171+
// TODO: Is this the intended behavior?
172+
loadedAssembly = null;
173+
return false;
159174
}
160175

161-
public override bool TryToResolve(
162-
AssemblyLoadContext loadContext, AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? loadedAssembly)
176+
foreach (var library in _dependencyContext.RuntimeLibraries)
163177
{
164-
if (_dependencyContext is null)
178+
var wrapper = new CompilationLibrary(
179+
library.Type,
180+
library.Name,
181+
library.Version,
182+
library.Hash,
183+
library.RuntimeAssemblyGroups.SelectMany(g => g.AssetPaths),
184+
library.Dependencies,
185+
library.Serviceable);
186+
187+
var assemblies = new List<string>();
188+
_assemblyResolver.TryResolveAssemblyPaths(wrapper, assemblies);
189+
190+
foreach (var assemblyPath in assemblies)
165191
{
166-
// TODO: Is this the intended behavior?
167-
loadedAssembly = null;
168-
return false;
169-
}
170-
171-
foreach (var library in _dependencyContext.RuntimeLibraries)
172-
{
173-
var wrapper = new CompilationLibrary(
174-
library.Type,
175-
library.Name,
176-
library.Version,
177-
library.Hash,
178-
library.RuntimeAssemblyGroups.SelectMany(g => g.AssetPaths),
179-
library.Dependencies,
180-
library.Serviceable);
181-
182-
var assemblies = new List<string>();
183-
_assemblyResolver.TryResolveAssemblyPaths(wrapper, assemblies);
184-
185-
foreach (var assemblyPath in assemblies)
192+
if (assemblyName.Name == Path.GetFileNameWithoutExtension(assemblyPath))
186193
{
187-
if (assemblyName.Name == Path.GetFileNameWithoutExtension(assemblyPath))
188-
{
189-
loadedAssembly = loadContext.LoadFromAssemblyPath(assemblyPath);
190-
log.Info("'{0}' ({1}) assembly is loaded from runtime libraries {2} dependencies",
191-
assemblyName,
192-
loadedAssembly.Location,
193-
library.Name);
194-
195-
return true;
196-
}
194+
loadedAssembly = loadContext.LoadFromAssemblyPath(assemblyPath);
195+
log.Info("'{0}' ({1}) assembly is loaded from runtime libraries {2} dependencies",
196+
assemblyName,
197+
loadedAssembly.Location,
198+
library.Name);
199+
200+
return true;
197201
}
198202
}
199-
200-
loadedAssembly = null;
201-
return false;
202203
}
203-
}
204204

205-
public class AdditionalRuntimesStrategy : ResolutionStrategy
206-
{
207-
private readonly IEnumerable<DotNet.RuntimeInfo> _additionalRuntimes;
208-
209-
public AdditionalRuntimesStrategy(string runtimeName)
210-
{
211-
_additionalRuntimes = DotNet.GetRuntimes(runtimeName, !Environment.Is64BitProcess);
212-
}
213-
214-
public override bool TryToResolve(AssemblyLoadContext loadContext, AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? loadedAssembly)
215-
{
216-
loadedAssembly = null;
217-
218-
if (!FindBestRuntime(assemblyName, out DotNet.RuntimeInfo? runtime))
219-
return false;
205+
loadedAssembly = null;
206+
return false;
207+
}
208+
}
220209

221-
string candidate = Path.Combine(runtime.Path, runtime.Version.ToString(), assemblyName.Name + ".dll");
222-
if (!File.Exists(candidate))
223-
return false;
210+
public class AdditionalRuntimesStrategy : ResolutionStrategy
211+
{
212+
private string _runtimeName;
213+
private bool _x86;
224214

225-
loadedAssembly = loadContext.LoadFromAssemblyPath(candidate);
226-
return true;
227-
}
215+
public AdditionalRuntimesStrategy(string runtimeName, bool x86)
216+
{
217+
_runtimeName = runtimeName;
218+
_x86 = x86;
219+
}
228220

229-
private bool FindBestRuntime(AssemblyName assemblyName, [NotNullWhen(true)] out DotNet.RuntimeInfo? bestRuntime)
230-
{
231-
bestRuntime = null;
232-
var targetVersion = assemblyName.Version;
221+
public override bool TryToResolve(AssemblyLoadContext loadContext, AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? loadedAssembly)
222+
{
223+
loadedAssembly = null;
224+
if (assemblyName.Version is null)
225+
return false;
233226

234-
if (targetVersion is null)
235-
return false;
227+
if (!DotNet.FindBestRuntime(assemblyName.Version, _runtimeName, _x86, out DotNet.RuntimeInfo? runtime))
228+
return false;
236229

237-
foreach (var candidate in _additionalRuntimes)
238-
{
239-
if (candidate.Version >= targetVersion)
240-
if (bestRuntime is null || bestRuntime.Version > candidate.Version)
241-
bestRuntime = candidate;
242-
}
230+
string candidate = Path.Combine(runtime.Path, runtime.Version.ToString(), assemblyName.Name + ".dll");
231+
if (!File.Exists(candidate))
232+
return false;
243233

244-
return bestRuntime is not null;
245-
}
234+
loadedAssembly = loadContext.LoadFromAssemblyPath(candidate);
235+
return true;
246236
}
237+
}
247238

248-
public class WindowsDesktopStrategy : AdditionalRuntimesStrategy
239+
public class WindowsDesktopStrategy : AdditionalRuntimesStrategy
240+
{
241+
public WindowsDesktopStrategy(bool x86) : base("Microsoft.WindowsDesktop.App", x86)
249242
{
250-
public WindowsDesktopStrategy() : base("Microsoft.WindowsDesktop.App")
251-
{
252-
}
253243
}
244+
}
254245

255-
public class AspNetCoreStrategy : AdditionalRuntimesStrategy
246+
public class AspNetCoreStrategy : AdditionalRuntimesStrategy
247+
{
248+
public AspNetCoreStrategy(bool x86) : base("Microsoft.AspNetCore.App", x86)
256249
{
257-
public AspNetCoreStrategy() : base("Microsoft.AspNetCore.App")
258-
{
259-
}
260250
}
251+
}
261252

262253
#endregion
263-
}
264254
}
265255
#endif

0 commit comments

Comments
 (0)