Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 29 additions & 29 deletions src/FluentAssertions.Analyzers.Tests/Tips/CollectionTests.cs

Large diffs are not rendered by default.

35 changes: 17 additions & 18 deletions src/FluentAssertions.Analyzers.Tests/Tips/NumericTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class NumericTests
[AssertionDiagnostic("actual.Should().BeGreaterThan(0{0});")]
[AssertionDiagnostic("actual.Should().BeGreaterThan(0{0}).ToString();")]
[Implemented]
public void NumericShouldBePositive_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic<NumericShouldBePositiveAnalyzer>(assertion);
public void NumericShouldBePositive_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion, DiagnosticMetadata.NumericShouldBePositive_ShouldBeGreaterThan);

[DataTestMethod]
[AssertionCodeFix(
Expand All @@ -21,13 +21,13 @@ public class NumericTests
oldAssertion: "actual.Should().BeGreaterThan(0{0}).ToString();",
newAssertion: "actual.Should().BePositive({0}).ToString();")]
[Implemented]
public void NumericShouldBePositive_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<NumericShouldBePositiveCodeFix, NumericShouldBePositiveAnalyzer>(oldAssertion, newAssertion);
public void NumericShouldBePositive_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<FluentAssertionsCodeFix, FluentAssertionsOperationAnalyzer>(oldAssertion, newAssertion);

[DataTestMethod]
[AssertionDiagnostic("actual.Should().BeLessThan(0{0});")]
[AssertionDiagnostic("actual.Should().BeLessThan(0{0}).ToString();")]
[Implemented]
public void NumericShouldBeNegative_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic<NumericShouldBeNegativeAnalyzer>(assertion);
public void NumericShouldBeNegative_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion, DiagnosticMetadata.NumericShouldBeNegative_ShouldBeLessThan);

[DataTestMethod]
[AssertionCodeFix(
Expand All @@ -37,17 +37,19 @@ public class NumericTests
oldAssertion: "actual.Should().BeLessThan(0{0}).ToString();",
newAssertion: "actual.Should().BeNegative({0}).ToString();")]
[Implemented]
public void NumericShouldBeNegative_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<NumericShouldBeNegativeCodeFix, NumericShouldBeNegativeAnalyzer>(oldAssertion, newAssertion);
public void NumericShouldBeNegative_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<FluentAssertionsCodeFix, FluentAssertionsOperationAnalyzer>(oldAssertion, newAssertion);

[DataTestMethod]
[AssertionDiagnostic("actual.Should().BeGreaterOrEqualTo(lower{0}).And.BeLessOrEqualTo(upper);")]
[AssertionDiagnostic("actual.Should().BeGreaterOrEqualTo(lower).And.BeLessOrEqualTo(upper{0});")]
[AssertionDiagnostic("actual.Should().BeGreaterOrEqualTo(lower{0}).And.BeLessOrEqualTo(upper{0});")]
[Implemented]
public void NumericShouldBeInRange_BeGreaterOrEqualToAndBeLessOrEqualTo_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion, DiagnosticMetadata.NumericShouldBeInRange_BeGreaterOrEqualToAndBeLessOrEqualTo);

[DataTestMethod]
[AssertionDiagnostic("actual.Should().BeLessOrEqualTo(upper{0}).And.BeGreaterOrEqualTo(lower);")]
[AssertionDiagnostic("actual.Should().BeLessOrEqualTo(upper).And.BeGreaterOrEqualTo(lower{0});")]
[AssertionDiagnostic("actual.Should().BeLessOrEqualTo(upper{0}).And.BeGreaterOrEqualTo(lower{0});")]
[Implemented]
public void NumericShouldBeInRange_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic<NumericShouldBeInRangeAnalyzer>(assertion);
public void NumericShouldBeInRange_BeLessOrEqualToAndBeGreaterOrEqualTo_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion, DiagnosticMetadata.NumericShouldBeInRange_BeLessOrEqualToAndBeGreaterOrEqualTo);

