File: AttributeParserTests.cs
Web Access
Project: src\test\Generators\Microsoft.Gen.Logging\Unit\Microsoft.Gen.Logging.Unit.Tests.csproj (Microsoft.Gen.Logging.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.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.Extensions.Compliance.Classification;
using Microsoft.Extensions.Compliance.Redaction;
using Microsoft.Extensions.Compliance.Testing;
using Microsoft.Extensions.Diagnostics.Enrichment;
using Microsoft.Extensions.Logging;
using Microsoft.Gen.Logging.Parsing;
using Microsoft.Gen.Shared;
using Xunit;
 
namespace Microsoft.Gen.Logging.Test;
 
public class AttributeParserTests
{
    [Fact]
    public async Task RandomAttribute()
    {
        var diagnostics = await RunGenerator(@"
                internal static partial class C
                {
                    [LoggerMessage(0, LogLevel.Debug, ""M {p0}"")]
                    static partial void M(ILogger logger, [Test] string p0);
                }
            ");
 
        Assert.Empty(diagnostics);
    }
 
    [Fact]
    public async Task LegacyAttribute()
    {
        var diagnostics = await RunGenerator(@"
                internal static partial class C
                {
#pragma warning disable CS0618
 
                    [LoggerMessage(0, LogLevel.Debug, ""M {p0}"")]
                    static partial void M(ILogger logger, [Test] string p0);
                }
            ");
 
        Assert.Empty(diagnostics);
    }
 
    [Fact]
    public async Task DataClassificationAttributeFullName()
    {
        var diagnostics = await RunGenerator(@"
                internal static partial class C
                {
                    [LoggerMessage(0, LogLevel.Debug, ""M {p0}"")]
                    static partial void M(ILogger logger, [PrivateDataAttribute] string p0);
                }
            ");
 
        Assert.Empty(diagnostics);
    }
 
    [Fact]
    public async Task DataClassificationAttributeShortName()
    {
        var diagnostics = await RunGenerator(@"
                internal static partial class C
                {
                    [LoggerMessage(0, LogLevel.Debug, ""M {p0}"")]
                    static partial void M(ILogger logger, [PrivateData] string p0);
                }
            ");
 
        Assert.Empty(diagnostics);
    }
 
    [Fact]
    public async Task MultipleAttributesOnDifferentTopics()
    {
        var diagnostics = await RunGenerator(@"
                internal static partial class C
                {
                    [LoggerMessage(0, LogLevel.Debug, ""M {p0}"")]
                    static partial void M(ILogger logger, [Test][PrivateData] string p0);
                }
            ");
 
        Assert.Empty(diagnostics);
    }
 
    [Theory]
    [InlineData("[PrivateData] string")]
    public async Task RedactorProviderIsInTheInstance(string type)
    {
        var diagnostics = await RunGenerator(@$"
            internal partial class TestInstance
            {{
                private readonly ILogger _logger;
                private readonly IRedactorProvider _redactorProvider;
 
                public TestInstance(ILogger logger, IRedactorProvider redactorProvider)
                {{
                    _logger = logger;
                    _redactorProvider = redactorProvider;
                }}
 
                [LoggerMessage(0, LogLevel.Debug, ""M0 {{p0}}"")]
                public partial void M0({type} p0);
            }}");
 
        Assert.Empty(diagnostics);
    }
 
    [Fact]
    public async Task DataClassOnAllTypes()
    {
        var diagnostics = await RunGenerator(@"
                internal static partial class C
                {
                    [LoggerMessage(0, LogLevel.Debug, ""M {p0}"")]
                    static partial void M(ILogger logger, [PrivateData] int p0);
                }
            ");
 
        Assert.Empty(diagnostics);
    }
 
    [Fact]
    public async Task MissingLogger()
    {
        var diagnostics = await RunGenerator(@"
                internal static partial class C
                {
                    [LoggerMessage(0, LogLevel.Debug, ""M {p0}"")]
                    static partial void M([PrivateData] string p0);
                }
            ");
 
        _ = Assert.Single(diagnostics);
        Assert.Equal(DiagDescriptors.MissingLoggerParameter.Id, diagnostics[0].Id);
    }
 
    [Fact]
    public async Task NotMissingDataClassificationAttributeInStatic()
    {
        var diagnostics = await RunGenerator(@"
                internal static partial class C
                {
                    [LoggerMessage(0, LogLevel.Debug, ""M {p0}"")]
                    static partial void M(ILogger logger, [PublicData] string p0);
                }
            ");
 
        Assert.Empty(diagnostics);
    }
 
    [Fact]
    public async Task MethodNotStatic()
    {
        var diagnostics = await RunGenerator(@$"
            internal partial class C
            {{
                [LoggerMessage(0, LogLevel.Debug, ""M {{p0}}"")]
                partial void M(ILogger logger, [PrivateData] string p0);
            }}");
 
        _ = Assert.Single(diagnostics);
        Assert.Equal(DiagDescriptors.LoggingMethodShouldBeStatic.Id, diagnostics[0].Id);
    }
 
    [Fact]
    public async Task CantUseDataClassificationWithLogProperties()
    {
        const string Source = @"
            class MyClass
            {
                public string Property { get; set; }
            }
 
            partial class LoggerClass
            {
                [LoggerMessage(0, LogLevel.Debug, ""No params..."")]
                static partial void M(ILogger logger, [PrivateData][LogProperties] MyClass p1);
            }";
 
        var diagnostics = await RunGenerator(Source);
 
        _ = Assert.Single(diagnostics);
 
        Assert.Equal(DiagDescriptors.CantUseDataClassificationWithLogPropertiesOrTagProvider.Id, diagnostics[0].Id);
    }
 
    [Fact]
    public async Task CantUseDataClassificationWithTagProvider()
    {
        const string Source = @"
            class MyClass
            {
                public string Property { get; set; }
            }
 
            static class Provider
            {
                public static void Provide(ITagCollector props, MyClass? value)
                {
                    return;
                }
            }
 
            partial class C
            {
                [LoggerMessage(0, LogLevel.Debug, ""Parameter"")]
                static partial void M(ILogger logger, [PrivateData][TagProvider(typeof(Provider), nameof(Provider.Provide))] MyClass p1);
            }";
 
        var diagnostics = await RunGenerator(Source);
 
        _ = Assert.Single(diagnostics);
 
        Assert.Equal(DiagDescriptors.CantUseDataClassificationWithLogPropertiesOrTagProvider.Id, diagnostics[0].Id);
    }
 
    private static async Task<IReadOnlyList<Diagnostic>> RunGenerator(string code)
    {
        var text = $@"
                namespace Test {{
                using Microsoft.Extensions.Compliance.Classification;
                using Microsoft.Extensions.Compliance.Testing;
                using Microsoft.Extensions.Compliance.Redaction;
                using Microsoft.Extensions.Logging;
                {code}
                }}
            ";
 
        var loggerAssembly = Assembly.GetAssembly(typeof(ILogger));
        var loggerMessageAssembly = Assembly.GetAssembly(typeof(LoggerMessageAttribute));
        var enrichmentAssembly = Assembly.GetAssembly(typeof(IEnrichmentTagCollector));
        var dataClassificationAssembly = Assembly.GetAssembly(typeof(DataClassification));
        var simpleDataClassificationAssembly = Assembly.GetAssembly(typeof(PrivateDataAttribute));
        var redactorProviderAssembly = Assembly.GetAssembly(typeof(IRedactorProvider));
        var refs = new[] { loggerAssembly!, loggerMessageAssembly!, enrichmentAssembly!, dataClassificationAssembly!, simpleDataClassificationAssembly!, redactorProviderAssembly! };
 
        var (d, _) = await RoslynTestUtils.RunGenerator(
            new LoggingGenerator(),
            refs,
            new[] { text }).ConfigureAwait(false);
 
        return d;
    }
}