How to use references of C# 9 source code generator in unit tests?
Asked Answered
S

0

6

I have a C# 9.0 source code generator that references a class library that contains an attribute that is used to identify the classes to process. I am now writing unit tests as described here:

        Compilation inputCompilation = CreateCompilation(@"
        using Dependncy;
        namespace MyCode
        {
            [Marker]
            public partial class SimpleObject
            {
                [PropertyMarker(0)]
                public int Test{get;set;}
            }
        }
        ");

        var generator = new MyCodeGen();

        // Create the driver that will control the generation, passing in our generator
        GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

        // Run the generation pass
        // (Note: the generator driver itself is immutable, and all calls return an updated version of the driver that you should use for subsequent calls)
        driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var diagnostics);

        // We can now assert things about the resulting compilation:
        Assert.IsTrue(diagnostics.IsEmpty); // there were no diagnostics created by the generators
        Assert.IsTrue(outputCompilation.SyntaxTrees.Count() == 2); // we have two syntax trees, the original 'user' provided one, and the one added by the generator
        Assert.IsTrue(outputCompilation.GetDiagnostics().IsEmpty); // verify the compilation with the added source has no diagnostics

        // Or we can look at the results directly:
        GeneratorDriverRunResult runResult = driver.GetRunResult();
        // The runResult contains the combined results of all generators passed to the driver
        Assert.IsTrue(runResult.GeneratedTrees.Length == 1);
        Assert.IsTrue(runResult.Diagnostics.IsEmpty);

        // Or you can access the individual results on a by-generator basis
        GeneratorRunResult generatorResult = runResult.Results[0];
        Assert.IsTrue(generatorResult.Generator == generator);
        Assert.IsTrue(generatorResult.Diagnostics.IsEmpty);
        Assert.IsTrue(generatorResult.GeneratedSources.Length == 1);
        Assert.IsTrue(generatorResult.Exception is null);

CreateCompilation looks like this

private static Compilation CreateCompilation(string source)
        => CSharpCompilation.Create("compilation",
            new[] { CSharpSyntaxTree.ParseText(source) },
            new[] { MetadataReference.CreateFromFile(typeof(Binder).GetTypeInfo().Assembly.Location), MetadataReference.CreateFromFile(typeof(MarkerAttribute).GetTypeInfo().Assembly.Location) },
            new CSharpCompilationOptions(OutputKind.ConsoleApplication));

However, the generator will not recognize the marker attribute (symbols are not equal). I guess, the issue is, that within the compilation, they are treated as from another assembly: One from the compilation within the UnitTest and the one that is references in the source code generator.

The source code generator obtains the symbol to compare with compilation.GetTypeByMetadataName(typeof(MarkerAttribute).FullName) but also references the containing assembly itself. I include the reference with GetDependencyTargetPaths as they do it in the official sample csproj.

Any idea how I have to create the compilation, so that the symbols are equal in the source generator?

Edit 1

I just found out, that the AttributeClass seems to match the type (i.e. Names are equal), but its Kind Property returns SymbolKind.ErrorType while the one obtained by the source gen with compilation.GetTypeByMetadataName(typeof(MarkerAttribute).FullName) returns SymbolKind.NamedType. So that's probably why they don't match. However, I still have no idea why the symbols are resolved differently.

Shower answered 7/3, 2021 at 15:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.