Skip to content

Commit

Permalink
feat: add nunit contains assertions (#297)
Browse files Browse the repository at this point in the history
  • Loading branch information
Meir017 committed Jan 22, 2024
1 parent 68da99d commit ed6aec1
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/FluentAssertions.Analyzers.TestUtils/GenerateCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ public static string XunitAssertion(string methodArguments, string assertion) =>

public static string Nunit3Assertion(string methodArguments, string assertion) => new StringBuilder()
.AppendLine("using System;")
.AppendLine("using System.Collections;")
.AppendLine("using System.Collections.Generic;")
.AppendLine("using System.Collections.Immutable;")
.AppendLine("using System.Text.RegularExpressions;")
Expand All @@ -257,6 +258,7 @@ public static string Nunit3Assertion(string methodArguments, string assertion) =

public static string Nunit4Assertion(string methodArguments, string assertion) => new StringBuilder()
.AppendLine("using System;")
.AppendLine("using System.Collections;")
.AppendLine("using System.Collections.Generic;")
.AppendLine("using System.Collections.Immutable;")
.AppendLine("using System.Text.RegularExpressions;")
Expand Down
18 changes: 18 additions & 0 deletions src/FluentAssertions.Analyzers.Tests/DiagnosticVerifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,24 @@ public static class DiagnosticVerifier
public static void VerifyFix(CodeFixVerifierArguments arguments)
=> VerifyFix(arguments, arguments.DiagnosticAnalyzers.Single(), arguments.CodeFixProviders.Single(), arguments.FixedSources.Single());

public static void VerifyNoFix(CodeFixVerifierArguments arguments)
=> VerifyNoFix(arguments, arguments.DiagnosticAnalyzers.Single(), arguments.CodeFixProviders.Single());
private static void VerifyNoFix(CsProjectArguments arguments, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider)
{
var document = CsProjectGenerator.CreateDocument(arguments);
var analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(new[] { analyzer }, new[] { document });
var compilerDiagnostics = GetCompilerDiagnostics(document);
var attempts = analyzerDiagnostics.Length;

for (int i = 0; i < attempts; ++i)
{
var actions = new List<CodeAction>();
var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None);
codeFixProvider.RegisterCodeFixesAsync(context).Wait();

actions.Should().BeEmpty("There should be no code fix actions available to suppress the diagnostic.");
}
}

