Skip to content

Commit

Permalink
xunit/xunit#2932: Add xUnit1050 to report ClassData pointing at class…
Browse files Browse the repository at this point in the history
… returning IEnumerable<object[]> as untyped
  • Loading branch information
bradwilson committed May 23, 2024
1 parent f80b09d commit 9869bce
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 109 deletions.
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ dotnet_diagnostic.CA1000.severity = none # Do not declare static members on ge
dotnet_diagnostic.CA1002.severity = none # Do not expose generic lists
dotnet_diagnostic.CA1014.severity = none # Mark assemblies with CLSCompliantAttribute
dotnet_diagnostic.CA1034.severity = none # Do not nest types
dotnet_diagnostic.CA1303.severity = none # Do not pass literals as localized parameters
dotnet_diagnostic.CA1707.severity = none # Remove the underscores from type name
dotnet_diagnostic.CA1720.severity = none # Identifier contains type name
dotnet_diagnostic.CA1724.severity = none # Type names should not match namespaces
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public void TestMethod() { }
}";

[Fact]
public async Task SuccessCase()
public async Task SuccessCaseV2()
{
var dataClassSource = @"
using System.Collections;
Expand All @@ -26,69 +26,76 @@ class DataClass: IEnumerable<object[]> {
IEnumerator IEnumerable.GetEnumerator() => null;
}";

await Verify.VerifyAnalyzer(new[] { TestMethodSource, dataClassSource });
await Verify.VerifyAnalyzerV2(LanguageVersion.CSharp7_1, [TestMethodSource, dataClassSource]);
}

public static TheoryData<string> SuccessCasesV3Data = new()
{
// IAsyncEnumerable<object[]>
// IEnumerable<ITheoryDataRow<>>
@"
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using Xunit;
public class DataClass : IAsyncEnumerable<object[]> {
public IAsyncEnumerator<object[]> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null;
class DataClass: IEnumerable<TheoryDataRow<int>> {
public IEnumerator<TheoryDataRow<int>> GetEnumerator() => null;
IEnumerator IEnumerable.GetEnumerator() => null;
}",
// IEnumerable<ITheoryDataRow>
// IAsyncEnumerable<TheoryDataRow<>>
@"
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using Xunit;
class DataClass: IEnumerable<ITheoryDataRow> {
public IEnumerator<ITheoryDataRow> GetEnumerator() => null;
IEnumerator IEnumerable.GetEnumerator() => null;
public class DataClass : IAsyncEnumerable<TheoryDataRow<int>> {
public IAsyncEnumerator<TheoryDataRow<int>> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null;
}",
// IAsyncEnumerable<ITheoryDataRow>
// IAsyncEnumerable<DerivedTheoryDataRow>
@"
using System.Collections.Generic;
using System.Threading;
using Xunit;
public class DataClass : IAsyncEnumerable<ITheoryDataRow> {
public IAsyncEnumerator<ITheoryDataRow> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null;
public class DataClass : IAsyncEnumerable<DerivedTheoryDataRow> {
public IAsyncEnumerator<DerivedTheoryDataRow> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null;
}
public class DerivedTheoryDataRow : TheoryDataRow<int> {
public DerivedTheoryDataRow(int t) : base(t) { }
}",
};

[Theory]
[MemberData(nameof(SuccessCasesV3Data))]
public async Task SuccessCases_V3(string dataClassSource)
public async Task SuccessCasesV3(string dataClassSource)
{
await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp8, new[] { TestMethodSource, dataClassSource });
await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp7_1, [TestMethodSource, dataClassSource]);
}

public static TheoryData<string> FailureCases = new()
public class X1007_ClassDataAttributeMustPointAtValidClass
{
// Incorrect enumeration type (object instead of object[])
@"
public static TheoryData<string> FailureCasesData = new()
{
// Incorrect enumeration type (object instead of object[])
@"
using System.Collections;
using System.Collections.Generic;
class DataClass: IEnumerable<object> {
public IEnumerator<object> GetEnumerator() => null;
IEnumerator IEnumerable.GetEnumerator() => null;
}",
// Abstract class
@"
// Abstract class
@"
using System.Collections;
using System.Collections.Generic;
abstract class DataClass: IEnumerable<object[]> {
public IEnumerator<object[]> GetEnumerator() => null;
IEnumerator IEnumerable.GetEnumerator() => null;
}",
// Missing parameterless constructor
@"
// Missing parameterless constructor
@"
using System.Collections;
using System.Collections.Generic;
Expand All @@ -97,8 +104,8 @@ public DataClass(string parameter) { }
public IEnumerator<object[]> GetEnumerator() => null;
IEnumerator IEnumerable.GetEnumerator() => null;
}",
// Parameterless constructor is internal
@"
// Parameterless constructor is internal
@"
using System.Collections;
using System.Collections.Generic;
Expand All @@ -107,8 +114,8 @@ internal DataClass() { }
public IEnumerator<object[]> GetEnumerator() => null;
IEnumerator IEnumerable.GetEnumerator() => null;
}",
// Parameterless constructor is private
@"
// Parameterless constructor is private
@"
using System.Collections;
using System.Collections.Generic;
Expand All @@ -117,44 +124,120 @@ private DataClass() { }
public IEnumerator<object[]> GetEnumerator() => null;
IEnumerator IEnumerable.GetEnumerator() => null;
}",
};
};

