File: Microsoft.NetCore.Analyzers\Performance\UseStringMethodCharOverloadWithSingleCharactersTests.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.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Testing;
using Test.Utilities;
using Xunit;
using VerifyCS = Test.Utilities.CSharpCodeFixVerifier<
    Microsoft.CodeAnalysis.CSharp.NetAnalyzers.Microsoft.NetCore.Analyzers.Performance.CSharpUseStringMethodCharOverloadWithSingleCharacters,
    Microsoft.NetCore.CSharp.Analyzers.Performance.CSharpUseStringMethodCharOverloadWithSingleCharactersFixer>;
using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier<
    Microsoft.NetCore.VisualBasic.Analyzers.Performance.BasicUseStringMethodCharOverloadWithSingleCharacters,
    Microsoft.NetCore.VisualBasic.Analyzers.Performance.BasicUseStringMethodCharOverloadWithSingleCharactersFixer>;
 
namespace Microsoft.NetCore.Analyzers.Performance.UnitTests
{
    public class UseStringMethodCharOverloadWithSingleCharactersTests
    {
        public static IEnumerable<object[]> Methods
        {
            get
            {
                yield return new object[] { nameof(string.StartsWith) };
                yield return new object[] { nameof(string.EndsWith) };
                yield return new object[] { nameof(string.IndexOf) };
                yield return new object[] { nameof(string.LastIndexOf) };
            }
        }
 
        public static IEnumerable<object[]> StartsWithEndsWithMethods
        {
            get
            {
                yield return new object[] { nameof(string.StartsWith) };
                yield return new object[] { nameof(string.EndsWith) };
            }
        }
 
        public static IEnumerable<object[]> IndexOfLastIndexOfMethods
        {
            get
            {
                yield return new object[] { nameof(string.IndexOf) };
                yield return new object[] { nameof(string.LastIndexOf) };
            }
        }
 
        [Theory]
        [MemberData(nameof(Methods))]
        public async Task CS_NotSingleChar(string method)
        {
            var testCode = $$"""
                public class TestClass
                {
                    public void TestMethod()
                    {
                        "test".{{method}}("abc");
                    }
                }
                """;
 
            await VerifyCSAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21);
        }
 
