Skip to content

Commit c5b62fc

Browse files
committed
Port issue 1779 to V4
1 parent 256576b commit c5b62fc

File tree

6 files changed

+147
-254
lines changed

6 files changed

+147
-254
lines changed

src/NUnitCommon/nunit.agent.core/Agents/LocalProcessAgentLauncher.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public Process CreateAgent(Guid agentId, string agencyUrl, TestPackage package)
8383

8484
if (AgentRuntime.Identifier == FrameworkIdentifiers.NetCoreApp)
8585
{
86-
startInfo.FileName = DotNet.GetDotNetExe(runAsX86);
86+
startInfo.FileName = DotNet.GetDotnetExecutable(runAsX86);
8787
startInfo.Arguments = $"\"{AgentPath}\" {arguments}";
8888
}
8989

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

Lines changed: 47 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using System.Reflection;
1414
using System.Runtime.InteropServices;
1515
using System.Runtime.Loader;
16+
using NUnit.Common;
1617
using TestCentric.Metadata;
1718

1819
namespace NUnit.Engine.Internal
@@ -23,20 +24,9 @@ internal sealed class TestAssemblyResolver : IDisposable
2324

2425
private readonly AssemblyLoadContext _loadContext;
2526

26-
private static readonly string INSTALL_DIR;
27-
private static readonly string WINDOWS_DESKTOP_DIR;
28-
private static readonly string ASP_NET_CORE_DIR;
29-
3027
// Our Strategies for resolving references
3128
private List<ResolutionStrategy> ResolutionStrategies;
3229

