Skip to content

Commit

Permalink
Fix validation of diagnostics in source generator tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Jan 13, 2021
1 parent c17587b commit c0a78d2
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,13 @@ private static bool IsInSourceFile(DiagnosticResult result, (string filename, So
private async Task<ImmutableArray<Diagnostic>> GetSortedDiagnosticsAsync(EvaluatedProjectState primaryProject, ImmutableArray<EvaluatedProjectState> additionalProjects, ImmutableArray<DiagnosticAnalyzer> analyzers, IVerifier verifier, CancellationToken cancellationToken)
{
var solution = await GetSolutionAsync(primaryProject, additionalProjects, verifier, cancellationToken);
return await GetSortedDiagnosticsAsync(solution, analyzers, CompilerDiagnostics, cancellationToken);
var additionalDiagnostics = primaryProject.AdditionalDiagnostics;
foreach (var project in additionalProjects)
{
additionalDiagnostics = additionalDiagnostics.AddRange(project.AdditionalDiagnostics);
}

return await GetSortedDiagnosticsAsync(solution, analyzers, additionalDiagnostics, CompilerDiagnostics, cancellationToken);
}

/// <summary>
Expand All @@ -975,11 +981,12 @@ private async Task<ImmutableArray<Diagnostic>> GetSortedDiagnosticsAsync(Evaluat
/// </summary>
/// <param name="solution">The <see cref="Solution"/> that the analyzer(s) will be run on.</param>
/// <param name="analyzers">The analyzer to run on the documents.</param>
/// <param name="additionalDiagnostics">Additional diagnostics reported for the solution, which need to be verified.</param>
/// <param name="compilerDiagnostics">The behavior of compiler diagnostics in validation scenarios.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that the task will observe.</param>
/// <returns>A collection of <see cref="Diagnostic"/>s that surfaced in the source code, sorted by
/// <see cref="Diagnostic.Location"/>.</returns>
protected async Task<ImmutableArray<Diagnostic>> GetSortedDiagnosticsAsync(Solution solution, ImmutableArray<DiagnosticAnalyzer> analyzers, CompilerDiagnostics compilerDiagnostics, CancellationToken cancellationToken)
protected async Task<ImmutableArray<Diagnostic>> GetSortedDiagnosticsAsync(Solution solution, ImmutableArray<DiagnosticAnalyzer> analyzers, ImmutableArray<Diagnostic> additionalDiagnostics, CompilerDiagnostics compilerDiagnostics, CancellationToken cancellationToken)
{
var diagnostics = ImmutableArray.CreateBuilder<Diagnostic>();
foreach (var project in solution.Projects)
Expand All @@ -991,6 +998,7 @@ protected async Task<ImmutableArray<Diagnostic>> GetSortedDiagnosticsAsync(Solut
diagnostics.AddRange(allDiagnostics.Where(diagnostic => !IsCompilerDiagnostic(diagnostic) || IsCompilerDiagnosticIncluded(diagnostic, compilerDiagnostics)));
}

diagnostics.AddRange(additionalDiagnostics);
var results = SortDistinctDiagnostics(diagnostics);
return results.ToImmutableArray();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public EvaluatedProjectState(ProjectState state, ReferenceAssemblies defaultRefe
state.Sources.ToImmutableArray(),
state.AdditionalFiles.ToImmutableArray(),
state.AdditionalProjectReferences.ToImmutableArray(),
state.AdditionalReferences.ToImmutableArray())
state.AdditionalReferences.ToImmutableArray(),
ImmutableArray<Diagnostic>.Empty)
{
}

Expand All @@ -37,7 +38,8 @@ private EvaluatedProjectState(
ImmutableArray<(string filename, SourceText content)> sources,
ImmutableArray<(string filename, SourceText content)> additionalFiles,
ImmutableArray<string> additionalProjectReferences,
ImmutableArray<MetadataReference> additionalReferences)
ImmutableArray<MetadataReference> additionalReferences,
ImmutableArray<Diagnostic> additionalDiagnostics)
{
Name = name;
AssemblyName = assemblyName;
Expand All @@ -49,6 +51,7 @@ private EvaluatedProjectState(
AdditionalFiles = additionalFiles;
AdditionalProjectReferences = additionalProjectReferences;
AdditionalReferences = additionalReferences;
AdditionalDiagnostics = additionalDiagnostics;
}

public string Name { get; }
Expand All @@ -71,6 +74,8 @@ private EvaluatedProjectState(

public ImmutableArray<MetadataReference> AdditionalReferences { get; }

public ImmutableArray<Diagnostic> AdditionalDiagnostics { get; }

public EvaluatedProjectState WithSources(ImmutableArray<(string filename, SourceText content)> sources)
{
if (sources == Sources)
Expand All @@ -81,6 +86,16 @@ public EvaluatedProjectState WithSources(ImmutableArray<(string filename, Source
return With(sources: sources);
}

public EvaluatedProjectState WithAdditionalDiagnostics(ImmutableArray<Diagnostic> additionalDiagnostics)
{
if (additionalDiagnostics == AdditionalDiagnostics)
{
return this;
}

return With(additionalDiagnostics: additionalDiagnostics);
}

private EvaluatedProjectState With(
Optional<string> name = default,
Optional<string> assemblyName = default,
Expand All @@ -91,7 +106,8 @@ private EvaluatedProjectState With(
Optional<ImmutableArray<(string filename, SourceText content)>> sources = default,
Optional<ImmutableArray<(string filename, SourceText content)>> additionalFiles = default,
Optional<ImmutableArray<string>> additionalProjectReferences = default,
Optional<ImmutableArray<MetadataReference>> additionalReferences = default)
Optional<ImmutableArray<MetadataReference>> additionalReferences = default,
Optional<ImmutableArray<Diagnostic>> additionalDiagnostics = default)
{
return new EvaluatedProjectState(
GetValueOrDefault(name, Name),
Expand All @@ -103,7 +119,8 @@ private EvaluatedProjectState With(
GetValueOrDefault(sources, Sources),
GetValueOrDefault(additionalFiles, AdditionalFiles),
GetValueOrDefault(additionalProjectReferences, AdditionalProjectReferences),
GetValueOrDefault(additionalReferences, AdditionalReferences));
GetValueOrDefault(additionalReferences, AdditionalReferences),
GetValueOrDefault(additionalDiagnostics, AdditionalDiagnostics));
}

private static T GetValueOrDefault<T>(Optional<T> optionalValue, T defaultValue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.DiagnosticVerifier.set ->
Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.DisabledDiagnostics.get -> System.Collections.Generic.List<string>
Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.ExpectedDiagnostics.get -> System.Collections.Generic.List<Microsoft.CodeAnalysis.Testing.DiagnosticResult>
Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.FormatVerifierMessage(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer> analyzers, Microsoft.CodeAnalysis.Diagnostic actual, Microsoft.CodeAnalysis.Testing.DiagnosticResult expected, string message) -> string
Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.GetSortedDiagnosticsAsync(Microsoft.CodeAnalysis.Solution solution, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer> analyzers, Microsoft.CodeAnalysis.Testing.CompilerDiagnostics compilerDiagnostics, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostic>>
Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.GetSortedDiagnosticsAsync(Microsoft.CodeAnalysis.Solution solution, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer> analyzers, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostic> additionalDiagnostics, Microsoft.CodeAnalysis.Testing.CompilerDiagnostics compilerDiagnostics, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostic>>
Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.MarkupOptions.get -> Microsoft.CodeAnalysis.Testing.MarkupOptions
Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.MarkupOptions.set -> void
Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.MatchDiagnosticsTimeout.get -> System.TimeSpan
Expand Down Expand Up @@ -119,6 +119,7 @@ Microsoft.CodeAnalysis.Testing.MetadataReferenceCollection.Add(string path) -> v
Microsoft.CodeAnalysis.Testing.MetadataReferenceCollection.MetadataReferenceCollection() -> void
Microsoft.CodeAnalysis.Testing.MetadataReferences
Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState
Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.AdditionalDiagnostics.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostic>
Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.AdditionalFiles.get -> System.Collections.Immutable.ImmutableArray<(string filename, Microsoft.CodeAnalysis.Text.SourceText content)>
Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.AdditionalProjectReferences.get -> System.Collections.Immutable.ImmutableArray<string>
Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.AdditionalReferences.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.MetadataReference>
Expand All @@ -130,6 +131,7 @@ Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.Name.get -> string
Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.OutputKind.get -> Microsoft.CodeAnalysis.OutputKind
Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.ReferenceAssemblies.get -> Microsoft.CodeAnalysis.Testing.ReferenceAssemblies
Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.Sources.get -> System.Collections.Immutable.ImmutableArray<(string filename, Microsoft.CodeAnalysis.Text.SourceText content)>
Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.WithAdditionalDiagnostics(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostic> additionalDiagnostics) -> Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState
Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.WithSources(System.Collections.Immutable.ImmutableArray<(string filename, Microsoft.CodeAnalysis.Text.SourceText content)> sources) -> Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState
Microsoft.CodeAnalysis.Testing.PackageIdentity
Microsoft.CodeAnalysis.Testing.PackageIdentity.Id.get -> string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ private async Task VerifyFixAsync(
bool done;
do
{
var analyzerDiagnostics = await GetSortedDiagnosticsAsync(project.Solution, analyzers, CompilerDiagnostics, cancellationToken).ConfigureAwait(false);
var analyzerDiagnostics = await GetSortedDiagnosticsAsync(project.Solution, analyzers, additionalDiagnostics: ImmutableArray<Diagnostic>.Empty, CompilerDiagnostics, cancellationToken).ConfigureAwait(false);
if (analyzerDiagnostics.Length == 0)
{
break;
Expand Down Expand Up @@ -577,7 +577,7 @@ private async Task VerifyFixAsync(
bool done;
do
{
var analyzerDiagnostics = await GetSortedDiagnosticsAsync(project.Solution, analyzers, CompilerDiagnostics, cancellationToken).ConfigureAwait(false);
var analyzerDiagnostics = await GetSortedDiagnosticsAsync(project.Solution, analyzers, additionalDiagnostics: ImmutableArray<Diagnostic>.Empty, CompilerDiagnostics, cancellationToken).ConfigureAwait(false);
if (analyzerDiagnostics.Length == 0)
{
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Microsoft.CodeAnalysis.Testing.SourceGeneratorTest<TVerifier>
Microsoft.CodeAnalysis.Testing.SourceGeneratorTest<TVerifier>.FixedCode.set -> void
Microsoft.CodeAnalysis.Testing.SourceGeneratorTest<TVerifier>.FixedState.get -> Microsoft.CodeAnalysis.Testing.SolutionState
Microsoft.CodeAnalysis.Testing.SourceGeneratorTest<TVerifier>.SourceGeneratorTest() -> void
Microsoft.CodeAnalysis.Testing.SourceGeneratorTest<TVerifier>.VerifySourceGeneratorAsync(Microsoft.CodeAnalysis.Testing.SolutionState testState, Microsoft.CodeAnalysis.Testing.SolutionState fixedState, Microsoft.CodeAnalysis.Testing.IVerifier verifier, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
Microsoft.CodeAnalysis.Testing.SourceGeneratorTest<TVerifier>.VerifySourceGeneratorAsync(Microsoft.CodeAnalysis.Testing.SolutionState testState, Microsoft.CodeAnalysis.Testing.SolutionState fixedState, Microsoft.CodeAnalysis.Testing.IVerifier verifier, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostic>>
Microsoft.CodeAnalysis.Testing.SourceGeneratorVerifier<TSourceGenerator, TTest, TVerifier>
Microsoft.CodeAnalysis.Testing.SourceGeneratorVerifier<TSourceGenerator, TTest, TVerifier>.SourceGeneratorVerifier() -> void
abstract Microsoft.CodeAnalysis.Testing.SourceGeneratorTest<TVerifier>.CreateGeneratorDriver(Microsoft.CodeAnalysis.Project project, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.ISourceGenerator> sourceGenerators) -> Microsoft.CodeAnalysis.GeneratorDriver
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ public string FixedCode
protected SourceGeneratorTest()
{
FixedState = new SolutionState(DefaultTestProjectName, Language, DefaultFilePathPrefix, DefaultFileExt);

// Disable test behaviors that don't make sense for source generator scenarios
TestBehaviors |= TestBehaviors.SkipSuppressionCheck | TestBehaviors.SkipGeneratedCodeCheck;
}

protected override IEnumerable<DiagnosticAnalyzer> GetDiagnosticAnalyzers()
Expand Down Expand Up @@ -71,8 +74,8 @@ public override async Task RunAsync(CancellationToken cancellationToken = defaul
var fixedState = rawFixedState.WithProcessedMarkup(MarkupOptions, defaultDiagnostic, supportedDiagnostics, fixableDiagnostics, DefaultFilePath);

await VerifyDiagnosticsAsync(new EvaluatedProjectState(testState, ReferenceAssemblies), testState.AdditionalProjects.Values.Select(additionalProject => new EvaluatedProjectState(additionalProject, ReferenceAssemblies)).ToImmutableArray(), testState.ExpectedDiagnostics.ToArray(), Verify.PushContext("Diagnostics of test state"), cancellationToken).ConfigureAwait(false);
await VerifyDiagnosticsAsync(new EvaluatedProjectState(fixedState, ReferenceAssemblies), fixedState.AdditionalProjects.Values.Select(additionalProject => new EvaluatedProjectState(additionalProject, ReferenceAssemblies)).ToImmutableArray(), fixedState.ExpectedDiagnostics.ToArray(), Verify.PushContext("Diagnostics of fixed state"), cancellationToken).ConfigureAwait(false);
await VerifySourceGeneratorAsync(testState, fixedState, Verify, cancellationToken).ConfigureAwait(false);
var diagnostics = await VerifySourceGeneratorAsync(testState, fixedState, Verify, cancellationToken).ConfigureAwait(false);
await VerifyDiagnosticsAsync(new EvaluatedProjectState(fixedState, ReferenceAssemblies).WithAdditionalDiagnostics(diagnostics), fixedState.AdditionalProjects.Values.Select(additionalProject => new EvaluatedProjectState(additionalProject, ReferenceAssemblies)).ToImmutableArray(), fixedState.ExpectedDiagnostics.ToArray(), Verify.PushContext("Diagnostics of test state with generated sources"), cancellationToken).ConfigureAwait(false);
}

/// <summary>
Expand All @@ -83,12 +86,12 @@ public override async Task RunAsync(CancellationToken cancellationToken = defaul
/// <param name="verifier">The verifier to use for test assertions.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that the task will observe.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
protected async Task VerifySourceGeneratorAsync(SolutionState testState, SolutionState fixedState, IVerifier verifier, CancellationToken cancellationToken)
protected async Task<ImmutableArray<Diagnostic>> VerifySourceGeneratorAsync(SolutionState testState, SolutionState fixedState, IVerifier verifier, CancellationToken cancellationToken)
{
await VerifySourceGeneratorAsync(Language, GetSourceGenerators().ToImmutableArray(), testState, fixedState, ApplySourceGeneratorAsync, verifier.PushContext("Source generator application"), cancellationToken);
return await VerifySourceGeneratorAsync(Language, GetSourceGenerators().ToImmutableArray(), testState, fixedState, ApplySourceGeneratorAsync, verifier.PushContext("Source generator application"), cancellationToken);
}

private async Task VerifySourceGeneratorAsync(
private async Task<ImmutableArray<Diagnostic>> VerifySourceGeneratorAsync(
string language,
ImmutableArray<ISourceGenerator> sourceGenerators,
SolutionState oldState,
Expand Down Expand Up @@ -129,6 +132,8 @@ private async Task VerifySourceGeneratorAsync(
verifier.Equal(newState.AdditionalFiles[i].content.ChecksumAlgorithm, actual.ChecksumAlgorithm, $"checksum algorithm of '{newState.AdditionalFiles[i].filename}' was expected to be '{newState.AdditionalFiles[i].content.ChecksumAlgorithm}' but was '{actual.ChecksumAlgorithm}'");
verifier.Equal(newState.AdditionalFiles[i].filename, updatedAdditionalDocuments[i].Name, $"file name was expected to be '{newState.AdditionalFiles[i].filename}' but was '{updatedAdditionalDocuments[i].Name}'");
}

return diagnostics;
}

private async Task<(Project project, ImmutableArray<Diagnostic> diagnostics)> ApplySourceGeneratorAsync(ImmutableArray<ISourceGenerator> sourceGenerators, Project project, IVerifier verifier, CancellationToken cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ Public Class VisualBasicSourceGeneratorTest(Of TSourceGenerator As {ISourceGener
End Property

Protected Overrides Function CreateGeneratorDriver(project As Project, sourceGenerators As ImmutableArray(Of ISourceGenerator)) As GeneratorDriver
Return VisualBasicGeneratorDriver.Create(sourceGenerators, project.AnalyzerOptions.AdditionalFiles, CType(project.ParseOptions, VisualBasicParseOptions), project.AnalyzerOptions.AnalyzerConfigOptionsProvider)
Return VisualBasicGeneratorDriver.Create(
sourceGenerators,
project.AnalyzerOptions.AdditionalFiles,
CType(project.ParseOptions, VisualBasicParseOptions),
project.AnalyzerOptions.AnalyzerConfigOptionsProvider)
End Function

Protected Overrides Function CreateCompilationOptions() As CompilationOptions
Expand Down
Loading

0 comments on commit c0a78d2

Please sign in to comment.