[DataTestMethod]
[AssertionCodeFix(
Expand All @@ -62,33 +64,30 @@ public class NumericTests
[AssertionCodeFix(
oldAssertion: "actual.Should().BeLessOrEqualTo(upper).And.BeGreaterOrEqualTo(lower{0});",
newAssertion: "actual.Should().BeInRange(lower, upper{0});")]
[Implemented]
public void NumericShouldBeInRange_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<NumericShouldBeInRangeCodeFix, NumericShouldBeInRangeAnalyzer>(oldAssertion, newAssertion);
[NotImplemented]
public void NumericShouldBeInRange_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<FluentAssertionsCodeFix, FluentAssertionsOperationAnalyzer>(oldAssertion, newAssertion);

[DataTestMethod]
[AssertionDiagnostic("Math.Abs(expected - actual).Should().BeLessOrEqualTo(delta{0});")]
[Implemented]
public void NumericShouldBeApproximately_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic<NumericShouldBeApproximatelyAnalyzer>(assertion);
public void NumericShouldBeApproximately_TestAnalyzer(string assertion) => VerifyCSharpDiagnostic(assertion, DiagnosticMetadata.NumericShouldBeApproximately_MathAbsShouldBeLessOrEqualTo);

[DataTestMethod]
[AssertionCodeFix(
oldAssertion: "Math.Abs(expected - actual).Should().BeLessOrEqualTo(delta{0});",
newAssertion: "actual.Should().BeApproximately(expected, delta{0});")]
[Implemented]
public void NumericShouldBeApproximately_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<NumericShouldBeApproximatelyCodeFix, NumericShouldBeApproximatelyAnalyzer>(oldAssertion, newAssertion);
public void NumericShouldBeApproximately_TestCodeFix(string oldAssertion, string newAssertion) => VerifyCSharpFix<FluentAssertionsCodeFix, FluentAssertionsOperationAnalyzer>(oldAssertion, newAssertion);

private void VerifyCSharpDiagnostic<TDiagnosticAnalyzer>(string sourceAssertion) where TDiagnosticAnalyzer : Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer, new()
private void VerifyCSharpDiagnostic(string sourceAssertion, DiagnosticMetadata metadata)
{
var source = GenerateCode.DoubleAssertion(sourceAssertion);

var type = typeof(TDiagnosticAnalyzer);
var diagnosticId = (string)type.GetField("DiagnosticId").GetValue(null);
var message = (string)type.GetField("Message").GetValue(null);

DiagnosticVerifier.VerifyCSharpDiagnosticUsingAllAnalyzers(source, new DiagnosticResult
{
Id = diagnosticId,
Message = message,
Id = FluentAssertionsOperationAnalyzer.DiagnosticId,
Message = metadata.Message,
VisitorName = metadata.Name,
Locations = new DiagnosticResultLocation[]
{
new DiagnosticResultLocation("Test0.cs", 10, 13)
Expand Down
6 changes: 6 additions & 0 deletions src/FluentAssertions.Analyzers/Tips/DiagnosticMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,11 @@ private DiagnosticMetadata(string message, string helpLink, [CallerMemberName] s
public static DiagnosticMetadata CollectionShouldOnlyHaveUniqueItems_ShouldHaveSameCountThisCollectionDistinct { get; } = new("Use .Should().OnlyHaveUniqueItems()", GetHelpLink("Collections-33"));
public static DiagnosticMetadata CollectionShouldOnlyHaveUniqueItemsByComparer_SelectShouldOnlyHaveUniqueItems { get; } = new("Use .Should().OnlyHaveUniqueItems()", GetHelpLink("Collections-34"));

public static DiagnosticMetadata NumericShouldBePositive_ShouldBeGreaterThan { get; } = new("Use .Should().BePositive()", GetHelpLink("Numeric-1"));
public static DiagnosticMetadata NumericShouldBeNegative_ShouldBeLessThan { get; } = new("Use .Should().BeNegative()", GetHelpLink("Numeric-2"));
public static DiagnosticMetadata NumericShouldBeInRange_BeGreaterOrEqualToAndBeLessOrEqualTo { get; } = new("Use .Should().BeInRange()", string.Empty);
public static DiagnosticMetadata NumericShouldBeInRange_BeLessOrEqualToAndBeGreaterOrEqualTo { get; } = new("Use .Should().BeInRange()", string.Empty);
public static DiagnosticMetadata NumericShouldBeApproximately_MathAbsShouldBeLessOrEqualTo { get; } = new("Use .Should().BeApproximately()", string.Empty);

private static string GetHelpLink(string id) => $"https://fluentassertions.com/tips/#{id}";
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace FluentAssertions.Analyzers;

public partial class CollectionCodeFix
public partial class FluentAssertionsCodeFix
{
private ExpressionSyntax GetNewExpressionForSelectShouldEqualOtherCollectionSelectSyntaxVisitor(ExpressionSyntax expression)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

namespace FluentAssertions.Analyzers;

public partial class CollectionCodeFix
public partial class FluentAssertionsCodeFix
{
private ExpressionSyntax GetCombinedAssertions(ExpressionSyntax expression, string removeMethod, string renameMethod)
=> GetCombinedAssertions(expression, removeMethod, renameMethod, "NotBeNullOrEmpty");

private ExpressionSyntax GetCombinedAssertions(ExpressionSyntax expression, string removeMethod, string renameMethod, string newMethod)
{
var remove = NodeReplacement.RemoveAndExtractArguments(removeMethod);
var newExpression = GetNewExpression(expression, NodeReplacement.RemoveMethodBefore(removeMethod), remove);

return GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments(renameMethod, "NotBeNullOrEmpty", remove.Arguments));
return GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments(renameMethod, newMethod, remove.Arguments));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
using System.Composition;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace FluentAssertions.Analyzers;

[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CollectionCodeFix)), Shared]
public partial class CollectionCodeFix : FluentAssertionsCodeFixProvider
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(FluentAssertionsCodeFix)), Shared]
public partial class FluentAssertionsCodeFix : FluentAssertionsCodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(FluentAssertionsOperationAnalyzer.DiagnosticId);

Expand Down Expand Up @@ -188,6 +189,54 @@ protected override ExpressionSyntax GetNewExpression(ExpressionSyntax expression
return GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments("Be", "HaveElementAt", remove.Arguments));
}

case nameof(DiagnosticMetadata.NumericShouldBePositive_ShouldBeGreaterThan):
return GetNewExpression(expression, NodeReplacement.RenameAndRemoveFirstArgument("BeGreaterThan", "BePositive"));
case nameof(DiagnosticMetadata.NumericShouldBeNegative_ShouldBeLessThan):
return GetNewExpression(expression, NodeReplacement.RenameAndRemoveFirstArgument("BeLessThan", "BeNegative"));

case nameof(DiagnosticMetadata.NumericShouldBeInRange_BeGreaterOrEqualToAndBeLessOrEqualTo):
{
var removeLess = NodeReplacement.RemoveAndExtractArguments("BeLessOrEqualTo");
var newExpression = GetNewExpression(expression, NodeReplacement.RemoveMethodBefore("BeLessOrEqualTo"), removeLess);

var renameGreater = NodeReplacement.RenameAndExtractArguments("BeGreaterOrEqualTo", "BeInRange");
newExpression = GetNewExpression(newExpression, renameGreater);

var arguments = renameGreater.Arguments.InsertRange(1, removeLess.Arguments);

var result = GetNewExpression(newExpression, NodeReplacement.WithArguments("BeInRange", arguments));

return result;
}
case nameof(DiagnosticMetadata.NumericShouldBeInRange_BeLessOrEqualToAndBeGreaterOrEqualTo):
{
var removeGreater = NodeReplacement.RemoveAndExtractArguments("BeGreaterOrEqualTo");
var newExpression = GetNewExpression(expression, NodeReplacement.RemoveMethodBefore("BeGreaterOrEqualTo"), removeGreater);

var renameLess = NodeReplacement.RenameAndExtractArguments("BeLessOrEqualTo", "BeInRange");
newExpression = GetNewExpression(newExpression, renameLess);

var arguments = removeGreater.Arguments.InsertRange(1, renameLess.Arguments);

return GetNewExpression(newExpression, NodeReplacement.WithArguments("BeInRange", arguments));
}
case nameof(DiagnosticMetadata.NumericShouldBeApproximately_MathAbsShouldBeLessOrEqualTo):
{
var remove = NodeReplacement.RemoveAndExtractArguments("Abs");
var newExpression = GetNewExpression(expression, remove);

var subtractExpression = (BinaryExpressionSyntax)remove.Arguments[0].Expression;

var actual = subtractExpression.Right as IdentifierNameSyntax;
var expected = subtractExpression.Left;

newExpression = GetNewExpression(newExpression, NodeReplacement.RenameAndPrependArguments("BeLessOrEqualTo", "BeApproximately", new SeparatedSyntaxList<ArgumentSyntax>().Add(SyntaxFactory.Argument(expected))));

newExpression = RenameIdentifier(newExpression, "Math", actual.Identifier.Text);

return newExpression;
}

default: throw new System.InvalidOperationException($"Invalid visitor name - {properties.VisitorName}");
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -28,7 +29,7 @@ public FluentAssertionsMetadata(Compilation compilation)
IReadonlyDictionaryOfT2 = compilation.GetTypeByMetadataName(typeof(IReadOnlyDictionary<,>).FullName);

Enumerable = compilation.GetTypeByMetadataName(typeof(Enumerable).FullName);

Math = compilation.GetTypeByMetadataName(typeof(Math).FullName);
}
public INamedTypeSymbol AssertionExtensions { get; }
public INamedTypeSymbol ReferenceTypeAssertionsOfT2 { get; }
Expand All @@ -39,5 +40,6 @@ public FluentAssertionsMetadata(Compilation compilation)
public INamedTypeSymbol BooleanAssertionsOfT1 { get; }
public INamedTypeSymbol NumericAssertionsOfT2 { get; }
public INamedTypeSymbol Enumerable { get; }
public INamedTypeSymbol Math { get; }
}
}
Loading