Skip to content

Commit

Permalink
Merge pull request #773 from manfred-brands/Issue767_GenericTestCase
Browse files Browse the repository at this point in the history
Add support for checking TestCase<> parameters
  • Loading branch information
mikkelbu authored Aug 7, 2024
2 parents ab3d365 + 7c96200 commit a00035e
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 3 deletions.
6 changes: 6 additions & 0 deletions documentation/NUnit1001.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ public void SampleTest(int numberValue)
{
Assert.That(numberValue, Is.EqualTo(1));
}

[TestCase<double>(42)]
public void SampleTest(int numberValue)
{
Assert.That(numberValue, Is.EqualTo(1));
}
```

### Problem
Expand Down
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<PropertyGroup>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
<LangVersion>10.0</LangVersion>
<LangVersion>preview</LangVersion>
<!-- Counter intuitive, but this only enabled the pre-shipped analyzer, we configure them below -->
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
Expand Down
3 changes: 2 additions & 1 deletion src/nunit.analyzers.tests/SetUpFixture.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Gu.Roslyn.Asserts;
using Microsoft.CodeAnalysis.CSharp;
using NUnit.Framework;

#if NUNIT4
Expand All @@ -16,7 +17,7 @@ public void SetDefaults()
Settings.Default = Settings.Default
#if NUNIT4
.WithMetadataReferences(MetadataReferences.Transitive(typeof(Assert), typeof(ClassicAssert)))
.WithParseOption(Settings.Default.ParseOptions.WithPreprocessorSymbols("NUNIT4"));
.WithParseOption(new CSharpParseOptions(LanguageVersion.Preview).WithPreprocessorSymbols("NUNIT4"));
#else
.WithMetadataReferences(MetadataReferences.Transitive(typeof(Assert)));
#endif
Expand Down
120 changes: 120 additions & 0 deletions src/nunit.analyzers.tests/TestCaseUsage/TestCaseUsageAnalyzerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,57 @@ namespace NUnit.Analyzers.Tests.TestCaseUsage
[TestFixture]
public sealed class TestCaseUsageAnalyzerTests
{
#if NUNIT4
#if NET6_0_OR_GREATER
// This can go once NUnit 4.2.0 is released and we update our reference.
private const string GenericTestCaseAttributeSource = """
using System;

namespace NUnit.Framework
{
#pragma warning disable CA1019 // Define accessors for attribute arguments

/// <summary>
/// Marks a method as a parameterized test suite and provides arguments for each test case.
/// </summary>
/// <typeparam name="T">The type of the argument for the test case.</typeparam>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public sealed class TestCaseAttribute<T> : TestCaseAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="TestCaseAttribute{T}"/> class.
/// </summary>
/// <param name="argument">The argument for the test case.</param>
public TestCaseAttribute(T argument)
: base(new object?[] { argument })
{
this.TypeArgs = new[] { typeof(T) };
}
}

/// <summary>
/// Marks a method as a parameterized test suite and provides arguments for each test case.
/// </summary>
/// <typeparam name="T">The type of the argument for the test case.</typeparam>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public sealed class TestCaseAttribute<T1, T2> : TestCaseAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="TestCaseAttribute{T1,T2}"/> class.
/// </summary>
/// <param name="argument">The argument for the test case.</param>
public TestCaseAttribute(T1 argument1, T2 argument2)
: base(new object?[] { argument1, argument2 })
{
this.TypeArgs = new[] { typeof(T1), typeof(T2) };
}
}
}

""";
#endif
#endif

private readonly DiagnosticAnalyzer analyzer = new TestCaseUsageAnalyzer();

private static IEnumerable<TestCaseData> SpecialConversions
Expand Down Expand Up @@ -757,6 +808,75 @@ public void TestWithGenericParameter<T>(T arg1) { }
}

#if NUNIT4
#if NET6_0_OR_GREATER
[Test]
public void AnalyzeWhenArgumentIsCorrectGenericTypeParameter()
{
var testCode = TestUtility.WrapClassInNamespaceAndAddUsing(@"
class AnalyzeWhenArgumentIsGenericTypeParameter
{
[TestCase<byte>(2)]
public void Test(byte a) { }
}");
RoslynAssert.Valid(this.analyzer, GenericTestCaseAttributeSource, testCode);
}

[Test]
public void AnalyzeWhenArgumentsAreCorrectGenericTypeParameter()
{
var testCode = TestUtility.WrapClassInNamespaceAndAddUsing(@"
class AnalyzeWhenArgumentIsGenericTypeParameter
{
[TestCase<byte, uint>(2, 3)]
public void Test(byte a, uint b) { }
}");
RoslynAssert.Valid(this.analyzer, GenericTestCaseAttributeSource, testCode);
}

[Test]
public void AnalyzeWhenArgumentIsWrongGenericTypeParameter()
{
var testCode = TestUtility.WrapClassInNamespaceAndAddUsing(@"
class AnalyzeWhenArgumentIsGenericTypeParameter
{
[TestCase<double>(↓2)]
public void Test(int a) { }
}");
RoslynAssert.Diagnostics(this.analyzer,
ExpectedDiagnostic.Create(AnalyzerIdentifiers.TestCaseParameterTypeMismatchUsage),
GenericTestCaseAttributeSource, testCode);
}

[Test]
public void AnalyzeWhenArgumentsAreWrongGenericTypeParameter()
{
var testCode = TestUtility.WrapClassInNamespaceAndAddUsing(@"
class AnalyzeWhenArgumentIsGenericTypeParameter
{
[TestCase<double, int>(↓2, ↓3)]
public void Test(int a, uint b) { }
}");
RoslynAssert.Diagnostics(this.analyzer,
ExpectedDiagnostic.Create(AnalyzerIdentifiers.TestCaseParameterTypeMismatchUsage),
GenericTestCaseAttributeSource, testCode);
}

[Test]
public void AnalyzeWhenTestMethodHasTypeParameterArgumentTypeAndGenericTestCase()
{
var testCode = TestUtility.WrapClassInNamespaceAndAddUsing(@"
public sealed class AnalyzeWhenTestMethodHasTypeParameterArgumentType
{
[TestCase<int>(1)]
[TestCase<uint>(1)]
[TestCase<float>(1)]
[TestCase<double>(1)]
public void TestWithGenericParameter<T>(T arg1) { }
}");
RoslynAssert.Valid(this.analyzer, GenericTestCaseAttributeSource, testCode);
}
#endif

[Test]
public void AnalyzeWhenTestMethodHasImplicitlySuppliedCancellationTokenParameterDueToCancelAfterOnMethod()
{
Expand Down
4 changes: 3 additions & 1 deletion src/nunit.analyzers/TestCaseUsage/TestCaseUsageAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ private static void AnalyzeMethod(

var testCaseAttributes = methodAttributes
.Where(a => a.ApplicationSyntaxReference is not null
&& SymbolEqualityComparer.Default.Equals(a.AttributeClass, testCaseType));
&& (SymbolEqualityComparer.Default.Equals(a.AttributeClass, testCaseType) ||
(a.AttributeClass is not null && a.AttributeClass.IsGenericType &&
SymbolEqualityComparer.Default.Equals(a.AttributeClass.BaseType, testCaseType))));

foreach (var attribute in testCaseAttributes)
{
Expand Down

0 comments on commit a00035e

Please sign in to comment.