diff --git a/TUnit.Core.SourceGenerator.Tests/AssemblyLoaderTests.Test.DotNet8_0.verified.txt b/TUnit.Core.SourceGenerator.Tests/AssemblyLoaderTests.Test.DotNet8_0.verified.txt index b08605376f..efe5742507 100644 --- a/TUnit.Core.SourceGenerator.Tests/AssemblyLoaderTests.Test.DotNet8_0.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/AssemblyLoaderTests.Test.DotNet8_0.verified.txt @@ -6,7 +6,21 @@ file static class AssemblyLoader_Guid [global::System.Runtime.CompilerServices.ModuleInitializer] public static void Initialize() { + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Core.SourceGenerator.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")); global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b8d4030011dbd70c")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Verify.TUnit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c7a34512ecd69090")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Verify, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c7a34512ecd69090")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Argon, Version=1.0.0.0, Culture=neutral, PublicKeyToken=00a55352ff068a54")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("DiffEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c7a34512ecd69090")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("EmptyFiles, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c7a34512ecd69090")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("SimpleInfoName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c7a34512ecd69090")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Assertions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b8d4030011dbd70c")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Core.SourceGenerator, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Engine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b8d4030011dbd70c")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("EnumerableAsyncProcessor, Version=2.1.0.0, Culture=neutral, PublicKeyToken=7a7adb9c614908c9")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Sourcy.Core, Version=0.7.6.0, Culture=neutral, PublicKeyToken=null")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.TestProject.Library, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")); } } diff --git a/TUnit.Core.SourceGenerator.Tests/AssemblyLoaderTests.Test.DotNet9_0.verified.txt b/TUnit.Core.SourceGenerator.Tests/AssemblyLoaderTests.Test.DotNet9_0.verified.txt index b08605376f..efe5742507 100644 --- a/TUnit.Core.SourceGenerator.Tests/AssemblyLoaderTests.Test.DotNet9_0.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/AssemblyLoaderTests.Test.DotNet9_0.verified.txt @@ -6,7 +6,21 @@ file static class AssemblyLoader_Guid [global::System.Runtime.CompilerServices.ModuleInitializer] public static void Initialize() { + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Core.SourceGenerator.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")); global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b8d4030011dbd70c")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Verify.TUnit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c7a34512ecd69090")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Verify, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c7a34512ecd69090")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Argon, Version=1.0.0.0, Culture=neutral, PublicKeyToken=00a55352ff068a54")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("DiffEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c7a34512ecd69090")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("EmptyFiles, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c7a34512ecd69090")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("SimpleInfoName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c7a34512ecd69090")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Assertions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b8d4030011dbd70c")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Core.SourceGenerator, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Engine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b8d4030011dbd70c")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("EnumerableAsyncProcessor, Version=2.1.0.0, Culture=neutral, PublicKeyToken=7a7adb9c614908c9")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Sourcy.Core, Version=0.7.6.0, Culture=neutral, PublicKeyToken=null")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.TestProject.Library, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")); } } diff --git a/TUnit.Core.SourceGenerator.Tests/AssemblyLoaderTests.Test.Net4_7.verified.txt b/TUnit.Core.SourceGenerator.Tests/AssemblyLoaderTests.Test.Net4_7.verified.txt index b08605376f..efe5742507 100644 --- a/TUnit.Core.SourceGenerator.Tests/AssemblyLoaderTests.Test.Net4_7.verified.txt +++ b/TUnit.Core.SourceGenerator.Tests/AssemblyLoaderTests.Test.Net4_7.verified.txt @@ -6,7 +6,21 @@ file static class AssemblyLoader_Guid [global::System.Runtime.CompilerServices.ModuleInitializer] public static void Initialize() { + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Core.SourceGenerator.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")); global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b8d4030011dbd70c")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Verify.TUnit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c7a34512ecd69090")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Verify, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c7a34512ecd69090")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Argon, Version=1.0.0.0, Culture=neutral, PublicKeyToken=00a55352ff068a54")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("DiffEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c7a34512ecd69090")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("EmptyFiles, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c7a34512ecd69090")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("SimpleInfoName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c7a34512ecd69090")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Assertions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b8d4030011dbd70c")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Core.SourceGenerator, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Engine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b8d4030011dbd70c")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("EnumerableAsyncProcessor, Version=2.1.0.0, Culture=neutral, PublicKeyToken=7a7adb9c614908c9")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Sourcy.Core, Version=0.7.6.0, Culture=neutral, PublicKeyToken=null")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.TestProject.Library, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")); } } diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/AssemblyLoaderGenerator.cs b/TUnit.Core.SourceGenerator/CodeGenerators/AssemblyLoaderGenerator.cs index 6dc3042d8b..2282c5819a 100644 --- a/TUnit.Core.SourceGenerator/CodeGenerators/AssemblyLoaderGenerator.cs +++ b/TUnit.Core.SourceGenerator/CodeGenerators/AssemblyLoaderGenerator.cs @@ -1,33 +1,41 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis; +using TUnit.Core.SourceGenerator.CodeGenerators.Equality; namespace TUnit.Core.SourceGenerator.CodeGenerators; [Generator] public class AssemblyLoaderGenerator : IIncrementalGenerator { + private static readonly string[] _excludedAssemblies = + [ + "b77a5c561934e089", + "b03f5f7f11d50a3a", + "31bf3856ad364e35", + "cc7b13ffcd2ddd51", + "7cec85d7bea7798e", + + ]; public void Initialize(IncrementalGeneratorInitializationContext context) { var provider = context.CompilationProvider - .Select((x, _) => x.GetUsedAssemblyReferences()) - .WithComparer(new AssemblyComparer()) - .Combine(context.CompilationProvider); + .WithComparer(new PreventCompilationTriggerOnEveryKeystrokeComparer()); - context.RegisterSourceOutput(provider, (sourceProductionContext, source) => GenerateCode(sourceProductionContext, source.Right, source.Left)); + context.RegisterSourceOutput(provider, (sourceProductionContext, source) => GenerateCode(sourceProductionContext, source)); } - private void GenerateCode(SourceProductionContext context, Compilation compilation, ImmutableArray metadataReferences) + private void GenerateCode(SourceProductionContext context, Compilation compilation) { - var assemblyReferences = metadataReferences + var assemblyReferences = compilation.References .Where(x => x.Properties.Kind == MetadataImageKind.Assembly) .Where(x => !string.IsNullOrEmpty(x.Display)); var assemblySymbols = assemblyReferences .Select(compilation.GetAssemblyOrModuleSymbol) .OfType() - .Where(x => !IsSystemAssembly(x)) - .Select(GetAssemblyFullName) - .Distinct(); + .Distinct(SymbolEqualityComparer.Default) + .OfType() + .ToArray(); var sourceBuilder = new SourceCodeWriter(); @@ -40,9 +48,10 @@ private void GenerateCode(SourceProductionContext context, Compilation compilati sourceBuilder.WriteLine("public static void Initialize()"); sourceBuilder.WriteLine("{"); + var visitedAssemblies = new HashSet(SymbolEqualityComparer.Default); foreach (var assembly in assemblySymbols) { - sourceBuilder.WriteLine($"global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load(\"{assembly}\"));"); + WriteAssemblyLoad(sourceBuilder, assembly, visitedAssemblies); } sourceBuilder.WriteLine("}"); @@ -51,20 +60,33 @@ private void GenerateCode(SourceProductionContext context, Compilation compilati context.AddSource("AssemblyLoader.g.cs", sourceBuilder.ToString()); } - private static bool IsSystemAssembly(IAssemblySymbol assemblySymbol) + private static void WriteAssemblyLoad(SourceCodeWriter sourceBuilder, IAssemblySymbol assembly, HashSet visitedAssemblies) { - // Check for well-known public key tokens of system assemblies - var publicKeyToken = assemblySymbol.Identity.PublicKeyToken; + if (!visitedAssemblies.Add(assembly) || IsSystemAssembly(assembly)) + { + return; + } - if (publicKeyToken == null) + sourceBuilder.WriteLine($"global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load(\"{GetAssemblyFullName(assembly)}\"));"); + + foreach (var innerAssembly in assembly.Modules.SelectMany(x => x.ReferencedAssemblySymbols)) + { + WriteAssemblyLoad(sourceBuilder, innerAssembly, visitedAssemblies); + } + } + + private static bool IsSystemAssembly(IAssemblySymbol assemblySymbol) + { + if (assemblySymbol.Identity.PublicKeyToken.IsDefaultOrEmpty) { return false; } - return publicKeyToken.SequenceEqual(new byte[] { 0xb7, 0x7a, 0x5c, 0x56, 0x19, 0x34, 0xe0, 0x89 }) // .NET Framework - || publicKeyToken.SequenceEqual(new byte[] { 0x7c, 0xec, 0x85, 0xd7, 0xbe, 0xa7, 0x79, 0x8e }) // .NET Core - || publicKeyToken.SequenceEqual(new byte[] { 0xcc, 0x7b, 0x13, 0xff, 0xcd, 0x2d, 0xdd, 0x51 }) // System.Private - || publicKeyToken.SequenceEqual(new byte[] { 0xb0, 0x3f, 0x5f, 0x7f, 0x11, 0xd5, 0x0a, 0x3a }); // mscorlib + var stringPublicTokenKey = BitConverter.ToString(assemblySymbol.Identity.PublicKeyToken.ToArray()) + .Replace("-", "") + .ToLowerInvariant(); + + return _excludedAssemblies.Contains(stringPublicTokenKey); } private static string GetAssemblyFullName(IAssemblySymbol assemblySymbol) @@ -72,22 +94,4 @@ private static string GetAssemblyFullName(IAssemblySymbol assemblySymbol) var identity = assemblySymbol.Identity; return $"{identity.Name}, Version={identity.Version}, Culture={(string.IsNullOrEmpty(identity.CultureName) ? "neutral" : identity.CultureName)}, PublicKeyToken={(identity.PublicKeyToken.Length > 0 ? BitConverter.ToString(identity.PublicKeyToken.ToArray()).Replace("-", "").ToLowerInvariant() : "null")}"; } - - private class AssemblyComparer : IEqualityComparer> - { - public bool Equals(ImmutableArray x, ImmutableArray y) - { - return GetAssemblyNamesString(x).Equals(GetAssemblyNamesString(y)); - } - - public int GetHashCode(ImmutableArray obj) - { - return GetAssemblyNamesString(obj).GetHashCode(); - } - - private static string GetAssemblyNamesString(ImmutableArray metadataReferences) - { - return string.Join("|", metadataReferences.Select(x => x.Display)); - } - } } \ No newline at end of file