Skip to content

Commit

Permalink
Merge pull request #69 from CommunityToolkit/refactor/samplegen/tests
Browse files Browse the repository at this point in the history
Refactored SampleGen test tooling
  • Loading branch information
Arlodotexe authored Jun 29, 2023
2 parents a852f23 + f53878b commit b32c389
Show file tree
Hide file tree
Showing 18 changed files with 740 additions and 570 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ on:
workflow_dispatch:

env:
DOTNET_VERSION: ${{ '6.0.x' }}
DOTNET_VERSION: ${{ '7.0.x' }}
ENABLE_DIAGNOSTICS: false
#COREHOST_TRACE: 1
COREHOST_TRACEFILE: corehosttrace.log
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE file in the project root for more information. -->
<!-- Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE file in the project root for more information. -->
<UserControl x:Class="CommunityToolkit.App.Shared.Renderers.GeneratedSampleOptionsRenderer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;

namespace CommunityToolkit.Tooling.SampleGen.Tests.Helpers;

internal class InMemoryAdditionalText : AdditionalText
{
private readonly SourceText _content;

public InMemoryAdditionalText(string path, string content)
{
Path = path;
_content = SourceText.From(content, Encoding.UTF8);
}

public override string Path { get; }

public override SourceText GetText(CancellationToken cancellationToken = default) => _content;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.CodeAnalysis;
using System.Collections.Immutable;

namespace CommunityToolkit.Tooling.SampleGen.Tests.Helpers;

public record SourceGeneratorRunResult(Compilation Compilation, ImmutableArray<Diagnostic> Diagnostics);
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Collections.Immutable;

namespace CommunityToolkit.Tooling.SampleGen.Tests.Helpers;

public static partial class TestHelpers
{
internal static IEnumerable<MetadataReference> GetAllReferencedAssemblies()
{
return from assembly in AppDomain.CurrentDomain.GetAssemblies()
where !assembly.IsDynamic
let reference = MetadataReference.CreateFromFile(assembly.Location)
select reference;
}

internal static SyntaxTree ToSyntaxTree(this string source)
{
return CSharpSyntaxTree.ParseText(source,
CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp10));
}

internal static CSharpCompilation CreateCompilation(this SyntaxTree syntaxTree, string assemblyName, IEnumerable<MetadataReference>? references = null)
{
return CSharpCompilation.Create(assemblyName, new[] { syntaxTree }, references ?? GetAllReferencedAssemblies(), new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
}

internal static CSharpCompilation CreateCompilation(this IEnumerable<SyntaxTree> syntaxTree, string assemblyName, IEnumerable<MetadataReference>? references = null)
{
return CSharpCompilation.Create(assemblyName, syntaxTree, references ?? GetAllReferencedAssemblies(), new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
}

internal static GeneratorDriver CreateSourceGeneratorDriver(this SyntaxTree syntaxTree, params IIncrementalGenerator[] generators)
{
return CSharpGeneratorDriver.Create(generators).WithUpdatedParseOptions((CSharpParseOptions)syntaxTree.Options);
}

internal static GeneratorDriver CreateSourceGeneratorDriver(this Compilation compilation, params IIncrementalGenerator[] generators)
{
return CSharpGeneratorDriver.Create(generators).WithUpdatedParseOptions((CSharpParseOptions)compilation.SyntaxTrees.First().Options);
}

internal static GeneratorDriver WithMarkdown(this GeneratorDriver driver, params string[] markdownFilesToCreate)
{
foreach (var markdown in markdownFilesToCreate)
{
if (!string.IsNullOrWhiteSpace(markdown))
{
var text = new InMemoryAdditionalText(@"C:\pathtorepo\components\experiment\samples\experiment.Samples\documentation.md", markdown);
driver = driver.AddAdditionalTexts(ImmutableArray.Create<AdditionalText>(text));
}
}

return driver;
}
}
61 changes: 61 additions & 0 deletions CommunityToolkit.Tooling.SampleGen.Tests/Helpers/TestHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Immutable;

namespace CommunityToolkit.Tooling.SampleGen.Tests.Helpers;

public static partial class TestHelpers
{
internal static SourceGeneratorRunResult RunSourceGenerator<TGenerator>(this string source, string assemblyName, string markdown = "") where TGenerator : class, IIncrementalGenerator, new() => RunSourceGenerator<TGenerator>(source.ToSyntaxTree(), assemblyName, markdown);

internal static SourceGeneratorRunResult RunSourceGenerator<TGenerator>(this SyntaxTree syntaxTree, string assemblyName, string markdown = "")
where TGenerator : class, IIncrementalGenerator, new()
{
var compilation = syntaxTree.CreateCompilation(assemblyName); // assembly name should always be supplied in param
return RunSourceGenerator<TGenerator>(compilation, markdown);
}

internal static SourceGeneratorRunResult RunSourceGenerator<TGenerator>(this Compilation compilation, string markdown = "")
where TGenerator : class, IIncrementalGenerator, new()
{
// Create a driver for the source generator
var driver = compilation
.CreateSourceGeneratorDriver(new TGenerator())
.WithMarkdown(markdown);

// Update the original compilation using the source generator
_ = driver.RunGeneratorsAndUpdateCompilation(compilation, out Compilation generatorCompilation, out ImmutableArray<Diagnostic> postGeneratorCompilationDiagnostics);

return new(generatorCompilation, postGeneratorCompilationDiagnostics);
}

internal static void AssertDiagnosticsAre(this IEnumerable<Diagnostic> diagnostics, params DiagnosticDescriptor[] expectedDiagnosticDescriptors)
{
var expectedIds = expectedDiagnosticDescriptors.Select(x => x.Id).ToHashSet();
var resultingIds = diagnostics.Select(diagnostic => diagnostic.Id).ToHashSet();

Assert.IsTrue(resultingIds.SetEquals(expectedIds), $"Expected [{string.Join(", ", expectedIds)}] diagnostic Ids. Got [{string.Join(", ", resultingIds)}]");
}

internal static void AssertNoCompilationErrors(this Compilation outputCompilation)
{
var generatedCompilationDiagnostics = outputCompilation.GetDiagnostics();
Assert.IsTrue(generatedCompilationDiagnostics.All(x => x.Severity != DiagnosticSeverity.Error), $"Expected no generated compilation errors. Got: \n{string.Join("\n", generatedCompilationDiagnostics.Where(x => x.Severity == DiagnosticSeverity.Error).Select(x => $"[{x.Id}: {x.GetMessage()}]"))}");
}

internal static string GetFileContentsByName(this Compilation compilation, string filename)
{
var generatedTree = compilation.SyntaxTrees.SingleOrDefault(tree => Path.GetFileName(tree.FilePath) == filename);
Assert.IsNotNull(generatedTree, $"No file named {filename} was generated");

return generatedTree.ToString();
}

internal static void AssertSourceGenerated(this Compilation compilation, string filename, string expectedContents)
{
}

internal static void AssertDiagnosticsAre(this SourceGeneratorRunResult result, params DiagnosticDescriptor[] expectedDiagnosticDescriptors) => AssertDiagnosticsAre(result.Diagnostics, expectedDiagnosticDescriptors);

internal static void AssertNoCompilationErrors(this SourceGeneratorRunResult result) => AssertNoCompilationErrors(result.Compilation);
}
Loading

0 comments on commit b32c389

Please sign in to comment.