File: Microsoft.NetCore.Analyzers\Performance\RecommendCaseInsensitiveStringComparison.CSharp.Tests.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.Threading.Tasks;
using Microsoft.CodeAnalysis.Testing;
using Test.Utilities;
using Xunit;
using VerifyCS = Test.Utilities.CSharpCodeFixVerifier<
    Microsoft.NetCore.Analyzers.Performance.RecommendCaseInsensitiveStringComparisonAnalyzer,
    Microsoft.NetCore.CSharp.Analyzers.Performance.CSharpRecommendCaseInsensitiveStringComparisonFixer>;
 
namespace Microsoft.NetCore.Analyzers.Performance.UnitTests
{
    public class RecommendCaseInsensitiveStringComparison_CSharp_Tests : RecommendCaseInsensitiveStringComparison_Base_Tests
    {
        [Theory]
        [MemberData(nameof(DiagnosedAndFixedData))]
        [MemberData(nameof(DiagnosedAndFixedInvertedData))]
        [MemberData(nameof(CSharpDiagnosedAndFixedNamedData))]
        [MemberData(nameof(CSharpDiagnosedAndFixedInvertedNamedData))]
        public async Task Diagnostic_Assign(string diagnosedLine, string fixedLine)
        {
            string originalCode = $@"using System;
class C
{{
    void M()
    {{
        string a = ""aBc"";
        string b = ""bc"";
        var result = [|{diagnosedLine}|];
    }}
}}";
            string fixedCode = $@"using System;
class C
{{
    void M()
    {{
        string a = ""aBc"";
        string b = ""bc"";
        var result = {fixedLine};
    }}
}}";
            await VerifyFixCSharpAsync(originalCode, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(DiagnosedAndFixedData))]
        [MemberData(nameof(DiagnosedAndFixedInvertedData))]
        [MemberData(nameof(CSharpDiagnosedAndFixedNamedData))]
        [MemberData(nameof(CSharpDiagnosedAndFixedInvertedNamedData))]
        public async Task Diagnostic_Return(string diagnosedLine, string fixedLine)
        {
            string originalCode = $@"using System;
class C
{{
    object M()
    {{
        string a = ""aBc"";
        string b = ""bc"";
        return [|{diagnosedLine}|];
    }}
}}";
            string fixedCode = $@"using System;
class C
{{
    object M()
    {{
        string a = ""aBc"";
        string b = ""bc"";
        return {fixedLine};
    }}
}}";
            await VerifyFixCSharpAsync(originalCode, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(DiagnosedAndFixedImplicitBooleanData))]
        [MemberData(nameof(DiagnosedAndFixedWithAppendedMethodData))]
        [MemberData(nameof(DiagnosedAndFixedWithAppendedMethodInvertedData))]
        [MemberData(nameof(CSharpDiagnosedAndFixedWithAppendedMethodNamedData))]
        public async Task Diagnostic_If(string diagnosedLine, string fixedLine, string appendedMethod)
        {
            string originalCode = $@"using System;
class C
{{
    int M()
    {{
        string a = ""aBc"";
        string b = ""bc"";
        bool myBoolean = false;
        if ([|{diagnosedLine}|]{appendedMethod})
        {{
            return 5;
        }}
        return 4;
    }}
}}";
            string fixedCode = $@"using System;
class C
{{
    int M()
    {{
        string a = ""aBc"";
        string b = ""bc"";
        bool myBoolean = false;
        if ({fixedLine}{appendedMethod})
        {{
            return 5;
        }}
        return 4;
    }}
}}";
            await VerifyFixCSharpAsync(originalCode, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(DiagnosedAndFixedData))]
        [MemberData(nameof(DiagnosedAndFixedInvertedData))]
        [MemberData(nameof(CSharpDiagnosedAndFixedNamedData))]
        [MemberData(nameof(CSharpDiagnosedAndFixedInvertedNamedData))]
        public async Task Diagnostic_IgnoreResult(string diagnosedLine, string fixedLine)
        {
            string originalCode = $@"using System;
class C
{{
    void M()
    {{
        string a = ""aBc"";
        string b = ""bc"";
        [|{diagnosedLine}|];
    }}
}}";
            string fixedCode = $@"using System;
class C
{{
    void M()
    {{
        string a = ""aBc"";
        string b = ""bc"";
        {fixedLine};
    }}
}}";
            await VerifyFixCSharpAsync(originalCode, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(DiagnosedAndFixedStringLiteralsData))]
        [MemberData(nameof(DiagnosedAndFixedStringLiteralsInvertedData))]
        [MemberData(nameof(CSharpDiagnosedAndFixedStringLiteralsNamedData))]
        [MemberData(nameof(CSharpDiagnosedAndFixedStringLiteralsInvertedNamedData))]
        public async Task Diagnostic_StringLiterals_ReturnExpressionBody(string diagnosedLine, string fixedLine)
        {
            string originalCode = $@"using System;
class C
{{
    object M() => [|{diagnosedLine}|];
}}";
            string fixedCode = $@"using System;
class C
{{
    object M() => {fixedLine};
}}";
            await VerifyFixCSharpAsync(originalCode, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(DiagnosedAndFixedStringReturningMethodsData))]
        [MemberData(nameof(DiagnosedAndFixedStringReturningMethodsInvertedData))]
        [MemberData(nameof(CSharpDiagnosedAndFixedStringReturningMethodsNamedData))]
        [MemberData(nameof(CSharpDiagnosedAndFixedStringReturningMethodsInvertedNamedData))]
        public async Task Diagnostic_StringReturningMethods_Discard(string diagnosedLine, string fixedLine)
        {
            string originalCode = $@"using System;
class C
{{
    public string GetStringA() => ""aBc"";
    public string GetStringB() => ""CdE"";
    void M()
    {{
        _ = [|{diagnosedLine}|];
    }}
}}";
            string fixedCode = $@"using System;
class C
{{
    public string GetStringA() => ""aBc"";
    public string GetStringB() => ""CdE"";
    void M()
    {{
        _ = {fixedLine};
    }}
}}";
            await VerifyFixCSharpAsync(originalCode, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(DiagnosedAndFixedParenthesizedData))]
        [MemberData(nameof(DiagnosedAndFixedParenthesizedInvertedData))]
        [MemberData(nameof(CSharpDiagnosedAndFixedParenthesizedNamedData))]
        [MemberData(nameof(CSharpDiagnosedAndFixedParenthesizedNamedInvertedData))]
        [MemberData(nameof(CSharpDiagnosedAndFixedParenthesizedComplexCasesData))]
        public async Task Diagnostic_Parenthesized_ReturnCastedToString(string diagnosedLine, string fixedLine)
        {
            string originalCode = $@"using System;
class C
{{
    string GetString() => ""aBc"";
    string M()
    {{
        string a = ""aBc"";
        string b = ""cDe"";
        return ([|{diagnosedLine}|]).ToString();
    }}
}}";
            string fixedCode = $@"using System;
class C
{{
    string GetString() => ""aBc"";
    string M()
    {{
        string a = ""aBc"";
        string b = ""cDe"";
        return ({fixedLine}).ToString();
    }}
}}";
            await VerifyFixCSharpAsync(originalCode, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(CSharpDiagnosedAndFixedEqualityToEqualsData))]
        public async Task Diagnostic_Equality_To_Equals(string diagnosedLine, string fixedLine)
        {
            string originalCode = $@"using System;
class C
{{
    string GetString() => ""cde"";
    bool M(string a, string b)
    {{
        bool result = [|{diagnosedLine}|];
        if ([|{diagnosedLine}|]) return result;
        return [|{diagnosedLine}|];
    }}
}}";
            string fixedCode = $@"using System;
class C
{{
    string GetString() => ""cde"";
    bool M(string a, string b)
    {{
        bool result = {fixedLine};
        if ({fixedLine}) return result;
        return {fixedLine};
    }}
}}";
            await VerifyFixCSharpAsync(originalCode, fixedCode);
        }
 