33-
static TestAssemblyResolver()
34-
{
35-
INSTALL_DIR = GetDotNetInstallDirectory().ShouldNotBeNull();
36-
WINDOWS_DESKTOP_DIR = Path.Combine(INSTALL_DIR, "shared", "Microsoft.WindowsDesktop.App");
37-
ASP_NET_CORE_DIR = Path.Combine(INSTALL_DIR, "shared", "Microsoft.AspNetCore.App");
38-
}
39-
4030
public TestAssemblyResolver(AssemblyLoadContext loadContext, string testAssemblyPath)
4131
{
4232
_loadContext = loadContext;
@@ -49,34 +39,37 @@ public TestAssemblyResolver(AssemblyLoadContext loadContext, string testAssembly
4939
[MemberNotNull(nameof(ResolutionStrategies))]
5040
private void InitializeResolutionStrategies(AssemblyLoadContext loadContext, string testAssemblyPath)
5141
{
52-
// First, looking only at direct references by the test assembly, try to determine if
53-
// this assembly is using WindowsDesktop (either SWF or WPF) and/or AspNetCore.
42+
// Decide whether to try WindowsDeskTop and/or AspNetCore runtimes before any others.
43+
// We base this on direct references only, so we will eventually try each of them
44+
// later in case there are any indirect references.
5445
AssemblyDefinition assemblyDef = AssemblyDefinition.ReadAssembly(testAssemblyPath);
55-
bool isWindowsDesktop = false;
56-
bool isAspNetCore = false;
46+
bool tryWindowsDesktopFirst = false;
47+
bool tryAspNetCoreFirst = false;
5748
foreach (var reference in assemblyDef.MainModule.GetTypeReferences())
5849
{
5950
string fn = reference.FullName;
6051
if (fn.StartsWith("System.Windows.") || fn.StartsWith("PresentationFramework"))
61-
isWindowsDesktop = true;
52+
tryWindowsDesktopFirst = true;
6253
if (fn.StartsWith("Microsoft.AspNetCore."))
63-
isAspNetCore = true;
54+
tryAspNetCoreFirst = true;
6455
}
6556

6657
// Initialize the list of ResolutionStrategies in the best order depending on
6758
// what we learned.
6859
ResolutionStrategies = new List<ResolutionStrategy>();
6960

70-
if (isWindowsDesktop && Directory.Exists(WINDOWS_DESKTOP_DIR))
71-
ResolutionStrategies.Add(new AdditionalDirectoryStrategy(WINDOWS_DESKTOP_DIR));
72-
if (isAspNetCore && Directory.Exists(ASP_NET_CORE_DIR))
73-
ResolutionStrategies.Add(new AdditionalDirectoryStrategy(ASP_NET_CORE_DIR));
61+
if (tryWindowsDesktopFirst)
62+
ResolutionStrategies.Add(new WindowsDesktopStrategy());
63+
if (tryAspNetCoreFirst)
64+
ResolutionStrategies.Add(new AspNetCoreStrategy());
65+
7466
ResolutionStrategies.Add(new TrustedPlatformAssembliesStrategy());
7567
ResolutionStrategies.Add(new RuntimeLibrariesStrategy(loadContext, testAssemblyPath));
76-
if (!isWindowsDesktop && Directory.Exists(WINDOWS_DESKTOP_DIR))
77-
ResolutionStrategies.Add(new AdditionalDirectoryStrategy(WINDOWS_DESKTOP_DIR));
78-
if (!isAspNetCore && Directory.Exists(ASP_NET_CORE_DIR))
79-
ResolutionStrategies.Add(new AdditionalDirectoryStrategy(ASP_NET_CORE_DIR));
68+
69+
if (!tryWindowsDesktopFirst)
70+
ResolutionStrategies.Add(new WindowsDesktopStrategy());
71+
if (!tryAspNetCoreFirst)
72+
ResolutionStrategies.Add(new AspNetCoreStrategy());
8073
}
8174

8275
public void Dispose()
@@ -93,9 +86,8 @@ public void Dispose()
9386
{
9487
Guard.ArgumentNotNull(loadContext);
9588

96-
Assembly? loadedAssembly;
9789
foreach (var strategy in ResolutionStrategies)
98-
if (strategy.TryToResolve(loadContext, assemblyName, out loadedAssembly))
90+
if (strategy.TryToResolve(loadContext, assemblyName, out Assembly? loadedAssembly))
9991
return loadedAssembly;
10092

10193
log.Info("Cannot resolve assembly '{0}'", assemblyName);
@@ -210,115 +202,60 @@ public override bool TryToResolve(
210202
}
211203
}
212204

213-
public class AdditionalDirectoryStrategy : ResolutionStrategy
205+
public class AdditionalRuntimesStrategy : ResolutionStrategy
214206
{
215-
private string _frameworkDirectory;
207+
private readonly IEnumerable<DotNet.RuntimeInfo> _additionalRuntimes;
216208

217-
public AdditionalDirectoryStrategy(string frameworkDirectory)
209+
public AdditionalRuntimesStrategy(string runtimeName)
218210
{
219-
_frameworkDirectory = frameworkDirectory;
211+
_additionalRuntimes = DotNet.GetRuntimes(runtimeName, !Environment.Is64BitProcess);
220212
}
221213

222-
public override bool TryToResolve(
223-
AssemblyLoadContext loadContext, AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? loadedAssembly)
214+
public override bool TryToResolve(AssemblyLoadContext loadContext, AssemblyName assemblyName, [NotNullWhen(true)] out Assembly? loadedAssembly)
224215
{
225216
loadedAssembly = null;
226-
if (assemblyName.Version is null)
227-
return false;
228217

229-
var versionDir = FindBestVersionDir(_frameworkDirectory, assemblyName.Version);
230-
231-
if (versionDir is not null)
232-
{
233-
string candidate = Path.Combine(_frameworkDirectory, versionDir, assemblyName.Name + ".dll");
234-
if (File.Exists(candidate))
235-
{
236-
loadedAssembly = loadContext.LoadFromAssemblyPath(candidate);
237-
log.Info("'{0}' ({1}) assembly is loaded from AdditionalFrameworkDirectory {2} dependencies with best candidate version {3}",
238-
assemblyName,
239-
loadedAssembly.Location,
240-
_frameworkDirectory,
241-
versionDir);
218+
if (!FindBestRuntime(assemblyName, out DotNet.RuntimeInfo? runtime))
219+
return false;
242220

243-
return true;
244-
}
245-
else
246-
{
247-
log.Debug("Best version dir for {0} is {1}, but there is no {2} file", _frameworkDirectory, versionDir, candidate);
248-
return false;
249-
}
250-
}
221+
string candidate = Path.Combine(runtime.Path, runtime.Version.ToString(), assemblyName.Name + ".dll");
222+
if (!File.Exists(candidate))
223+
return false;
251224

252-
return false;
225+
loadedAssembly = loadContext.LoadFromAssemblyPath(candidate);
226+
return true;
253227
}
254-
}
255228

256-
#endregion
229+
private bool FindBestRuntime(AssemblyName assemblyName, [NotNullWhen(true)] out DotNet.RuntimeInfo? bestRuntime)
230+
{
231+
bestRuntime = null;
232+
var targetVersion = assemblyName.Version;
257233

258-
#region HelperMethods
234+
if (targetVersion is null)
235+
return false;
259236

260-
private static string? GetDotNetInstallDirectory()
261-
{
262-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
263-
{
264-
// Running on Windows so use registry
265-
if (Environment.Is64BitProcess)
266-
{
267-
using (RegistryKey? key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\dotnet\SetUp\InstalledVersions\x64\sharedHost\"))
268-
return (string?)key?.GetValue("Path");
269-
}
270-
else
237+
foreach (var candidate in _additionalRuntimes)
271238
{
272-
using (RegistryKey? key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\dotnet\SetUp\InstalledVersions\x86\"))
273-
return (string?)key?.GetValue("InstallLocation");
239+
if (candidate.Version >= targetVersion)
240+
if (bestRuntime is null || bestRuntime.Version > candidate.Version)
241+
bestRuntime = candidate;
274242
}
243+
244+
return bestRuntime is not null;
275245
}
276-
else
277-
return "/usr/shared/dotnet/";
278246
}
279247

280-
private static string? FindBestVersionDir(string libraryDir, Version targetVersion)
248+
public class WindowsDesktopStrategy : AdditionalRuntimesStrategy
281249
{
282-
string target = targetVersion.ToString();
283-
Version bestVersion = new Version(0, 0);
284-
foreach (var subdir in Directory.GetDirectories(libraryDir))
250+
public WindowsDesktopStrategy() : base("Microsoft.WindowsDesktop.App")
285251
{
286-
Version version;
287-
if (TryGetVersionFromString(Path.GetFileName(subdir), out version))
288-
{
289-
if (version >= targetVersion)
290-
if (bestVersion.Major == 0 || bestVersion > version)
291-
bestVersion = version;
292-
}
293252
}
294-
295-
return bestVersion.Major > 0
296-
? bestVersion.ToString()
297-
: null;
298253
}
299254

300-
private static bool TryGetVersionFromString(string text, out Version newVersion)
255+
public class AspNetCoreStrategy : AdditionalRuntimesStrategy
301256
{
302-
const string VERSION_CHARS = ".0123456789";
303-
304-
int len = 0;
305-
foreach (char c in text)
306-
{
307-
if (VERSION_CHARS.Contains(c))
308-
len++;
309-
else
310-
break;
311-
}
312-
313-
try
257+
public AspNetCoreStrategy() : base("Microsoft.AspNetCore.App")
314258
{
315-
newVersion = new Version(text.Substring(0, len));
316-
return true;
317-
}
318-
catch
319-
{
320-
newVersion = new Version();
321-
return false;
322259
}
323260
}
324261

0 commit comments

Comments
 (0)