[Theory]
[MemberData(nameof(FailureCasesData))]
public async Task FailureCases(string dataClassSource)
{
var expectedV2 =
Verify
.Diagnostic("xUnit1007")
.WithSpan(6, 23, 6, 32)
.WithArguments("DataClass", "IEnumerable<object[]>");
var expectedV3 =
Verify
.Diagnostic("xUnit1007")
.WithSpan(6, 23, 6, 32)
.WithArguments("DataClass", "IEnumerable<object[]>, IAsyncEnumerable<object[]>, IEnumerable<ITheoryDataRow>, or IAsyncEnumerable<ITheoryDataRow>");

await Verify.VerifyAnalyzerV2([TestMethodSource, dataClassSource], expectedV2);
await Verify.VerifyAnalyzerV3([TestMethodSource, dataClassSource], expectedV3);
}

[Fact]
public async Task IAsyncEnumerableNotSupportedInV2()
{
var dataClassSource = @"
using System.Collections.Generic;
using System.Threading;
[Theory]
[MemberData(nameof(FailureCases))]
public async Task FailureCase(string dataClassSource)
{
var expectedV2 =
Verify
.Diagnostic()
.WithSpan(6, 23, 6, 32)
.WithArguments("DataClass", "IEnumerable<object[]>");
var expectedV3 =
Verify
.Diagnostic()
.WithSpan(6, 23, 6, 32)
.WithArguments("DataClass", "IEnumerable<object[]>, IAsyncEnumerable<object[]>, IEnumerable<ITheoryDataRow>, or IAsyncEnumerable<ITheoryDataRow>");

await Verify.VerifyAnalyzerV2(new[] { TestMethodSource, dataClassSource }, expectedV2);
await Verify.VerifyAnalyzerV3(new[] { TestMethodSource, dataClassSource }, expectedV3);
public class DataClass : IAsyncEnumerable<object[]> {
public IAsyncEnumerator<object[]> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null;
}";
var expected =
Verify
.Diagnostic("xUnit1007")
.WithSpan(6, 23, 6, 32)
.WithArguments("DataClass", "IEnumerable<object[]>");

await Verify.VerifyAnalyzerV2(LanguageVersion.CSharp7_1, [TestMethodSource, dataClassSource], expected);
}
}

[Fact]
public async Task IAsyncEnumerableSupportedOnlyInV3()
public class X1050_ClassDataTheoryDataRowIsRecommendedForStronglyTypedAnalysis
{
var dataClassSource = @"
public static TheoryData<string> FailureCasesData = new()
{
// IEnumerable<object[]>
@"
using System.Collections;
using System.Collections.Generic;
using Xunit;
public class DataClass : IEnumerable<object[]> {
public IEnumerator<object[]> GetEnumerator() => null;
IEnumerator IEnumerable.GetEnumerator() => null;
}",
// IAsyncEnumerable<object[]>
@"
using System.Collections.Generic;
using System.Threading;
using Xunit;
public class DataClass : IAsyncEnumerable<object[]> {
public IAsyncEnumerator<object[]> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null;
}";
var expectedV2 =
Verify
.Diagnostic()
.WithSpan(6, 23, 6, 32)
.WithArguments("DataClass", "IEnumerable<object[]>");

await Verify.VerifyAnalyzerV2(LanguageVersion.CSharp8, new[] { TestMethodSource, dataClassSource }, expectedV2);
await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp8, new[] { TestMethodSource, dataClassSource });
}",
// IEnumerable<ITheoryDataRow>
@"
using System.Collections;
using System.Collections.Generic;
using Xunit;
public class DataClass : IEnumerable<ITheoryDataRow> {
public IEnumerator<ITheoryDataRow> GetEnumerator() => null;
IEnumerator IEnumerable.GetEnumerator() => null;
}",
// IAsyncEnumerable<ITheoryDataRow>
@"
using System.Collections.Generic;
using System.Threading;
using Xunit;
public class DataClass : IAsyncEnumerable<ITheoryDataRow> {
public IAsyncEnumerator<ITheoryDataRow> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null;
}",
// IEnumerable<TheoryDataRow>
@"
using System.Collections;
using System.Collections.Generic;
using Xunit;
public class DataClass : IEnumerable<TheoryDataRow> {
public IEnumerator<TheoryDataRow> GetEnumerator() => null;
IEnumerator IEnumerable.GetEnumerator() => null;
}",
// IAsyncEnumerable<TheoryDataRow>
@"
using System.Collections.Generic;
using System.Threading;
using Xunit;
public class DataClass : IAsyncEnumerable<TheoryDataRow> {
public IAsyncEnumerator<TheoryDataRow> GetAsyncEnumerator(CancellationToken cancellationToken = default) => null;
}",
};

