44using System . Diagnostics ;
55using System . IO ;
66using System . Linq ;
7- using System . Reflection ;
87using System . Runtime . InteropServices ;
98using System . Threading ;
109using Microsoft . VisualStudio . TestPlatform . ObjectModel ;
@@ -608,19 +607,19 @@ void handler()
608607 }
609608
610609 public static IRunnerReporter GetRunnerReporter (
611- LoggerHelper logger ,
610+ LoggerHelper ? logger ,
612611 RunSettings runSettings ,
613612 IReadOnlyList < string > assemblyFileNames )
614613 {
615614 var reporter = default ( IRunnerReporter ) ;
616- var availableReporters = new Lazy < IReadOnlyList < IRunnerReporter > > ( ( ) => GetAvailableRunnerReporters ( assemblyFileNames ) ) ;
615+ var availableReporters = new Lazy < IReadOnlyList < IRunnerReporter > > ( ( ) => GetAvailableRunnerReporters ( logger , assemblyFileNames ) ) ;
617616
618617 try
619618 {
620619 if ( ! string . IsNullOrEmpty ( runSettings . ReporterSwitch ) )
621620 {
622621 reporter = availableReporters . Value . FirstOrDefault ( r => string . Equals ( r . RunnerSwitch , runSettings . ReporterSwitch , StringComparison . OrdinalIgnoreCase ) ) ;
623- if ( reporter is null )
622+ if ( reporter is null && logger is not null )
624623 logger . LogWarning ( "Could not find requested reporter '{0}'" , runSettings . ReporterSwitch ) ;
625624 }
626625
@@ -632,119 +631,30 @@ public static IRunnerReporter GetRunnerReporter(
632631 return reporter ?? new DefaultRunnerReporterWithTypes ( ) ;
633632 }
634633
635- static IReadOnlyList < IRunnerReporter > GetAvailableRunnerReporters ( IReadOnlyList < string > sources )
634+ public static IReadOnlyList < IRunnerReporter > GetAvailableRunnerReporters (
635+ LoggerHelper ? logger ,
636+ IReadOnlyList < string > sources )
636637 {
637- #if NETCOREAPP
638- // Combine all input libs and merge their contexts to find the potential reporters
639638 var result = new List < IRunnerReporter > ( ) ;
640- var dcjr = new DependencyContextJsonReader ( ) ;
641- var deps =
642- sources
643- . Select ( Path . GetFullPath )
644- . Select ( s => s . Replace ( ".dll" , ".deps.json" ) )
645- . Where ( File . Exists )
646- . Select ( f => new MemoryStream ( Encoding . UTF8 . GetBytes ( File . ReadAllText ( f ) ) ) )
647- . Select ( dcjr . Read ) ;
648- var ctx = deps . Aggregate ( DependencyContext . Default , ( context , dependencyContext ) => context . Merge ( dependencyContext ) ) ;
649- dcjr . Dispose ( ) ;
650-
651- var depsAssms = ctx . GetRuntimeAssemblyNames ( InternalRuntimeEnvironment . GetRuntimeIdentifier ( ) ) . ToList ( ) ;
652-
653- // Make sure to also check assemblies within the directory of the sources
654- var dllsInSources =
639+
640+ // We need to combine the source folders with our folder to find all potential runners
641+ var folders =
655642 sources
656- . Select ( Path . GetFullPath )
657- . Select ( Path . GetDirectoryName )
658- . Distinct ( StringComparer . OrdinalIgnoreCase )
643+ . Select ( s => Path . GetDirectoryName ( Path . GetFullPath ( s ) ) )
659644 . WhereNotNull ( )
660- . SelectMany ( p => Directory . GetFiles ( p , "*.dll" ) . Select ( f => Path . Combine ( p , f ) ) )
661- . Select ( f => new AssemblyName { Name = Path . GetFileNameWithoutExtension ( f ) } )
662- . ToList ( ) ;
645+ . Concat ( new [ ] { Path . GetDirectoryName ( typeof ( VsTestRunner ) . Assembly . GetLocalCodeBase ( ) ) } )
646+ . Distinct ( ) ;
663647
664- foreach ( var assemblyName in depsAssms . Concat ( dllsInSources ) )
648+ foreach ( var folder in folders )
665649 {
666- try
667- {
668- var assembly = Assembly . Load ( assemblyName ) ;
669- foreach ( var type in assembly . DefinedTypes )
670- {
671- #pragma warning disable CS0618
672- if ( type == null || type . IsAbstract || type == typeof ( DefaultRunnerReporter ) . GetTypeInfo ( ) || type == typeof ( DefaultRunnerReporterWithTypes ) . GetTypeInfo ( ) || type . ImplementedInterfaces . All ( i => i != typeof ( IRunnerReporter ) ) )
673- continue ;
674- #pragma warning restore CS0618
675-
676- var ctor = type . DeclaredConstructors . FirstOrDefault ( c => c . GetParameters ( ) . Length == 0 ) ;
677- if ( ctor == null )
678- {
679- ConsoleHelper . SetForegroundColor ( ConsoleColor . Yellow ) ;
680- Console . WriteLine ( $ "Type { type . FullName } in assembly { assembly } appears to be a runner reporter, but does not have an empty constructor.") ;
681- ConsoleHelper . ResetColor ( ) ;
682- continue ;
683- }
650+ result . AddRange ( RunnerReporterUtility . GetAvailableRunnerReporters ( folder , out var messages ) ) ;
684651
685- result . Add ( ( IRunnerReporter ) ctor . Invoke ( Array . Empty < object > ( ) ) ) ;
686- }
687- }
688- catch
689- {
690- continue ;
691- }
652+ if ( logger is not null )
653+ foreach ( var message in messages )
654+ logger . LogWarning ( message ) ;
692655 }
693656
694657 return result ;
695- #else
696- var result = new List < IRunnerReporter > ( ) ;
697- var runnerPath = Path . GetDirectoryName ( Assembly . GetExecutingAssembly ( ) . GetLocalCodeBase ( ) ) ;
698- var runnerReporterInterfaceAssemblyFullName = typeof ( IRunnerReporter ) . Assembly . GetName ( ) . FullName ;
699-
700- if ( runnerPath != null )
701- foreach ( var dllFile in Directory . GetFiles ( runnerPath , "*.dll" ) . Select ( f => Path . Combine ( runnerPath , f ) ) )
702- {
703- Type ? [ ] types ;
704-
705- try
706- {
707- var assembly = Assembly . LoadFile ( dllFile ) ;
708-
709- // Calling Assembly.GetTypes can be very expensive, while Assembly.GetReferencedAssemblies
710- // is relatively cheap. We can avoid loading types for assemblies that couldn't possibly
711- // reference IRunnerReporter.
712- if ( ! assembly . GetReferencedAssemblies ( ) . Where ( name => name . FullName == runnerReporterInterfaceAssemblyFullName ) . Any ( ) )
713- continue ;
714-
715- types = assembly . GetTypes ( ) ;
716- }
717- catch ( ReflectionTypeLoadException ex )
718- {
719- types = ex . Types ;
720- }
721- catch
722- {
723- continue ;
724- }
725-
726- foreach ( var type in types )
727- {
728- #pragma warning disable CS0618
729- if ( type == null || type . IsAbstract || type == typeof ( DefaultRunnerReporter ) || type == typeof ( DefaultRunnerReporterWithTypes ) || ! type . GetInterfaces ( ) . Any ( t => t == typeof ( IRunnerReporter ) ) )
730- continue ;
731- #pragma warning restore CS0618
732-
733- var ctor = type . GetConstructor ( new Type [ 0 ] ) ;
734- if ( ctor == null )
735- {
736- ConsoleHelper . SetForegroundColor ( ConsoleColor . Yellow ) ;
737- Console . WriteLine ( $ "Type { type . FullName } in assembly { dllFile } appears to be a runner reporter, but does not have an empty constructor.") ;
738- ConsoleHelper . ResetColor ( ) ;
739- continue ;
740- }
741-
742- result . Add ( ( IRunnerReporter ) ctor . Invoke ( new object [ 0 ] ) ) ;
743- }
744- }
745-
746- return result ;
747- #endif
748658 }
749659
750660 static IList < DiscoveredTestCase > GetVsTestCases (
0 commit comments