1313using System . Reflection ;
1414using System . Runtime . InteropServices ;
1515using System . Runtime . Loader ;
16+ using NUnit . Common ;
1617using TestCentric . Metadata ;
1718
1819namespace 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