Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,37 @@ Test helpers and extension methods to simplify testing of .NET source generators
## Testing a source generator

```csharp
var result = SourceGenerator.Run<YourSourceGenerator>("your source");
var (generator, result) = SourceGenerator.Run<YourSourceGenerator>("your source");
```

You can also run a generator against multiple source files by passing any `IEnumerable<string>`.

```csharp
var sources = new[]
{
"namespace Tests; public partial class First;",
"namespace Tests; public partial class Second;"
};

var (generator, result) = SourceGenerator.Run<YourSourceGenerator>(sources);
```

## Testing an incremental source generator

```csharp
var result = IncrementalGenerator.Run<YourSourceGenerator>("your source");
var (generator, result) = IncrementalGenerator.Run<YourSourceGenerator>("your source");
```

The same multiple-source overload is available for incremental generators.

```csharp
var sources = new[]
{
"namespace Tests; public partial class First;",
"namespace Tests; public partial class Second;"
};

var (generator, result) = IncrementalGenerator.Run<YourSourceGenerator>(sources);
```

## Obtaining the generated source
Expand Down Expand Up @@ -50,15 +74,15 @@ Using one of the testing framework packages below, you can also assert the diffe
[![MSTest](https://img.shields.io/nuget/dt/SourceGeneratorTestHelpers.MSTest?label=MSTest)](https://www.nuget.org/packages/SourceGeneratorTestHelpers.MSTest)

```csharp
var result = IncrementalGenerator.Run<YourSourceGenerator>("your source");
var (_, result) = IncrementalGenerator.Run<YourSourceGenerator>("your source");

result.ShouldProduce("TestId.g.cs", "expected source");
```

_Note: If you do not wish to assert on errors produced during diagnostics of the source generator run, you can simply disable them as such._

```csharp
var result = IncrementalGenerator.Run<YourSourceGenerator>("your source");
var (_, result) = IncrementalGenerator.Run<YourSourceGenerator>("your source");

result.ShouldProduce("TestId.g.cs", "expected source", false);
```
Expand All @@ -75,7 +99,7 @@ public class SourceGeneratorTests
[Fact]
public Task ShouldProductTestId()
{
var result = IncrementalGenerator.Run<YourSourceGenerator>("your source");
var (_, result) = IncrementalGenerator.Run<YourSourceGenerator>("your source");
return result.VerifyAsync("TestId.g.cs");
}
}
Expand All @@ -90,7 +114,7 @@ public class SourceGeneratorTests
[Test]
public Task ShouldProductTestId()
{
var result = IncrementalGenerator.Run<YourSourceGenerator>("your source");
var (_, result) = IncrementalGenerator.Run<YourSourceGenerator>("your source");
return result.VerifyAsync("TestId.g.cs");
}
}
Expand All @@ -106,8 +130,8 @@ public class SourceGeneratorTests :
[TestMethod]
public Task ShouldProductTestId()
{
var result = IncrementalGenerator.Run<YourSourceGenerator>("your source");
return VerifyAsync("TestId.g.cs");
var (_, result) = IncrementalGenerator.Run<YourSourceGenerator>("your source");
return VerifyAsync(result, "TestId.g.cs");
}
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace SourceGeneratorTestHelpers.MSTest;

