File: Rules\CannotRemoveBaseTypeOrInterfaceTests.cs
Web Access
Project: ..\..\..\test\Microsoft.DotNet.ApiCompatibility.Tests\Microsoft.DotNet.ApiCompatibility.Tests.csproj (Microsoft.DotNet.ApiCompatibility.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.CodeAnalysis;
using Microsoft.DotNet.ApiCompatibility.Tests;
using Microsoft.DotNet.ApiSymbolExtensions.Tests;
 
namespace Microsoft.DotNet.ApiCompatibility.Rules.Tests
{
    public class CannotRemoveBaseTypeOrInterfaceTests
    {
        private static readonly TestRuleFactory s_ruleFactory = new((settings, context) => new CannotRemoveBaseTypeOrInterface(settings, context));
 
        [Fact]
        public void PromotedBaseClassOrInterfaceIsNotReported()
        {
            string leftSyntax = @"
namespace CompatTests
{
  public class First : FirstBase, IFirstInterface { }
  public class Second : SecondBase { }
  public class SecondBase { }
  public class FirstBase { }
  public interface IFirstInterface { }
}
";
 
            string rightSyntax = @"
namespace CompatTests
{
  public class First : NewBase { }
  public class Second : NewSecondBase { }
  public class NewBase : FirstBase, INewInterface { }
  public class FirstBase { }
  public class SecondBase { }
  public class NewSecondBase : NewSecondBaseBase { }
  public class NewSecondBaseBase : SecondBase { }
  public interface IFirstInterface { }
  public interface INewInterface : IFirstInterface { }
}
";
 
            IAssemblySymbol left = SymbolFactory.GetAssemblyFromSyntax(leftSyntax);
            IAssemblySymbol right = SymbolFactory.GetAssemblyFromSyntax(rightSyntax);
 
            ApiComparer differ = new(s_ruleFactory);
 
            Assert.Empty(differ.GetDifferences(left, right));
        }
 
        [Fact]
        public void RemovedInterfaceAndBaseClassAreReported()
        {
            string leftSyntax = @"
namespace CompatTests
{
  public class First : FirstBase, IFirstInterface { }
  public class FirstBase { }
  public interface IFirstInterface { }
}
";
            string rightSyntax = @"
namespace CompatTests
{
  public class First : NewBase { }
  public class NewBase : INewInterface { }
  public class FirstBase { }
  public interface IFirstInterface { }
  public interface INewInterface { }
}
";
            IAssemblySymbol left = SymbolFactory.GetAssemblyFromSyntax(leftSyntax);
            IAssemblySymbol right = SymbolFactory.GetAssemblyFromSyntax(rightSyntax);
            ApiComparer differ = new(s_ruleFactory);
 
            IEnumerable<CompatDifference> differences = differ.GetDifferences(left, right);
 
            CompatDifference[] expected = new[]
            {
                CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveBaseType, string.Empty, DifferenceType.Changed, "T:CompatTests.First"),
                CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveBaseInterface, string.Empty, DifferenceType.Changed, "T:CompatTests.First"),
            };
            Assert.Equal(expected, differences);
 
            Assert.Contains("CompatTests.FirstBase", differences.ElementAt(0).Message);
            Assert.Contains("CompatTests.IFirstInterface", differences.ElementAt(1).Message);
        }
 
        [Theory]
        [InlineData(true)]
        [InlineData(false)]
        public void RemovedInternalInterfaceIsReportedWhenIncludeInternals(bool includeInternals)
        {
            string leftSyntax = @"
namespace CompatTests
{
  public class First : IFirstInternalInterface, IFirstInterface { }
  public interface IFirstInterface { }
  internal interface IFirstInternalInterface { }
}
";
            string rightSyntax = @"
namespace CompatTests
{
  public class First : IFirstInterface { }
  public interface IFirstInterface { }
  internal interface IFirstInternalInterface { }
}
";
            IAssemblySymbol left = SymbolFactory.GetAssemblyFromSyntax(leftSyntax);
            IAssemblySymbol right = SymbolFactory.GetAssemblyFromSyntax(rightSyntax);
            ApiComparer differ = new(s_ruleFactory, new ApiComparerSettings(includeInternalSymbols: includeInternals));
 
            IEnumerable<CompatDifference> differences = differ.GetDifferences(left, right);
 
            if (includeInternals)
            {
                CompatDifference[] expected = new[]
                {
                    CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveBaseInterface, string.Empty, DifferenceType.Changed, "T:CompatTests.First")
                };
                Assert.Equal(expected, differences);
            }
            else
            {
                Assert.Empty(differences);
            }
        }
 
        [Fact]
        public void RemovedFromLeftReportedOnStrictMode()
        {
            string leftSyntax = @"
namespace CompatTests
{
  public class First { }
  public class FirstBase { }
  public interface IFirstInterface { }
}
";
            string rightSyntax = @"
namespace CompatTests
{
  public class First : FirstBase, IFirstInterface { }
  public class FirstBase { }
  public interface IFirstInterface { }
}
";
            IAssemblySymbol left = SymbolFactory.GetAssemblyFromSyntax(leftSyntax);
            IAssemblySymbol right = SymbolFactory.GetAssemblyFromSyntax(rightSyntax);
            ApiComparer differ = new(s_ruleFactory, new ApiComparerSettings(strictMode: true));
 
            CompatDifference[] differences = differ.GetDifferences(left, right).ToArray();
 
            CompatDifference[] expected = new[]
            {
                CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveBaseType, string.Empty, DifferenceType.Changed, "T:CompatTests.First"),
                CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveBaseInterface, string.Empty, DifferenceType.Changed, "T:CompatTests.First"),
            };
            Assert.Equal(expected, differences);
 
            string firstMessage = differences[0].Message;
            string secondMessage = differences[1].Message;
 
            Assert.Contains("CompatTests.FirstBase", firstMessage);
            Assert.Contains("CompatTests.IFirstInterface", secondMessage);
 
            Assert.True(firstMessage.IndexOf("right") > firstMessage.IndexOf("left"));
            Assert.True(secondMessage.IndexOf("right") > firstMessage.IndexOf("left"));
        }
 
        [Theory]
        [InlineData(true)]
        [InlineData(false)]
        public void SameOnBothSidesDoesNotFail(bool strictMode)
        {
            string leftSyntax = @"
namespace CompatTests
{
  public class First : FirstBase, IFirstInterface { }
  public class FirstBase : SecondBase { }
  public class SecondBase : ThirdBase, ISecondInterface { }
  public class ThirdBase : IThirdInterface { }
  public interface IFirstInterface : IFourthInterface { }
  public interface ISecondInterface { }
  public interface IThirdInterface { }
  public interface IFourthInterface { }
}
";
            string rightSyntax = @"
namespace CompatTests
{
  public class First : FirstBase, IFirstInterface { }
  public class FirstBase : SecondBase { }
  public class SecondBase : ThirdBase, ISecondInterface { }
  public class ThirdBase : IThirdInterface { }
  public interface IFirstInterface : IFourthInterface { }
  public interface ISecondInterface { }
  public interface IThirdInterface { }
  public interface IFourthInterface { }
}
";
            IAssemblySymbol left = SymbolFactory.GetAssemblyFromSyntax(leftSyntax);
            IAssemblySymbol right = SymbolFactory.GetAssemblyFromSyntax(rightSyntax);
 
            ApiComparer differ = new(s_ruleFactory, new ApiComparerSettings(strictMode: strictMode));
 
            Assert.Empty(differ.GetDifferences(left, right));
        }
 
        [Fact]
        public void MultiRightReportsRightDifferences()
        {
            string leftSyntax = @"
namespace CompatTests
{
  public class First : FirstBase, IFirstInterface { }
  public class FirstBase : SecondBase { }
  public class SecondBase : ThirdBase, ISecondInterface { }
  public class ThirdBase { }
  public interface IFirstInterface { }
  internal interface ISecondInterface { }
}
";
            string[] rightSyntaxes = new[]
            {
                @"
namespace CompatTests
{
  public class First : FirstBase, IFirstInterface { }
  public class FirstBase : SecondBase { }
  public class SecondBase : ThirdBase, ISecondInterface { }
  public class ThirdBase { }
  public interface IFirstInterface { }
  internal interface ISecondInterface { }
}
",
                @"
namespace CompatTests
{
  public class First : FirstBase { }
  public class FirstBase : NewSecondBase { }
  public class NewSecondBase : SecondBase, IFirstInterface { }
  public class SecondBase : NewThirdBase, ISecondInterface { }
  public class NewThirdBase { }
  public class ThirdBase { }
  public interface IFirstInterface { }
  internal interface ISecondInterface { }
}
",
                @"
namespace CompatTests
{
  public class First : FirstBase { }
  public class FirstBase : NewSecondBase { }
  public class NewSecondBase : SecondBase { }
  public class SecondBase : NewThirdBase, ISecondInterface { }
  public class NewThirdBase : ThirdBase { }
  public class ThirdBase { }
  public interface IFirstInterface { }
  internal interface ISecondInterface { }
}
"
            };
            ElementContainer<IAssemblySymbol> leftContainer = new(SymbolFactory.GetAssemblyFromSyntax(leftSyntax),
                new MetadataInformation("left", @"ref\a.dll"));
            IReadOnlyList<ElementContainer<IAssemblySymbol>> right = SymbolFactoryExtensions.GetElementContainersFromSyntaxes(rightSyntaxes);
            ApiComparer differ = new(s_ruleFactory);
 
            IEnumerable<CompatDifference> differences = differ.GetDifferences(leftContainer, right);
 
            CompatDifference[] expectedDiffs =
            {
                new CompatDifference(leftContainer.MetadataInformation, right[1].MetadataInformation, DiagnosticIds.CannotRemoveBaseType, string.Empty, DifferenceType.Changed, "T:CompatTests.SecondBase"),
                new CompatDifference(leftContainer.MetadataInformation, right[2].MetadataInformation, DiagnosticIds.CannotRemoveBaseInterface, string.Empty, DifferenceType.Changed, "T:CompatTests.First"),
            };
 
            Assert.Equal(expectedDiffs, differences);
        }
 
        [Fact]
        public void MembersPushedDownToNewBaseNotReported()
        {
            string leftSyntax = @"
namespace CompatTests
{
  public class First : FirstBase { }
  public class Second : SecondBase
  {
    public string MyMethod() => string.Empty;
    public int MyProperty => 0;
  }
  public class SecondBase : ThirdBase
  {
    public void AnotherMethod() { }
    public string MethodWithArguments(string a) => MethodWithArguments(a, string.Empty);
    public string MethodWithArguments(string a, string b) => MethodWithArguments(a, string.Empty, string.Empty);
    public string MethodWithArguments(string a, string b, string c) => c;
  }
  public class FirstBase { }
  public class ThirdBase { }
}
";
            string rightSyntax = @"
namespace CompatTests
{
  public class First : NewBase { }
  public class Second : NewSecondBase
  {
    public int MyProperty => 0;
  }
  public class NewBase : FirstBase { }
  public class FirstBase { }
  public class NewSecondBase : SecondBase
  {
    public string MyMethod() => string.Empty;
  }
  public class SecondBase : NewThirdBase
  {
    public void AnotherMethod() { }
    public string MethodWithArguments(string a) => MethodWithArguments(a, string.Empty);
  }
  public class NewThirdBase : ThirdBase
  {
    public string MethodWithArguments(string a, string b) => MethodWithArguments(a, string.Empty, string.Empty);
    public string MethodWithArguments(string a, string b, string c) => c;
  }
  public class ThirdBase { }
}
";
            IAssemblySymbol left = SymbolFactory.GetAssemblyFromSyntax(leftSyntax);
            IAssemblySymbol right = SymbolFactory.GetAssemblyFromSyntax(rightSyntax);
            ApiComparer differ = new(s_ruleFactory);
 
            Assert.Empty(differ.GetDifferences(left, right));
        }
    }
}