diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFixTests.cs index eea3bf9c..88dbb6d9 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFixTests.cs @@ -295,15 +295,58 @@ public void Test(object actual, object expected) } [Test] - public void CodeFixPreservesLineBreakBeforeMessage() + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapInTestMethod($@" + ↓ClassicAssert.AreEqual( + 2d, + 3d, + 0.0000001d{commaAndMessage});"); + + var fixedCode = TestUtility.WrapInTestMethod($@" + Assert.That( + 3d, + Is.EqualTo(2d).Within(0.0000001d){commaAndMessage});"); + + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapInTestMethod($@" + ↓ClassicAssert.AreEqual( + 2d, + 3d, + 0.0000001d{commaAndMessage} + );"); + + var fixedCode = TestUtility.WrapInTestMethod($@" + Assert.That( + 3d, + Is.EqualTo(2d).Within(0.0000001d){commaAndMessage} + );"); + + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) { - var code = TestUtility.WrapInTestMethod(@" - ClassicAssert.AreEqual(2d, 3d, 0.0000001d, - ""message"");"); + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapInTestMethod($@" + ↓ClassicAssert.AreEqual( + 2d, 3d, 0.0000001d{optionalNewline});"); - var fixedCode = TestUtility.WrapInTestMethod(@" - Assert.That(3d, Is.EqualTo(2d).Within(0.0000001d), - ""message"");"); + var fixedCode = TestUtility.WrapInTestMethod($@" + Assert.That( + 3d, Is.EqualTo(2d).Within(0.0000001d){optionalNewline});"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreNotEqualClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreNotEqualClassicModelAssertUsageCodeFixTests.cs index e49697fa..bfeb92a1 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreNotEqualClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreNotEqualClassicModelAssertUsageCodeFixTests.cs @@ -103,5 +103,60 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapInTestMethod($@" + ↓ClassicAssert.AreNotEqual( + 2d, + 3d{commaAndMessage});"); + + var fixedCode = TestUtility.WrapInTestMethod($@" + Assert.That( + 3d, + Is.Not.EqualTo(2d){commaAndMessage});"); + + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapInTestMethod($@" + ↓ClassicAssert.AreNotEqual( + 2d, + 3d{commaAndMessage} + );"); + + var fixedCode = TestUtility.WrapInTestMethod($@" + Assert.That( + 3d, + Is.Not.EqualTo(2d){commaAndMessage} + );"); + + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapInTestMethod($@" + ↓ClassicAssert.AreNotEqual( + 2d, 3d{optionalNewline});"); + + var fixedCode = TestUtility.WrapInTestMethod($@" + Assert.That( + 3d, Is.Not.EqualTo(2d){optionalNewline});"); + + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreNotSameClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreNotSameClassicModelAssertUsageCodeFixTests.cs index 25e26060..3055b58f 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreNotSameClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreNotSameClassicModelAssertUsageCodeFixTests.cs @@ -133,5 +133,92 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expected = new object(); + var actual = new object(); + + ↓ClassicAssert.AreNotSame( + expected, + actual{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expected = new object(); + var actual = new object(); + + Assert.That( + actual, + Is.Not.SameAs(expected){commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expected = new object(); + var actual = new object(); + + ↓ClassicAssert.AreNotSame( + expected, + actual{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expected = new object(); + var actual = new object(); + + Assert.That( + actual, + Is.Not.SameAs(expected){commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expected = new object(); + var actual = new object(); + + ↓ClassicAssert.AreNotSame( + expected, actual{optionalNewline}); + }}"); + + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expected = new object(); + var actual = new object(); + + Assert.That( + actual, Is.Not.SameAs(expected){optionalNewline}); + }}"); + + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreSameClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreSameClassicModelAssertUsageCodeFixTests.cs index ca0dc975..288c5816 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreSameClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/AreSameClassicModelAssertUsageCodeFixTests.cs @@ -133,5 +133,91 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expected = new object(); + var actual = new object(); + + ↓ClassicAssert.AreSame( + expected, + actual{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expected = new object(); + var actual = new object(); + + Assert.That( + actual, + Is.SameAs(expected){commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expected = new object(); + var actual = new object(); + + ↓ClassicAssert.AreSame( + expected, + actual{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expected = new object(); + var actual = new object(); + + Assert.That( + actual, + Is.SameAs(expected){commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expected = new object(); + var actual = new object(); + + ↓ClassicAssert.AreSame( + expected, actual{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expected = new object(); + var actual = new object(); + + Assert.That( + actual, Is.SameAs(expected){optionalNewline}); + }}"); + + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/ContainsClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/ContainsClassicModelAssertUsageCodeFixTests.cs index 2e137c37..1df5d73d 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/ContainsClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/ContainsClassicModelAssertUsageCodeFixTests.cs @@ -133,5 +133,91 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, instanceDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var instance = new object(); + var collection = Array.Empty(); + + ↓ClassicAssert.Contains( + instance, + collection{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var instance = new object(); + var collection = Array.Empty(); + + Assert.That( + collection, + Does.Contain(instance){commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, instanceDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var instance = new object(); + var collection = Array.Empty(); + + ↓ClassicAssert.Contains( + instance, + collection{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var instance = new object(); + var collection = Array.Empty(); + + Assert.That( + collection, + Does.Contain(instance){commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, instanceDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var instance = new object(); + var collection = Array.Empty(); + + ↓ClassicAssert.Contains( + instance, collection{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var instance = new object(); + var collection = Array.Empty(); + + Assert.That( + collection, Does.Contain(instance){optionalNewline}); + }}"); + + RoslynAssert.CodeFix(analyzer, fix, instanceDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/GreaterClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/GreaterClassicModelAssertUsageCodeFixTests.cs index 13c4cb2d..e1c09bbb 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/GreaterClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/GreaterClassicModelAssertUsageCodeFixTests.cs @@ -231,5 +231,75 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.Greater( + 2d, + 3d{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + 2d, + Is.GreaterThan(3d){commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.Greater( + 2d, + 3d{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + 2d, + Is.GreaterThan(3d){commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.Greater( + 2d, + 3d{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + 2d, + Is.GreaterThan(3d){optionalNewline}); + }}"); + + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/GreaterOrEqualClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/GreaterOrEqualClassicModelAssertUsageCodeFixTests.cs index b5823959..16bdff1d 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/GreaterOrEqualClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/GreaterOrEqualClassicModelAssertUsageCodeFixTests.cs @@ -231,5 +231,74 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.GreaterOrEqual( + 2d, + 3d{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + 2d, + Is.GreaterThanOrEqualTo(3d){commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.GreaterOrEqual( + 2d, + 3d{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + 2d, + Is.GreaterThanOrEqualTo(3d){commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.GreaterOrEqual( + 2d, + 3d{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + 2d, + Is.GreaterThanOrEqualTo(3d){optionalNewline}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsEmptyClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsEmptyClassicModelAssertUsageCodeFixTests.cs index 756b3b47..d3ff7601 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsEmptyClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsEmptyClassicModelAssertUsageCodeFixTests.cs @@ -159,5 +159,82 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var collection = Array.Empty(); + + ↓ClassicAssert.IsEmpty( + collection{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var collection = Array.Empty(); + + Assert.That( + collection, + Is.Empty{commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var collection = Array.Empty(); + + ↓ClassicAssert.IsEmpty( + collection{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var collection = Array.Empty(); + + Assert.That( + collection, + Is.Empty{commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var collection = Array.Empty(); + + ↓ClassicAssert.IsEmpty( + collection, ""message""{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var collection = Array.Empty(); + + Assert.That( + collection, Is.Empty, ""message""{optionalNewline}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsFalseAndFalseClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsFalseAndFalseClassicModelAssertUsageCodeFixTests.cs index 65a03a34..ca3c6858 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsFalseAndFalseClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsFalseAndFalseClassicModelAssertUsageCodeFixTests.cs @@ -1,9 +1,16 @@ +using System.Collections.Generic; +using System.Linq; using Gu.Roslyn.Asserts; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Analyzers.ClassicModelAssertUsage; using NUnit.Analyzers.Constants; using NUnit.Framework; +#if NUNIT4 +using NUnit.Framework.Legacy; +#else +using ClassicAssert = NUnit.Framework.Assert; +#endif namespace NUnit.Analyzers.Tests.ClassicModelAssertUsage { @@ -12,6 +19,12 @@ public sealed class IsFalseAndFalseClassicModelAssertUsageCodeFixTests { private static readonly DiagnosticAnalyzer analyzer = new ClassicModelAssertUsageAnalyzer(); private static readonly CodeFixProvider fix = new IsFalseAndFalseClassicModelAssertUsageCodeFix(); + private static readonly Dictionary diagnosticIdsToAssertions = new() + { + { AnalyzerIdentifiers.FalseUsage, nameof(ClassicAssert.False) }, + { AnalyzerIdentifiers.IsFalseUsage, nameof(ClassicAssert.IsFalse) }, + }; + private static readonly string[] diagnosticIds = diagnosticIdsToAssertions.Keys.ToArray(); [Test] public void VerifyGetFixableDiagnosticIds() @@ -155,5 +168,85 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen( + [ValueSource(nameof(diagnosticIds))] string diagnosticId, + [Values] bool hasMessage) + { + var assertion = diagnosticIdsToAssertions[diagnosticId]; + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.{assertion}( + false{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + false, + Is.False{commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen( + [ValueSource(nameof(diagnosticIds))] string diagnosticId, + [Values] bool hasMessage) + { + var assertion = diagnosticIdsToAssertions[diagnosticId]; + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.{assertion}( + false{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + false, + Is.False{commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine( + [ValueSource(nameof(diagnosticIds))] string diagnosticId, + [Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var assertion = diagnosticIdsToAssertions[diagnosticId]; + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.{assertion}( + false, ""message""{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + false, Is.False, ""message""{optionalNewline}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsInstanceOfClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsInstanceOfClassicModelAssertUsageCodeFixTests.cs index 9c26ada2..3e06e049 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsInstanceOfClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsInstanceOfClassicModelAssertUsageCodeFixTests.cs @@ -301,5 +301,140 @@ public void VerifyIsInstanceOfGenericFixWithMessageAndArrayParamsInNonstandardOr Assert.That(actual, Is.InstanceOf(), $""{""first""}, {""second""}"");"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = typeof(int); + var actual = 42; + + ↓ClassicAssert.IsInstanceOf( + expected, + actual, + ""message""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = typeof(int); + var actual = 42; + + Assert.That( + actual, + Is.InstanceOf(expected), + ""message""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = typeof(int); + var actual = 42; + + ↓ClassicAssert.IsInstanceOf( + expected, + actual, + ""message"" + ); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = typeof(int); + var actual = 42; + + Assert.That( + actual, + Is.InstanceOf(expected), + ""message"" + ); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixForGenericMaintainsReasonableTriviaWithEndOfLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var actual = 42; + + ↓ClassicAssert.IsInstanceOf( + actual{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var actual = 42; + + Assert.That( + actual, + Is.InstanceOf(){commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixForGenericMaintainsReasonableTriviaWithNewLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var actual = 42; + + ↓ClassicAssert.IsInstanceOf( + actual{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var actual = 42; + + Assert.That( + actual, + Is.InstanceOf(){commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var actual = 42; + + ↓ClassicAssert.IsInstanceOf( + actual, ""message""{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var actual = 42; + + Assert.That( + actual, Is.InstanceOf(), ""message""{optionalNewline}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNaNClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNaNClassicModelAssertUsageCodeFixTests.cs index d474e249..26c4b617 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNaNClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNaNClassicModelAssertUsageCodeFixTests.cs @@ -123,5 +123,82 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = double.NaN; + + ↓ClassicAssert.IsNaN( + expr{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = double.NaN; + + Assert.That( + expr, + Is.NaN{commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = double.NaN; + + ↓ClassicAssert.IsNaN( + expr{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = double.NaN; + + Assert.That( + expr, + Is.NaN{commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = double.NaN; + + ↓ClassicAssert.IsNaN( + expr, ""message""{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = double.NaN; + + Assert.That( + expr, Is.NaN, ""message""{optionalNewline}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotEmptyClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotEmptyClassicModelAssertUsageCodeFixTests.cs index b410f513..ceae65b6 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotEmptyClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotEmptyClassicModelAssertUsageCodeFixTests.cs @@ -195,5 +195,82 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var collection = Array.Empty(); + + ↓ClassicAssert.IsNotEmpty( + collection{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var collection = Array.Empty(); + + Assert.That( + collection, + Is.Not.Empty{commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var collection = Array.Empty(); + + ↓ClassicAssert.IsNotEmpty( + collection{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var collection = Array.Empty(); + + Assert.That( + collection, + Is.Not.Empty{commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var collection = Array.Empty(); + + ↓ClassicAssert.IsNotEmpty( + collection, ""message""{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var collection = Array.Empty(); + + Assert.That( + collection, Is.Not.Empty, ""message""{optionalNewline}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotInstanceOfClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotInstanceOfClassicModelAssertUsageCodeFixTests.cs index 259cd1bd..a53e6927 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotInstanceOfClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotInstanceOfClassicModelAssertUsageCodeFixTests.cs @@ -301,5 +301,113 @@ public void VerifyIsNotInstanceOfGenericFixWithMessageAndArrayParamsInNonstandar Assert.That(actual, Is.Not.InstanceOf(), $""{""first""}, {""second""}"");"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen() + { + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = typeof(int); + var actual = 42; + + ↓ClassicAssert.IsNotInstanceOf( + expected, + actual, + ""message""); + }"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@" + public void TestMethod() + { + var expected = typeof(int); + var actual = 42; + + Assert.That( + actual, + Is.Not.InstanceOf(expected), + ""message""); + }"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expected = typeof(int); + var actual = 42; + + ↓ClassicAssert.IsNotInstanceOf( + expected, + actual{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expected = typeof(int); + var actual = 42; + + Assert.That( + actual, + Is.Not.InstanceOf(expected){commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixForGenericMaintainsReasonableTriviaWithEndOfLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var actual = 42; + + ↓ClassicAssert.IsNotInstanceOf( + actual{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var actual = 42; + + Assert.That( + actual, + Is.Not.InstanceOf(){commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var actual = 42; + + ↓ClassicAssert.IsNotInstanceOf( + actual, ""message""{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var actual = 42; + + Assert.That( + actual, Is.Not.InstanceOf(), ""message""{optionalNewline}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotNullAndNotNullClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotNullAndNotNullClassicModelAssertUsageCodeFixTests.cs index f1c37fdc..4427fb9a 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotNullAndNotNullClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNotNullAndNotNullClassicModelAssertUsageCodeFixTests.cs @@ -1,9 +1,16 @@ +using System.Collections.Generic; +using System.Linq; using Gu.Roslyn.Asserts; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Analyzers.ClassicModelAssertUsage; using NUnit.Analyzers.Constants; using NUnit.Framework; +#if NUNIT4 +using NUnit.Framework.Legacy; +#else +using ClassicAssert = NUnit.Framework.Assert; +#endif namespace NUnit.Analyzers.Tests.ClassicModelAssertUsage { @@ -12,6 +19,12 @@ public sealed class IsNotNullAndNotNullClassicModelAssertUsageCodeFixTests { private static readonly DiagnosticAnalyzer analyzer = new ClassicModelAssertUsageAnalyzer(); private static readonly CodeFixProvider fix = new IsNotNullAndNotNullClassicModelAssertUsageCodeFix(); + private static readonly Dictionary diagnosticIdsToAssertions = new() + { + { AnalyzerIdentifiers.NotNullUsage, nameof(ClassicAssert.NotNull) }, + { AnalyzerIdentifiers.IsNotNullUsage, nameof(ClassicAssert.IsNotNull) }, + }; + private static readonly string[] diagnosticIds = diagnosticIdsToAssertions.Keys.ToArray(); [Test] public void VerifyGetFixableDiagnosticIds() @@ -126,5 +139,91 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen( + [ValueSource(nameof(diagnosticIds))] string diagnosticId, + [Values] bool hasMessage) + { + var assertion = diagnosticIdsToAssertions[diagnosticId]; + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + object? obj = null; + ↓ClassicAssert.{assertion}( + obj{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + object? obj = null; + Assert.That( + obj, + Is.Not.Null{commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen( + [ValueSource(nameof(diagnosticIds))] string diagnosticId, + [Values] bool hasMessage) + { + var assertion = diagnosticIdsToAssertions[diagnosticId]; + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + object? obj = null; + ↓ClassicAssert.{assertion}( + obj{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + object? obj = null; + Assert.That( + obj, + Is.Not.Null{commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine( + [ValueSource(nameof(diagnosticIds))] string diagnosticId, + [Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var assertion = diagnosticIdsToAssertions[diagnosticId]; + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + object? obj = null; + ↓ClassicAssert.{assertion}( + obj, ""message""{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + object? obj = null; + Assert.That( + obj, Is.Not.Null, ""message""{optionalNewline}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNullAndNullClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNullAndNullClassicModelAssertUsageCodeFixTests.cs index 6c517696..4797d42a 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNullAndNullClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsNullAndNullClassicModelAssertUsageCodeFixTests.cs @@ -1,9 +1,16 @@ +using System.Collections.Generic; +using System.Linq; using Gu.Roslyn.Asserts; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Analyzers.ClassicModelAssertUsage; using NUnit.Analyzers.Constants; using NUnit.Framework; +#if NUNIT4 +using NUnit.Framework.Legacy; +#else +using ClassicAssert = NUnit.Framework.Assert; +#endif namespace NUnit.Analyzers.Tests.ClassicModelAssertUsage { @@ -12,6 +19,12 @@ public sealed class IsNullAndNullClassicModelAssertUsageCodeFixTests { private static readonly DiagnosticAnalyzer analyzer = new ClassicModelAssertUsageAnalyzer(); private static readonly CodeFixProvider fix = new IsNullAndNullClassicModelAssertUsageCodeFix(); + private static readonly Dictionary diagnosticIdsToAssertions = new() + { + { AnalyzerIdentifiers.NullUsage, nameof(ClassicAssert.Null) }, + { AnalyzerIdentifiers.IsNullUsage, nameof(ClassicAssert.IsNull) }, + }; + private static readonly string[] diagnosticIds = diagnosticIdsToAssertions.Keys.ToArray(); [Test] public void VerifyGetFixableDiagnosticIds() @@ -126,5 +139,91 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen( + [ValueSource(nameof(diagnosticIds))] string diagnosticId, + [Values] bool hasMessage) + { + var assertion = diagnosticIdsToAssertions[diagnosticId]; + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + object? obj = null; + ↓ClassicAssert.{assertion}( + obj{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + object? obj = null; + Assert.That( + obj, + Is.Null{commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen( + [ValueSource(nameof(diagnosticIds))] string diagnosticId, + [Values] bool hasMessage) + { + var assertion = diagnosticIdsToAssertions[diagnosticId]; + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + object? obj = null; + ↓ClassicAssert.{assertion}( + obj{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + object? obj = null; + Assert.That( + obj, + Is.Null{commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine( + [ValueSource(nameof(diagnosticIds))] string diagnosticId, + [Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var assertion = diagnosticIdsToAssertions[diagnosticId]; + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + object? obj = null; + ↓ClassicAssert.{assertion}( + obj, ""message""{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + object? obj = null; + Assert.That( + obj, Is.Null, ""message""{optionalNewline}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCodeFixTests.cs index be20d208..7cb41e77 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCodeFixTests.cs @@ -1,9 +1,16 @@ +using System.Collections.Generic; +using System.Linq; using Gu.Roslyn.Asserts; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Analyzers.ClassicModelAssertUsage; using NUnit.Analyzers.Constants; using NUnit.Framework; +#if NUNIT4 +using NUnit.Framework.Legacy; +#else +using ClassicAssert = NUnit.Framework.Assert; +#endif namespace NUnit.Analyzers.Tests.ClassicModelAssertUsage { @@ -12,6 +19,12 @@ public sealed class IsTrueAndTrueClassicModelAssertUsageCodeFixTests { private static readonly DiagnosticAnalyzer analyzer = new ClassicModelAssertUsageAnalyzer(); private static readonly CodeFixProvider fix = new IsTrueAndTrueClassicModelAssertUsageCodeFix(); + private static readonly Dictionary diagnosticIdsToAssertions = new() + { + { AnalyzerIdentifiers.TrueUsage, nameof(ClassicAssert.True) }, + { AnalyzerIdentifiers.IsTrueUsage, nameof(ClassicAssert.IsTrue) }, + }; + private static readonly string[] diagnosticIds = diagnosticIdsToAssertions.Keys.ToArray(); [Test] public void VerifyGetFixableDiagnosticIds() @@ -194,5 +207,85 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen( + [ValueSource(nameof(diagnosticIds))] string diagnosticId, + [Values] bool hasMessage) + { + var assertion = diagnosticIdsToAssertions[diagnosticId]; + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.{assertion}( + true{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + true, + Is.True{commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen( + [ValueSource(nameof(diagnosticIds))] string diagnosticId, + [Values] bool hasMessage) + { + var assertion = diagnosticIdsToAssertions[diagnosticId]; + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.{assertion}( + true{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + true, + Is.True{commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine( + [ValueSource(nameof(diagnosticIds))] string diagnosticId, + [Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var assertion = diagnosticIdsToAssertions[diagnosticId]; + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.{assertion}( + true, ""message""{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + true, Is.True, ""message""{optionalNewline}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCondensedCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCondensedCodeFixTests.cs index 2f840b1d..5bd2d66e 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCondensedCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/IsTrueAndTrueClassicModelAssertUsageCondensedCodeFixTests.cs @@ -1,9 +1,16 @@ +using System.Collections.Generic; +using System.Linq; using Gu.Roslyn.Asserts; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using NUnit.Analyzers.ClassicModelAssertUsage; using NUnit.Analyzers.Constants; using NUnit.Framework; +#if NUNIT4 +using NUnit.Framework.Legacy; +#else +using ClassicAssert = NUnit.Framework.Assert; +#endif namespace NUnit.Analyzers.Tests.ClassicModelAssertUsage { @@ -12,6 +19,12 @@ public sealed class IsTrueAndTrueClassicModelAssertUsageCondensedCodeFixTests { private static readonly DiagnosticAnalyzer analyzer = new ClassicModelAssertUsageAnalyzer(); private static readonly CodeFixProvider fix = new IsTrueAndTrueClassicModelAssertUsageCondensedCodeFix(); + private static readonly Dictionary diagnosticIdsToAssertions = new() + { + { AnalyzerIdentifiers.TrueUsage, nameof(ClassicAssert.True) }, + { AnalyzerIdentifiers.IsTrueUsage, nameof(ClassicAssert.IsTrue) }, + }; + private static readonly string[] diagnosticIds = diagnosticIdsToAssertions.Keys.ToArray(); [Test] public void VerifyGetFixableDiagnosticIds() @@ -121,5 +134,85 @@ public void TestMethod() RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription + IsTrueAndTrueClassicModelAssertUsageCondensedCodeFix.Suffix); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen( + [ValueSource(nameof(diagnosticIds))] string diagnosticId, + [Values] bool hasMessage) + { + var assertion = diagnosticIdsToAssertions[diagnosticId]; + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.{assertion}( + true{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + true{commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, + fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription + IsTrueAndTrueClassicModelAssertUsageCondensedCodeFix.Suffix); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen( + [ValueSource(nameof(diagnosticIds))] string diagnosticId, + [Values] bool hasMessage) + { + var assertion = diagnosticIdsToAssertions[diagnosticId]; + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.{assertion}( + true{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + true{commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, + fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription + IsTrueAndTrueClassicModelAssertUsageCondensedCodeFix.Suffix); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine( + [ValueSource(nameof(diagnosticIds))] string diagnosticId, + [Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var assertion = diagnosticIdsToAssertions[diagnosticId]; + var expectedDiagnostic = ExpectedDiagnostic.Create(diagnosticId); + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.{assertion}( + true, ""message""{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + true, ""message""{optionalNewline}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription + IsTrueAndTrueClassicModelAssertUsageCondensedCodeFix.Suffix); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/LessClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/LessClassicModelAssertUsageCodeFixTests.cs index f1198094..10af177b 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/LessClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/LessClassicModelAssertUsageCodeFixTests.cs @@ -231,5 +231,74 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.Less( + 2d, + 3d{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + 2d, + Is.LessThan(3d){commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.Less( + 2d, + 3d{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + 2d, + Is.LessThan(3d){commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.Less( + 2d, + 3d{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + 2d, + Is.LessThan(3d){optionalNewline}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/LessOrEqualClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/LessOrEqualClassicModelAssertUsageCodeFixTests.cs index c54be504..8e8e6fc5 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/LessOrEqualClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/LessOrEqualClassicModelAssertUsageCodeFixTests.cs @@ -231,5 +231,74 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.LessOrEqual( + 2d, + 3d{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + 2d, + Is.LessThanOrEqualTo(3d){commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.LessOrEqual( + 2d, + 3d{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + 2d, + Is.LessThanOrEqualTo(3d){commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + ↓ClassicAssert.LessOrEqual( + 2d, + 3d{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + Assert.That( + 2d, + Is.LessThanOrEqualTo(3d){optionalNewline}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/NotZeroClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/NotZeroClassicModelAssertUsageCodeFixTests.cs index d05fbe00..73551c95 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/NotZeroClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/NotZeroClassicModelAssertUsageCodeFixTests.cs @@ -123,5 +123,83 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = default(int); + + ↓ClassicAssert.NotZero( + expr{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = default(int); + + Assert.That( + expr, + Is.Not.Zero{commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = default(int); + + ↓ClassicAssert.NotZero( + expr{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = default(int); + + Assert.That( + expr, + Is.Not.Zero{commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = default(int); + + ↓ClassicAssert.NotZero( + expr, ""message""{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = default(int); + + Assert.That( + expr, Is.Not.Zero, ""message""{optionalNewline}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/ClassicModelAssertUsage/ZeroClassicModelAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ClassicModelAssertUsage/ZeroClassicModelAssertUsageCodeFixTests.cs index 5b8fdbe2..a6e2f4a6 100644 --- a/src/nunit.analyzers.tests/ClassicModelAssertUsage/ZeroClassicModelAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ClassicModelAssertUsage/ZeroClassicModelAssertUsageCodeFixTests.cs @@ -123,5 +123,83 @@ public void TestMethod() }"); RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = default(int); + + ↓ClassicAssert.Zero( + expr{commaAndMessage}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = default(int); + + Assert.That( + expr, + Is.Zero{commaAndMessage}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = default(int); + + ↓ClassicAssert.Zero( + expr{commaAndMessage} + ); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = default(int); + + Assert.That( + expr, + Is.Zero{commaAndMessage} + ); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + + var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = default(int); + + ↓ClassicAssert.Zero( + expr, ""message""{optionalNewline}); + }}"); + var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + public void TestMethod() + {{ + var expr = default(int); + + Assert.That( + expr, Is.Zero, ""message""{optionalNewline}); + }}"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } } } diff --git a/src/nunit.analyzers.tests/CollectionAssertUsage/CollectionAssertUsageAnalyzerTests.cs b/src/nunit.analyzers.tests/CollectionAssertUsage/CollectionAssertUsageAnalyzerTests.cs index 96d13729..e6b45990 100644 --- a/src/nunit.analyzers.tests/CollectionAssertUsage/CollectionAssertUsageAnalyzerTests.cs +++ b/src/nunit.analyzers.tests/CollectionAssertUsage/CollectionAssertUsageAnalyzerTests.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using Gu.Roslyn.Asserts; using Microsoft.CodeAnalysis.Diagnostics; +using NUnit.Analyzers.ClassicModelAssertUsage; using NUnit.Analyzers.CollectionAssertUsage; using NUnit.Analyzers.Constants; using NUnit.Framework; diff --git a/src/nunit.analyzers.tests/CollectionAssertUsage/CollectionAssertUsageCodeFixTests.cs b/src/nunit.analyzers.tests/CollectionAssertUsage/CollectionAssertUsageCodeFixTests.cs index 665d555e..ca4e9f81 100644 --- a/src/nunit.analyzers.tests/CollectionAssertUsage/CollectionAssertUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/CollectionAssertUsage/CollectionAssertUsageCodeFixTests.cs @@ -2,6 +2,7 @@ using Gu.Roslyn.Asserts; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; +using NUnit.Analyzers.ClassicModelAssertUsage; using NUnit.Analyzers.CollectionAssertUsage; using NUnit.Analyzers.Constants; using NUnit.Framework; @@ -123,6 +124,130 @@ public void AnalyzeTwoCollectionWhenNoMessageArgumentsAreUsed(string method) RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); } + [Test] + public void CodeFixForOneCollectionParameterAssertMaintainsReasonableTriviaWithEndOfLineClosingParen( + [ValueSource(nameof(OneCollectionParameterAsserts))] string method, + [Values] bool hasMessage) + { + var commaAndMessage = hasMessage ? ",\r\n \"message\"" : string.Empty; + var code = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ 1, 2, 3 }}; + ↓CollectionAssert.{method}( + collection{commaAndMessage});"); + var fixedCode = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ 1, 2, 3 }}; + Assert.That( + collection, + {CollectionAssertUsageAnalyzer.OneCollectionParameterAsserts[method]}{commaAndMessage});"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); + } + + [Test] + public void CodeFixForOneCollectionParameterAssertMaintainsReasonableTriviaWithNewLineClosingParen( + [ValueSource(nameof(OneCollectionParameterAsserts))] string method, + [Values] bool hasMessage) + { + var commaAndMessage = hasMessage ? ",\r\n \"message\"" : string.Empty; + var code = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ 1, 2, 3 }}; + ↓CollectionAssert.{method}( + collection{commaAndMessage} + );"); + var fixedCode = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ 1, 2, 3 }}; + Assert.That( + collection, + {CollectionAssertUsageAnalyzer.OneCollectionParameterAsserts[method]}{commaAndMessage} + );"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); + } + + [Test] + public void CodeFixForTwoCollectionParameterAssertMaintainsReasonableTriviaWithEndOfLineClosingParen( + [ValueSource(nameof(TwoCollectionParameterAsserts))] string method, + [Values] bool hasMessage) + { + var commaAndMessage = hasMessage ? ",\r\n \"message\"" : string.Empty; + var code = TestUtility.WrapInTestMethod(@$" + var collection1 = new[] {{ 1, 2, 3 }}; + var collection2 = new[] {{ 2, 4, 6 }}; + ↓CollectionAssert.{method}( + collection1, + collection2{commaAndMessage});"); + var fixedCode = TestUtility.WrapInTestMethod(@$" + var collection1 = new[] {{ 1, 2, 3 }}; + var collection2 = new[] {{ 2, 4, 6 }}; + Assert.That( + {GetAdjustedTwoCollectionConstraint(method, insertNewline: true)}{commaAndMessage});"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); + } + + [Test] + public void CodeFixForTwoCollectionParameterAssertMaintainsReasonableTriviaWithNewLineClosingParen( + [ValueSource(nameof(TwoCollectionParameterAsserts))] string method, + [Values] bool hasMessage) + { + var commaAndMessage = hasMessage ? ",\r\n \"message\"" : string.Empty; + var code = TestUtility.WrapInTestMethod(@$" + var collection1 = new[] {{ 1, 2, 3 }}; + var collection2 = new[] {{ 2, 4, 6 }}; + ↓CollectionAssert.{method}( + collection1, + collection2{commaAndMessage} + );"); + var fixedCode = TestUtility.WrapInTestMethod(@$" + var collection1 = new[] {{ 1, 2, 3 }}; + var collection2 = new[] {{ 2, 4, 6 }}; + Assert.That( + {GetAdjustedTwoCollectionConstraint(method, insertNewline: true)}{commaAndMessage} + );"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); + } + + [Test] + public void CodeFixForCollectionAndItemParameterAssertMaintainsReasonableTriviaWithEndOfLineClosingParen( + [ValueSource(nameof(CollectionAndItemParameterAsserts))] string method, + [Values] bool hasMessage) + { + var commaAndMessage = hasMessage ? ",\r\n \"message\"" : string.Empty; + var code = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ typeof(byte), typeof(char) }}; + var expected = typeof(byte); + ↓CollectionAssert.{method}( + collection, + expected{commaAndMessage});"); + var fixedCode = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ typeof(byte), typeof(char) }}; + var expected = typeof(byte); + Assert.That( + collection, + {CollectionAssertUsageAnalyzer.CollectionAndItemParameterAsserts[method]}{commaAndMessage});"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); + } + + [Test] + public void CodeFixForCollectionAndItemParameterAssertMaintainsReasonableTriviaWithNewLineClosingParen( + [ValueSource(nameof(CollectionAndItemParameterAsserts))] string method, + [Values] bool hasMessage) + { + var commaAndMessage = hasMessage ? ",\r\n \"message\"" : string.Empty; + var code = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ typeof(byte), typeof(char) }}; + var expected = typeof(byte); + ↓CollectionAssert.{method}( + collection, + expected{commaAndMessage} + );"); + var fixedCode = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ typeof(byte), typeof(char) }}; + var expected = typeof(byte); + Assert.That( + collection, + {CollectionAssertUsageAnalyzer.CollectionAndItemParameterAsserts[method]}{commaAndMessage} + );"); + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); + } + [TestCaseSource(nameof(TwoCollectionParameterAsserts))] public void AnalyzeTwoCollectionWhenOnlyMessageArgumentIsUsed(string method) { @@ -325,7 +450,50 @@ public void AnalyzeCollectionAndItemWhenFormatAndParamsArgumentsAreUsedOutOfOrde RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); } - private static string GetAdjustedTwoCollectionConstraint(string method) + [Test] + public void CodeFixForTwoCollectionParameterAssertsMaintainsReasonableTriviaWithAllArgumentsOnSameLine( + [ValueSource(nameof(TwoCollectionParameterAsserts))] string method, + [Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapInTestMethod(@$" + var collection1 = new[] {{ 1, 2, 3 }}; + var collection2 = new[] {{ 2, 4, 6 }}; + ↓CollectionAssert.{method}( + collection1, + collection2{optionalNewline});"); + var fixedCode = TestUtility.WrapInTestMethod(@$" + var collection1 = new[] {{ 1, 2, 3 }}; + var collection2 = new[] {{ 2, 4, 6 }}; + Assert.That( + {GetAdjustedTwoCollectionConstraint(method, insertNewline: true)}{optionalNewline});"); + + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + [Test] + public void CodeFixForCollectionAndItemParameterAssertMaintainsReasonableTriviaWithAllArgumentsOnSameLine( + [ValueSource(nameof(CollectionAndItemParameterAsserts))] string method, + [Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ typeof(byte), typeof(char) }}; + var expected = typeof(byte); + ↓CollectionAssert.{method}( + collection, + expected{optionalNewline});"); + var fixedCode = TestUtility.WrapInTestMethod(@$" + var collection = new[] {{ typeof(byte), typeof(char) }}; + var expected = typeof(byte); + Assert.That( + collection, + {CollectionAssertUsageAnalyzer.CollectionAndItemParameterAsserts[method]}{optionalNewline});"); + + RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription); + } + + private static string GetAdjustedTwoCollectionConstraint(string method, bool insertNewline = false) { (string actualArgument, string constraintArgument) = CollectionAssertUsageCodeFix.CollectionAssertToOneUnswappedParameterConstraints.ContainsKey(method) @@ -334,7 +502,10 @@ private static string GetAdjustedTwoCollectionConstraint(string method) string constraint = CollectionAssertUsageAnalyzer.TwoCollectionParameterAsserts[method] .Replace("expected", constraintArgument); - return $"{actualArgument}, {constraint}"; + return insertNewline + ? $@"{actualArgument}, + {constraint}" + : $"{actualArgument}, {constraint}"; } } } diff --git a/src/nunit.analyzers.tests/ConstraintsUsage/ComparisonConstraintUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ConstraintsUsage/ComparisonConstraintUsageCodeFixTests.cs index 232872b0..3e39e3cc 100644 --- a/src/nunit.analyzers.tests/ConstraintsUsage/ComparisonConstraintUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ConstraintsUsage/ComparisonConstraintUsageCodeFixTests.cs @@ -1,4 +1,6 @@ +using System.Collections.Generic; using System.Globalization; +using System.Linq; using Gu.Roslyn.Asserts; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; @@ -12,13 +14,26 @@ public class ComparisonConstraintUsageCodeFixTests { private static readonly DiagnosticAnalyzer analyzer = new ComparisonConstraintUsageAnalyzer(); private static readonly CodeFixProvider fix = new ComparisonConstraintUsageCodeFix(); - - [TestCase(">=", "Is.GreaterThanOrEqualTo")] - [TestCase(">", "Is.GreaterThan")] - [TestCase("<=", "Is.LessThanOrEqualTo")] - [TestCase("<", "Is.LessThan")] - public void FixesComparisonOperator(string operatorToken, string constraint) + private static readonly Dictionary operatorTokensToConstraints = new() + { + { ">=", "Is.GreaterThanOrEqualTo" }, + { ">", "Is.GreaterThan" }, + { "<=", "Is.LessThanOrEqualTo" }, + { "<", "Is.LessThan" }, + }; + private static readonly string[] operatorTokens = operatorTokensToConstraints.Keys.ToArray(); + private static readonly Dictionary operatorTokensToConstraintsReversed = new() + { + { ">=", "Is.LessThan" }, + { ">", "Is.LessThanOrEqualTo" }, + { "<=", "Is.GreaterThan" }, + { "<", "Is.GreaterThanOrEqualTo" }, + }; + + [Test] + public void FixesComparisonOperator([ValueSource(nameof(operatorTokens))] string operatorToken) { + var constraint = operatorTokensToConstraints[operatorToken]; var code = TestUtility.WrapInTestMethod(@$" int actual = 5; Assert.That(↓actual {operatorToken} 9);"); @@ -33,12 +48,10 @@ public void FixesComparisonOperator(string operatorToken, string constraint) RoslynAssert.CodeFix(analyzer, fix, diagnostic, code, fixedCode); } - [TestCase(">=", "Is.GreaterThanOrEqualTo")] - [TestCase(">", "Is.GreaterThan")] - [TestCase("<=", "Is.LessThanOrEqualTo")] - [TestCase("<", "Is.LessThan")] - public void FixesComparisonOperatorWithIsTrue(string operatorToken, string constraint) + [Test] + public void FixesComparisonOperatorWithIsTrue([ValueSource(nameof(operatorTokens))] string operatorToken) { + var constraint = operatorTokensToConstraints[operatorToken]; var code = TestUtility.WrapInTestMethod(@$" int actual = 5; Assert.That(↓actual {operatorToken} 9, Is.True);"); @@ -53,12 +66,10 @@ public void FixesComparisonOperatorWithIsTrue(string operatorToken, string const RoslynAssert.CodeFix(analyzer, fix, diagnostic, code, fixedCode); } - [TestCase(">=", "Is.LessThan")] - [TestCase(">", "Is.LessThanOrEqualTo")] - [TestCase("<=", "Is.GreaterThan")] - [TestCase("<", "Is.GreaterThanOrEqualTo")] - public void FixesWhenComparisonOperatorUsedWithIsFalse(string operatorToken, string constraint) + [Test] + public void FixesWhenComparisonOperatorUsedWithIsFalse([ValueSource(nameof(operatorTokens))] string operatorToken) { + var constraint = operatorTokensToConstraintsReversed[operatorToken]; var code = TestUtility.WrapInTestMethod(@$" int actual = 5; Assert.That(↓actual {operatorToken} 9, Is.False);"); @@ -73,12 +84,10 @@ public void FixesWhenComparisonOperatorUsedWithIsFalse(string operatorToken, str RoslynAssert.CodeFix(analyzer, fix, diagnostic, code, fixedCode); } - [TestCase(">=", "Is.LessThan")] - [TestCase(">", "Is.LessThanOrEqualTo")] - [TestCase("<=", "Is.GreaterThan")] - [TestCase("<", "Is.GreaterThanOrEqualTo")] - public void FixesWhenComparisonOperatorUseConstantOnLeftHandSide(string operatorToken, string constraint) + [Test] + public void FixesWhenComparisonOperatorUseConstantOnLeftHandSide([ValueSource(nameof(operatorTokens))] string operatorToken) { + var constraint = operatorTokensToConstraintsReversed[operatorToken]; var code = TestUtility.WrapInTestMethod(@$" int actual = 5; Assert.That(↓9 {operatorToken} actual, Is.True);"); @@ -92,5 +101,73 @@ public void FixesWhenComparisonOperatorUseConstantOnLeftHandSide(string operator RoslynAssert.CodeFix(analyzer, fix, diagnostic, code, fixedCode); } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen([ValueSource(nameof(operatorTokens))] string operatorToken) + { + var constraint = operatorTokensToConstraintsReversed[operatorToken]; + var code = TestUtility.WrapInTestMethod(@$" + int actual = 5; + Assert.That( + ↓actual {operatorToken} 9, + Is.False);"); + + var fixedCode = TestUtility.WrapInTestMethod(@$" + int actual = 5; + Assert.That( + actual, + {constraint}(9));"); + + var diagnostic = ExpectedDiagnostic.Create(AnalyzerIdentifiers.ComparisonConstraintUsage, + string.Format(CultureInfo.InvariantCulture, ComparisonConstraintUsageConstants.Message, constraint)); + + RoslynAssert.CodeFix(analyzer, fix, diagnostic, code, fixedCode); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen([ValueSource(nameof(operatorTokens))] string operatorToken) + { + var constraint = operatorTokensToConstraints[operatorToken]; + var code = TestUtility.WrapInTestMethod(@$" + int actual = 5; + Assert.That( + ↓actual {operatorToken} 9 + );"); + + var fixedCode = TestUtility.WrapInTestMethod(@$" + int actual = 5; + Assert.That( + actual, + {constraint}(9) + );"); + + var diagnostic = ExpectedDiagnostic.Create(AnalyzerIdentifiers.ComparisonConstraintUsage, + string.Format(CultureInfo.InvariantCulture, ComparisonConstraintUsageConstants.Message, constraint)); + + RoslynAssert.CodeFix(analyzer, fix, diagnostic, code, fixedCode); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine( + [ValueSource(nameof(operatorTokens))] string operatorToken, + [Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var constraint = operatorTokensToConstraints[operatorToken]; + var code = TestUtility.WrapInTestMethod(@$" + int actual = 5; + Assert.That( + ↓actual {operatorToken} 9, ""message""{optionalNewline});"); + + var fixedCode = TestUtility.WrapInTestMethod(@$" + int actual = 5; + Assert.That( + actual, {constraint}(9), ""message""{optionalNewline});"); + + var diagnostic = ExpectedDiagnostic.Create(AnalyzerIdentifiers.ComparisonConstraintUsage, + string.Format(CultureInfo.InvariantCulture, ComparisonConstraintUsageConstants.Message, constraint)); + + RoslynAssert.CodeFix(analyzer, fix, diagnostic, code, fixedCode); + } } } diff --git a/src/nunit.analyzers.tests/ConstraintsUsage/EqualConstraintUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ConstraintsUsage/EqualConstraintUsageCodeFixTests.cs index 9b6fc0be..79c35cd5 100644 --- a/src/nunit.analyzers.tests/ConstraintsUsage/EqualConstraintUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ConstraintsUsage/EqualConstraintUsageCodeFixTests.cs @@ -308,19 +308,61 @@ public void FixesEqualsMethodWithAssertFalseWithMessage() } [Test] - public void CodeFixPreservesLineBreakBeforeMessage() + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen([Values] bool hasMessage) { - var code = TestUtility.WrapInTestMethod(@" + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapInTestMethod($@" var actual = ""abc""; + ClassicAssert.False( + actual.Equals(""bcd""){commaAndMessage});"); - ClassicAssert.False(actual.Equals(""bcd""), - ""Assertion message from new line"");"); + var fixedCode = TestUtility.WrapInTestMethod($@" + var actual = ""abc""; + Assert.That( + actual, + Is.Not.EqualTo(""bcd""){commaAndMessage});"); - var fixedCode = TestUtility.WrapInTestMethod(@" + RoslynAssert.CodeFix(analyzer, fix, equalConstraintDiagnostic, code, fixedCode); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen([Values] bool hasMessage) + { + var commaAndMessage = hasMessage + ? ",\r\n \"message\"" + : string.Empty; + var code = TestUtility.WrapInTestMethod($@" + var actual = ""abc""; + Assert.That( + actual.Equals(""abc""), + Is.True{commaAndMessage} + );"); + + var fixedCode = TestUtility.WrapInTestMethod($@" var actual = ""abc""; + Assert.That( + actual, + Is.EqualTo(""abc""){commaAndMessage} + );"); - Assert.That(actual, Is.Not.EqualTo(""bcd""), - ""Assertion message from new line"");"); + RoslynAssert.CodeFix(analyzer, fix, equalConstraintDiagnostic, code, fixedCode); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapInTestMethod($@" + var actual = ""abc""; + Assert.That( + actual.Equals(""abc""), Is.True{optionalNewline});"); + + var fixedCode = TestUtility.WrapInTestMethod($@" + var actual = ""abc""; + Assert.That( + actual, Is.EqualTo(""abc""){optionalNewline});"); RoslynAssert.CodeFix(analyzer, fix, equalConstraintDiagnostic, code, fixedCode); } diff --git a/src/nunit.analyzers.tests/ConstraintsUsage/SomeItemsConstraintUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ConstraintsUsage/SomeItemsConstraintUsageCodeFixTests.cs index 1c23876c..923f9b5d 100644 --- a/src/nunit.analyzers.tests/ConstraintsUsage/SomeItemsConstraintUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ConstraintsUsage/SomeItemsConstraintUsageCodeFixTests.cs @@ -24,11 +24,11 @@ public class SomeItemsConstraintUsageCodeFixTests public void AnalyzeWhenListContainsUsedAssertThat() { var testCode = TestUtility.WrapInTestMethod(@" - Assert.That(↓new List {1, 2, 3}.Contains(1));", + Assert.That(↓new List { 1, 2, 3 }.Contains(1));", additionalUsings: "using System.Collections.Generic;"); var fixedCode = TestUtility.WrapInTestMethod(@" - Assert.That(new List {1, 2, 3}, Does.Contain(1));", + Assert.That(new List { 1, 2, 3 }, Does.Contain(1));", additionalUsings: "using System.Collections.Generic;"); RoslynAssert.CodeFix(analyzer, fix, doesContainDiagnostic, testCode, fixedCode); @@ -38,11 +38,11 @@ public void AnalyzeWhenListContainsUsedAssertThat() public void AnalyzeWhenListContainsUsedAssertIsTrue() { var testCode = TestUtility.WrapInTestMethod(@" - ClassicAssert.IsTrue(↓new List {1, 2, 3}.Contains(1));", + ClassicAssert.IsTrue(↓new List { 1, 2, 3 }.Contains(1));", additionalUsings: "using System.Collections.Generic;"); var fixedCode = TestUtility.WrapInTestMethod(@" - Assert.That(new List {1, 2, 3}, Does.Contain(1));", + Assert.That(new List { 1, 2, 3 }, Does.Contain(1));", additionalUsings: "using System.Collections.Generic;"); RoslynAssert.CodeFix(analyzer, fix, doesContainDiagnostic, testCode, fixedCode); @@ -52,11 +52,11 @@ public void AnalyzeWhenListContainsUsedAssertIsTrue() public void AnalyzeWhenListContainsUsedAssertIsFalse() { var testCode = TestUtility.WrapInTestMethod(@" - ClassicAssert.IsFalse(↓new List {1, 2, 3}.Contains(1));", + ClassicAssert.IsFalse(↓new List { 1, 2, 3 }.Contains(1));", additionalUsings: "using System.Collections.Generic;"); var fixedCode = TestUtility.WrapInTestMethod(@" - Assert.That(new List {1, 2, 3}, Does.Not.Contain(1));", + Assert.That(new List { 1, 2, 3 }, Does.Not.Contain(1));", additionalUsings: "using System.Collections.Generic;"); RoslynAssert.CodeFix(analyzer, fix, doesNotContainDiagnostic, testCode, fixedCode); @@ -66,11 +66,11 @@ public void AnalyzeWhenListContainsUsedAssertIsFalse() public void AnalyzeWhenLinqContainsUsedAssertThat() { var testCode = TestUtility.WrapInTestMethod(@" - Assert.That(↓new[] {1, 2, 3}.Contains(1));", + Assert.That(↓new[] { 1, 2, 3 }.Contains(1));", additionalUsings: "using System.Linq;"); var fixedCode = TestUtility.WrapInTestMethod(@" - Assert.That(new[] {1, 2, 3}, Does.Contain(1));", + Assert.That(new[] { 1, 2, 3 }, Does.Contain(1));", additionalUsings: "using System.Linq;"); RoslynAssert.CodeFix(analyzer, fix, doesContainDiagnostic, testCode, fixedCode); @@ -80,11 +80,11 @@ public void AnalyzeWhenLinqContainsUsedAssertThat() public void AnalyzeWhenLinqContainsUsedAssertIsTrue() { var testCode = TestUtility.WrapInTestMethod(@" - ClassicAssert.IsTrue(↓new[] {1, 2, 3}.Contains(1));", + ClassicAssert.IsTrue(↓new[] { 1, 2, 3 }.Contains(1));", additionalUsings: "using System.Linq;"); var fixedCode = TestUtility.WrapInTestMethod(@" - Assert.That(new[] {1, 2, 3}, Does.Contain(1));", + Assert.That(new[] { 1, 2, 3 }, Does.Contain(1));", additionalUsings: "using System.Linq;"); RoslynAssert.CodeFix(analyzer, fix, doesContainDiagnostic, testCode, fixedCode); @@ -94,11 +94,64 @@ public void AnalyzeWhenLinqContainsUsedAssertIsTrue() public void AnalyzeWhenLinqContainsUsedAssertIsFalse() { var testCode = TestUtility.WrapInTestMethod(@" - ClassicAssert.IsFalse(↓new[] {1, 2, 3}.Contains(1));", + ClassicAssert.IsFalse(↓new[] { 1, 2, 3 }.Contains(1));", additionalUsings: "using System.Linq;"); var fixedCode = TestUtility.WrapInTestMethod(@" - Assert.That(new[] {1, 2, 3}, Does.Not.Contain(1));", + Assert.That(new[] { 1, 2, 3 }, Does.Not.Contain(1));", + additionalUsings: "using System.Linq;"); + + RoslynAssert.CodeFix(analyzer, fix, doesNotContainDiagnostic, testCode, fixedCode); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen() + { + var testCode = TestUtility.WrapInTestMethod(@" + ClassicAssert.IsFalse( + ↓new[] { 1, 2, 3 }.Contains(1));", + additionalUsings: "using System.Linq;"); + + var fixedCode = TestUtility.WrapInTestMethod(@" + Assert.That( + new[] { 1, 2, 3 }, + Does.Not.Contain(1));", + additionalUsings: "using System.Linq;"); + + RoslynAssert.CodeFix(analyzer, fix, doesNotContainDiagnostic, testCode, fixedCode); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen() + { + var testCode = TestUtility.WrapInTestMethod(@" + ClassicAssert.IsFalse( + ↓new[] { 1, 2, 3 }.Contains(1) + );", + additionalUsings: "using System.Linq;"); + + var fixedCode = TestUtility.WrapInTestMethod(@" + Assert.That( + new[] { 1, 2, 3 }, + Does.Not.Contain(1) + );", + additionalUsings: "using System.Linq;"); + + RoslynAssert.CodeFix(analyzer, fix, doesNotContainDiagnostic, testCode, fixedCode); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine([Values] bool newlineBeforeClosingParen) + { + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var testCode = TestUtility.WrapInTestMethod($@" + ClassicAssert.IsFalse( + ↓new[] {{ 1, 2, 3 }}.Contains(1), ""message""{optionalNewline});", + additionalUsings: "using System.Linq;"); + + var fixedCode = TestUtility.WrapInTestMethod($@" + Assert.That( + new[] {{ 1, 2, 3 }}, Does.Not.Contain(1), ""message""{optionalNewline});", additionalUsings: "using System.Linq;"); RoslynAssert.CodeFix(analyzer, fix, doesNotContainDiagnostic, testCode, fixedCode); diff --git a/src/nunit.analyzers.tests/ConstraintsUsage/StringConstraintUsageCodeFixTests.cs b/src/nunit.analyzers.tests/ConstraintsUsage/StringConstraintUsageCodeFixTests.cs index 785f19dd..fb05ee19 100644 --- a/src/nunit.analyzers.tests/ConstraintsUsage/StringConstraintUsageCodeFixTests.cs +++ b/src/nunit.analyzers.tests/ConstraintsUsage/StringConstraintUsageCodeFixTests.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; +using System.Linq; using Gu.Roslyn.Asserts; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; @@ -12,19 +14,22 @@ public class StringConstraintUsageCodeFixTests private static readonly DiagnosticAnalyzer analyzer = new StringConstraintUsageAnalyzer(); private static readonly CodeFixProvider fix = new StringConstraintUsageCodeFix(); - private static readonly object[] PositiveAssertData = new[] + private static readonly Dictionary PositiveAssertDictionary = new() { - new[] { nameof(string.Contains), AnalyzerIdentifiers.StringContainsConstraintUsage, "Does.Contain" }, - new[] { nameof(string.StartsWith), AnalyzerIdentifiers.StringStartsWithConstraintUsage, "Does.StartWith" }, - new[] { nameof(string.EndsWith), AnalyzerIdentifiers.StringEndsWithConstraintUsage, "Does.EndWith" } + { nameof(string.Contains), new[] { nameof(string.Contains), AnalyzerIdentifiers.StringContainsConstraintUsage, "Does.Contain" } }, + { nameof(string.StartsWith), new[] { nameof(string.StartsWith), AnalyzerIdentifiers.StringStartsWithConstraintUsage, "Does.StartWith" } }, + { nameof(string.EndsWith), new[] { nameof(string.EndsWith), AnalyzerIdentifiers.StringEndsWithConstraintUsage, "Does.EndWith" } }, }; + private static readonly object[] PositiveAssertData = PositiveAssertDictionary.Values.ToArray(); - private static readonly object[] NegativeAssertData = new[] + private static readonly Dictionary NegativeAssertDictionary = new() { - new[] { nameof(string.Contains), AnalyzerIdentifiers.StringContainsConstraintUsage, "Does.Not.Contain" }, - new[] { nameof(string.StartsWith), AnalyzerIdentifiers.StringStartsWithConstraintUsage, "Does.Not.StartWith" }, - new[] { nameof(string.EndsWith), AnalyzerIdentifiers.StringEndsWithConstraintUsage, "Does.Not.EndWith" } + { nameof(string.Contains), new[] { nameof(string.Contains), AnalyzerIdentifiers.StringContainsConstraintUsage, "Does.Not.Contain" } }, + { nameof(string.StartsWith), new[] { nameof(string.StartsWith), AnalyzerIdentifiers.StringStartsWithConstraintUsage, "Does.Not.StartWith" } }, + { nameof(string.EndsWith), new[] { nameof(string.EndsWith), AnalyzerIdentifiers.StringEndsWithConstraintUsage, "Does.Not.EndWith" } }, }; + private static readonly object[] NegativeAssertData = NegativeAssertDictionary.Values.ToArray(); + private static readonly string[] StringConstraints = PositiveAssertDictionary.Keys.ToArray(); [TestCaseSource(nameof(PositiveAssertData))] public void AnalyzeStringBooleanMethodAssertTrue(string method, string analyzerId, string suggestedConstraint) @@ -121,5 +126,57 @@ public void AnalyzeStringBooleanMethodAssertThatIsFalse(string method, string an RoslynAssert.CodeFix(analyzer, fix, ExpectedDiagnostic.Create(analyzerId), code, fixedCode); } + + [TestCaseSource(nameof(NegativeAssertData))] + public void CodeFixMaintainsReasonableTriviaWithEndOfLineClosingParen(string method, string analyzerId, string suggestedConstraint) + { + var code = TestUtility.WrapInTestMethod($@" + ClassicAssert.IsFalse( + ↓""abc"".{method}(""ab""));"); + + var fixedCode = TestUtility.WrapInTestMethod($@" + Assert.That( + ""abc"", + {suggestedConstraint}(""ab""));"); + + RoslynAssert.CodeFix(analyzer, fix, ExpectedDiagnostic.Create(analyzerId), code, fixedCode); + } + + [TestCaseSource(nameof(NegativeAssertData))] + public void CodeFixMaintainsReasonableTriviaWithNewLineClosingParen(string method, string analyzerId, string suggestedConstraint) + { + var code = TestUtility.WrapInTestMethod($@" + Assert.That( + ↓""abc"".{method}(""ab""), + Is.False + );"); + + var fixedCode = TestUtility.WrapInTestMethod($@" + Assert.That( + ""abc"", + {suggestedConstraint}(""ab"") + );"); + + RoslynAssert.CodeFix(analyzer, fix, ExpectedDiagnostic.Create(analyzerId), code, fixedCode); + } + + [Test] + public void CodeFixMaintainsReasonableTriviaWithAllArgumentsOnSameLine( + [ValueSource(nameof(StringConstraints))] string method, + [Values] bool newlineBeforeClosingParen) + { + var analyzerId = NegativeAssertDictionary[method][1]; + var suggestedConstraint = NegativeAssertDictionary[method][2]; + var optionalNewline = newlineBeforeClosingParen ? "\r\n " : string.Empty; + var code = TestUtility.WrapInTestMethod($@" + Assert.That( + ↓""abc"".{method}(""ab""), Is.False{optionalNewline});"); + + var fixedCode = TestUtility.WrapInTestMethod($@" + Assert.That( + ""abc"", {suggestedConstraint}(""ab""){optionalNewline});"); + + RoslynAssert.CodeFix(analyzer, fix, ExpectedDiagnostic.Create(analyzerId), code, fixedCode); + } } } diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFix.cs index fc46671b..c52e099f 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/AreEqualClassicModelAssertUsageCodeFix.cs @@ -27,7 +27,7 @@ protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArg SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsEqualTo))) .WithArgumentList(SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList(expectedArgument))); + SyntaxFactory.SingletonSeparatedList(expectedArgument.WithoutTrivia()))); // The tolerance argument has to be added to the "Is.EqualTo(expected)" as ".Within(tolerance)" if (argumentNamesToArguments.TryGetValue(NUnitFrameworkConstants.NameOfDeltaParameter, out var toleranceArgument)) @@ -42,7 +42,7 @@ protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArg equalToInvocationNode, SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfEqualConstraintWithin))) .WithArgumentList(SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList(toleranceArgumentNoColon))); + SyntaxFactory.SingletonSeparatedList(toleranceArgumentNoColon.WithoutTrivia()))); } var actualArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfActualParameter].WithNameColon(null); diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/ClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/ClassicModelAssertUsageCodeFix.cs index 58b12827..0d0e1a25 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/ClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/ClassicModelAssertUsageCodeFix.cs @@ -77,6 +77,21 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) if (CodeFixHelper.GetInterpolatedMessageArgumentOrDefault(messageArgument, args, unconditional: false, argsIsArray) is ArgumentSyntax interpolatedMessageArgument) newArguments.Add(interpolatedMessageArgument); + // Fix trailing trivia for new argument based on the old arguments + var oldArguments = invocationNode.ArgumentList.Arguments.ToList(); + newArguments[0] = newArguments[0].WithTriviaFrom(oldArguments[0]) + .WithoutTrailingTrivia(); + + if (oldArguments.Count > 1 && newArguments.Count > 1) + { + newArguments[1] = newArguments[1].WithTriviaFrom(oldArguments[1]) + .WithoutTrailingTrivia(); + } + + var lastIndex = newArguments.Count - 1; + newArguments[lastIndex] = newArguments[lastIndex] + .WithTriviaFrom(oldArguments.Last()); + var newArgumentsList = invocationNode.ArgumentList.WithArguments(newArguments); newInvocationNode = newInvocationNode.WithArgumentList(newArgumentsList); diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/ContainsClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/ContainsClassicModelAssertUsageCodeFix.cs index 94876d81..760d9313 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/ContainsClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/ContainsClassicModelAssertUsageCodeFix.cs @@ -28,7 +28,7 @@ protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArg SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfDoes), SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfDoesContain))) .WithArgumentList(SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList(expectedArgument)))); + SyntaxFactory.SingletonSeparatedList(expectedArgument.WithoutTrivia())))); var actualArgument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfActualParameter].WithNameColon(null); return (actualArgument, constraintArgument); diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/GreaterClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/GreaterClassicModelAssertUsageCodeFix.cs index 2045d853..46857334 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/GreaterClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/GreaterClassicModelAssertUsageCodeFix.cs @@ -28,7 +28,7 @@ protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArg SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsGreaterThan))) .WithArgumentList(SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList(arg2Argument)))); + SyntaxFactory.SingletonSeparatedList(arg2Argument.WithoutTrivia())))); var arg1Argument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfArg1Parameter].WithNameColon(null); return (arg1Argument, constraintArgument); diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/GreaterOrEqualClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/GreaterOrEqualClassicModelAssertUsageCodeFix.cs index 0fab1795..c743bf12 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/GreaterOrEqualClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/GreaterOrEqualClassicModelAssertUsageCodeFix.cs @@ -28,7 +28,7 @@ protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArg SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsGreaterThanOrEqualTo))) .WithArgumentList(SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList(arg2Argument)))); + SyntaxFactory.SingletonSeparatedList(arg2Argument.WithoutTrivia())))); var arg1Argument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfArg1Parameter].WithNameColon(null); return (arg1Argument, constraintArgument); diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/LessClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/LessClassicModelAssertUsageCodeFix.cs index e0cb12ca..1d60a39b 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/LessClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/LessClassicModelAssertUsageCodeFix.cs @@ -28,7 +28,7 @@ protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArg SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsLessThan))) .WithArgumentList(SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList(arg2Argument)))); + SyntaxFactory.SingletonSeparatedList(arg2Argument.WithoutTrivia())))); var arg1Argument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfArg1Parameter].WithNameColon(null); return (arg1Argument, constraintArgument); diff --git a/src/nunit.analyzers/ClassicModelAssertUsage/LessOrEqualClassicModelAssertUsageCodeFix.cs b/src/nunit.analyzers/ClassicModelAssertUsage/LessOrEqualClassicModelAssertUsageCodeFix.cs index 4c69a932..19959efb 100644 --- a/src/nunit.analyzers/ClassicModelAssertUsage/LessOrEqualClassicModelAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/ClassicModelAssertUsage/LessOrEqualClassicModelAssertUsageCodeFix.cs @@ -28,7 +28,7 @@ protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArg SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIs), SyntaxFactory.IdentifierName(NUnitFrameworkConstants.NameOfIsLessThanOrEqualTo))) .WithArgumentList(SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList(arg2Argument)))); + SyntaxFactory.SingletonSeparatedList(arg2Argument.WithoutTrivia())))); var arg1Argument = argumentNamesToArguments[NUnitFrameworkConstants.NameOfArg1Parameter].WithNameColon(null); return (arg1Argument, constraintArgument); diff --git a/src/nunit.analyzers/CollectionAssertUsage/CollectionAssertUsageCodeFix.cs b/src/nunit.analyzers/CollectionAssertUsage/CollectionAssertUsageCodeFix.cs index 043bc667..f179ce2a 100644 --- a/src/nunit.analyzers/CollectionAssertUsage/CollectionAssertUsageCodeFix.cs +++ b/src/nunit.analyzers/CollectionAssertUsage/CollectionAssertUsageCodeFix.cs @@ -93,11 +93,12 @@ protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArg Diagnostic diagnostic, IReadOnlyDictionary argumentNamesToArguments) { - var (actualArgument, constraintArgument) = GetActualAndConstraintArguments(diagnostic, argumentNamesToArguments); + var (actualArgument, originalConstraintArgument) = GetActualAndConstraintArguments(diagnostic, argumentNamesToArguments); + var newConstraintArgument = originalConstraintArgument; if (argumentNamesToArguments.TryGetValue(NameOfComparerParameter, out ArgumentSyntax? comparerArgument)) { - ExpressionSyntax expression = constraintArgument.Expression; + ExpressionSyntax expression = originalConstraintArgument.Expression; // We need to drop the 'AsCollection' when using an IComparer. if (expression is MemberAccessExpressionSyntax memberAccessExpression && @@ -106,7 +107,7 @@ protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArg expression = memberAccessExpression.Expression; } - constraintArgument = Argument( + newConstraintArgument = Argument( InvocationExpression( MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, @@ -115,7 +116,7 @@ protected override (ArgumentSyntax ActualArgument, ArgumentSyntax? ConstraintArg ArgumentList(SingletonSeparatedList(comparerArgument)))); } - return (actualArgument, constraintArgument); + return (actualArgument, newConstraintArgument); } private static (ArgumentSyntax actualArgument, ArgumentSyntax constraintArgument) GetActualAndConstraintArguments( @@ -142,7 +143,7 @@ private static (ArgumentSyntax actualArgument, ArgumentSyntax constraintArgument { var secondParameterName = CollectionAssertToSecondParameterName[methodName]; var secondArgument = argumentNamesToArguments[secondParameterName].WithNameColon(null); - var constraintArgument = Argument(constraints.CreateConstraint(secondArgument)); + var constraintArgument = Argument(constraints.CreateConstraint(secondArgument.WithoutTrivia())); return (firstArgument, constraintArgument); } diff --git a/src/nunit.analyzers/ConstraintUsage/BaseConditionConstraintCodeFix.cs b/src/nunit.analyzers/ConstraintUsage/BaseConditionConstraintCodeFix.cs index 406a50ab..c6622747 100644 --- a/src/nunit.analyzers/ConstraintUsage/BaseConditionConstraintCodeFix.cs +++ b/src/nunit.analyzers/ConstraintUsage/BaseConditionConstraintCodeFix.cs @@ -73,7 +73,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) if (assertMethod is null) return; - var newAssertNode = UpdateAssertNode(assertNode, assertMethod, actual, constraintExpression); + var newAssertNode = UpdateAssertNode(assertNode, conditionNode, assertMethod, actual, constraintExpression); if (newAssertNode is null) return; @@ -107,10 +107,10 @@ protected virtual (ExpressionSyntax? actual, ExpressionSyntax? constraintExpress return SyntaxFactory.InvocationExpression( SyntaxFactory.ParseExpression(constraintString), - SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(expected)))); + SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(expected.WithoutTrivia())))); } - protected static InvocationExpressionSyntax? UpdateAssertNode(InvocationExpressionSyntax assertNode, IMethodSymbol assertMethod, + protected static InvocationExpressionSyntax? UpdateAssertNode(InvocationExpressionSyntax assertNode, ExpressionSyntax? conditionNode, IMethodSymbol assertMethod, ExpressionSyntax actual, ExpressionSyntax constraintExpression) { // Replace the original ClassicAssert. invocation name into Assert.That @@ -126,10 +126,21 @@ protected virtual (ExpressionSyntax? actual, ExpressionSyntax? constraintExpress ? assertNode.ArgumentList.Arguments.Skip(2) : assertNode.ArgumentList.Arguments.Skip(1); + var actualArgument = SyntaxFactory.Argument(actual); + var actualArgumentWithCorrectTrivia = conditionNode is not null + ? actualArgument.WithLeadingTrivia(conditionNode.GetLeadingTrivia()) // ignore the trailing trivia, as there is a following argument + : actualArgument; + + var lastOriginalArgument = assertNode.ArgumentList.Arguments.Last(); + var constraintArgument = SyntaxFactory.Argument(constraintExpression).WithTriviaFrom(lastOriginalArgument); + var constraintArgumentWithCorrectTrivia = remainingArguments.Any() + ? constraintArgument.WithoutTrailingTrivia() // remove the trailing trivia, as there is a following argument + : constraintArgument; + var newArguments = new[] { - SyntaxFactory.Argument(actual), - SyntaxFactory.Argument(constraintExpression) + actualArgumentWithCorrectTrivia, + constraintArgumentWithCorrectTrivia }.Union(remainingArguments); var newArgumentsList = assertNode.ArgumentList.WithArguments(newArguments); diff --git a/src/nunit.analyzers/Extensions/ArgumentListSyntaxExtensions.cs b/src/nunit.analyzers/Extensions/ArgumentListSyntaxExtensions.cs index dffbd509..f23a88cc 100644 --- a/src/nunit.analyzers/Extensions/ArgumentListSyntaxExtensions.cs +++ b/src/nunit.analyzers/Extensions/ArgumentListSyntaxExtensions.cs @@ -3,6 +3,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Formatting; namespace NUnit.Analyzers.Extensions { @@ -13,7 +14,20 @@ public static ArgumentListSyntax WithArguments( IEnumerable newArguments) { var originalArguments = @this.Arguments; - var originalSeparators = originalArguments.GetSeparators(); + var originalSeparators = originalArguments.GetSeparators().ToArray(); + + // To match the old style as closely as possible, do not attempt anything if the number of arguments stayed the same + if (originalArguments.Count == newArguments.Count()) + { + return @this.WithArguments(SyntaxFactory.SeparatedList(newArguments, originalSeparators)) + .WithAdditionalAnnotations(Formatter.Annotation); + } + + // Otherwise, the number of arguments has either increased or decreased, in which case + // there is no one-size-fits-all answer on what to do about the trivias around separators. + // Therefore, add a newline after the separator if either the opening parenthesis + // or any of the original separators had a trailing newline. + var shouldAddTrailingNewlineAfterComma = TryGetFirstEndOfLineTrivia(@this.OpenParenToken, originalSeparators, out var trailingTrivia); var nodesAndTokens = new List { newArguments.First() }; @@ -25,7 +39,10 @@ public static ArgumentListSyntax WithArguments( if (separator == default(SyntaxToken)) { - separator = SyntaxFactory.Token(SyntaxKind.CommaToken); + separator = SyntaxFactory.Token( + SyntaxFactory.TriviaList(), + SyntaxKind.CommaToken, + shouldAddTrailingNewlineAfterComma ? SyntaxFactory.TriviaList(trailingTrivia) : SyntaxFactory.TriviaList()); } nodesAndTokens.Add(separator); @@ -34,7 +51,42 @@ public static ArgumentListSyntax WithArguments( var newSeparatedList = SyntaxFactory.SeparatedList(nodesAndTokens); - return @this.WithArguments(newSeparatedList); + return @this.WithArguments(newSeparatedList) + .WithAdditionalAnnotations(Formatter.Annotation); + } + + private static bool TryGetFirstEndOfLineTrivia(SyntaxToken openParenToken, SyntaxToken[] separators, out SyntaxTrivia trailingTrivia) + { + // If there's only one argument, there are no separators. + // Therefore, use the trailing trivia of the opening parenthesis into account to make our best guess. + if (separators.Length == 0) + { + foreach (var trivia in openParenToken.TrailingTrivia) + { + if (trivia.IsKind(SyntaxKind.EndOfLineTrivia)) + { + trailingTrivia = trivia; + return true; + } + } + } + else + { + foreach (var separator in separators) + { + foreach (var trivia in separator.TrailingTrivia) + { + if (trivia.IsKind(SyntaxKind.EndOfLineTrivia)) + { + trailingTrivia = trivia; + return true; + } + } + } + } + + trailingTrivia = default; + return false; } } }