File: Microsoft.NetCore.Analyzers\Runtime\UseStringEqualsOverStringCompareTests.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.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Test.Utilities;
using Xunit;
 
using VerifyCS = Test.Utilities.CSharpCodeFixVerifier<
    Microsoft.NetCore.Analyzers.Runtime.UseStringEqualsOverStringCompare,
    Microsoft.NetCore.Analyzers.Runtime.UseStringEqualsOverStringCompareFixer>;
using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier<
    Microsoft.NetCore.Analyzers.Runtime.UseStringEqualsOverStringCompare,
    Microsoft.NetCore.Analyzers.Runtime.UseStringEqualsOverStringCompareFixer>;
 
namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests
{
    public class UseStringEqualsOverStringCompareTests
    {
        #region Test Data
        private static DiagnosticDescriptor Rule => UseStringEqualsOverStringCompare.Rule;
 
        private static readonly (string CompareCall, string EqualsCall)[] CS_ComparisonEqualityMethodCallPairs = new[]
        {
            ("string.Compare(x, y)", "string.Equals(x, y)"),
            ("string.Compare(x, y, false)", "string.Equals(x, y, StringComparison.CurrentCulture)"),
            ("string.Compare(x, y, true)", "string.Equals(x, y, StringComparison.CurrentCultureIgnoreCase)"),
            ("string.Compare(x, y, StringComparison.CurrentCulture)", "string.Equals(x, y, StringComparison.CurrentCulture)"),
            ("string.Compare(x, y, StringComparison.Ordinal)", "string.Equals(x, y, StringComparison.Ordinal)"),
            ("string.Compare(x, y, StringComparison.OrdinalIgnoreCase)", "string.Equals(x, y, StringComparison.OrdinalIgnoreCase)"),
            ("string.CompareOrdinal(x, y)", "string.Equals(x, y)"),
        };
 
        private static readonly (string CompareCall, string EqualsCall)[] VB_ComparisonEqualityMethodPairs = new[]
        {
            ("String.Compare(x, y)", "String.Equals(x, y)"),
            ("String.Compare(x, y, false)", "String.Equals(x, y, StringComparison.CurrentCulture)"),
            ("String.Compare(x, y, true)", "String.Equals(x, y, StringComparison.CurrentCultureIgnoreCase)"),
            ("String.Compare(x, y, StringComparison.CurrentCulture)", "String.Equals(x, y, StringComparison.CurrentCulture)"),
            ("String.Compare(x, y, StringComparison.Ordinal)", "String.Equals(x, y, StringComparison.Ordinal)"),
            ("String.Compare(x, y, StringComparison.OrdinalIgnoreCase)", "String.Equals(x, y, StringComparison.OrdinalIgnoreCase)"),
            ("String.CompareOrdinal(x, y)", "String.Equals(x, y)"),
        };
 
        public static IEnumerable<object[]> CS_ComparisonLeftOfLiteralTestData { get; } = CS_ComparisonEqualityMethodCallPairs
            .Select(pair => new object[] { $"{pair.CompareCall} == 0", pair.EqualsCall });
 
        public static IEnumerable<object[]> CS_EqualsComparisonLeftOfLiteralTestData { get; } = CS_ComparisonEqualityMethodCallPairs
            .Select(pair => new object[] { $"[|{pair.CompareCall}.Equals(0)|]", pair.EqualsCall });
 
        public static IEnumerable<object[]> VB_ComparisonLeftOfLiteralTestData { get; } = VB_ComparisonEqualityMethodPairs
            .Select(pair => new object[] { $"{pair.CompareCall} = 0", pair.EqualsCall });
 
        public static IEnumerable<object[]> CS_ComparisonRightOfLiteralTestData { get; } = CS_ComparisonEqualityMethodCallPairs
            .Select(pair => new object[] { $"0 == {pair.CompareCall}", pair.EqualsCall });
 
        public static IEnumerable<object[]> VB_ComparisonRightOfLiteralTestData { get; } = VB_ComparisonEqualityMethodPairs
            .Select(pair => new object[] { $"0 = {pair.CompareCall}", pair.EqualsCall });
 
        public static IEnumerable<object[]> CS_InvertedComparisonLeftOfLiteralTestData { get; } = CS_ComparisonEqualityMethodCallPairs
            .Select(pair => new object[] { $"{pair.CompareCall} != 0", $"!{pair.EqualsCall}" });
 
        public static IEnumerable<object[]> CS_InvertedEqualsComparisonLeftOfLiteralTestData { get; } = CS_ComparisonEqualityMethodCallPairs
            .Select(pair => new object[] { $"![|{pair.CompareCall}.Equals(0)|]", $"!{pair.EqualsCall}" });
 
        public static IEnumerable<object[]> VB_InvertedComparisonLeftOfLiteralTestData { get; } = VB_ComparisonEqualityMethodPairs
            .Select(pair => new object[] { $"{pair.CompareCall} <> 0", $"Not {pair.EqualsCall}" });
 
        public static IEnumerable<object[]> CS_InvertedComparisonRightOfLiteralTestData { get; } = CS_ComparisonEqualityMethodCallPairs
            .Select(pair => new object[] { $"0 != {pair.CompareCall}", $"!{pair.EqualsCall}" });
 
        public static IEnumerable<object[]> VB_InvertedComparisonRightOfLiteralTestData { get; } = VB_ComparisonEqualityMethodPairs
            .Select(pair => new object[] { $"0 <> {pair.CompareCall}", $"Not {pair.EqualsCall}" });
 
        public static IEnumerable<object[]> CS_StringCompareExpressionsTestData { get; } = CS_ComparisonEqualityMethodCallPairs
            .Select(pair => new object[] { pair.CompareCall });
 
        public static IEnumerable<object[]> VB_StringCompareExpressionsTestData { get; } = VB_ComparisonEqualityMethodPairs
            .Select(pair => new object[] { pair.CompareCall });
 
        public static IEnumerable<object[]> CS_IneligibleStringCompareOverloadTestData
        {
            get
            {
                yield return new[] { "string.Compare(x, y, true, System.Globalization.CultureInfo.InvariantCulture)" };
                yield return new[] { "string.Compare(x, y, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.CompareOptions.None)" };
                yield return new[] { "string.CompareOrdinal(x, indexA: 0, y, indexB: 0, length: 0)" };
            }
        }
 
        public static IEnumerable<object[]> VB_IneligibleStringCompareOverloadTestData
        {
            get
            {
                yield return new[] { "String.Compare(x, y, true, System.Globalization.CultureInfo.InvariantCulture)" };
                yield return new[] { "String.Compare(x, y, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.CompareOptions.None)" };
                yield return new[] { "String.CompareOrdinal(x, indexA:= 0, y, indexB:= 0, length:= 0)" };
            }
        }
 
        #endregion
 
        [Theory]
        [MemberData(nameof(CS_ComparisonLeftOfLiteralTestData))]
        [MemberData(nameof(CS_ComparisonRightOfLiteralTestData))]
        [MemberData(nameof(CS_InvertedComparisonLeftOfLiteralTestData))]
        [MemberData(nameof(CS_InvertedComparisonRightOfLiteralTestData))]
        public Task StringCompareResult_CompareToZero_Diagnostic_CSAsync(string testExpression, string fixedExpression)
        {
            string testCode = $@"
using System;
 
public class Testopolis
{{
    public bool Huh(string x, string y)
    {{
        return {{|#0:{testExpression}|}};
    }}
}}";
            string fixedCode = $@"
using System;
 
public class Testopolis
{{
    public bool Huh(string x, string y)
    {{
        return {fixedExpression};
    }}
}}";
 
            return VerifyCS.VerifyCodeFixAsync(testCode, VerifyCS.Diagnostic(Rule).WithLocation(0), fixedCode);
        }
 
        [Theory, WorkItem(6609, "https://github.com/dotnet/roslyn-analyzers/issues/6609")]
        [MemberData(nameof(CS_InvertedEqualsComparisonLeftOfLiteralTestData))]
        [MemberData(nameof(CS_EqualsComparisonLeftOfLiteralTestData))]
        public Task ComparisonWithEquals(string testExpression, string fixedExpression)
        {
            string testCode = $@"
using System;
 
public class Testopolis
{{
    public bool Huh(string x, string y)
    {{
        return {testExpression};
    }}
}}";
            string fixedCode = $@"
using System;
 
public class Testopolis
{{
    public bool Huh(string x, string y)
    {{
        return {fixedExpression};
    }}
}}";
 
            return VerifyCS.VerifyCodeFixAsync(testCode, fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(VB_ComparisonLeftOfLiteralTestData))]
        [MemberData(nameof(VB_ComparisonRightOfLiteralTestData))]
        [MemberData(nameof(VB_InvertedComparisonLeftOfLiteralTestData))]
        [MemberData(nameof(VB_InvertedComparisonRightOfLiteralTestData))]
        public Task StringCompareResult_CompareToZero_Diagnostic_VBAsync(string testExpression, string fixedExpression)
        {
            string testCode = $@"
Imports System
 
Public Class Testopolis
 
    Public Function Huh(x As String, y As String) As Boolean
        Return {{|#0:{testExpression}|}}
    End Function
End Class";
            string fixedCode = $@"
Imports System
 
Public Class Testopolis
 
    Public Function Huh(x As String, y As String) As Boolean
        Return {fixedExpression}
    End Function
End Class";
 
            return VerifyVB.VerifyCodeFixAsync(testCode, VerifyVB.Diagnostic(Rule).WithLocation(0), fixedCode);
        }
 
        [Theory]
        [MemberData(nameof(CS_StringCompareExpressionsTestData))]
        public Task StringCompareResult_CompareToNonLiteralZero_NoDiagnostic_CSAsync(string expression)
        {
            string code = $@"
using System;
 
public class Testopolis
{{
    private const int Zero = 0;
 
    public void Method(string x, string y)
    {{
        bool a = {expression} == Zero;
        bool b = {expression} != Zero;
        bool c = Zero == {expression};
        bool d = Zero != {expression};
    }}
}}";
 
            return VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Theory]
        [MemberData(nameof(VB_StringCompareExpressionsTestData))]
        public Task StringCompareResult_CompareToNonLiteralZero_NoDiagnostic_VBAsync(string expression)
        {
            var code = $@"
Imports System
 
Public Class Testopolis
    Private Const Zero As Integer = 0
 
    Public Sub Method(x As String, y As String)
        Dim a = {expression} = Zero
        Dim b = {expression} <> Zero
        Dim c = Zero = {expression}
        Dim d = Zero <> {expression}
    End Sub
End Class";
 
            return VerifyVB.VerifyAnalyzerAsync(code);
        }
 
        [Theory]
        [MemberData(nameof(CS_StringCompareExpressionsTestData))]
        public Task StringCompareResult_CompareToLiteralNonZero_NoDiagnostic_CSAsync(string expression)
        {
            string code = $@"
using System;
 
public class Testopolis
{{
    public void Method(string x, string y)
    {{
        bool a = {expression} == 1;
        bool b = {expression} != 1;
        bool c = 1 == {expression};
        bool d = 1 != {expression};
    }}
}}";
 
            return VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Theory]
        [MemberData(nameof(VB_StringCompareExpressionsTestData))]
        public Task StringCompareResult_CompareToLiteralNonZero_NoDiagnostic_VBAsync(string expression)
        {
            string code = $@"
Imports System
 
Public Class Testopolis
    Public Sub Method(x As String, y As String)
        Dim a = {expression} = 1
        Dim b = {expression} <> 1
        Dim c = 1 = {expression}
        Dim d = 1 <> {expression}
    End Sub
End Class";
 
            return VerifyVB.VerifyAnalyzerAsync(code);
        }
 
        [Theory]
        [MemberData(nameof(CS_IneligibleStringCompareOverloadTestData))]
        public Task IneligibleStringCompareOverload_NoDiagnostic_CSAsync(string expression)
        {
            string code = $@"
using System;
 
public class Testopolis
{{
    public void Method(string x, string y)
    {{
        bool a = {expression} == 0;
        bool b = {expression} != 0;
        bool c = 0 == {expression};
        bool d = 0 != {expression};
    }}
}}";
 
            return VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Theory]
        [MemberData(nameof(VB_IneligibleStringCompareOverloadTestData))]
        public Task IneligibleStringCompareOverload_NoDiagnostic_VBAsync(string expression)
        {
            string code = $@"
Imports System
 
Public Class Testopolis
    Public Sub Method(x As String, y As String)
        Dim a = {expression} = 0
        Dim b = {expression} <> 0
        Dim c = 0 = {expression}
        Dim d = 0 <> {expression}
    End Sub
End Class";
 
            return VerifyVB.VerifyAnalyzerAsync(code);
        }
    }
}