From 6d5bf76c6a0656cf08a317f0f00cc760fbd93479 Mon Sep 17 00:00:00 2001 From: Louis Zanella Date: Fri, 29 Dec 2023 11:08:28 -0500 Subject: [PATCH 1/3] Fix for Assert.That(str1, Is.EqualTo(str2).IgnoreCase) and Not.EqualTo --- .../Helpers/ProjectBuilder.cs | 2 +- ...nit3ToFluentAssertionsAnalyzerUnitTests.cs | 29 +++++++++++++++++-- .../NunitAssertAnalyzerCodeFixProvider.cs | 22 +++++++++++--- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/Meziantou.FluentAssertionsAnalyzers.Tests/Helpers/ProjectBuilder.cs b/Meziantou.FluentAssertionsAnalyzers.Tests/Helpers/ProjectBuilder.cs index 13c06b5..28ee535 100644 --- a/Meziantou.FluentAssertionsAnalyzers.Tests/Helpers/ProjectBuilder.cs +++ b/Meziantou.FluentAssertionsAnalyzers.Tests/Helpers/ProjectBuilder.cs @@ -283,7 +283,7 @@ public ProjectBuilder ShouldReportDiagnosticWithMessage(string message) public ProjectBuilder ShouldFixCodeWith(string codeFix) { - return ShouldFixCodeWith(index: null, codeFix); + return ShouldFixCodeWith(index: null, codeFix ?? SourceCode); } public ProjectBuilder ShouldFixCodeWith(int? index, string codeFix) diff --git a/Meziantou.FluentAssertionsAnalyzers.Tests/NUnit3ToFluentAssertionsAnalyzerUnitTests.cs b/Meziantou.FluentAssertionsAnalyzers.Tests/NUnit3ToFluentAssertionsAnalyzerUnitTests.cs index 09e10cd..6e402a2 100644 --- a/Meziantou.FluentAssertionsAnalyzers.Tests/NUnit3ToFluentAssertionsAnalyzerUnitTests.cs +++ b/Meziantou.FluentAssertionsAnalyzers.Tests/NUnit3ToFluentAssertionsAnalyzerUnitTests.cs @@ -20,7 +20,7 @@ private static Task Assert(string sourceCode, string fix = null) { return CreateProjectBuilder() .WithSourceCode(sourceCode) - .ShouldFixCodeWith(fix ?? sourceCode) + .ShouldFixCodeWith(fix) .ValidateAsync(); } @@ -537,7 +537,6 @@ public void MyTest() [InlineData(@"Assert.Less(0d, 1, ""because"")", @"0d.Should().BeLessThan(1, ""because"")")] [InlineData(@"Assert.Less(0d, 1, ""because"", 2, 3)", @"0d.Should().BeLessThan(1, ""because"", 2, 3)")] - [InlineData(@"Assert.LessOrEqual(0, 1)", @"0.Should().BeLessThanOrEqualTo(1)")] [InlineData(@"Assert.LessOrEqual(0, 1, ""because"")", @"0.Should().BeLessThanOrEqualTo(1, ""because"")")] [InlineData(@"Assert.LessOrEqual(0, 1, ""because"", 2, 3)", @"0.Should().BeLessThanOrEqualTo(1, ""because"", 2, 3)")] @@ -778,7 +777,9 @@ public void MyTest() [InlineData(@"Assert.That(""aa"", Has.Length.EqualTo(2))", @"""aa"".Should().HaveLength(2)")] [InlineData(@"Assert.That("""", Is.EqualTo(""expected""))", @""""".Should().Be(""expected"")")] + [InlineData(@"Assert.That("""", Is.EqualTo(""expected"").IgnoreCase)", @""""".Should().BeEquivalentTo(""expected"")")] [InlineData(@"Assert.That("""", Is.Not.EqualTo(""expected""))", @""""".Should().NotBe(""expected"")")] + [InlineData(@"Assert.That("""", Is.Not.EqualTo(""expected"").IgnoreCase)", @""""".Should().NotBeEquivalentTo(""expected"")")] [InlineData(@"Assert.That(collection, Is.EqualTo(new int[0]))", @"collection.Should().Equal(new int[0])")] [InlineData(@"Assert.That(collection, Is.Not.EqualTo(new int[0]))", @"collection.Should().NotEqual(new int[0])")] [InlineData(@"Assert.That((IEnumerable)collection, Is.EqualTo(new int[0]))", @"((IEnumerable)collection).Should().Equal(new int[0])")] @@ -844,4 +845,28 @@ public void MyTest() } """); } + + [Theory] + [InlineData(@"Assert.That(collection, Is.EqualTo(otherCollection).IgnoreCase)")] + [InlineData(@"Assert.That(collection, Is.EquivalentTo(otherCollection).IgnoreCase)")] + [InlineData(@"Assert.That(collection, Is.Not.EqualTo(otherCollection).IgnoreCase)")] + [InlineData(@"Assert.That(collection, Is.Not.EquivalentTo(otherCollection).IgnoreCase)")] + + public Task AssertThatStringCollectionIsEqualToIgnoreCase_ExpectReportButNoFix(string code) + { + return Assert($$""" + using System.Collections.Generic; + using NUnit.Framework; + + class Test + { + public void MyTest() + { + var collection = new string[1]; + var otherCollection = new string[1]; + [|{{code}}|]; + } + } + """); + } } diff --git a/Meziantou.FluentAssertionsAnalyzers/NunitAssertAnalyzerCodeFixProvider.cs b/Meziantou.FluentAssertionsAnalyzers/NunitAssertAnalyzerCodeFixProvider.cs index 8e0e9c8..fc2b02d 100644 --- a/Meziantou.FluentAssertionsAnalyzers/NunitAssertAnalyzerCodeFixProvider.cs +++ b/Meziantou.FluentAssertionsAnalyzers/NunitAssertAnalyzerCodeFixProvider.cs @@ -186,9 +186,9 @@ private static async Task Rewrite(Document document, SyntaxNode nodeTo } else { - var useBeApproximately = leftType.SpecialType is SpecialType.System_Double or SpecialType.System_Single + var useBeApproximately = leftType.SpecialType is SpecialType.System_Double or SpecialType.System_Single && arguments.FirstOrDefault(x => x.NameColon?.Name.Identifier.ValueText is "delta") is not null; - + result = rewrite.UsingShould(right, useBeApproximately ? "BeApproximately" : "Be", ArgumentList(left, arguments.Skip(2))); } } @@ -500,6 +500,13 @@ private static async Task Rewrite(Document document, SyntaxNode nodeTo result = rewrite.UsingShould(arguments[0], replacementMethodName, ArgumentList(expected, arguments.Skip(2))); } } + else if (IsMethod(out expected, isSymbol, "EqualTo", "IgnoreCase")) + { + // Don't provide a fix if the assertion is applied to a string collection, as there is no straight conversion. + var isString = method.Parameters[0].Type.SpecialType == SpecialType.System_String; + if (isString) + result = rewrite.UsingShould(arguments[0], "BeEquivalentTo", ArgumentList(expected, arguments.Skip(2))); + } else if (IsMethod(out expected, isSymbol, "Not", "EqualTo")) { var (isCollection, isMigrationSupported) = IsCollection(arguments[0]); @@ -509,6 +516,13 @@ private static async Task Rewrite(Document document, SyntaxNode nodeTo result = rewrite.UsingShould(arguments[0], replacementMethodName, ArgumentList(expected, arguments.Skip(2))); } } + else if (IsMethod(out expected, isSymbol, "Not", "EqualTo", "IgnoreCase")) + { + // Don't provide a fix if the assertion is applied to a string collection, as there is no straight conversion. + var isString = method.Parameters[0].Type.SpecialType == SpecialType.System_String; + if (isString) + result = rewrite.UsingShould(arguments[0], "NotBeEquivalentTo", ArgumentList(expected, arguments.Skip(2))); + } else if (IsMethod(out expected, isSymbol, "SameAs")) { result = rewrite.UsingShould(arguments[0], "BeSameAs", ArgumentList(expected, arguments.Skip(2))); @@ -882,13 +896,13 @@ private static (ArgumentSyntax left, ArgumentSyntax right) GetLeftRight(Separate var right = arguments[1]; var leftValue = semanticModel.GetConstantValue(left.Expression, cancellationToken); var rightValue = semanticModel.GetConstantValue(right.Expression, cancellationToken); - + // Don't invert if both are constant if (leftValue.HasValue && rightValue.HasValue) { return (left, right); } - + // Invert if right is constant if (rightValue.HasValue) { From 23536646243e574cb9b69c97be96f499d09f963c Mon Sep 17 00:00:00 2001 From: Louis Zanella Date: Sat, 30 Dec 2023 09:50:36 -0500 Subject: [PATCH 2/3] review --- .../NUnit3ToFluentAssertionsAnalyzerUnitTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Meziantou.FluentAssertionsAnalyzers.Tests/NUnit3ToFluentAssertionsAnalyzerUnitTests.cs b/Meziantou.FluentAssertionsAnalyzers.Tests/NUnit3ToFluentAssertionsAnalyzerUnitTests.cs index 6e402a2..07222d1 100644 --- a/Meziantou.FluentAssertionsAnalyzers.Tests/NUnit3ToFluentAssertionsAnalyzerUnitTests.cs +++ b/Meziantou.FluentAssertionsAnalyzers.Tests/NUnit3ToFluentAssertionsAnalyzerUnitTests.cs @@ -851,7 +851,6 @@ public void MyTest() [InlineData(@"Assert.That(collection, Is.EquivalentTo(otherCollection).IgnoreCase)")] [InlineData(@"Assert.That(collection, Is.Not.EqualTo(otherCollection).IgnoreCase)")] [InlineData(@"Assert.That(collection, Is.Not.EquivalentTo(otherCollection).IgnoreCase)")] - public Task AssertThatStringCollectionIsEqualToIgnoreCase_ExpectReportButNoFix(string code) { return Assert($$""" From 4f1d8226947e3c97cc889eb7c654da5f2e8edbc9 Mon Sep 17 00:00:00 2001 From: Louis Zanella Date: Sat, 30 Dec 2023 17:02:05 -0500 Subject: [PATCH 3/3] review --- .../Helpers/ProjectBuilder.cs | 7 ++++- ...nit3ToFluentAssertionsAnalyzerUnitTests.cs | 29 ++++++++++++------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/Meziantou.FluentAssertionsAnalyzers.Tests/Helpers/ProjectBuilder.cs b/Meziantou.FluentAssertionsAnalyzers.Tests/Helpers/ProjectBuilder.cs index 28ee535..526d1ab 100644 --- a/Meziantou.FluentAssertionsAnalyzers.Tests/Helpers/ProjectBuilder.cs +++ b/Meziantou.FluentAssertionsAnalyzers.Tests/Helpers/ProjectBuilder.cs @@ -283,7 +283,12 @@ public ProjectBuilder ShouldReportDiagnosticWithMessage(string message) public ProjectBuilder ShouldFixCodeWith(string codeFix) { - return ShouldFixCodeWith(index: null, codeFix ?? SourceCode); + return ShouldFixCodeWith(index: null, codeFix); + } + + public ProjectBuilder ShouldFixCodeWithOriginalCode() + { + return ShouldFixCodeWith(index: null, SourceCode); } public ProjectBuilder ShouldFixCodeWith(int? index, string codeFix) diff --git a/Meziantou.FluentAssertionsAnalyzers.Tests/NUnit3ToFluentAssertionsAnalyzerUnitTests.cs b/Meziantou.FluentAssertionsAnalyzers.Tests/NUnit3ToFluentAssertionsAnalyzerUnitTests.cs index 07222d1..53e81ab 100644 --- a/Meziantou.FluentAssertionsAnalyzers.Tests/NUnit3ToFluentAssertionsAnalyzerUnitTests.cs +++ b/Meziantou.FluentAssertionsAnalyzers.Tests/NUnit3ToFluentAssertionsAnalyzerUnitTests.cs @@ -18,10 +18,19 @@ private static ProjectBuilder CreateProjectBuilder() private static Task Assert(string sourceCode, string fix = null) { - return CreateProjectBuilder() - .WithSourceCode(sourceCode) - .ShouldFixCodeWith(fix) - .ValidateAsync(); + var projectBuilder = CreateProjectBuilder() + .WithSourceCode(sourceCode); + + if (fix is null) + { + projectBuilder.ShouldFixCodeWithOriginalCode(); + } + else + { + projectBuilder.ShouldFixCodeWith(fix); + } + + return projectBuilder.ValidateAsync(); } [Fact] @@ -51,7 +60,7 @@ public void MyTest() } """); } - + [Fact] public Task Assert_Inconclusive_NoReport() { @@ -69,7 +78,7 @@ public void MyTest() } """); } - + [Fact] public Task Assert_Ignore_NoReport() { @@ -87,7 +96,7 @@ public void MyTest() } """); } - + [Fact] public Task Assert_Fail_ExcludedFromOptions() { @@ -107,7 +116,7 @@ public void MyTest() .AddAnalyzerConfiguration("mfa_excluded_methods", "M:NUnit.Framework.Assert.Fail") .ValidateAsync(); } - + [Fact] public Task Assert_Fail_String_ExcludedFromOptions() { @@ -127,7 +136,7 @@ public void MyTest() .AddAnalyzerConfiguration("mfa_excluded_methods", "M:NUnit.Framework.Assert.Fail(System.String)") .ValidateAsync(); } - + [Fact] public Task Assert_MultiMethods_ExcludedFromOptions() { @@ -317,7 +326,7 @@ public void MyTest() [InlineData(@"Assert.AreEqual(0d, (double?)null, delta: 2d)", @"((double?)null).Should().BeApproximately(0d, 2d)")] [InlineData(@"Assert.AreEqual(0d, (double?)null, delta: 2d, ""because"")", @"((double?)null).Should().BeApproximately(0d, 2d, ""because"")")] [InlineData(@"Assert.AreEqual(0d, (double?)null, delta: 2d, ""because"", 1, 2)", @"((double?)null).Should().BeApproximately(0d, 2d, ""because"", 1, 2)")] - + [InlineData(@"Assert.AreEqual(0f, (float?)null, delta: 0.1f)", @"((float?)null).Should().BeApproximately(0f, 0.1f)")] [InlineData(@"Assert.AreEqual(0f, 1f, delta: 0.1f)", @"1f.Should().BeApproximately(0f, 0.1f)")]