internal abstract class GeneratorDriverTestBase : VerifyBase
public abstract class GeneratorDriverTestBase : VerifyBase
{
/// <summary>Verifies that the generated source from a <see cref="GeneratorDriverRunResult"/> with a specific file path ending matches the expected source.</summary>
/// <param name="result">The <see cref="GeneratorDriverRunResult"/> to get the source from.</param>
Expand Down
32 changes: 24 additions & 8 deletions src/SourceGeneratorTestHelpers/Common/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ internal static (ImmutableArray<Diagnostic> CompilationDiagnostics, GeneratorDri
CSharpCompilationOptions? cSharpCompilationOptions
)
{
var sources = new[] { source };
cSharpParseOptions ??= DefaultCSharpParseOptions;
var syntaxTrees = new[] { CSharpSyntaxTree.ParseText(source, cSharpParseOptions, encoding: Encoding.UTF8) };

return InternalRunGenerator(generator, sources, cSharpParseOptions, metadataReferences, cSharpCompilationOptions);
return InternalRunGenerator(generator, syntaxTrees, cSharpParseOptions, metadataReferences, cSharpCompilationOptions);
}

internal static (ImmutableArray<Diagnostic> CompilationDiagnostics, GeneratorDriverRunResult Result) InternalRunGenerator(
Expand All @@ -35,8 +36,20 @@ internal static (ImmutableArray<Diagnostic> CompilationDiagnostics, GeneratorDri
CSharpCompilationOptions? cSharpCompilationOptions
)
{
var syntaxTrees = sources.Select(source => CSharpSyntaxTree.ParseText(source, cSharpParseOptions, encoding: Encoding.UTF8)).ToArray();
cSharpParseOptions ??= DefaultCSharpParseOptions;
var syntaxTrees = sources.Select(source => CSharpSyntaxTree.ParseText(source, cSharpParseOptions, encoding: Encoding.UTF8)).ToArray();

return InternalRunGenerator(generator, syntaxTrees, cSharpParseOptions, metadataReferences, cSharpCompilationOptions);
}

private static (ImmutableArray<Diagnostic> CompilationDiagnostics, GeneratorDriverRunResult Result) InternalRunGenerator(
ISourceGenerator generator,
SyntaxTree[] syntaxTrees,
CSharpParseOptions cSharpParseOptions,
IEnumerable<MetadataReference>? metadataReferences,
CSharpCompilationOptions? cSharpCompilationOptions
)
{
metadataReferences ??= DefaultMetadataReferences;
cSharpCompilationOptions ??= DefaultCSharpCompilationOptions;

Expand Down Expand Up @@ -97,11 +110,14 @@ Action<string> assertAction

internal static GeneratedSource? InternalGetSource(this GeneratorDriverRunResult result, string filePathEndsWith)
{
var sources = InternalGetSources(result);

var matchingSources = sources.Where(x => x.FilePath.EndsWith(filePathEndsWith, StringComparison.InvariantCultureIgnoreCase)).ToList();

return matchingSources.Count == 0 ? null : matchingSources[0];
foreach (var generatedTree in result.GeneratedTrees)
{
var source = GetGeneratedSource(generatedTree);
if (source.FilePath.EndsWith(filePathEndsWith, StringComparison.InvariantCultureIgnoreCase))
return source;
}

return null;
}

internal static ImmutableList<GeneratedSource> InternalGetSources(this GeneratorDriverRunResult result)
Expand Down
40 changes: 24 additions & 16 deletions src/SourceGeneratorTestHelpers/IncrementalGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@ public static class IncrementalGenerator
/// The C# compilation options to compile with. By default, Output will be set to library, and Nullable will be set to
/// enable.
/// </param>
/// <returns>The results of the <see cref="IIncrementalGenerator"/> execution.</returns>
public static GeneratorDriverRunResult Run<T>(
/// <returns>The generator instance used for the run and the result of the <see cref="IIncrementalGenerator"/> execution.</returns>
public static (T Generator, GeneratorDriverRunResult Result) Run<T>(
string source,
CSharpParseOptions? cSharpParseOptions = null,
IEnumerable<MetadataReference>? metadataReferences = null,
CSharpCompilationOptions? cSharpCompilationOptions = null
)
where T : IIncrementalGenerator, new()
{
var generator = new T().AsSourceGenerator();
var generator = new T();
var sourceGenerator = generator.AsSourceGenerator();
var result = Helpers.InternalRunGenerator(sourceGenerator, source, cSharpParseOptions, metadataReferences, cSharpCompilationOptions).Result;

return Helpers.InternalRunGenerator(generator, source, cSharpParseOptions, metadataReferences, cSharpCompilationOptions).Result;
return (generator, result);
}

/// <summary>Gather compilation diagnostics and executes a specified <see cref="IIncrementalGenerator"/> against the provided sources within a testing environment.</summary>
Expand All @@ -40,18 +42,20 @@ public static GeneratorDriverRunResult Run<T>(
/// The C# compilation options to compile with. By default, Output will be set to library, and Nullable will be set to
/// enable.
/// </param>
/// <returns>The compilation diagnostics and the result of the <see cref="IIncrementalGenerator"/> execution.</returns>
public static (ImmutableArray<Diagnostic> Diagnostics, GeneratorDriverRunResult Result) RunWithDiagnostics<T>(
/// <returns>The generator instance used for the run, the result of the <see cref="IIncrementalGenerator"/> execution, and the compilation diagnostics.</returns>
public static (T Generator, GeneratorDriverRunResult Result, ImmutableArray<Diagnostic> Diagnostics) RunWithDiagnostics<T>(
string source,
CSharpParseOptions? cSharpParseOptions = null,
IEnumerable<MetadataReference>? metadataReferences = null,
CSharpCompilationOptions? cSharpCompilationOptions = null
)
where T : IIncrementalGenerator, new()
{
var generator = new T().AsSourceGenerator();
var generator = new T();
var sourceGenerator = generator.AsSourceGenerator();
var result = Helpers.InternalRunGenerator(sourceGenerator, source, cSharpParseOptions, metadataReferences, cSharpCompilationOptions);

return Helpers.InternalRunGenerator(generator, source, cSharpParseOptions, metadataReferences, cSharpCompilationOptions);
return (generator, result.Result, result.CompilationDiagnostics);
}

/// <summary>Executes a specified <see cref="IIncrementalGenerator"/> against the provided sources within a testing environment.</summary>
Expand All @@ -63,18 +67,20 @@ public static (ImmutableArray<Diagnostic> Diagnostics, GeneratorDriverRunResult
/// The C# compilation options to compile with. By default, Output will be set to library, and Nullable will be set to
/// enable.
/// </param>
/// <returns>The result of the <see cref="IIncrementalGenerator"/> execution.</returns>
public static GeneratorDriverRunResult Run<T>(
/// <returns>The generator instance used for the run and the result of the <see cref="IIncrementalGenerator"/> execution.</returns>
public static (T Generator, GeneratorDriverRunResult Result) Run<T>(
IEnumerable<string> sources,
CSharpParseOptions? cSharpParseOptions = null,
IEnumerable<MetadataReference>? metadataReferences = null,
CSharpCompilationOptions? cSharpCompilationOptions = null
)
where T : IIncrementalGenerator, new()
{
var generator = new T().AsSourceGenerator();
var generator = new T();
var sourceGenerator = generator.AsSourceGenerator();
var result = Helpers.InternalRunGenerator(sourceGenerator, sources, cSharpParseOptions, metadataReferences, cSharpCompilationOptions).Result;

return Helpers.InternalRunGenerator(generator, sources, cSharpParseOptions, metadataReferences, cSharpCompilationOptions).Result;
return (generator, result);
}

/// <summary>Gather compilation diagnostics and executes a specified <see cref="IIncrementalGenerator"/> against the provided sources within a testing environment.</summary>
Expand All @@ -86,17 +92,19 @@ public static GeneratorDriverRunResult Run<T>(
/// The C# compilation options to compile with. By default, Output will be set to library, and Nullable will be set to
/// enable.
/// </param>
/// <returns>The compilation diagnostics and the result of the <see cref="IIncrementalGenerator"/> execution.</returns>
public static (ImmutableArray<Diagnostic> Diagnostics, GeneratorDriverRunResult Result) RunWithDiagnostics<T>(
/// <returns>The generator instance used for the run, the result of the <see cref="IIncrementalGenerator"/> execution, and the compilation diagnostics.</returns>
public static (T Generator, GeneratorDriverRunResult Result, ImmutableArray<Diagnostic> Diagnostics) RunWithDiagnostics<T>(
IEnumerable<string> sources,
CSharpParseOptions? cSharpParseOptions = null,
IEnumerable<MetadataReference>? metadataReferences = null,
CSharpCompilationOptions? cSharpCompilationOptions = null
)
where T : IIncrementalGenerator, new()
{
var generator = new T().AsSourceGenerator();
var generator = new T();
var sourceGenerator = generator.AsSourceGenerator();
var result = Helpers.InternalRunGenerator(sourceGenerator, sources, cSharpParseOptions, metadataReferences, cSharpCompilationOptions);

return Helpers.InternalRunGenerator(generator, sources, cSharpParseOptions, metadataReferences, cSharpCompilationOptions);
return (generator, result.Result, result.CompilationDiagnostics);
}
}
34 changes: 19 additions & 15 deletions src/SourceGeneratorTestHelpers/SourceGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Collections.Immutable;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using SourceGeneratorTestHelpers.Common;
Expand All @@ -17,8 +17,8 @@ public static class SourceGenerator
/// The C# compilation options to compile with. By default, Output will be set to library, and Nullable will be set to
/// enable.
/// </param>
/// <returns>The results of the <see cref="ISourceGenerator"/> execution.</returns>
public static GeneratorDriverRunResult Run<T>(
/// <returns>The generator instance used for the run and the result of the <see cref="ISourceGenerator"/> execution.</returns>
public static (T Generator, GeneratorDriverRunResult Result) Run<T>(
string source,
CSharpParseOptions? cSharpParseOptions = null,
IEnumerable<MetadataReference>? metadataReferences = null,
Expand All @@ -27,8 +27,9 @@ public static GeneratorDriverRunResult Run<T>(
where T : ISourceGenerator, new()
{
var generator = new T();
var result = Helpers.InternalRunGenerator(generator, source, cSharpParseOptions, metadataReferences, cSharpCompilationOptions).Result;

return Helpers.InternalRunGenerator(generator, source, cSharpParseOptions, metadataReferences, cSharpCompilationOptions).Result;
return (generator, result);
}

/// <summary>Gather compilation diagnostics and executes a specified <see cref="ISourceGenerator"/> against the provided source within a testing environment.</summary>
Expand All @@ -40,8 +41,8 @@ public static GeneratorDriverRunResult Run<T>(
/// The C# compilation options to compile with. By default, Output will be set to library, and Nullable will be set to
/// enable.
/// </param>
/// <returns>The compilation diagnostics and the result of the <see cref="ISourceGenerator"/> execution.</returns>
public static (ImmutableArray<Diagnostic> Diagnostics, GeneratorDriverRunResult Result) RunWithDiagnostics<T>(
/// <returns>The generator instance used for the run, the result of the <see cref="ISourceGenerator"/> execution, and the compilation diagnostics.</returns>
public static (T Generator, GeneratorDriverRunResult Result, ImmutableArray<Diagnostic> Diagnostics) RunWithDiagnostics<T>(
string source,
CSharpParseOptions? cSharpParseOptions = null,
IEnumerable<MetadataReference>? metadataReferences = null,
Expand All @@ -50,11 +51,12 @@ public static (ImmutableArray<Diagnostic> Diagnostics, GeneratorDriverRunResult
where T : ISourceGenerator, new()
{
var generator = new T();
var result = Helpers.InternalRunGenerator(generator, source, cSharpParseOptions, metadataReferences, cSharpCompilationOptions);

return Helpers.InternalRunGenerator(generator, source, cSharpParseOptions, metadataReferences, cSharpCompilationOptions);
return (generator, result.Result, result.CompilationDiagnostics);
}

/// <summary>Executes a specified <see cref="ISourceGenerator"/> against the provided source within a testing environment.</summary>
/// <summary>Executes a specified <see cref="ISourceGenerator"/> against the provided sources within a testing environment.</summary>
/// <typeparam name="T">The type of <see cref="ISourceGenerator"/> to execute.</typeparam>
/// <param name="sources">The sources to be analyzed and processed by the <see cref="ISourceGenerator"/>.</param>
/// <param name="cSharpParseOptions">The C# source parsing options to compile with. By default, LangVersion will be set to latest.</param>
Expand All @@ -63,8 +65,8 @@ public static (ImmutableArray<Diagnostic> Diagnostics, GeneratorDriverRunResult
/// The C# compilation options to compile with. By default, Output will be set to library, and Nullable will be set to
/// enable.
/// </param>
/// <returns>The result of the <see cref="ISourceGenerator"/> execution.</returns>
public static GeneratorDriverRunResult Run<T>(
/// <returns>The generator instance used for the run and the result of the <see cref="ISourceGenerator"/> execution.</returns>
public static (T Generator, GeneratorDriverRunResult Result) Run<T>(
IEnumerable<string> sources,
CSharpParseOptions? cSharpParseOptions = null,
IEnumerable<MetadataReference>? metadataReferences = null,
Expand All @@ -73,11 +75,12 @@ public static GeneratorDriverRunResult Run<T>(
where T : ISourceGenerator, new()
{
var generator = new T();
var result = Helpers.InternalRunGenerator(generator, sources, cSharpParseOptions, metadataReferences, cSharpCompilationOptions).Result;

return Helpers.InternalRunGenerator(generator, sources, cSharpParseOptions, metadataReferences, cSharpCompilationOptions).Result;
return (generator, result);
}

/// <summary>Gather compilation diagnostics and executes a specified <see cref="ISourceGenerator"/> against the provided source within a testing environment.</summary>
/// <summary>Gather compilation diagnostics and executes a specified <see cref="ISourceGenerator"/> against the provided sources within a testing environment.</summary>
/// <typeparam name="T">The type of <see cref="ISourceGenerator"/> to execute.</typeparam>
/// <param name="sources">The sources to be analyzed and processed by the <see cref="ISourceGenerator"/>.</param>
/// <param name="cSharpParseOptions">The C# source parsing options to compile with. By default, LangVersion will be set to latest.</param>
Expand All @@ -86,8 +89,8 @@ public static GeneratorDriverRunResult Run<T>(
/// The C# compilation options to compile with. By default, Output will be set to library, and Nullable will be set to
/// enable.
/// </param>
/// <returns>The compilation diagnostics and the result of the <see cref="ISourceGenerator"/> execution.</returns>
public static (ImmutableArray<Diagnostic> Diagnostics, GeneratorDriverRunResult Result) RunWithDiagnostics<T>(
/// <returns>The generator instance used for the run, the result of the <see cref="ISourceGenerator"/> execution, and the compilation diagnostics.</returns>
public static (T Generator, GeneratorDriverRunResult Result, ImmutableArray<Diagnostic> Diagnostics) RunWithDiagnostics<T>(
IEnumerable<string> sources,
CSharpParseOptions? cSharpParseOptions = null,
IEnumerable<MetadataReference>? metadataReferences = null,
Expand All @@ -96,7 +99,8 @@ public static (ImmutableArray<Diagnostic> Diagnostics, GeneratorDriverRunResult
where T : ISourceGenerator, new()
{
var generator = new T();
var result = Helpers.InternalRunGenerator(generator, sources, cSharpParseOptions, metadataReferences, cSharpCompilationOptions);

return Helpers.InternalRunGenerator(generator, sources, cSharpParseOptions, metadataReferences, cSharpCompilationOptions);
return (generator, result.Result, result.CompilationDiagnostics);
}
}
Loading
Loading