[Theory]
[MemberData(nameof(FailureCasesData))]
public async Task FailureCases(string dataClassSource)
{
var expected =
Verify
.Diagnostic("xUnit1050")
.WithSpan(6, 23, 6, 32);

await Verify.VerifyAnalyzerV3(LanguageVersion.CSharp7_1, [TestMethodSource, dataClassSource], expected);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ public class TestData {
public class TestClass {
[Theory]
[ClassData(typeof([|TestData|]))]
[ClassData(typeof({|xUnit1007:TestData|}))]
public void TestMethod(int _) { }
}";

var after = @"
var afterV2 = @"
using System.Collections.Generic;
using Xunit;
Expand All @@ -34,8 +33,10 @@ public class TestClass {
[ClassData(typeof(TestData))]
public void TestMethod(int _) { }
}";
var afterV3 = afterV2.Replace("typeof(TestData)", "typeof({|xUnit1050:TestData|})");

await Verify.VerifyCodeFix(before, after, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass);
await Verify.VerifyCodeFixV2(before, afterV2, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass);
await Verify.VerifyCodeFixV3(before, afterV3, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass);
}

[Fact]
Expand All @@ -55,11 +56,10 @@ public class TestData : IEnumerable<object[]> {
public class TestClass {
[Theory]
[ClassData(typeof([|TestData|]))]
[ClassData(typeof({|xUnit1007:TestData|}))]
public void TestMethod(int _) { }
}";

var after = @"
var afterV2 = @"
using System.Collections;
using System.Collections.Generic;
using Xunit;
Expand All @@ -76,8 +76,10 @@ public class TestClass {
[ClassData(typeof(TestData))]
public void TestMethod(int _) { }
}";
var afterV3 = afterV2.Replace("typeof(TestData)", "typeof({|xUnit1050:TestData|})");

await Verify.VerifyCodeFix(before, after, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass);
await Verify.VerifyCodeFixV2(before, afterV2, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass);
await Verify.VerifyCodeFixV3(before, afterV3, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass);
}

[Fact]
Expand All @@ -97,11 +99,10 @@ public class TestData : IEnumerable<object[]> {
public class TestClass {
[Theory]
[ClassData(typeof([|TestData|]))]
[ClassData(typeof({|xUnit1007:TestData|}))]
public void TestMethod(int _) { }
}";

var after = @"
var afterV2 = @"
using System.Collections;
using System.Collections.Generic;
using Xunit;
Expand All @@ -122,8 +123,10 @@ public class TestClass {
[ClassData(typeof(TestData))]
public void TestMethod(int _) { }
}";
var afterV3 = afterV2.Replace("typeof(TestData)", "typeof({|xUnit1050:TestData|})");

await Verify.VerifyCodeFix(before, after, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass);
await Verify.VerifyCodeFixV2(before, afterV2, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass);
await Verify.VerifyCodeFixV3(before, afterV3, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass);
}

[Fact]
Expand All @@ -141,11 +144,10 @@ public abstract class TestData : IEnumerable<object[]> {
public class TestClass {
[Theory]
[ClassData(typeof([|TestData|]))]
[ClassData(typeof({|xUnit1007:TestData|}))]
public void TestMethod(int _) { }
}";

var after = @"
var afterV2 = @"
using System.Collections;
using System.Collections.Generic;
using Xunit;
Expand All @@ -160,7 +162,9 @@ public class TestClass {
[ClassData(typeof(TestData))]
public void TestMethod(int _) { }
}";
var afterV3 = afterV2.Replace("typeof(TestData)", "typeof({|xUnit1050:TestData|})");

await Verify.VerifyCodeFix(before, after, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass);
await Verify.VerifyCodeFixV2(before, afterV2, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass);
await Verify.VerifyCodeFixV3(before, afterV3, ClassDataAttributeMustPointAtValidClassFixer.Key_FixDataClass);
}
}
Loading

0 comments on commit 9869bce

Please sign in to comment.