Skip to content

Commit

Permalink
Allow excluding assertion methods
Browse files Browse the repository at this point in the history
  • Loading branch information
meziantou committed Dec 5, 2023
1 parent 6a1ccab commit f83f98d
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,89 @@ class Test
public void MyTest()
{
Assert.Inconclusive();
Assert.Inconclusive("");
}
}
""");
}

[Fact]
public Task Assert_Ignore_NoReport()
{
return Assert(
"""
using NUnit.Framework;
class Test
{
public void MyTest()
{
Assert.Ignore();
Assert.Ignore("");
}
}
""");
}

[Fact]
public Task Assert_Fail_ExcludedFromOptions()
{
return CreateProjectBuilder()
.WithSourceCode("""
using NUnit.Framework;
class Test
{
public void MyTest()
{
Assert.Fail();
[|Assert.Fail("dummy")|];
}
}
""")
.AddAnalyzerConfiguration("mfa_excluded_methods", "M:NUnit.Framework.Assert.Fail")
.ValidateAsync();
}

[Fact]
public Task Assert_Fail_String_ExcludedFromOptions()
{
return CreateProjectBuilder()
.WithSourceCode("""
using NUnit.Framework;
class Test
{
public void MyTest()
{
[|Assert.Fail()|];
Assert.Fail("dummy");
}
}
""")
.AddAnalyzerConfiguration("mfa_excluded_methods", "M:NUnit.Framework.Assert.Fail(System.String)")
.ValidateAsync();
}

[Fact]
public Task Assert_MultiMethods_ExcludedFromOptions()
{
return CreateProjectBuilder()
.WithSourceCode("""
using NUnit.Framework;
class Test
{
public void MyTest()
{
Assert.Fail();
Assert.Fail("dummy");
}
}
""")
.AddAnalyzerConfiguration("mfa_excluded_methods", "M:NUnit.Framework.Assert.Fail;M:NUnit.Framework.Assert.Fail(System.String)")
.ValidateAsync();
}

[Fact]
public Task EnumerableTest()
Expand Down
39 changes: 34 additions & 5 deletions Meziantou.FluentAssertionsAnalyzers/AssertAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,39 @@ private sealed class AnalyzerContext(Compilation compilation)
public bool IsNUnitAvailable => _nunitAssertionExceptionSymbol is not null;
public bool IsXUnitAvailable => _xunitAssertSymbol is not null;

private static readonly char[] SymbolsSeparators = [';'];

private bool IsMethodExcluded(AnalyzerOptions options, IInvocationOperation operation)
{
var location = operation.Syntax.GetLocation().SourceTree;
if (location is null)
return false;

var fileOptions = options.AnalyzerConfigOptionsProvider.GetOptions(location);
if (fileOptions is null)
return false;

if (!fileOptions.TryGetValue("mfa_excluded_methods", out var symbolDocumentationIds))
return false;

var parts = symbolDocumentationIds.Split(SymbolsSeparators, StringSplitOptions.RemoveEmptyEntries);
foreach (var part in parts)
{
var symbols = DocumentationCommentId.GetSymbolsForDeclarationId(part, compilation);
foreach (var symbol in symbols)
{
if (SymbolEqualityComparer.Default.Equals(symbol, operation.TargetMethod))
return true;
}
}

return false;
}

public void AnalyzeXunitInvocation(OperationAnalysisContext context)
{
var op = (IInvocationOperation)context.Operation;
if (op.TargetMethod.ContainingType.Equals(_xunitAssertSymbol, SymbolEqualityComparer.Default))
if (op.TargetMethod.ContainingType.Equals(_xunitAssertSymbol, SymbolEqualityComparer.Default) && !IsMethodExcluded(context.Options, op))
{
context.ReportDiagnostic(Diagnostic.Create(XunitRule, op.Syntax.GetLocation()));
}
Expand All @@ -103,7 +132,7 @@ public void AnalyzeXunitInvocation(OperationAnalysisContext context)
public void AnalyzeMsTestInvocation(OperationAnalysisContext context)
{
var op = (IInvocationOperation)context.Operation;
if (IsMsTestAssertClass(op.TargetMethod.ContainingType))
if (IsMsTestAssertClass(op.TargetMethod.ContainingType) && !IsMethodExcluded(context.Options, op))
{
context.ReportDiagnostic(Diagnostic.Create(MSTestsRule, op.Syntax.GetLocation()));
}
Expand All @@ -121,9 +150,9 @@ public void AnalyzeMsTestThrow(OperationAnalysisContext context)
public void AnalyzeNunitInvocation(OperationAnalysisContext context)
{
var op = (IInvocationOperation)context.Operation;
if (IsNunitAssertClass(op.TargetMethod.ContainingType))
if (IsNunitAssertClass(op.TargetMethod.ContainingType) && !IsMethodExcluded(context.Options, op))
{
if (op.TargetMethod.Name is "Inconclusive")
if (op.TargetMethod.Name is "Inconclusive" or "Ignore" && op.TargetMethod.ContainingType.Equals(_nunitAssertSymbol, SymbolEqualityComparer.Default))
return;

context.ReportDiagnostic(Diagnostic.Create(NUnitRule, op.Syntax.GetLocation()));
Expand Down Expand Up @@ -178,5 +207,5 @@ private bool IsNunitAssertClass(ITypeSymbol typeSymbol)
|| typeSymbol.Equals(_nunitStringAssertSymbol, SymbolEqualityComparer.Default)
|| typeSymbol.Equals(_nunitClassicAssertSymbol, SymbolEqualityComparer.Default);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<Version>1.0.13</Version>
<Version>1.0.14</Version>
<IncludeBuildOutput>false</IncludeBuildOutput>
<developmentDependency>true</developmentDependency>
<Description>A Roslyn analyzer to help migrate from Xunit / NUnit assertions to FluentAssertions</Description>
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,12 @@
## Installation

Install the NuGet package: [Meziantou.FluentAssertionsAnalyzers](https://www.nuget.org/packages/Meziantou.FluentAssertionsAnalyzers/)

## Configuration

You can exclude assertion methods using the `.editorconfig` file:

````
[*.cs]
mfa_excluded_methods=M:NUnit.Framework.Assert.Fail;M:NUnit.Framework.Assert.Fail(System.String)
````

0 comments on commit f83f98d

Please sign in to comment.