        [Fact]
        public async Task Diagnostic_Equality_To_Equals_Trivia()
        {
            string originalCode = $@"using System;
class C
{{
    bool M(string a, string b)
    {{
        // Trivia
        bool /* Trivia */ result = /* Trivia */ [|a.ToLowerInvariant() // Trivia
            == /* Trivia */ b.ToLowerInvariant()|] /* Trivia */; // Trivia
        if (/* Trivia */ [|a.ToLower() /* Trivia */ != /* Trivia */ b.ToLower()|] /* Trivia */) // Trivia
            return /* Trivia */ [|b /* Trivia */ != /* Trivia */ a.ToLowerInvariant()|] /* Trivia */; // Trivia
        return // Trivia
            [|""abc"" /* Trivia */ == /* Trivia */ a.ToUpperInvariant()|] /* Trivia */; // Trivia
        // Trivia
    }}
}}";
            string fixedCode = $@"using System;
class C
{{
    bool M(string a, string b)
    {{
        // Trivia
        bool /* Trivia */ result = /* Trivia */ a.Equals(b, StringComparison.InvariantCultureIgnoreCase) /* Trivia */; // Trivia
        if (/* Trivia */ !a.Equals(b, StringComparison.CurrentCultureIgnoreCase) /* Trivia */) // Trivia
            return /* Trivia */ !b /* Trivia */ .Equals /* Trivia */ (a, StringComparison.InvariantCultureIgnoreCase) /* Trivia */; // Trivia
        return // Trivia
            ""abc"" /* Trivia */ .Equals /* Trivia */ (a, StringComparison.InvariantCultureIgnoreCase) /* Trivia */; // Trivia
        // Trivia
    }}
}}";
            await VerifyFixCSharpAsync(originalCode, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(NoDiagnosticData))]
        [InlineData("\"aBc\".CompareTo(null)")]
        [InlineData("\"aBc\".ToUpperInvariant().CompareTo((object)null)")]
        [InlineData("\"aBc\".CompareTo(value: (object)\"cDe\")")]
        [InlineData("\"aBc\".CompareTo(strB: \"cDe\")")]
        public async Task NoDiagnostic_All(string ignoredLine)
        {
            string originalCode = $@"using System;
class C
{{
    object M()
    {{
        char ch = 'c';
        object obj = 3;
        return {ignoredLine};
    }}
}}";
 
            await VerifyNoDiagnosticCSharpAsync(originalCode);
        }
 
