File: Microsoft.CodeQuality.Analyzers\ApiDesignGuidelines\EnumsShouldHaveZeroValueTests.cs
Web Access
Project: ..\..\..\src\Microsoft.CodeAnalysis.NetAnalyzers\tests\Microsoft.CodeAnalysis.NetAnalyzers.UnitTests\Microsoft.CodeAnalysis.NetAnalyzers.UnitTests.csproj (Microsoft.CodeAnalysis.NetAnalyzers.UnitTests)
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the MIT license.  See License.txt in the project root for license information.
 
using System;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Testing;
using Test.Utilities;
using Xunit;
using VerifyCS = Test.Utilities.CSharpCodeFixVerifier<
    Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines.EnumsShouldHaveZeroValueAnalyzer,
    Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines.EquatableFixer>;
using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier<
    Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines.EnumsShouldHaveZeroValueAnalyzer,
    Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines.EquatableFixer>;
 
namespace Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines.UnitTests
{
    public class EnumsShouldHaveZeroValueTests
    {
        [Fact]
        public async Task CSharp_EnumsShouldZeroValueFlagsRenameAsync()
        {
            var code = @"
public class Outer
{
    [System.Flags]
    public enum E
    {
        A = 0,
        B = 3
    }
}
 
[System.Flags]
public enum E2
{
    A2 = 0,
    B2 = 1
}
 
[System.Flags]
public enum E3
{
    A3 = (ushort)0,
    B3 = (ushort)1
}
 
[System.Flags]
public enum E4
{
    A4 = 0,
    B4 = (int)2  // Sample comment
}
 
[System.Flags]
public enum NoZeroValuedField
{
    A5 = 1,
    B5 = 2
}";
            await VerifyCS.VerifyAnalyzerAsync(code,
                GetCSharpRenameResultAt(7, 9, "E", "A"),
                GetCSharpRenameResultAt(15, 5, "E2", "A2"),
                GetCSharpRenameResultAt(22, 5, "E3", "A3"),
                GetCSharpRenameResultAt(29, 5, "E4", "A4"));
        }
 
