Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add analyzer for ParallelizableAttribute #97

Merged
merged 1 commit into from
Dec 28, 2018
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Gu.Roslyn.Asserts;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using NUnit.Analyzers.Constants;
using NUnit.Analyzers.ParallelizableUsage;
using NUnit.Framework;

namespace NUnit.Analyzers.Tests.ParallelizableUsage
{
[TestFixture]
public sealed class ParallelizableUsageAnalyzerTests
{
private readonly DiagnosticAnalyzer analyzer = new ParallelizableUsageAnalyzer();

[Test]
public void VerifySupportedDiagnostics()
{
var diagnostics = analyzer.SupportedDiagnostics;

var expectedIdentifiers = new List<string>
{
AnalyzerIdentifiers.ParallelScopeSelfNoEffectOnAssemblyUsage,
AnalyzerIdentifiers.ParallelScopeChildrenOnNonParameterizedTestMethodUsage,
AnalyzerIdentifiers.ParallelScopeFixturesOnTestMethodUsage
};
CollectionAssert.AreEquivalent(expectedIdentifiers, diagnostics.Select(d => d.Id));

foreach (var diagnostic in diagnostics)
{
Assert.That(diagnostic.Title.ToString(), Is.EqualTo(ParallelizableUsageAnalyzerConstants.Title),
$"{diagnostic.Id} : {nameof(DiagnosticDescriptor.Title)}");
Assert.That(diagnostic.Category, Is.EqualTo(Categories.Usage),
$"{diagnostic.Id} : {nameof(DiagnosticDescriptor.Category)}");
}

var diagnosticMessage = diagnostics.Select(_ => _.MessageFormat.ToString()).ToImmutableArray();

Assert.That(diagnosticMessage, Contains.Item(ParallelizableUsageAnalyzerConstants.ParallelScopeSelfNoEffectOnAssemblyMessage),
$"{ParallelizableUsageAnalyzerConstants.ParallelScopeSelfNoEffectOnAssemblyMessage} is missing.");
Assert.That(diagnosticMessage, Contains.Item(ParallelizableUsageAnalyzerConstants.ParallelScopeChildrenOnNonParameterizedTestMethodMessage),
$"{ParallelizableUsageAnalyzerConstants.ParallelScopeChildrenOnNonParameterizedTestMethodMessage} is missing.");
Assert.That(diagnosticMessage, Contains.Item(ParallelizableUsageAnalyzerConstants.ParallelScopeFixturesOnTestMethodMessage),
$"{ParallelizableUsageAnalyzerConstants.ParallelScopeFixturesOnTestMethodMessage} is missing.");
}

[TestCase(ParallelizableUsageAnalyzerConstants.ParallelScope.Self, ParallelScope.Self)]
[TestCase(ParallelizableUsageAnalyzerConstants.ParallelScope.Children, ParallelScope.Children)]
[TestCase(ParallelizableUsageAnalyzerConstants.ParallelScope.Fixtures, ParallelScope.Fixtures)]
public void ConstantMatchesValueInNUnit(int enumValue, ParallelScope parallelScope)
{
Assert.That(enumValue, Is.EqualTo((int)parallelScope));
}

[TestCase(ParallelScope.All)]
[TestCase(ParallelScope.Children)]
[TestCase(ParallelScope.Fixtures)]
public void AnalyzeWhenAssemblyAttributeIsNotParallelScopeSelf(ParallelScope parallelScope)
{
var enumValue = parallelScope.ToString();
var testCode = $@"
using NUnit.Framework;
[assembly: Parallelizable(ParallelScope.{enumValue})]";
AnalyzerAssert.Valid<ParallelizableUsageAnalyzer>(testCode);
}

[Test]
public void AnalyzeWhenAssemblyAttributeIsExplicitlyParallelScopeSelf()
{
var expectedDiagnostic = ExpectedDiagnostic.Create(
AnalyzerIdentifiers.ParallelScopeSelfNoEffectOnAssemblyUsage,
ParallelizableUsageAnalyzerConstants.ParallelScopeSelfNoEffectOnAssemblyMessage);

var testCode = $@"
using NUnit.Framework;
[assembly: ↓Parallelizable(ParallelScope.Self)]";
AnalyzerAssert.Diagnostics(analyzer, expectedDiagnostic, testCode);
}

[Test]
public void AnalyzeWhenAssemblyAttributeIsImplicitlyParallelScopeSelf()
{
var expectedDiagnostic = ExpectedDiagnostic.Create(
AnalyzerIdentifiers.ParallelScopeSelfNoEffectOnAssemblyUsage,
ParallelizableUsageAnalyzerConstants.ParallelScopeSelfNoEffectOnAssemblyMessage);

var testCode = $@"
using NUnit.Framework;
[assembly: ↓Parallelizable()]";
AnalyzerAssert.Diagnostics(analyzer, expectedDiagnostic, testCode);
}

[Theory]
public void AnalyzeWhenAttributeIsOnClass(ParallelScope parallelScope)
{
var enumValue = parallelScope.ToString();
var testCode = TestUtility.WrapClassInNamespaceAndAddUsing($@"
[TestFixture]
[Parallelizable(ParallelScope.{enumValue})]
public sealed class AnalyzeWhenAttributeIsOnClass
{{
}}");
AnalyzerAssert.Valid<ParallelizableUsageAnalyzer>(testCode);
}

[Test]
public void AnalyzeWhenAttributeIsOnSimpleTestMethodParallelScopeSelf()
{
var testCode = TestUtility.WrapClassInNamespaceAndAddUsing(@"
[TestFixture]
public sealed class AnalyzeWhenAttributeIsOnSimpleTestMethodParallelScopeSelf
{
[Test]
[Parallelizable(ParallelScope.Self)]
public void Test()
{
}
}");
AnalyzerAssert.Valid<ParallelizableUsageAnalyzer>(testCode);
}

[TestCase(ParallelScope.All)]
[TestCase(ParallelScope.Children)]
public void AnalyzeWhenAttributeIsOnSimpleTestMethodContainsParallelScopeChildren(ParallelScope parallelScope)
{
var expectedDiagnostic = ExpectedDiagnostic.Create(
AnalyzerIdentifiers.ParallelScopeChildrenOnNonParameterizedTestMethodUsage,
ParallelizableUsageAnalyzerConstants.ParallelScopeChildrenOnNonParameterizedTestMethodMessage);

var enumValue = parallelScope.ToString();
var testCode = TestUtility.WrapClassInNamespaceAndAddUsing($@"
[TestFixture]
public sealed class AnalyzeWhenAttributeIsOnSimpleTestMethodContainsParallelScopeChildren
{{
[Test]
[↓Parallelizable(ParallelScope.{enumValue})]
public void Test()
{{
}}
}}");
AnalyzerAssert.Diagnostics(analyzer, expectedDiagnostic, testCode);
}

[Test]
public void AnalyzeWhenAttributeIsOnSimpleTestMethodIsParallelScopeFixtures()
{
var expectedDiagnostic = ExpectedDiagnostic.Create(
AnalyzerIdentifiers.ParallelScopeFixturesOnTestMethodUsage,
ParallelizableUsageAnalyzerConstants.ParallelScopeFixturesOnTestMethodMessage);

var testCode = TestUtility.WrapClassInNamespaceAndAddUsing(@"
[TestFixture]
public sealed class AnalyzeWhenAttributeIsOnSimpleTestMethodIsParallelScopeFixtures
{
[Test]
[↓Parallelizable(ParallelScope.Fixtures)]
public void Test()
{
}
}");
AnalyzerAssert.Diagnostics(analyzer, expectedDiagnostic, testCode);
}

[TestCaseSource(nameof(ParallelScopesExceptFixtures))]
public void AnalyzeWhenAttributeIsOnTestCaseTestMethodNotParallelScopeFixtures(ParallelScope parallelScope)
{
var enumValue = parallelScope.ToString();
var testCode = TestUtility.WrapClassInNamespaceAndAddUsing($@"
[TestFixture]
public sealed class AnalyzeWhenAttributeIsOnTestCaseTestMethodNotParallelScopeFixtures
{{
[TestCase(1)]
[Parallelizable(ParallelScope.{enumValue})]
public void Test(int data)
{{
}}
}}");
AnalyzerAssert.Valid<ParallelizableUsageAnalyzer>(testCode);
}

[Test]
public void AnalyzeWhenAttributeIsOnTestCaseTestMethodIsParallelScopeFixtures()
{
var expectedDiagnostic = ExpectedDiagnostic.Create(
AnalyzerIdentifiers.ParallelScopeFixturesOnTestMethodUsage,
ParallelizableUsageAnalyzerConstants.ParallelScopeFixturesOnTestMethodMessage);

var testCode = TestUtility.WrapClassInNamespaceAndAddUsing(@"
[TestFixture]
public sealed class AnalyzeWhenAttributeIsOnTestCaseTestMethodIsParallelScopeFixtures
{
[TestCase(1)]
[↓Parallelizable(ParallelScope.Fixtures)]
public void Test(int data)
{
}
}");
AnalyzerAssert.Diagnostics(analyzer, expectedDiagnostic, testCode);
}

[TestCaseSource(nameof(ParallelScopesExceptFixtures))]
public void AnalyzeWhenAttributeIsOnParametricTestMethodNotParallelScopeFixtures(ParallelScope parallelScope)
{
var enumValue = parallelScope.ToString();
var testCode = TestUtility.WrapClassInNamespaceAndAddUsing($@"
[TestFixture]
public sealed class AnalyzeWhenAttributeIsOnParametricTestMethodNotParallelScopeFixtures
{{
[Test]
[Parallelizable(ParallelScope.{enumValue})]
public void Test([Values(1, 2, 3)] int data)
{{
}}
}}");
AnalyzerAssert.Valid<ParallelizableUsageAnalyzer>(testCode);
}

[Test]
public void AnalyzeWhenAttributeIsOnParametricTestMethodIsParallelScopeFixtures()
{
var expectedDiagnostic = ExpectedDiagnostic.Create(
AnalyzerIdentifiers.ParallelScopeFixturesOnTestMethodUsage,
ParallelizableUsageAnalyzerConstants.ParallelScopeFixturesOnTestMethodMessage);

var testCode = TestUtility.WrapClassInNamespaceAndAddUsing(@"
[TestFixture]
public sealed class AnalyzeWhenAttributeIsOnParametricTestMethodIsParallelScopeFixtures
{
[Test]
[↓Parallelizable(ParallelScope.Fixtures)]
public void Test([Values(1, 2, 3)] int data)
{
}
}");
AnalyzerAssert.Diagnostics(analyzer, expectedDiagnostic, testCode);
}

private static IEnumerable<ParallelScope> ParallelScopesExceptFixtures =>
new ParallelScope[] { ParallelScope.All, ParallelScope.Children, ParallelScope.Self };
}
}
4 changes: 3 additions & 1 deletion src/nunit.analyzers/Constants/AnalyzerIdentifiers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ internal static class AnalyzerIdentifiers
internal const string TestMethodExpectedResultTypeMismatchUsage = "NUNIT_11";
internal const string TestMethodSpecifiedExpectedResultForVoidUsage = "NUNIT_12";
internal const string TestMethodNoExpectedResultButNonVoidReturnType = "NUNIT_13";

internal const string ParallelScopeSelfNoEffectOnAssemblyUsage = "NUNIT_14";
internal const string ParallelScopeChildrenOnNonParameterizedTestMethodUsage = "NUNIT_15";
internal const string ParallelScopeFixturesOnTestMethodUsage = "NUNIT_16";
}
}
3 changes: 3 additions & 0 deletions src/nunit.analyzers/Constants/NunitFrameworkConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ public static class NunitFrameworkConstants
public const string FullNameOfTypeTestCaseAttribute = "NUnit.Framework.TestCaseAttribute";
public const string FullNameOfTypeTestCaseSourceAttribute = "NUnit.Framework.TestCaseSourceAttribute";
public const string FullNameOfTypeTestAttribute = "NUnit.Framework.TestAttribute";
public const string FullNameOfTypeParallelizableAttribute = "NUnit.Framework.ParallelizableAttribute";
public const string FullNameOfTypeITestBuilder = "NUnit.Framework.Interfaces.ITestBuilder";

public const string NameOfTestCaseAttribute = "TestCaseAttribute";
public const string NameOfTestCaseSourceAttribute = "TestCaseSourceAttribute";
public const string NameOfTestAttribute = "TestAttribute";
public const string NameOfParallelizableAttribute = "ParallelizableAttribute";

public const string NameOfExpectedResult = "ExpectedResult";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace NUnit.Analyzers.Constants
{
class ParallelizableUsageAnalyzerConstants
{
internal const string Title = "Find Incorrect ParallelizableAttribute Usage";
internal const string ParallelScopeSelfNoEffectOnAssemblyMessage = "Specifying ParallelScope.Self on assembly level has no effect";
internal const string ParallelScopeChildrenOnNonParameterizedTestMethodMessage = "One may not specify ParallelScope.Children on a non-parameterized test method";
internal const string ParallelScopeFixturesOnTestMethodMessage = "One may not specify ParallelScope.Fixtures on a test method";

internal class ParallelScope
{
internal const int Self = 1;
internal const int Children = 256;
internal const int Fixtures = 512;
}
}
}
Loading