Skip to content

Commit c36129f

Browse files
committed
Add 'CSharpSuppressorTest<TSuppressor>'
1 parent 8a048a8 commit c36129f

File tree

1 file changed

+160
-0
lines changed

1 file changed

+160
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Collections.Immutable;
8+
using System.Linq;
9+
using CommunityToolkit.WinUI;
10+
using Microsoft.CodeAnalysis;
11+
using Microsoft.CodeAnalysis.CSharp;
12+
using Microsoft.CodeAnalysis.CSharp.Testing;
13+
using Microsoft.CodeAnalysis.Diagnostics;
14+
using Microsoft.CodeAnalysis.Testing;
15+
using Windows.UI.ViewManagement;
16+
using Windows.UI.Xaml;
17+
using Windows.Foundation;
18+
using System.Diagnostics.CodeAnalysis;
19+
using System.ComponentModel;
20+
21+
namespace CommunityToolkit.GeneratedDependencyProperty.Tests.Helpers;
22+
23+
/// <summary>
24+
/// A custom <see cref="CSharpAnalyzerTest{TAnalyzer, TVerifier}"/> for testing diagnostic suppressors.
25+
/// </summary>
26+
/// <typeparam name="TSuppressor">The type of the suppressor to test.</typeparam>
27+
// Adapted from https://github.com/ImmediatePlatform/Immediate.Validations
28+
public sealed class CSharpSuppressorTest<TSuppressor> : CSharpAnalyzerTest<TSuppressor, DefaultVerifier>
29+
where TSuppressor : DiagnosticSuppressor, new()
30+
{
31+
/// <summary>
32+
/// The list of analyzers to run on the input code.
33+
/// </summary>
34+
private readonly List<DiagnosticAnalyzer> _analyzers = [];
35+
36+
/// <summary>
37+
/// Whether to enable unsafe blocks.
38+
/// </summary>
39+
private readonly bool _allowUnsafeBlocks;
40+
41+
/// <summary>
42+
/// The C# language version to use to parse code.
43+
/// </summary>
44+
private readonly LanguageVersion _languageVersion;
45+
46+
/// <summary>
47+
/// Creates a new <see cref="CSharpSuppressorTest{TSuppressor}"/> instance with the specified parameters.
48+
/// </summary>
49+
/// <param name="source">The source code to analyze.</param>
50+
/// <param name="allowUnsafeBlocks">Whether to enable unsafe blocks.</param>
51+
/// <param name="languageVersion">The language version to use to run the test.</param>
52+
public CSharpSuppressorTest(
53+
string source,
54+
bool allowUnsafeBlocks = true,
55+
LanguageVersion languageVersion = LanguageVersion.CSharp13)
56+
{
57+
_allowUnsafeBlocks = allowUnsafeBlocks;
58+
_languageVersion = languageVersion;
59+
60+
TestCode = source;
61+
TestState.ReferenceAssemblies = ReferenceAssemblies.Net.Net80;
62+
TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(Point).Assembly.Location));
63+
TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(ApplicationView).Assembly.Location));
64+
TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(DependencyProperty).Assembly.Location));
65+
TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(GeneratedDependencyPropertyAttribute).Assembly.Location));
66+
}
67+
68+
/// <inheritdoc/>
69+
protected override IEnumerable<DiagnosticAnalyzer> GetDiagnosticAnalyzers()
70+
{
71+
return base.GetDiagnosticAnalyzers().Concat(_analyzers);
72+
}
73+
74+
/// <inheritdoc/>
75+
protected override CompilationOptions CreateCompilationOptions()
76+
{
77+
return new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: _allowUnsafeBlocks);
78+
}
79+
80+
/// <inheritdoc/>
81+
protected override ParseOptions CreateParseOptions()
82+
{
83+
return new CSharpParseOptions(_languageVersion, DocumentationMode.Diagnose);
84+
}
85+
86+
/// <summary>
87+
/// Adds a new analyzer to the set of analyzers to run on the input code.
88+
/// </summary>
89+
/// <param name="assemblyQualifiedTypeName">The type of analyzer to activate.</param>
90+
/// <returns>The current test instance.</returns>
91+
public CSharpSuppressorTest<TSuppressor> WithAnalyzer(
92+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string assemblyQualifiedTypeName)
93+
{
94+
_analyzers.Add((DiagnosticAnalyzer)Activator.CreateInstance(Type.GetType(assemblyQualifiedTypeName)!)!);
95+
96+
return this;
97+
}
98+
99+
/// <summary>
100+
/// Specifies the diagnostics to enable.
101+
/// </summary>
102+
/// <param name="diagnostics">The set of diagnostics.</param>
103+
/// <returns>The current test instance.</returns>
104+
public CSharpSuppressorTest<TSuppressor> WithSpecificDiagnostics(params DiagnosticResult[] diagnostics)
105+
{
106+
ImmutableDictionary<string, ReportDiagnostic> diagnosticOptions = diagnostics.ToImmutableDictionary(
107+
descriptor => descriptor.Id,
108+
descriptor => descriptor.Severity.ToReportDiagnostic());
109+
110+
// Transform to enable the diagnostics
111+
Solution EnableDiagnostics(Solution solution, ProjectId projectId)
112+
{
113+
CompilationOptions options =
114+
solution.GetProject(projectId)?.CompilationOptions
115+
?? throw new InvalidOperationException("Compilation options missing.");
116+
117+
return solution.WithProjectCompilationOptions(
118+
projectId,
119+
options.WithSpecificDiagnosticOptions(diagnosticOptions));
120+
}
121+
122+
SolutionTransforms.Clear();
123+
SolutionTransforms.Add(EnableDiagnostics);
124+
125+
return this;
126+
}
127+
128+
/// <summary>
129+
/// Specifies the diagnostics that should be produced.
130+
/// </summary>
131+
/// <param name="diagnostics">The set of diagnostics.</param>
132+
/// <returns>The current test instance.</returns>
133+
public CSharpSuppressorTest<TSuppressor> WithExpectedDiagnosticsResults(params DiagnosticResult[] diagnostics)
134+
{
135+
ExpectedDiagnostics.AddRange(diagnostics);
136+
137+
return this;
138+
}
139+
}
140+
141+
/// <summary>
142+
/// Extensions for <see cref="DiagnosticSeverity"/>.
143+
/// </summary>
144+
file static class DiagnosticSeverityExtensions
145+
{
146+
/// <summary>
147+
/// Converts a <see cref="DiagnosticSeverity"/> value into a <see cref="ReportDiagnostic"/> one.
148+
/// </summary>
149+
public static ReportDiagnostic ToReportDiagnostic(this DiagnosticSeverity severity)
150+
{
151+
return severity switch
152+
{
153+
DiagnosticSeverity.Hidden => ReportDiagnostic.Hidden,
154+
DiagnosticSeverity.Info => ReportDiagnostic.Info,
155+
DiagnosticSeverity.Warning => ReportDiagnostic.Warn,
156+
DiagnosticSeverity.Error => ReportDiagnostic.Error,
157+
_ => throw new InvalidEnumArgumentException(nameof(severity), (int)severity, typeof(DiagnosticSeverity)),
158+
};
159+
}
160+
}

0 commit comments

Comments
 (0)