/// <summary>
/// General verifier for codefixes.
Expand Down
116 changes: 114 additions & 2 deletions src/FluentAssertions.Analyzers.Tests/Tips/NunitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -667,16 +667,119 @@ public void Nunit4_AssertIsNotInstanceOf_TestCodeFix(string oldAssertion, string

#endregion

#region Assert.Contains.cs

[DataTestMethod]
[AssertionDiagnostic("Assert.Contains(expected, actual{0});")]
[Implemented]
public void Nunit3_AssertContains_ICollection_TestCodeNoFix(string assertion)
=> Nunit3VerifyNoFix("object expected, ICollection actual", assertion);

[DataTestMethod]
[AssertionDiagnostic("ClassicAssert.Contains(expected, actual{0});")]
[Implemented]
public void Nunit4_AssertContains_ICollection_TestCodeNoFix(string assertion)
=> Nunit4VerifyNoFix("object expected, ICollection actual", assertion);

[DataTestMethod]
[AssertionDiagnostic("Assert.Contains(expected, actual{0});")]
[Implemented]
public void Nunit3_AssertContains_TestAnalyzer(string assertion)
=> Nunit3VerifyDiagnostic("object expected, string[] actual", assertion);

[DataTestMethod]
[AssertionDiagnostic("ClassicAssert.Contains(expected, actual{0});")]
[Implemented]
public void Nunit4_AssertContains_TestAnalyzer(string assertion)
=> Nunit4VerifyDiagnostic("object expected, string[] actual", assertion);

[DataTestMethod]
[AssertionCodeFix(
oldAssertion: "Assert.Contains(expected, actual{0});",
newAssertion: "actual.Should().Contain(expected{0});")]
[Implemented]
public void Nunit3_AssertContains_TestCodeFix(string oldAssertion, string newAssertion)
{
Nunit3VerifyFix("string expected, string[] actual", oldAssertion, newAssertion);
Nunit3VerifyFix("string expected, List<string> actual", oldAssertion, newAssertion);
Nunit3VerifyFix("string expected, object[] actual", oldAssertion, newAssertion);
Nunit3VerifyFix("string expected, List<object> actual", oldAssertion, newAssertion);
Nunit3VerifyFix("DateTime expected, DateTime[] actual", oldAssertion, newAssertion);
Nunit3VerifyFix("DateTime expected, List<DateTime> actual", oldAssertion, newAssertion);
}

[DataTestMethod]
[AssertionCodeFix(
oldAssertion: "ClassicAssert.Contains(expected, actual{0});",
newAssertion: "actual.Should().Contain(expected{0});")]
[Implemented]
public void Nunit4_AssertContains_TestCodeFix(string oldAssertion, string newAssertion)
{
Nunit4VerifyFix("string expected, string[] actual", oldAssertion, newAssertion);
Nunit4VerifyFix("string expected, List<string> actual", oldAssertion, newAssertion);
Nunit4VerifyFix("string expected, object[] actual", oldAssertion, newAssertion);
Nunit4VerifyFix("string expected, List<object> actual", oldAssertion, newAssertion);
Nunit4VerifyFix("DateTime expected, DateTime[] actual", oldAssertion, newAssertion);
Nunit4VerifyFix("DateTime expected, List<DateTime> actual", oldAssertion, newAssertion);
}

[DataTestMethod]
[DataRow(
/* methodArguments: */ "object expected, string[] actual",
/* oldAssertion: */ "Assert.Contains(expected, actual);",
/* newAssertion: */ "actual.Should().Contain((string)expected);")]
[DataRow(
/* methodArguments: */ "object expected, List<string> actual",
/* oldAssertion: */ "Assert.Contains(expected, actual);",
/* newAssertion: */ "actual.Should().Contain((string)expected);")]
[DataRow(
/* methodArguments: */ "object expected, DateTime[] actual",
/* oldAssertion: */ "Assert.Contains(expected, actual);",
/* newAssertion: */ "actual.Should().Contain((DateTime)expected);")]
[DataRow(
/* methodArguments: */ "object expected, List<DateTime> actual",
/* oldAssertion: */ "Assert.Contains(expected, actual);",
/* newAssertion: */ "actual.Should().Contain((DateTime)expected);")]
[Implemented]
public void Nunit3_AssertContains_WithCasting_TestCodeFix(string methodArguments, string oldAssertion, string newAssertion)
=> Nunit3VerifyFix(methodArguments, oldAssertion, newAssertion);

[DataTestMethod]
[DataRow(
/* methodArguments: */ "object expected, string[] actual",
/* oldAssertion: */ "ClassicAssert.Contains(expected, actual);",
/* newAssertion: */ "actual.Should().Contain((string)expected);")]
[DataRow(
/* methodArguments: */ "object expected, List<string> actual",
/* oldAssertion: */ "ClassicAssert.Contains(expected, actual);",
/* newAssertion: */ "actual.Should().Contain((string)expected);")]
[DataRow(
/* methodArguments: */ "object expected, DateTime[] actual",
/* oldAssertion: */ "ClassicAssert.Contains(expected, actual);",
/* newAssertion: */ "actual.Should().Contain((DateTime)expected);")]
[DataRow(
/* methodArguments: */ "object expected, List<DateTime> actual",
/* oldAssertion: */ "ClassicAssert.Contains(expected, actual);",
/* newAssertion: */ "actual.Should().Contain((DateTime)expected);")]
[Implemented]
public void Nunit4_AssertContains_WithCasting_TestCodeFix(string methodArguments, string oldAssertion, string newAssertion)
=> Nunit4VerifyFix(methodArguments, oldAssertion, newAssertion);

#endregion

private void Nunit3VerifyDiagnostic(string methodArguments, string assertion)
=> VerifyDiagnostic(GenerateCode.Nunit3Assertion(methodArguments, assertion), PackageReference.Nunit_3_14_0);
private void Nunit3VerifyFix(string methodArguments, string oldAssertion, string newAssertion)
=> VerifyFix(GenerateCode.Nunit3Assertion(methodArguments, oldAssertion), GenerateCode.Nunit3Assertion(methodArguments, newAssertion), PackageReference.Nunit_3_14_0);
private void Nunit3VerifyNoFix(string methodArguments, string assertion)
=> VerifyNoFix(GenerateCode.Nunit3Assertion(methodArguments, assertion), PackageReference.Nunit_3_14_0);

private void Nunit4VerifyDiagnostic(string methodArguments, string assertion)
=> VerifyDiagnostic(GenerateCode.Nunit4Assertion(methodArguments, assertion), PackageReference.Nunit_4_0_1);
private void Nunit4VerifyFix(string methodArguments, string oldAssertion, string newAssertion)
=> VerifyFix(GenerateCode.Nunit4Assertion(methodArguments, oldAssertion), GenerateCode.Nunit4Assertion(methodArguments, newAssertion), PackageReference.Nunit_4_0_1);

private void Nunit4VerifyNoFix(string methodArguments, string assertion)
=> VerifyNoFix(GenerateCode.Nunit4Assertion(methodArguments, assertion), PackageReference.Nunit_4_0_1);
private void VerifyDiagnostic(string source, PackageReference nunit)
{
DiagnosticVerifier.VerifyDiagnostic(new DiagnosticVerifierArguments()
Expand All @@ -689,7 +792,7 @@ private void VerifyDiagnostic(string source, PackageReference nunit)
Message = AssertAnalyzer.Message,
Locations = new DiagnosticResultLocation[]
{
new("Test0.cs", 15, 13)
new("Test0.cs", 16, 13)
},
Severity = DiagnosticSeverity.Info
})
Expand All @@ -706,4 +809,13 @@ private void VerifyFix(string oldSource, string newSource, PackageReference nuni
.WithPackageReferences(PackageReference.FluentAssertions_6_12_0, nunit)
);
}
private void VerifyNoFix(string source, PackageReference nunit)
{
DiagnosticVerifier.VerifyNoFix(new CodeFixVerifierArguments()
.WithDiagnosticAnalyzer<AssertAnalyzer>()
.WithCodeFixProvider<NunitCodeFixProvider>()
.WithSources(source)
.WithPackageReferences(PackageReference.FluentAssertions_6_12_0, nunit)
);
}
}
26 changes: 26 additions & 0 deletions src/FluentAssertions.Analyzers/Tips/NunitCodeFixProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using Microsoft.CodeAnalysis.Operations;
using CreateChangedDocument = System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Document>>;
using System;
using FluentAssertions.Analyzers.Utilities;
using Microsoft.CodeAnalysis.Simplification;