        [Theory]
        [MemberData(nameof(DiagnosticNoFixStartsWithContainsIndexOfData))]
        public async Task Diagnostic_NoFix_StartsWithContainsIndexOf(string diagnosedLine)
        {
            string originalCode = $@"using System;
class C
{{
    string GetStringA() => ""aBc"";
    string GetStringB() => ""cDe"";
    object M()
    {{
        string a = ""AbC"";
        string b = ""CdE"";
        return [|{diagnosedLine}|];
    }}
}}";
            await VerifyFixCSharpAsync(originalCode, originalCode);
        }
 
        [Theory]
        [MemberData(nameof(DiagnosticNoFixCompareToData))]
        [MemberData(nameof(DiagnosticNoFixCompareToInvertedData))]
        [MemberData(nameof(CSharpDiagnosticNoFixCompareToNamedData))]
        [MemberData(nameof(CSharpDiagnosticNoFixCompareToInvertedNamedData))]
        public async Task Diagnostic_NoFix_CompareTo(string diagnosedLine)
        {
            string originalCode = $@"using System;
class C
{{
    string GetStringA() => ""aBc"";
    string GetStringB() => ""cDe"";
    int M()
    {{
        string a = ""AbC"";
        string b = ""CdE"";
        return [|{diagnosedLine}|];
    }}
}}";
            await VerifyFixCSharpAsync(originalCode, originalCode);
        }
 
        [Theory]
        [MemberData(nameof(CSharpDiagnosticNoFixEqualsData))]
        public async Task Diagnostic_NoFix_Equals(string diagnosedLine)
        {
            string originalCode = $@"using System;
class C
{{
    string GetString() => string.Empty;
    bool M()
    {{
        string a = ""aBc"";
        string b = ""dEf"";
        return [|{diagnosedLine}|];
    }}
}}";
 
            await VerifyFixCSharpAsync(originalCode, originalCode);
        }
 
        [Fact, WorkItem(7053, "https://github.com/dotnet/roslyn-analyzers/issues/7053")]
        public Task Net48_Contains_NoDiagnostic()
        {
            const string code = """
                                using System;
 
                                class C
                                {
                                    void M(string s)
                                    {
                                        s.ToUpperInvariant().Contains("ABC");
                                    }
                                }
                                """;
 
            return new VerifyCS.Test
            {
                TestCode = code,
                ReferenceAssemblies = ReferenceAssemblies.NetFramework.Net48.Default,
                MarkupOptions = MarkupOptions.UseFirstDescriptor
            }.RunAsync();
        }
 
        [Theory, WorkItem(7053, "https://github.com/dotnet/roslyn-analyzers/issues/7053")]
        [InlineData("StartsWith")]
        [InlineData("IndexOf")]
        public Task Net48_Diagnostic(string method)
        {
            var code = $$"""
                         using System;
 
                         class C
                         {
                             void M(string s)
                             {
                                 [|s.ToUpperInvariant().{{method}}("ABC")|];
                             }
                         }
                         """;
            var fixedCode = $$"""
                              using System;
 
                              class C
                              {
                                  void M(string s)
                                  {
                                      s.{{method}}("ABC", StringComparison.InvariantCultureIgnoreCase);
                                  }
                              }
                              """;
 
            return new VerifyCS.Test
            {
                TestCode = code,
                FixedCode = fixedCode,
                ReferenceAssemblies = ReferenceAssemblies.NetFramework.Net48.Default,
                MarkupOptions = MarkupOptions.UseFirstDescriptor
            }.RunAsync();
        }
 
        private async Task VerifyNoDiagnosticCSharpAsync(string originalSource)
        {
            VerifyCS.Test test = new()
            {
                TestCode = originalSource,
                FixedCode = originalSource
            };
 
            await test.RunAsync();
        }
 
        private async Task VerifyFixCSharpAsync(string originalSource, string fixedSource)
        {
            VerifyCS.Test test = new()
            {
                TestCode = originalSource,
                FixedCode = fixedSource,
                MarkupOptions = MarkupOptions.UseFirstDescriptor
            };
 
            await test.RunAsync();
        }
    }
}