File: Microsoft.CodeQuality.Analyzers\ApiDesignGuidelines\EquatableAnalyzerTests.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;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Testing;
using Test.Utilities;
using Xunit;
using VerifyCS = Test.Utilities.CSharpCodeFixVerifier<
    Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines.EquatableAnalyzer,
    Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines.EquatableFixer>;
 
namespace Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines.UnitTests
{
    public class EquatableAnalyzerTests
    {
        [Fact]
        public async Task NoDiagnosticForStructWithNoEqualsOverrideAndNoIEquatableImplementationAsync()
        {
            var code = @"
struct S
{
}
";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Fact]
        public async Task NoDiagnosticForClassWithNoEqualsOverrideAndNoIEquatableImplementationAsync()
        {
            var code = @"
class C
{
}
";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Fact]
        public async Task DiagnosticForStructWithEqualsOverrideButNoIEquatableImplementationAsync()
        {
            var code = @"
struct S
{
    public override bool Equals(object other)
    {
        return true;
    }
}
";
            await VerifyCS.VerifyAnalyzerAsync(code,
                GetCSharpResultAt(2, 8, EquatableAnalyzer.ImplementIEquatableDescriptor, "S"));
        }
 
        [Fact]
        public async Task NoDiagnosticForClassWithEqualsOverrideAndNoIEquatableImplementationAsync()
        {
            var code = @"
class C
{
    public override bool Equals(object other)
    {
        return true;
    }
}
";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Fact]
        public async Task DiagnosticForStructWithIEquatableImplementationButNoEqualsOverrideAsync()
        {
            var code = @"
using System;
 
struct S : IEquatable<S>
{
    public bool Equals(S other)
    {
        return true;
    }
}
";
            await VerifyCS.VerifyAnalyzerAsync(code,
                GetCSharpResultAt(4, 8, EquatableAnalyzer.OverridesObjectEqualsDescriptor, "S"));
        }
 
        [Fact]
        public async Task DiagnosticForClassWithIEquatableImplementationButNoEqualsOverrideAsync()
        {
            var code = @"
using System;
 
class C : IEquatable<C>
{
    public bool Equals(C other)
    {
        return true;
    }
}
";
            await VerifyCS.VerifyAnalyzerAsync(code,
                GetCSharpResultAt(4, 7, EquatableAnalyzer.OverridesObjectEqualsDescriptor, "C"));
        }
 
        [Fact]
        public async Task NoDiagnosticForClassWithIEquatableImplementationWithNoParameterListAndNoEqualsOverrideAsync()
        {
            var code = @"
using System;
 
class C : {|CS0535:IEquatable<C>|}
{
    public bool {|CS0548:Equals|}
    {
        {|CS1014:return|} true{|CS1014:;|}
    }
}
";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Fact]
        public async Task NoDiagnosticForClassWithIEquatableImplementationWithMalformedParameterListAndNoEqualsOverrideAsync()
        {
            var code = @"
using System;
 
class C : {|CS0535:IEquatable<C>|}
{
    public bool Equals({|CS1026:|}
    {
        return true;
    }
}
";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Fact]
        public async Task NoDiagnosticForClassWithIEquatableImplementationWithMalformedParameterListAndNoEqualsOverride2Async()
        {
            var code = @"
using System;
 
class C : {|CS0535:IEquatable<C>|}
{
    public bool Equals{|CS1003:)|}
    {
        return true;
    }
{|CS1022:}|}
";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Fact]
        public async Task NoDiagnosticForClassWithIEquatableImplementationWithNoParametersAndNoEqualsOverrideAsync()
        {
            var code = @"
using System;
 
class C : {|CS0535:IEquatable<C>|}
{
    public bool Equals()
    {
        return true;
    }
}
";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Fact]
        public async Task NoDiagnosticForClassWithIEquatableImplementationWithMalformedParameterDeclarationAndNoEqualsOverrideAsync()
        {
            var code = @"
using System;
 
class C : {|CS0535:IEquatable<C>|}
{
    public bool Equals({|CS0246:x|}{|CS1001:)|}
    {
        return true;
    }
}
";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Fact]
        public async Task NoDiagnosticForClassWithIEquatableImplementationWithWrongReturnTypeAndNoEqualsOverrideAsync()
        {
            var code = @"
using System;
 
class C : {|CS0738:IEquatable<C>|}
{
    public int Equals(C x)
    {
        return 1;
    }
}
";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Fact]
        public async Task DiagnosticForClassWithIEquatableImplementationWithNoBodyAndNoEqualsOverrideAsync()
        {
            var code = @"
using System;
 
class C : IEquatable<C>
{
    public bool {|CS0501:Equals|}(C other){|CS1002:|}
}
";
            await VerifyCS.VerifyAnalyzerAsync(code,
                GetCSharpResultAt(4, 7, EquatableAnalyzer.OverridesObjectEqualsDescriptor, "C"));
        }
 
        [Fact]
        public async Task NoDiagnosticForClassWithIEquatableImplementationWithNoReturnTypeAndNoEqualsOverrideAsync()
        {
            var code = @"
using System;
 
class C : {|CS0535:IEquatable<C>|}
{
    public {|CS1520:Equals|}(C other)
    {
        {|CS0127:return|} true;
    }
}
";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Fact]
        public async Task NoDiagnosticForClassWithEqualsOverrideWithWrongSignatureAndNoIEquatableImplementationAsync()
        {
            var code = @"
using System;
 
class C
{
    public override bool {|CS0115:Equals|}(object other, int n)
    {
        return true;
    }
}
";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Fact]
        public async Task DiagnosticForClassWithExplicitIEquatableImplementationAndNoEqualsOverrideAsync()
        {
            var code = @"
using System;
 
class C : IEquatable<C>
{
    bool IEquatable<C>.Equals(C other)
    {
        return true;
    }
}
";
            await VerifyCS.VerifyAnalyzerAsync(code,
                GetCSharpResultAt(4, 7, EquatableAnalyzer.OverridesObjectEqualsDescriptor, "C"));
        }
 
        [Fact]
        public async Task DiagnosticForDerivedStructWithEqualsOverrideAndNoIEquatableImplementationAsync()
        {
            var code = @"
using System;
 
struct B
{
    public override bool Equals(object other)
    {
        return false;
    }
}
 
struct C : {|CS0527:B|}
{
    public override bool Equals(object other)
    {
        return true;
    }
}
";
            await VerifyCS.VerifyAnalyzerAsync(code,
                GetCSharpResultAt(4, 8, EquatableAnalyzer.ImplementIEquatableDescriptor, "B"),
                // Test0.cs(12,8): warning CA1066: Implement IEquatable when overriding Object.Equals
                GetCSharpResultAt(12, 8, EquatableAnalyzer.ImplementIEquatableDescriptor, "C"));
        }
 
        [Fact, WorkItem(1914, "https://github.com/dotnet/roslyn-analyzers/issues/1914")]
        public async Task NoDiagnosticForParentClassWithIEquatableImplementationAsync()
        {
            var code = @"
using System;
 
public interface IValueObject<T> : IEquatable<T> { }
 
public struct S : IValueObject<S>
{
    private readonly int value;
 
    public override bool Equals(object obj) => obj is S other && Equals(other);
 
    public bool Equals(S other) => value == other.value;
 
    public override int GetHashCode() => value;
}";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Fact, WorkItem(2027, "https://github.com/dotnet/roslyn-analyzers/issues/2027")]
        public async Task NoDiagnosticForDerivedTypesWithBaseTypeWithIEquatableImplementation_01Async()
        {
            var code = @"
using System;
 
public class A<T> : IEquatable<T>
    where T : A<T>
{
    public virtual bool Equals(T other) => false;
 
    public override bool Equals(object obj) => Equals(obj as T);
}
 
public class B : A<B>
{
}";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Fact, WorkItem(2027, "https://github.com/dotnet/roslyn-analyzers/issues/2027")]
        public async Task NoDiagnosticForDerivedTypesWithBaseTypeWithIEquatableImplementation_02Async()
        {
            var code = @"
using System;
 
public class A<T> : IEquatable<T>
    where T: class
{
    public virtual bool Equals(T other) => false;
 
    public override bool Equals(object obj) => Equals(obj as T);
}
 
public class B : A<B>
{
}
 
public class C<T> : A<T>
    where T : class
{
}
 
public class D : C<D>
{
}";
            await VerifyCS.VerifyAnalyzerAsync(code);
        }
 
        [Fact, WorkItem(2324, "https://github.com/dotnet/roslyn-analyzers/issues/2324")]
        public async Task CA1066_CSharp_RefStruct_NoDiagnosticAsync()
        {
            await new VerifyCS.Test
            {
                TestCode = @"
public ref struct S
{
    public override bool Equals(object other)
    {
        return false;
    }
}
",
                LanguageVersion = LanguageVersion.CSharp8
            }.RunAsync();
        }
 
        private static DiagnosticResult GetCSharpResultAt(int line, int column, DiagnosticDescriptor rule, string typeName)
#pragma warning disable RS0030 // Do not use banned APIs
            => VerifyCS.Diagnostic(rule)
                .WithLocation(line, column)
#pragma warning restore RS0030 // Do not use banned APIs
                .WithArguments(typeName);
    }
}