/
TestHelper.cs
151 lines (124 loc) · 5.25 KB
/
TestHelper.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Riok.Mapperly.Abstractions;
namespace Riok.Mapperly.Tests;
public static class TestHelper
{
private static readonly GeneratorDriverOptions _enableIncrementalTrackingDriverOptions =
new(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true);
public static Task<VerifyResult> VerifyGenerator(string source, TestHelperOptions? options = null, params object?[] args)
{
var driver = Generate(source, options);
var verify = Verify(driver);
if (args.Any())
{
verify.UseParameters(args);
}
return verify.ToTask();
}
public static MapperGenerationResult GenerateMapper(
string source,
TestHelperOptions? options = null,
IReadOnlyCollection<TestAssembly>? additionalAssemblies = null
)
{
options ??= TestHelperOptions.NoDiagnostics;
var result = Generate(source, options, additionalAssemblies).GetRunResult();
var methods = ExtractAllMethods(result.GeneratedTrees.SingleOrDefault()?.GetRoot())
.Select(x => new GeneratedMethod(x))
.ToDictionary(x => x.Name);
var groupedDiagnostics = result.Diagnostics
.GroupBy(x => x.Descriptor.Id)
.ToDictionary(x => x.Key, x => (IReadOnlyCollection<Diagnostic>)x.ToList());
var mapperResult = new MapperGenerationResult(result.Diagnostics, groupedDiagnostics, methods);
if (options.AllowedDiagnostics != null)
{
mapperResult.Should().NotHaveDiagnostics(options.AllowedDiagnostics);
}
return mapperResult;
}
public static CSharpCompilation BuildCompilation(params SyntaxTree[] syntaxTrees) =>
BuildCompilation("Tests", NullableContextOptions.Enable, true, syntaxTrees);
public static TestAssembly BuildAssembly(string name, params SyntaxTree[] syntaxTrees)
{
var compilation = BuildCompilation(name, NullableContextOptions.Enable, false, syntaxTrees);
return new TestAssembly(compilation);
}
public static GeneratorDriver GenerateTracked(Compilation compilation)
{
var generator = new MapperGenerator();
var driver = CSharpGeneratorDriver.Create(
new[] { generator.AsSourceGenerator() },
driverOptions: _enableIncrementalTrackingDriverOptions
);
return driver.RunGenerators(compilation);
}
private static GeneratorDriver Generate(
string source,
TestHelperOptions? options,
IReadOnlyCollection<TestAssembly>? additionalAssemblies = null
)
{
options ??= TestHelperOptions.NoDiagnostics;
var syntaxTree = CSharpSyntaxTree.ParseText(source, CSharpParseOptions.Default.WithLanguageVersion(options.LanguageVersion));
var compilation = BuildCompilation(options.AssemblyName, options.NullableOption, true, syntaxTree);
if (additionalAssemblies != null)
{
compilation = compilation.AddReferences(additionalAssemblies.Select(x => x.MetadataReference));
}
var generator = new MapperGenerator();
GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
return driver.RunGenerators(compilation);
}
private static CSharpCompilation BuildCompilation(
string name,
NullableContextOptions nullableOption,
bool addMapperlyReferences,
params SyntaxTree[] syntaxTrees
)
{
var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, nullableContextOptions: nullableOption);
var compilation = CSharpCompilation.Create(name, syntaxTrees, options: compilationOptions);
var references = AppDomain.CurrentDomain
.GetAssemblies()
.Where(x => !x.IsDynamic && !string.IsNullOrWhiteSpace(x.Location))
.Select(x => MetadataReference.CreateFromFile(x.Location));
compilation = compilation.AddReferences(references);
if (addMapperlyReferences)
{
compilation = compilation.AddReferences(
MetadataReference.CreateFromFile(typeof(MapperGenerator).Assembly.Location),
MetadataReference.CreateFromFile(typeof(MapperAttribute).Assembly.Location)
);
}
return compilation;
}
private static IEnumerable<MethodDeclarationSyntax> ExtractAllMethods(SyntaxNode? root)
{
if (root == null)
yield break;
foreach (var node in root.ChildNodes())
{
// a namespace can contain classes
if (node is NamespaceDeclarationSyntax)
{
foreach (var method in ExtractAllMethods(node))
{
yield return method;
}
}
// a class can contain methods or other classes
if (node is not ClassDeclarationSyntax classNode)
continue;
foreach (var method in classNode.ChildNodes().OfType<MethodDeclarationSyntax>())
{
yield return method;
}
foreach (var method in ExtractAllMethods(node))
{
yield return method;
}
}
}
}