-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #36 from ByronMayne/35-compiler-error-when-using-e…
…nforceextendedanalyzerrules-due-to-console-ussage Created new unit tests to validate banned api analyzer
- Loading branch information
Showing
11 changed files
with
333 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
155 changes: 155 additions & 0 deletions
155
src/SourceGenerator.Foundations.Tests/CompiliationTestBase.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using Microsoft.CodeAnalysis.Text; | ||
using System.Collections.Immutable; | ||
using System.Reflection; | ||
using System.Text; | ||
using Xunit.Abstractions; | ||
|
||
namespace SGF | ||
{ | ||
/// <summary> | ||
/// There is an attribute called `ExtendedAnalyzerRules` which are applied to source generators. Since | ||
/// SGF injects code into their projects we want to make sure we don't add warnings or errors. | ||
/// </summary> | ||
public class CompiliationTestBase | ||
{ | ||
public SgfAnalyzerConfigOptions AnalyzerOptions { get; } | ||
|
||
/// <summary> | ||
/// Gets the name of the currently running test method | ||
/// </summary> | ||
public string TestMethodName { get; } | ||
|
||
private readonly List<MetadataReference> m_references; | ||
private readonly List<DiagnosticAnalyzer> m_analyzers; | ||
protected readonly List<IIncrementalGenerator> m_incrementalGenerators; | ||
|
||
protected CompiliationTestBase(ITestOutputHelper outputHelper) | ||
{ | ||
m_analyzers = new List<DiagnosticAnalyzer>(); | ||
m_references = new List<MetadataReference>(); | ||
m_incrementalGenerators = new List<IIncrementalGenerator>(); | ||
|
||
AnalyzerOptions = new SgfAnalyzerConfigOptions(); | ||
TestMethodName = "Unkonwn"; | ||
Type type = outputHelper.GetType(); | ||
FieldInfo? testField = type.GetField("test", BindingFlags.Instance | BindingFlags.NonPublic); | ||
ITest? test = testField?.GetValue(outputHelper) as ITest; | ||
if (test != null) | ||
{ | ||
TestMethodName = test.TestCase.TestMethod.ToString()!; | ||
} | ||
|
||
AddAssemblyReference("System.Runtime"); | ||
AddAssemblyReference("netstandard"); | ||
AddAssemblyReference("System.Console"); | ||
AddAssemblyReference("Microsoft.CodeAnalysis"); | ||
AddAssemblyReference("System.Linq"); | ||
AddMetadataReference<object>(); | ||
AddMetadataReference<SgfGeneratorAttribute>(); | ||
} | ||
|
||
|
||
|
||
protected async Task ComposeAsync( | ||
string[]? source = null, | ||
Action<ImmutableArray<Diagnostic>>[]? assertDiagnostics = null, | ||
Action<Compilation>[]? assertCompilation = null) | ||
{ | ||
|
||
source ??= []; | ||
|
||
CSharpCompilationOptions compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); | ||
|
||
CompilationWithAnalyzersOptions analyzerOptions = new CompilationWithAnalyzersOptions( | ||
new AnalyzerOptions([], new SgfAnalyzerConfigOptionsProvider(AnalyzerOptions)), | ||
null, true, false); | ||
|
||
// Create a Roslyn compilation for the syntax tree. | ||
Compilation compilation = CSharpCompilation.Create(TestMethodName) | ||
.AddSyntaxTrees(source.Select(t => ParseSyntaxTree(t)).ToArray()) | ||
.WithOptions(compilationOptions) | ||
.AddReferences(m_references); | ||
|
||
// Create an instance of our EnumGenerator incremental source generator | ||
HoistSourceGenerator generator = new HoistSourceGenerator(); | ||
|
||
// The GeneratorDriver is used to run our generator against a compilation | ||
GeneratorDriver driver = CSharpGeneratorDriver.Create(generator); | ||
|
||
// Run the source generator! | ||
driver = driver.RunGenerators(compilation) | ||
.RunGeneratorsAndUpdateCompilation(compilation, out compilation, out var _); | ||
|
||
CompilationWithAnalyzers analysis = compilation.WithAnalyzers([.. m_analyzers], analyzerOptions); | ||
|
||
ImmutableArray<Diagnostic> diagnostics = await analysis.GetAllDiagnosticsAsync(); | ||
if (assertDiagnostics is not null) | ||
{ | ||
foreach (Action<ImmutableArray<Diagnostic>> assert in assertDiagnostics) | ||
{ | ||
assert(diagnostics); | ||
} | ||
} | ||
|
||
if (assertCompilation is not null) | ||
{ | ||
foreach (Action<Compilation> assert in assertCompilation) | ||
{ | ||
assert(compilation); | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Adds a new <see cref="DiagnosticAnalyzer"/> that will be eveluated during compliation. | ||
/// </summary> | ||
protected void AddAnalyzer<T>() where T : DiagnosticAnalyzer, new() | ||
=> m_analyzers.Add(new T()); | ||
|
||
/// <summary> | ||
/// Adds a new <see cref="DiagnosticAnalyzer"/> that will be eveluated during compliation. | ||
/// </summary> | ||
protected void AddAnalyzer<T>(T instance) where T : DiagnosticAnalyzer | ||
=> m_analyzers.Add(instance); | ||
|
||
/// <summary> | ||
/// Adds a new <see cref="IIncrementalGenerator"/> that will be executed during compliation. | ||
/// </summary> | ||
protected void AddGenerator<T>() where T : IIncrementalGenerator, new() | ||
=> m_incrementalGenerators.Add(new T()); | ||
|
||
/// <summary> | ||
/// Adds a new <see cref="IIncrementalGenerator"/> that will be executed during compliation. | ||
/// </summary> | ||
protected void AddGenerator<T>(T instance) where T : IIncrementalGenerator | ||
=> m_incrementalGenerators.Add(instance); | ||
|
||
/// <summary> | ||
/// Adds a new <see cref="MetadataReference"/> for the given assembly that the type belongs in | ||
/// </summary> | ||
protected void AddMetadataReference<T>() | ||
=> m_references.Add(MetadataReference.CreateFromFile(typeof(T).Assembly.Location)); | ||
|
||
protected void AddAssemblyReference(string assemblyName) | ||
{ | ||
#pragma warning disable RS1035 // Do not use APIs banned for analyzers | ||
Assembly assembly = Assembly.Load(assemblyName); | ||
if (assembly is not null) | ||
{ | ||
m_references.Add(MetadataReference.CreateFromFile(assembly.Location)); | ||
} | ||
#pragma warning restore RS1035 // Do not use APIs banned for analyzers | ||
} | ||
|
||
protected static SyntaxTree ParseSyntaxTree(string source, string fileName = "TestClass.cs") | ||
{ | ||
SourceText sourceText = SourceText.From(source, Encoding.UTF8); | ||
CSharpParseOptions parseOptions = new CSharpParseOptions(LanguageVersion.CSharp12); | ||
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(source, path: fileName); | ||
return syntaxTree; | ||
} | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
src/SourceGenerator.Foundations.Tests/DiagnosticAsserts.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
using Microsoft.CodeAnalysis; | ||
using System.Collections.Immutable; | ||
using System.Text; | ||
|
||
namespace SourceGenerator.Foundations.Tests | ||
{ | ||
/// <summary> | ||
/// Contains asserts for checking if diagnostics match expected values. | ||
/// </summary> | ||
internal static class DiagnosticAsserts | ||
{ | ||
public static Action<ImmutableArray<Diagnostic>> NoErrors() | ||
=> NoneOfSeverity(DiagnosticSeverity.Error); | ||
|
||
public static Action<ImmutableArray<Diagnostic>> NoErrorsOrWarnings() | ||
=> NoneOfSeverity(DiagnosticSeverity.Warning, DiagnosticSeverity.Error); | ||
|
||
public static Action<ImmutableArray<Diagnostic>> NoneOfSeverity(params DiagnosticSeverity[] severity) | ||
{ | ||
HashSet<DiagnosticSeverity> set = new HashSet<DiagnosticSeverity>(severity); | ||
|
||
return diagnostics => | ||
{ | ||
List<Diagnostic> filtered = diagnostics | ||
.Where(d => set.Contains(d.Severity)) | ||
.ToList(); | ||
|
||
if(filtered.Count > 0) | ||
{ | ||
StringBuilder errorBuilder = new StringBuilder(); | ||
|
||
foreach(var dia in filtered) | ||
{ | ||
Location location = dia.Location; | ||
string? filePath = Path.GetFileName(location.SourceTree?.FilePath); | ||
|
||
string error = $"{filePath} {location.SourceSpan.Start} {dia.Severity} {dia.Descriptor.Id}: {dia.GetMessage()}"; | ||
|
||
errorBuilder.AppendLine(error); | ||
} | ||
Assert.Fail(errorBuilder.ToString()); | ||
} | ||
}; | ||
} | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
src/SourceGenerator.Foundations.Tests/ExtendedAnalyzerRuleTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
using Microsoft.CodeAnalysis.CSharp.Analyzers; | ||
using SGF; | ||
using Xunit.Abstractions; | ||
|
||
namespace SourceGenerator.Foundations.Tests | ||
{ | ||
/// <summary> | ||
/// Runs Roslyns analyzers to validate that code that was added | ||
/// by the <see cref="HoistSourceGenerator"/> does not cause | ||
/// errors due to `EnforceExtendedAnalyzerRules` | ||
/// </summary> | ||
public class ExtendedAnalyzerRuleTests : CompiliationTestBase | ||
{ | ||
public ExtendedAnalyzerRuleTests(ITestOutputHelper outputHelper) : base(outputHelper) | ||
{ | ||
AnalyzerOptions.EnforceExtendedAnalyzerRules = true; | ||
|
||
AddGenerator<HoistSourceGenerator>(); | ||
AddAnalyzer<CSharpSymbolIsBannedInAnalyzersAnalyzer>(); | ||
} | ||
|
||
[Fact] | ||
public Task No_Banned_Apis_In_Source() | ||
{ | ||
string source = """ | ||
using SGF; | ||
namespace Yellow | ||
{ | ||
[SgfGenerator] | ||
public class CustomGenerator : IncrementalGenerator | ||
{ | ||
public CustomGenerator() : base("Generator") | ||
{} | ||
public override void OnInitialize(SgfInitializationContext context) | ||
{} | ||
} | ||
} | ||
"""; | ||
|
||
return ComposeAsync([source], | ||
assertDiagnostics: [ | ||
DiagnosticAsserts.NoErrors() | ||
]); | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
40 changes: 40 additions & 0 deletions
40
src/SourceGenerator.Foundations.Tests/SgfAnalyzerConfigOptions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using System.Diagnostics.CodeAnalysis; | ||
|
||
namespace SGF | ||
{ | ||
|
||
public class SgfAnalyzerConfigOptions : AnalyzerConfigOptions | ||
{ | ||
private const string BUILD_PROP_EXTENDED_RULES = "build_property.EnforceExtendedAnalyzerRules"; | ||
|
||
private readonly Dictionary<string, string?> m_values; | ||
|
||
public bool EnforceExtendedAnalyzerRules | ||
{ | ||
get => m_values.TryGetValue(BUILD_PROP_EXTENDED_RULES, out string? rawValue) && | ||
bool.TryParse(rawValue, out bool parsedValue) && | ||
parsedValue; | ||
set => m_values[BUILD_PROP_EXTENDED_RULES] = value | ||
? "true" // has to be lower | ||
: "false"; | ||
} | ||
|
||
public SgfAnalyzerConfigOptions() | ||
{ | ||
m_values = new Dictionary<string, string?>(); | ||
|
||
EnforceExtendedAnalyzerRules = false; | ||
} | ||
|
||
public override bool TryGetValue(string key, [NotNullWhen(true)] out string? value) | ||
{ | ||
if (m_values.TryGetValue(key, out value)) | ||
{ | ||
value ??= ""; | ||
return true; | ||
} | ||
return false; | ||
} | ||
} | ||
} |
Oops, something went wrong.