        [Fact, WorkItem(1432, "https://github.com/dotnet/roslyn-analyzers/issues/1432")]
        public async Task CSharp_EnumsShouldZeroValueFlagsRename_InternalAsync()
        {
            var code = @"
class Outer
{
    [System.Flags]
    private enum E
    {
        A = 0,
        B = 3
    }
}
 
[System.Flags]
internal enum E2
{
    A2 = 0,
    B2 = 1
}
 
[System.Flags]
internal enum E3
{
    A3 = (ushort)0,
    B3 = (ushort)1
}
 
[System.Flags]
internal enum E4
{
    A4 = 0,
    B4 = (int)2  // Sample comment
}
 
[System.Flags]
internal enum NoZeroValuedField
{
    A5 = 1,
    B5 = 2
}";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Fact]
        public async Task CSharp_EnumsShouldZeroValueFlagsMultipleZeroAsync()
        {
            var code = @"// Some comment
public class Outer
{
    [System.Flags]
    public enum E
    {
        None = 0,
        A = 0
    }
}
 
// Some comment
[System.Flags]
public enum E2
{
    None = 0,
    A = None
}";
            await VerifyCS.VerifyAnalyzerAsync(code,
                GetCSharpMultipleZeroResultAt(5, 17, "E"),
                GetCSharpMultipleZeroResultAt(14, 13, "E2"));
        }
 
        [Fact, WorkItem(1432, "https://github.com/dotnet/roslyn-analyzers/issues/1432")]
        public async Task CSharp_EnumsShouldZeroValueFlagsMultipleZero_InternalAsync()
        {
            var code = @"// Some comment
public class Outer
{
    [System.Flags]
    private enum E
    {
        None = 0,
        A = 0
    }
}
 
// Some comment
[System.Flags]
internal enum E2
{
    None = 0,
    A = None
}";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Fact]
        public async Task CSharp_EnumsShouldZeroValueNotFlagsNoZeroValueAsync()
        {
            var code = @"
public class Outer
{
    public enum E
    {
        A = 1
    }
 
    public enum E2
    {
        None = 1,
        A = 2
    }
}
 
public enum E3
{
    None = 0,
    A = 1
}
 
public enum E4
{
    None = 0,
    A = 0
}
";
            await VerifyCS.VerifyAnalyzerAsync(code,
                GetCSharpNoZeroResultAt(4, 17, "E"),
                GetCSharpNoZeroResultAt(9, 17, "E2"));
        }
 
        [Fact, WorkItem(1432, "https://github.com/dotnet/roslyn-analyzers/issues/1432")]
        public async Task CSharp_EnumsShouldZeroValueNotFlagsNoZeroValue_InternalAsync()
        {
            var code = @"
public class Outer
{
    private enum E
    {
        A = 1
    }
 
    private enum E2
    {
        None = 1,
        A = 2
    }
}
 
enum E3
{
    None = 0,
    A = 1
}
 
internal enum E4
{
    None = 0,
    A = 0
}
";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Fact]
        public async Task VisualBasic_EnumsShouldZeroValueFlagsRenameAsync()
        {
            var code = @"
Public Class C
    <System.Flags>
    Public Enum E
        A = 0
        B = 1
    End Enum
End Class
 
<System.Flags>
Public Enum E2
    A2 = 0
    B2 = 1
End Enum
 
<System.Flags>
Public Enum E3
    A3 = CUShort(0)
    B3 = CUShort(1)
End Enum
 
<System.Flags>
Public Enum NoZeroValuedField
    A5 = 1
    B5 = 2
End Enum
";
            await VerifyVB.VerifyAnalyzerAsync(code,
                GetBasicRenameResultAt(5, 9, "E", "A"),
                GetBasicRenameResultAt(12, 5, "E2", "A2"),
                GetBasicRenameResultAt(18, 5, "E3", "A3"));
        }
 
        [Fact, WorkItem(1432, "https://github.com/dotnet/roslyn-analyzers/issues/1432")]
        public async Task VisualBasic_EnumsShouldZeroValueFlagsRename_InternalAsync()
        {
            var code = @"
Public Class C
    <System.Flags>
    Private Enum E
        A = 0
        B = 1
    End Enum
End Class
 
<System.Flags>
Enum E2
    A2 = 0
    B2 = 1
End Enum
 
<System.Flags>
Friend Enum E3
    A3 = CUShort(0)
    B3 = CUShort(1)
End Enum
 
<System.Flags>
Friend Enum NoZeroValuedField
    A5 = 1
    B5 = 2
End Enum
";
            await VerifyVB.VerifyAnalyzerAsync(code);
        }
 
        [WorkItem(836193, "DevDiv")]
        [Fact]
        public async Task VisualBasic_EnumsShouldZeroValueFlagsRename_AttributeListHasTriviaAsync()
        {
            var code = @"
Public Class Outer
    <System.Flags> _
    Public Enum E
	    A = 0
	    B = 1
    End Enum
End Class
 
<System.Flags> _
Public Enum E2
	A2 = 0
	B2 = 1
End Enum
 
<System.Flags> _
Public Enum E3
	A3 = CUShort(0)
	B3 = CUShort(1)
End Enum
 
<System.Flags> _
Public Enum NoZeroValuedField
	A5 = 1
	B5 = 2
End Enum
";
            await VerifyVB.VerifyAnalyzerAsync(code,
                GetBasicRenameResultAt(5, 6, "E", "A"),
                GetBasicRenameResultAt(12, 2, "E2", "A2"),
                GetBasicRenameResultAt(18, 2, "E3", "A3"));
        }
 
        [Fact]
        public async Task VisualBasic_EnumsShouldZeroValueFlagsMultipleZeroAsync()
        {
            var code = @"
Public Class Outer
    <System.Flags>
    Public Enum E
	    None = 0
	    A = 0
    End Enum
End Class
 
<System.Flags>
Public Enum E2
	None = 0
	A = None
End Enum
 
<System.Flags>
Public Enum E3
    A3 = 0
    B3 = CUInt(0)  ' Not a constant
End Enum";
 
            await VerifyVB.VerifyAnalyzerAsync(code,
                GetBasicMultipleZeroResultAt(4, 17, "E"),
                GetBasicMultipleZeroResultAt(11, 13, "E2"),
                GetBasicMultipleZeroResultAt(17, 13, "E3"));
        }
 
        [Fact, WorkItem(1432, "https://github.com/dotnet/roslyn-analyzers/issues/1432")]
        public async Task VisualBasic_EnumsShouldZeroValueFlagsMultipleZero_InternalAsync()
        {
            var code = @"
Public Class Outer
    <System.Flags>
    Private Enum E
	    None = 0
	    A = 0
    End Enum
End Class
 
<System.Flags>
Enum E2
	None = 0
	A = None
End Enum
 
<System.Flags>
Friend Enum E3
	A3 = 0
	B3 = CUInt(0)  ' Not a constant
End Enum";
 
            await VerifyVB.VerifyAnalyzerAsync(code);
        }
 
        [Fact]
        public async Task VisualBasic_EnumsShouldZeroValueNotFlagsNoZeroValueAsync()
        {
            var code = @"
Public Class Outer
    Public Enum E
	    A = 1
    End Enum
 
    Public Enum E2
	    None = 1
	    A = 2
    End Enum
End Class
 
Public Enum E3
    None = 0
    A = 1
End Enum
 
Public Enum E4
    None = 0
    A = 0
End Enum
";
 
            await VerifyVB.VerifyAnalyzerAsync(code,
                GetBasicNoZeroResultAt(3, 17, "E"),
                GetBasicNoZeroResultAt(7, 17, "E2"));
        }
 
        [Fact, WorkItem(1432, "https://github.com/dotnet/roslyn-analyzers/issues/1432")]
        public async Task VisualBasic_EnumsShouldZeroValueNotFlagsNoZeroValue_InternalAsync()
        {
            var code = @"
Public Class Outer
    Private Enum E
	    A = 1
    End Enum
 
    Friend Enum E2
	    None = 1
	    A = 2
    End Enum
End Class
 
Enum E3
    None = 0
    A = 1
End Enum
 
Friend Enum E4
    None = 0
    A = 0
End Enum
";
 
            await VerifyVB.VerifyAnalyzerAsync(code);
        }
 
        [Theory, WorkItem(5777, "https://github.com/dotnet/roslyn-analyzers/issues/5777")]
        [InlineData("")]
        [InlineData("dotnet_code_quality.additional_enum_none_names = Never")]
        [InlineData("dotnet_code_quality.CA1008.additional_enum_none_names = Never")]
        [InlineData("dotnet_code_quality.additional_enum_none_names = Never|Zero")]
        [InlineData("dotnet_code_quality.CA1008.additional_enum_none_names = Never|Zero")]
        public async Task EnumNoneValueMatchesUserOption(string editorConfigText)
        {
            var csTest = new VerifyCS.Test
            {
                TestState =
                {
                    Sources =
                    {
                        @"
using System;
 
[Flags]
public enum E1
{
    None = 0,
    A = 1
}
 
[Flags]
public enum E2
{
    Never = 0,
    A = 1
}
 
[Flags]
public enum E3
{
    Zero = 0,
    A = 1
}"},
                    AnalyzerConfigFiles = { ("/.editorconfig", $@"root = true
 
[*]
{editorConfigText}
"), },
                },
            };
 
            if (editorConfigText.Length == 0)
            {
                csTest.ExpectedDiagnostics.Add(GetCSharpRenameResultAt(14, 5, "E2", "Never"));
            }
 
            if (!editorConfigText.EndsWith("Zero", StringComparison.Ordinal))
            {
                csTest.ExpectedDiagnostics.Add(GetCSharpRenameResultAt(21, 5, "E3", "Zero"));
            }
 
            await csTest.RunAsync();
 
            var vbTest = new VerifyVB.Test
            {
                TestState =
                {
                    Sources =
                    {
                        @"
Imports System
 
<System.Flags>
Public Enum E
    None = 0
    A = 1
End Enum
 
<Flags>
Public Enum E2
    Never = 0
    A = 1
End Enum
 
<Flags>
Public Enum E3
    Zero = 0
    A = 1
End Enum"},
                    AnalyzerConfigFiles = { ("/.editorconfig", $@"root = true
 
[*]
{editorConfigText}
"), },
                },
            };
 
            if (editorConfigText.Length == 0)
            {
                vbTest.ExpectedDiagnostics.Add(GetCSharpRenameResultAt(12, 5, "E2", "Never"));
            }
 
            if (!editorConfigText.EndsWith("Zero", StringComparison.Ordinal))
            {
                vbTest.ExpectedDiagnostics.Add(GetCSharpRenameResultAt(18, 5, "E3", "Zero"));
            }
 
            await vbTest.RunAsync();
        }
 
        private static DiagnosticResult GetCSharpMultipleZeroResultAt(int line, int column, string typeName)
#pragma warning disable RS0030 // Do not use banned APIs
            => VerifyCS.Diagnostic(EnumsShouldHaveZeroValueAnalyzer.RuleMultipleZero)
                .WithLocation(line, column)
#pragma warning restore RS0030 // Do not use banned APIs
                .WithArguments(typeName);
 
        private static DiagnosticResult GetBasicMultipleZeroResultAt(int line, int column, string typeName)
#pragma warning disable RS0030 // Do not use banned APIs
            => VerifyCS.Diagnostic(EnumsShouldHaveZeroValueAnalyzer.RuleMultipleZero)
                .WithLocation(line, column)
#pragma warning restore RS0030 // Do not use banned APIs
                .WithArguments(typeName);
 
        private static DiagnosticResult GetCSharpNoZeroResultAt(int line, int column, string typeName)
#pragma warning disable RS0030 // Do not use banned APIs
            => VerifyCS.Diagnostic(EnumsShouldHaveZeroValueAnalyzer.RuleNoZero)
                .WithLocation(line, column)
#pragma warning restore RS0030 // Do not use banned APIs
                .WithArguments(typeName);
 
        private static DiagnosticResult GetBasicNoZeroResultAt(int line, int column, string typeName)
#pragma warning disable RS0030 // Do not use banned APIs
            => VerifyCS.Diagnostic(EnumsShouldHaveZeroValueAnalyzer.RuleNoZero)
                .WithLocation(line, column)
#pragma warning restore RS0030 // Do not use banned APIs
                .WithArguments(typeName);
 
        private static DiagnosticResult GetCSharpRenameResultAt(int line, int column, string typeName, string newName)
#pragma warning disable RS0030 // Do not use banned APIs
            => VerifyCS.Diagnostic(EnumsShouldHaveZeroValueAnalyzer.RuleRename)
                .WithLocation(line, column)
#pragma warning restore RS0030 // Do not use banned APIs
                .WithArguments(typeName, newName);
 
        private static DiagnosticResult GetBasicRenameResultAt(int line, int column, string typeName, string newName)
#pragma warning disable RS0030 // Do not use banned APIs
            => VerifyCS.Diagnostic(EnumsShouldHaveZeroValueAnalyzer.RuleRename)
                .WithLocation(line, column)
#pragma warning restore RS0030 // Do not use banned APIs
                .WithArguments(typeName, newName);
    }
}