| File: GeneratorTests.cs | Web Access |
| Project: src\test\Generators\Microsoft.Gen.MetricsReports\Unit\Microsoft.Gen.MetricsReports.Unit.Tests.csproj (Microsoft.Gen.MetricsReports.Unit.Tests) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Diagnostics.Metrics; using System.IO; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.Extensions.Diagnostics.Metrics; using Microsoft.Gen.Shared; using Xunit; using Xunit.Abstractions; namespace Microsoft.Gen.MetricsReports.Test; public class GeneratorTests(ITestOutputHelper output) { private const string ReportFilename = "MetricsReport.json"; [Fact] public void GeneratorShouldNotDoAnythingIfGeneralExecutionContextDoesNotHaveClassDeclarationSyntaxReceiver() { GeneratorExecutionContext defaultGeneralExecutionContext = default; new MetricsReportsGenerator().Execute(defaultGeneralExecutionContext); Assert.Null(defaultGeneralExecutionContext.SyntaxReceiver); } [Theory] [CombinatorialData] public async Task TestAll(bool useExplicitReportPath) { Dictionary<string, string>? options = useExplicitReportPath ? new() { ["build_property.MetricsReportOutputPath"] = Directory.GetCurrentDirectory() } : null; foreach (var inputFile in Directory.GetFiles("TestClasses")) { var stem = Path.GetFileNameWithoutExtension(inputFile); var goldenFileName = Path.ChangeExtension(stem, ".json"); var goldenReportPath = Path.Combine("GoldenReports", goldenFileName); var generatedReportPath = Path.Combine(Directory.GetCurrentDirectory(), ReportFilename); if (File.Exists(goldenReportPath)) { var d = await RunGenerator(await File.ReadAllTextAsync(inputFile), options); Assert.Empty(d); var golden = await File.ReadAllTextAsync(goldenReportPath); var generated = await File.ReadAllTextAsync(generatedReportPath); if (golden != generated) { output.WriteLine($"MISMATCH: golden report {goldenReportPath}, generated {generatedReportPath}"); output.WriteLine("----"); output.WriteLine("golden:"); output.WriteLine(golden); output.WriteLine("----"); output.WriteLine("generated:"); output.WriteLine(generated); output.WriteLine("----"); } File.Delete(generatedReportPath); Assert.Equal(golden, generated); } else { // generate the golden file if it doesn't already exist output.WriteLine($"Generating golden report: {goldenReportPath}"); _ = await RunGenerator(await File.ReadAllTextAsync(inputFile), options, reportFileName: goldenFileName); } } } [Fact] public async Task ShouldNot_Generate_WhenDisabledViaConfig() { var inputFile = Directory.GetFiles("TestClasses").First(); var options = new Dictionary<string, string> { ["build_property.GenerateMetricsReport"] = bool.FalseString, ["build_property.MetricsReportOutputPath"] = Path.GetTempPath() }; var d = await RunGenerator(await File.ReadAllTextAsync(inputFile), options); Assert.Empty(d); Assert.False(File.Exists(Path.Combine(Path.GetTempPath(), ReportFilename))); } [Theory] [CombinatorialData] public async Task Should_EmitWarning_WhenPathUnavailable(bool isReportPathProvided) { var inputFile = Directory.GetFiles("TestClasses").First(); var options = new Dictionary<string, string> { ["build_property.outputpath"] = string.Empty }; if (isReportPathProvided) { options.Add("build_property.MetricsReportOutputPath", string.Empty); } var diags = await RunGenerator(await File.ReadAllTextAsync(inputFile), options); var diag = Assert.Single(diags); Assert.Equal("AUDREPGEN000", diag.Id); Assert.Equal(DiagnosticSeverity.Info, diag.Severity); } [Fact] public async Task Should_UseProjectDir_WhenOutputPathIsRelative() { var projectDir = Path.GetTempPath(); var outputPath = Guid.NewGuid().ToString(); var fullReportPath = Path.Combine(projectDir, outputPath); Directory.CreateDirectory(fullReportPath); try { var inputFile = Directory.GetFiles("TestClasses").First(); var options = new Dictionary<string, string> { ["build_property.projectdir"] = projectDir, ["build_property.outputpath"] = outputPath }; var diags = await RunGenerator(await File.ReadAllTextAsync(inputFile), options); Assert.Empty(diags); Assert.True(File.Exists(Path.Combine(fullReportPath, ReportFilename))); } finally { Directory.Delete(fullReportPath, recursive: true); } } private static async Task<IReadOnlyList<Diagnostic>> RunGenerator( string code, Dictionary<string, string>? analyzerOptions = null, CancellationToken cancellationToken = default, string? reportFileName = null) { Assembly[] refs = [ Assembly.GetAssembly(typeof(Meter))!, Assembly.GetAssembly(typeof(CounterAttribute))!, Assembly.GetAssembly(typeof(HistogramAttribute))!, Assembly.GetAssembly(typeof(GaugeAttribute))! ]; var generator = reportFileName is null ? new MetricsReportsGenerator() : new MetricsReportsGenerator(reportFileName); var (d, _) = await RoslynTestUtils.RunGenerator( generator, refs, new[] { code }, new OptionsProvider(analyzerOptions), includeBaseReferences: true, cancellationToken: cancellationToken).ConfigureAwait(false); return d; } private sealed class Options : AnalyzerConfigOptions { private readonly Dictionary<string, string> _options; public Options(Dictionary<string, string>? analyzerOptions) { _options = analyzerOptions ?? []; _options.TryAdd("build_property.GenerateMetricsReport", bool.TrueString); _options.TryAdd("build_property.outputpath", Directory.GetCurrentDirectory()); } public override bool TryGetValue(string key, out string value) => _options.TryGetValue(key, out value!); } private sealed class OptionsProvider(Dictionary<string, string>? analyzerOptions) : AnalyzerConfigOptionsProvider { public override AnalyzerConfigOptions GlobalOptions => new Options(analyzerOptions); public override AnalyzerConfigOptions GetOptions(SyntaxTree tree) => throw new NotSupportedException(); public override AnalyzerConfigOptions GetOptions(AdditionalText textFile) => throw new NotSupportedException(); } }