        [Theory]
        [MemberData(nameof(Methods))]
        public async Task CS_RegularStringLiteral(string method)
        {
            var testCode = $$"""
                public class TestClass
                {
                    public void TestMethod()
                    {
                        "test".{{method}}{|CA1866:("a")|};
                    }
                }
                """;
 
            await VerifyCSAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21);
        }
 
        [Theory]
        [MemberData(nameof(Methods))]
        public async Task CS_StringComparisonOrdinal(string method)
        {
            var testCode = $$"""
                using System;
 
                public class TestClass
                {
                    public void TestMethod()
                    {
                        "test".{{method}}{|CA1865:("a", StringComparison.Ordinal)|};
                    }
                }
                """;
 
            var fixedCode = $$"""
                using System;
 
                public class TestClass
                {
                    public void TestMethod()
                    {
                        "test".{{method}}('a');
                    }
                }
                """;
 
            await VerifyCSAsync(
                testCode, ReferenceAssemblies.NetStandard.NetStandard21, fixedCode,
                (d, _, v) =>
                {
                    v.EqualOrDiff(
                        $"Use 'string.{method}(char)' instead of 'string.{method}(string)' when you have a string with a single char",
                        d.GetMessage(CultureInfo.InvariantCulture));
                });
        }
 
        [Fact]
        [WorkItem(6930, "https://github.com/dotnet/roslyn-analyzers/issues/6930")]
        public async Task CS_PreservesTrivia()
        {
            var testCode = $$"""
                using System;
 
                public class TestClass
                {
                    public void TestMethod(string str)
                    {
                        if (str != "abc"
                                || 2 == str.IndexOf{|CA1865:(".", StringComparison.Ordinal)|}
                                || str == "test")
                        {
                	        return;
                        }
                    }
                }
                """;
 
            var fixedCode = $$"""
                using System;
 
                public class TestClass
                {
                    public void TestMethod(string str)
                    {
                        if (str != "abc"
                                || 2 == str.IndexOf('.')
                                || str == "test")
                        {
                	        return;
                        }
                    }
                }
                """;
 
            await VerifyCSAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(IndexOfLastIndexOfMethods))]
        public async Task CS_IndexOfLastIndexOf_PreservesStartsIndexAndCount(string method)
        {
            var testCode = $$"""
                using System;
 
                public class TestClass
                {
                    public void TestMethod()
                    {
                        "test".{{method}}{|CA1865:("a", 0, 1, StringComparison.Ordinal)|};
                    }
                }
                """;
 
            var fixedCode = $$"""
                using System;
 
                public class TestClass
                {
                    public void TestMethod()
                    {
                        "test".{{method}}('a', 0, 1);
                    }
                }
                """;
 
            await VerifyCSAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(Methods))]
        public async Task CS_NamedArguments(string method)
        {
            var testCode = $$"""
                using System;
 
                public class TestClass
                {
                    public void TestMethod()
                    {
                        "test".{{method}}{|CA1865:(comparisonType: StringComparison.Ordinal, value: "a")|};
                    }
                }
                """;
 
            var fixedCode = $$"""
                using System;
 
                public class TestClass
                {
                    public void TestMethod()
                    {
                        "test".{{method}}('a');
                    }
                }
                """;
 
            await VerifyCSAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(IndexOfLastIndexOfMethods))]
        public async Task CS_IndexOfLastIndexOf_PreservesStartsIndexAndCount_NamedArguments(string method)
        {
            var testCode = $$"""
                using System;
 
                public class TestClass
                {
                    public void TestMethod()
                    {
                        "test".{{method}}{|CA1865:("a", count: 1, startIndex: 0, comparisonType: StringComparison.Ordinal)|};
                    }
                }
                """;
 
            var fixedCode = $$"""
                using System;
 
                public class TestClass
                {
                    public void TestMethod()
                    {
                        "test".{{method}}('a', count: 1, startIndex: 0);
                    }
                }
                """;
 
            await VerifyCSAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(Methods))]
        public async Task CS_StringComparisonInvariantCultureAndAsciiChar(string method)
        {
            var testCode = $$"""
                using System;
 
                public class TestClass
                {
                    public void TestMethod()
                    {
                        "test".{{method}}{|CA1865:("a", StringComparison.InvariantCulture)|};
                    }
                }
                """;
 
            var fixedCode = $$"""
                using System;
 
                public class TestClass
                {
                    public void TestMethod()
                    {
                        "test".{{method}}('a');
                    }
                }
                """;
 
            await VerifyCSAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(Methods))]
        public async Task CS_StringComparisonInvariantCultureAndNonAsciiChar(string method)
        {
            var testCode = $$"""
                using System;
 
                public class TestClass
                {
                    public void TestMethod()
                    {
                        "test".{{method}}{|CA1867:("あ", StringComparison.InvariantCulture)|};
                    }
                }
                """;
 
            await VerifyCSAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21);
        }
 
        [Theory]
        [MemberData(nameof(StartsWithEndsWithMethods))]
        public async Task CS_CultureInfoInvariantCulture(string method)
        {
            var testCode = $$"""
                using System.Globalization;
 
                public class TestClass
                {
                    public void TestMethod()
                    {
                        "test".{{method}}{|CA1865:("a", false, CultureInfo.InvariantCulture)|};
                    }
                }
                """;
 
            var fixedCode = $$"""
                using System.Globalization;
 
                public class TestClass
                {
                    public void TestMethod()
                    {
                        "test".{{method}}('a');
                    }
                }
                """;
 
            await VerifyCSAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(StartsWithEndsWithMethods))]
        public async Task CS_CultureInfoAnythingElse(string method)
        {
            var testCode = $$"""
                using System.Globalization;
 
                public class TestClass
                {
                    public void TestMethod()
                    {
                        "test".{{method}}{|CA1867:("a", false, CultureInfo.CurrentCulture)|};
                    }
                }
                """;
 
            await VerifyCSAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21);
        }
 
        [Theory]
        [MemberData(nameof(Methods))]
        public async Task CS_StringComparisonAnythingElse(string method)
        {
            var testCode = $$"""
                using System;
 
                public class TestClass
                {
                    public void TestMethod()
                    {
                        "test".{{method}}{|CA1867:("a", StringComparison.CurrentCulture)|};
                    }
                }
                """;
 
            await VerifyCSAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21);
        }
 
        [Theory]
        [MemberData(nameof(Methods))]
        public async Task VB_NotSingleChar(string method)
        {
            var testCode = $$"""
                Public Class TestClass
                    Public Sub TestMethod()
                        Dim a = "test".{{method}}("abc")
                    End Sub
                End Class
                """;
 
            await VerifyVBAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21);
        }
 
        [Fact]
        [WorkItem(6930, "https://github.com/dotnet/roslyn-analyzers/issues/6930")]
        public async Task VB_PreservesTrivia()
        {
            var testCode = $$"""
                Imports System
 
                Public Class TestClass
                    Public Sub TestMethod(str As String)
                        If str <> "abc" _
                                Or 2 = str.IndexOf{|CA1865:(".", StringComparison.Ordinal)|} _
                                Or str = "test"
                            Return
                        End If
                    End Sub
                End Class
                """;
 
            var fixedCode = $$"""
                Imports System
 
                Public Class TestClass
                    Public Sub TestMethod(str As String)
                        If str <> "abc" _
                                Or 2 = str.IndexOf("."c) _
                                Or str = "test"
                            Return
                        End If
                    End Sub
                End Class
                """;
 
            await VerifyVBAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(Methods))]
        public async Task VB_RegularStringLiteral(string method)
        {
            var testCode = $$"""
                Public Class TestClass
                    Public Sub TestMethod()
                        Dim a = "test".{{method}}{|CA1866:("a")|}
                    End Sub
                End Class
                """;
 
            await VerifyVBAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21);
        }
 
        [Theory]
        [MemberData(nameof(Methods))]
        public async Task VB_StringComparisonOrdinal(string method)
        {
            var testCode = $$"""
                Imports System
 
                Public Class TestClass
                    Public Sub TestMethod()
                        Dim a = "test".{{method}}{|CA1865:("a", StringComparison.Ordinal)|}
                    End Sub
                End Class
                """;
 
            var fixedCode = $$"""
                Imports System
 
                Public Class TestClass
                    Public Sub TestMethod()
                        Dim a = "test".{{method}}("a"c)
                    End Sub
                End Class
                """;
 
            await VerifyVBAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(IndexOfLastIndexOfMethods))]
        public async Task VB_IndexOfLastIndexOf_PreservesStartsIndexAndCount(string method)
        {
            var testCode = $$"""
                Imports System
 
                Public Class TestClass
                    Public Sub TestMethod()
                        Dim a = "test".{{method}}{|CA1865:("a", 0, 1, StringComparison.Ordinal)|}
                    End Sub
                End Class
                """;
 
            var fixedCode = $$"""
                Imports System
 
                Public Class TestClass
                    Public Sub TestMethod()
                        Dim a = "test".{{method}}("a"c, 0, 1)
                    End Sub
                End Class
                """;
 
            await VerifyVBAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(Methods))]
        public async Task VB_StringComparisonInvariantCultureAndAsciiChar(string method)
        {
            var testCode = $$"""
                Imports System
 
                Public Class TestClass
                    Public Sub TestMethod()
                        Dim a = "test".{{method}}{|CA1865:("a", StringComparison.InvariantCulture)|}
                    End Sub
                End Class
                """;
 
            var fixedCode = $$"""
                Imports System
 
                Public Class TestClass
                    Public Sub TestMethod()
                        Dim a = "test".{{method}}{|CA1865:("a"c)|}
                    End Sub
                End Class
                """;
 
            await VerifyVBAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(Methods))]
        public async Task VB_StringComparisonInvariantCultureAndNonAsciiChar(string method)
        {
            var testCode = $$"""
                Imports System
 
                Public Class TestClass
                    Public Sub TestMethod()
                        Dim a = "test".{{method}}{|CA1867:("あ", StringComparison.InvariantCulture)|}
                    End Sub
                End Class
                """;
 
            await VerifyVBAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21);
        }
 
        [Theory]
        [MemberData(nameof(Methods))]
        public async Task VB_StringComparisonAnythingElse(string method)
        {
            var testCode = $$"""
                Imports System
 
                Public Class TestClass
                    Public Sub TestMethod()
                        Dim a = "test".{{method}}{|CA1867:("a", StringComparison.CurrentCulture)|}
                    End Sub
                End Class
                """;
 
            await VerifyVBAsync(testCode, ReferenceAssemblies.NetStandard.NetStandard21);
        }
 
        private static async Task VerifyCSAsync(
            string source,
            ReferenceAssemblies referenceAssemblies,
            string fixedSource = null,
            Action<Diagnostic, DiagnosticResult, IVerifier> diagnosticVerifier = null)
        {
            await new VerifyCS.Test()
            {
                TestCode = source,
                ReferenceAssemblies = referenceAssemblies,
                FixedCode = fixedSource,
                DiagnosticVerifier = diagnosticVerifier,
            }.RunAsync();
        }
 
        private static async Task VerifyVBAsync(
            string source,
            ReferenceAssemblies referenceAssemblies,
            string fixedSource = null,
            Action<Diagnostic, DiagnosticResult, IVerifier> diagnosticVerifier = null)
        {
            await new VerifyVB.Test()
            {
                TestCode = source,
                ReferenceAssemblies = referenceAssemblies,
                FixedCode = fixedSource,
                DiagnosticVerifier = diagnosticVerifier,
            }.RunAsync();
        }
    }
}