namespace FluentAssertions.Analyzers;

Expand Down Expand Up @@ -171,6 +173,30 @@ private CreateChangedDocument TryComputeFixForNunitClassicAssert(IInvocationOper
case "IsNotInstanceOf" when ArgumentsAreTypeOf(invocation, t.Object): // Assert.IsNotInstanceOf<T>(object actual)
case "IsNotInstanceOf" when ArgumentsAreTypeOf(invocation, t.Object, t.String, t.ObjectArray): // Assert.IsNotInstanceOf<T>(object actual, string message, params object[] parms)
return DocumentEditorUtils.RenameGenericMethodToSubjectShouldGenericAssertion(invocation, context, "NotBeOfType", subjectIndex: 0, argumentsToRemove: []);
case "Contains": // Assert.Contains(object expected, ICollection actual)
{
var collectionArgument = invocation.Arguments[1].Value.UnwrapConversion();
if (collectionArgument.Type.ImplementsOrIsInterface(SpecialType.System_Collections_Generic_IEnumerable_T))
{
return async ctx => await DocumentEditorUtils.RewriteExpression(invocation, [
(EditActionContext editActionContext) =>
{
ITypeSymbol elementType = collectionArgument.Type switch
{
INamedTypeSymbol namedType => namedType.TypeArguments[0],
IArrayTypeSymbol arrayType => arrayType.ElementType,
_ => null
};
var argumentToCast = editActionContext.InvocationExpression.ArgumentList.Arguments[0];
var castExpression = editActionContext.Editor.Generator.CastExpression(elementType, argumentToCast.Expression);
editActionContext.Editor.ReplaceNode(argumentToCast.Expression, castExpression.WithAdditionalAnnotations(Simplifier.Annotation));
},
EditAction.SubjectShouldAssertion(argumentIndex: 1, "Contain")
], context, ctx);
}
return null;
}
}
return null;
}
Expand Down

0 comments on commit ed6aec1

Please sign in to comment.