Update xUnit2018 to vary its message based on whether assertion library supports exactMatch
This commit is contained in:
Родитель
350ab935b5
Коммит
6b8347c422
|
@ -1,20 +1,29 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Xunit;
|
||||
using Xunit.Analyzers;
|
||||
using Verify = CSharpVerifier<Xunit.Analyzers.AssertIsTypeShouldNotBeUsedForAbstractType>;
|
||||
using Verify_v2_Pre2_9_3 = CSharpVerifier<AssertIsTypeShouldNotBeUsedForAbstractTypeTests.Analyzer_v2_Pre2_9_3>;
|
||||
using Verify_v3_Pre0_6_0 = CSharpVerifier<AssertIsTypeShouldNotBeUsedForAbstractTypeTests.Analyzer_v3_Pre0_6_0>;
|
||||
|
||||
public class AssertIsTypeShouldNotBeUsedForAbstractTypeTests
|
||||
{
|
||||
public static TheoryData<string, string> Methods = new()
|
||||
public static TheoryData<string> Methods = ["IsType", "IsNotType"];
|
||||
public static TheoryData<string, string, bool> MethodsWithReplacements = new()
|
||||
{
|
||||
{ "IsType", "IsAssignableFrom" },
|
||||
{ "IsNotType", "IsNotAssignableFrom" },
|
||||
{ "IsType", "Assert.IsAssignableFrom", false },
|
||||
{ "IsType", "exactMatch: false", true },
|
||||
{ "IsNotType", "Assert.IsNotAssignableFrom", false },
|
||||
{ "IsNotType", "exactMatch: false", true },
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(Methods))]
|
||||
[MemberData(nameof(MethodsWithReplacements))]
|
||||
public async Task Interface_Triggers(
|
||||
string method,
|
||||
string replacement)
|
||||
string replacement,
|
||||
bool supportsInexactTypeAssertions)
|
||||
{
|
||||
var source = string.Format(/* lang=c#-test */ """
|
||||
using System;
|
||||
|
@ -28,14 +37,18 @@ public class AssertIsTypeShouldNotBeUsedForAbstractTypeTests
|
|||
""", method);
|
||||
var expected = Verify.Diagnostic().WithLocation(0).WithArguments("interface", "System.IDisposable", replacement);
|
||||
|
||||
await Verify.VerifyAnalyzer(source, expected);
|
||||
if (supportsInexactTypeAssertions)
|
||||
await Verify.VerifyAnalyzer(source, expected);
|
||||
else
|
||||
{
|
||||
await Verify_v2_Pre2_9_3.VerifyAnalyzer(source, expected);
|
||||
await Verify_v3_Pre0_6_0.VerifyAnalyzer(source, expected);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(Methods))]
|
||||
public async Task Interface_WithExactMatchFlag_TriggersForTrue(
|
||||
string method,
|
||||
string replacement)
|
||||
public async Task Interface_WithExactMatchFlag_TriggersForTrue(string method)
|
||||
{
|
||||
// We can only trigger when we know the literal true is being used; anything else,
|
||||
// we let the runtime figure it out.
|
||||
|
@ -48,20 +61,25 @@ public class AssertIsTypeShouldNotBeUsedForAbstractTypeTests
|
|||
var flag = true;
|
||||
|
||||
{{|#0:Assert.{0}<IDisposable>(new object(), true)|}};
|
||||
{{|#1:Assert.{0}<IDisposable>(new object(), exactMatch: true)|}};
|
||||
Assert.{0}<IDisposable>(new object(), flag);
|
||||
}}
|
||||
}}
|
||||
""", method);
|
||||
var expected = Verify.Diagnostic().WithLocation(0).WithArguments("interface", "System.IDisposable", replacement);
|
||||
var expected = new[] {
|
||||
Verify.Diagnostic().WithLocation(0).WithArguments("interface", "System.IDisposable", "exactMatch: false"),
|
||||
Verify.Diagnostic().WithLocation(1).WithArguments("interface", "System.IDisposable", "exactMatch: false"),
|
||||
};
|
||||
|
||||
await Verify.VerifyAnalyzer(source, expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(Methods))]
|
||||
[MemberData(nameof(MethodsWithReplacements))]
|
||||
public async Task AbstractClass_Triggers(
|
||||
string method,
|
||||
string replacement)
|
||||
string replacement,
|
||||
bool supportsInexactTypeAssertions)
|
||||
{
|
||||
var source = string.Format(/* lang=c#-test */ """
|
||||
using System.IO;
|
||||
|
@ -75,15 +93,21 @@ public class AssertIsTypeShouldNotBeUsedForAbstractTypeTests
|
|||
""", method);
|
||||
var expected = Verify.Diagnostic().WithLocation(0).WithArguments("abstract class", "System.IO.Stream", replacement);
|
||||
|
||||
await Verify.VerifyAnalyzer(source, expected);
|
||||
if (supportsInexactTypeAssertions)
|
||||
await Verify.VerifyAnalyzer(source, expected);
|
||||
else
|
||||
{
|
||||
await Verify_v2_Pre2_9_3.VerifyAnalyzer(source, expected);
|
||||
await Verify_v3_Pre0_6_0.VerifyAnalyzer(source, expected);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(Methods))]
|
||||
public async Task AbstractClass_WithExactMatchFlag_TriggersForTrue(
|
||||
string method,
|
||||
string replacement)
|
||||
public async Task AbstractClass_WithExactMatchFlag_TriggersForTrue(string method)
|
||||
{
|
||||
// We can only trigger when we know the literal true is being used; anything else,
|
||||
// we let the runtime figure it out.
|
||||
var source = string.Format(/* lang=c#-test */ """
|
||||
using System.IO;
|
||||
using Xunit;
|
||||
|
@ -93,20 +117,25 @@ public class AssertIsTypeShouldNotBeUsedForAbstractTypeTests
|
|||
var flag = true;
|
||||
|
||||
{{|#0:Assert.{0}<Stream>(new object(), true)|}};
|
||||
{{|#1:Assert.{0}<Stream>(new object(), exactMatch: true)|}};
|
||||
Assert.{0}<Stream>(new object(), flag);
|
||||
}}
|
||||
}}
|
||||
""", method);
|
||||
var expected = Verify.Diagnostic().WithLocation(0).WithArguments("abstract class", "System.IO.Stream", replacement);
|
||||
var expected = new[] {
|
||||
Verify.Diagnostic().WithLocation(0).WithArguments("abstract class", "System.IO.Stream", "exactMatch: false"),
|
||||
Verify.Diagnostic().WithLocation(1).WithArguments("abstract class", "System.IO.Stream", "exactMatch: false"),
|
||||
};
|
||||
|
||||
await Verify.VerifyAnalyzer(source, expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(Methods))]
|
||||
[MemberData(nameof(MethodsWithReplacements))]
|
||||
public async Task UsingStatic_Triggers(
|
||||
string method,
|
||||
string replacement)
|
||||
string replacement,
|
||||
bool supportsInexactTypeAssertions)
|
||||
{
|
||||
var source = string.Format(/* lang=c#-test */ """
|
||||
using System;
|
||||
|
@ -120,25 +149,49 @@ public class AssertIsTypeShouldNotBeUsedForAbstractTypeTests
|
|||
""", method);
|
||||
var expected = Verify.Diagnostic().WithLocation(0).WithArguments("interface", "System.IDisposable", replacement);
|
||||
|
||||
await Verify.VerifyAnalyzer(source, expected);
|
||||
if (supportsInexactTypeAssertions)
|
||||
await Verify.VerifyAnalyzer(source, expected);
|
||||
else
|
||||
{
|
||||
await Verify_v2_Pre2_9_3.VerifyAnalyzer(source, expected);
|
||||
await Verify_v3_Pre0_6_0.VerifyAnalyzer(source, expected);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(Methods))]
|
||||
public async Task NonAbstractClass_DoesNotTrigger(
|
||||
string method,
|
||||
string _)
|
||||
public async Task NonAbstractClass_DoesNotTrigger(string method)
|
||||
{
|
||||
var source = string.Format(/* lang=c#-test */ """
|
||||
using Xunit;
|
||||
|
||||
class TestClass {{
|
||||
void TestMethod() {{
|
||||
var flag = true;
|
||||
|
||||
Assert.{0}<string>(new object());
|
||||
Assert.{0}<string>(new object(), flag);
|
||||
Assert.{0}<string>(new object(), exactMatch: flag);
|
||||
Assert.{0}<string>(new object(), true);
|
||||
Assert.{0}<string>(new object(), exactMatch: true);
|
||||
Assert.{0}<string>(new object(), false);
|
||||
Assert.{0}<string>(new object(), exactMatch: false);
|
||||
}}
|
||||
}}
|
||||
""", method);
|
||||
|
||||
await Verify.VerifyAnalyzer(source);
|
||||
}
|
||||
|
||||
internal class Analyzer_v2_Pre2_9_3 : AssertIsTypeShouldNotBeUsedForAbstractType
|
||||
{
|
||||
protected override XunitContext CreateXunitContext(Compilation compilation) =>
|
||||
XunitContext.ForV2(compilation, new Version(2, 9, 2));
|
||||
}
|
||||
|
||||
internal class Analyzer_v3_Pre0_6_0 : AssertIsTypeShouldNotBeUsedForAbstractType
|
||||
{
|
||||
protected override XunitContext CreateXunitContext(Compilation compilation) =>
|
||||
XunitContext.ForV3(compilation, new Version(0, 5, 999));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -174,7 +174,7 @@ public static partial class Descriptors
|
|||
"Do not compare an object's exact type to an abstract class or interface",
|
||||
Assertions,
|
||||
Warning,
|
||||
"Do not compare an object's exact type to the {0} '{1}'. Use Assert.{2} instead."
|
||||
"Do not compare an object's exact type to the {0} '{1}'. Use {2} instead."
|
||||
);
|
||||
|
||||
// Note: X2019 was already covered by X2014, and should not be reused
|
||||
|
|
|
@ -59,8 +59,16 @@ public class AssertIsTypeShouldNotBeUsedForAbstractType : AssertUsageAnalyzerBas
|
|||
|
||||
var typeName = SymbolDisplay.ToDisplayString(type);
|
||||
|
||||
if (!ReplacementMethods.TryGetValue(invocationOperation.TargetMethod.Name, out var replacement))
|
||||
return;
|
||||
string? replacement;
|
||||
|
||||
if (xunitContext.Assert.SupportsInexactTypeAssertions)
|
||||
replacement = "exactMatch: false";
|
||||
else
|
||||
{
|
||||
if (!ReplacementMethods.TryGetValue(invocationOperation.TargetMethod.Name, out replacement))
|
||||
return;
|
||||
replacement = "Assert." + replacement;
|
||||
}
|
||||
|
||||
context.ReportDiagnostic(
|
||||
Diagnostic.Create(
|
||||
|
|
Загрузка…
Ссылка в новой задаче