Restructure DoNotUseConfigureAwaitTests
This commit is contained in:
Родитель
a34764a363
Коммит
a041234dfb
|
@ -4,7 +4,7 @@ using Verify = CSharpVerifier<Xunit.Analyzers.DoNotUseConfigureAwait>;
|
|||
public class DoNotUseConfigureAwaitTests
|
||||
{
|
||||
[Fact]
|
||||
public async void SuccessCase_NoCall()
|
||||
public async void NoCall_DoesNotTrigger()
|
||||
{
|
||||
var source = @"
|
||||
using System.Threading.Tasks;
|
||||
|
@ -20,10 +20,28 @@ public class TestClass {
|
|||
await Verify.VerifyAnalyzer(source);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void SuccessCase_ConfigureAwaitTrue()
|
||||
public class ConfigureAwait_Boolean
|
||||
{
|
||||
var source = @"
|
||||
[Fact]
|
||||
public async void NonTestMethod_DoesNotTrigger()
|
||||
{
|
||||
var source = @"
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
public class NonTestClass {
|
||||
public async Task NonTestMethod() {
|
||||
await Task.Delay(1).ConfigureAwait(false);
|
||||
}
|
||||
}";
|
||||
|
||||
await Verify.VerifyAnalyzer(source);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void True_DoesNotTrigger()
|
||||
{
|
||||
var source = @"
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
|
@ -34,120 +52,138 @@ public class TestClass {
|
|||
}
|
||||
}";
|
||||
|
||||
await Verify.VerifyAnalyzer(source);
|
||||
}
|
||||
await Verify.VerifyAnalyzer(source);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("false")]
|
||||
[InlineData("1 == 2")]
|
||||
public async void FailureCase_Task_Async(string argumentValue)
|
||||
{
|
||||
var source = @$"
|
||||
public static TheoryData<string> InvalidValues = new()
|
||||
{
|
||||
"false", // Literal false
|
||||
"1 == 2", // Logical false (we don't compute)
|
||||
"1 == 1", // Logical true (we don't compute)
|
||||
"booleanVar", // Reference value (we don't do lookup)
|
||||
};
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(InvalidValues))]
|
||||
public async void InvalidValue_TaskWithAwait_Triggers(string argumentValue)
|
||||
{
|
||||
var source = @$"
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
public class TestClass {{
|
||||
[Fact]
|
||||
public async Task TestMethod() {{
|
||||
await Task.Delay(1).[|ConfigureAwait({argumentValue})|];
|
||||
var booleanVar = true;
|
||||
await Task.Delay(1).ConfigureAwait({argumentValue});
|
||||
}}
|
||||
}}";
|
||||
var expected =
|
||||
Verify
|
||||
.Diagnostic()
|
||||
.WithSpan(9, 29, 9, 45 + argumentValue.Length)
|
||||
.WithArguments(argumentValue, "Omit ConfigureAwait, or use ConfigureAwait(true) to avoid CA2007.");
|
||||
|
||||
await Verify.VerifyAnalyzer(source);
|
||||
}
|
||||
await Verify.VerifyAnalyzer(source, expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("false")]
|
||||
[InlineData("1 == 2")]
|
||||
public async void FailureCase_Task_NonAsync(string argumentValue)
|
||||
{
|
||||
var source = @$"
|
||||
[Theory]
|
||||
[MemberData(nameof(InvalidValues))]
|
||||
public async void InvalidValue_TaskWithoutAwait_Triggers(string argumentValue)
|
||||
{
|
||||
var source = @$"
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
public class TestClass {{
|
||||
[Fact]
|
||||
public void TestMethod() {{
|
||||
Task.Delay(1).[|ConfigureAwait({argumentValue})|].GetAwaiter().GetResult();
|
||||
var booleanVar = true;
|
||||
Task.Delay(1).ConfigureAwait({argumentValue}).GetAwaiter().GetResult();
|
||||
}}
|
||||
}}";
|
||||
var expected =
|
||||
Verify
|
||||
.Diagnostic()
|
||||
.WithSpan(9, 23, 9, 39 + argumentValue.Length)
|
||||
.WithArguments(argumentValue, "Omit ConfigureAwait, or use ConfigureAwait(true) to avoid CA2007.");
|
||||
|
||||
await Verify.VerifyAnalyzer(source);
|
||||
}
|
||||
await Verify.VerifyAnalyzer(source, expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("false")]
|
||||
[InlineData("1 == 2")]
|
||||
public async void FailureCase_TaskOfT(string argumentValue)
|
||||
{
|
||||
var source = @$"
|
||||
[Theory]
|
||||
[MemberData(nameof(InvalidValues))]
|
||||
public async void InvalidValue_TaskOfT_Triggers(string argumentValue)
|
||||
{
|
||||
var source = @$"
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
public class TestClass {{
|
||||
[Fact]
|
||||
public async Task TestMethod() {{
|
||||
var booleanVar = true;
|
||||
var task = Task.FromResult(42);
|
||||
await task.[|ConfigureAwait({argumentValue})|];
|
||||
await task.ConfigureAwait({argumentValue});
|
||||
}}
|
||||
}}";
|
||||
var expected =
|
||||
Verify
|
||||
.Diagnostic()
|
||||
.WithSpan(10, 20, 10, 36 + argumentValue.Length)
|
||||
.WithArguments(argumentValue, "Omit ConfigureAwait, or use ConfigureAwait(true) to avoid CA2007.");
|
||||
|
||||
await Verify.VerifyAnalyzer(source);
|
||||
}
|
||||
await Verify.VerifyAnalyzer(source, expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("false")]
|
||||
[InlineData("1 == 2")]
|
||||
public async void FailureCase_ValueTask(string argumentValue)
|
||||
{
|
||||
var source = @$"
|
||||
[Theory]
|
||||
[MemberData(nameof(InvalidValues))]
|
||||
public async void InvalidValue_ValueTask_Triggers(string argumentValue)
|
||||
{
|
||||
var source = @$"
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
public class TestClass {{
|
||||
[Fact]
|
||||
public async Task TestMethod() {{
|
||||
var booleanVar = true;
|
||||
var valueTask = default(ValueTask);
|
||||
await valueTask.[|ConfigureAwait({argumentValue})|];
|
||||
await valueTask.ConfigureAwait({argumentValue});
|
||||
}}
|
||||
}}";
|
||||
var expected =
|
||||
Verify
|
||||
.Diagnostic()
|
||||
.WithSpan(10, 25, 10, 41 + argumentValue.Length)
|
||||
.WithArguments(argumentValue, "Omit ConfigureAwait, or use ConfigureAwait(true) to avoid CA2007.");
|
||||
|
||||
await Verify.VerifyAnalyzer(source);
|
||||
}
|
||||
await Verify.VerifyAnalyzer(source, expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("false")]
|
||||
[InlineData("1 == 2")]
|
||||
public async void FailureCase_ValueTaskOfT(string argumentValue)
|
||||
{
|
||||
var source = @$"
|
||||
[Theory]
|
||||
[MemberData(nameof(InvalidValues))]
|
||||
public async void InvalidValue_ValueTaskOfT_Triggers(string argumentValue)
|
||||
{
|
||||
var source = @$"
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
public class TestClass {{
|
||||
[Fact]
|
||||
public async Task TestMethod() {{
|
||||
var booleanVar = true;
|
||||
var valueTask = default(ValueTask<int>);
|
||||
await valueTask.[|ConfigureAwait({argumentValue})|];
|
||||
await valueTask.ConfigureAwait({argumentValue});
|
||||
}}
|
||||
}}";
|
||||
var expected =
|
||||
Verify
|
||||
.Diagnostic()
|
||||
.WithSpan(10, 25, 10, 41 + argumentValue.Length)
|
||||
.WithArguments(argumentValue, "Omit ConfigureAwait, or use ConfigureAwait(true) to avoid CA2007.");
|
||||
|
||||
await Verify.VerifyAnalyzer(source);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void IgnoredCase()
|
||||
{
|
||||
var source = @"
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
public class NonTestClass {
|
||||
public async Task NonTestMethod() {
|
||||
await Task.Delay(1).ConfigureAwait(false);
|
||||
}
|
||||
}";
|
||||
|
||||
await Verify.VerifyAnalyzer(source);
|
||||
await Verify.VerifyAnalyzer(source, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -310,7 +310,7 @@ public static class Descriptors
|
|||
"Do not call ConfigureAwait(false) in test method",
|
||||
Usage,
|
||||
Warning,
|
||||
"Test methods should not call ConfigureAwait(false), as it may bypass parallelization limits. Omit ConfigureAwait, or use ConfigureAwait(true) to avoid CA2007."
|
||||
"Test methods should not call ConfigureAwait({0}), as it may bypass parallelization limits. {1}"
|
||||
);
|
||||
|
||||
public static DiagnosticDescriptor X1031_DoNotUseBlockingTaskOperations { get; } =
|
||||
|
|
|
@ -66,13 +66,25 @@ public class DoNotUseConfigureAwait : XunitDiagnosticAnalyzer
|
|||
if (invocationChildren.Count != 2)
|
||||
return;
|
||||
|
||||
// we want to exempt calls with "(true)" because of CA2007
|
||||
// We only care about invocations with a single parameter
|
||||
var arguments = invocationChildren[1];
|
||||
var argumentChildren = arguments.ChildNodes().ToList();
|
||||
if (argumentChildren.Count == 1 &&
|
||||
argumentChildren[0] is ArgumentSyntax argumentSyntax &&
|
||||
argumentSyntax.ChildNodes().FirstOrDefault() is LiteralExpressionSyntax literalExpression &&
|
||||
literalExpression.IsKind(SyntaxKind.TrueLiteralExpression))
|
||||
if (argumentChildren.Count != 1 || argumentChildren[0] is not ArgumentSyntax argumentSyntax)
|
||||
return;
|
||||
|
||||
// Determine the invocation type and resolution
|
||||
var parameterType = invocation.TargetMethod.Parameters[0].Type;
|
||||
string resolution;
|
||||
|
||||
// We want to exempt calls with "(true)" because of CA2007
|
||||
if (SymbolEqualityComparer.Default.Equals(parameterType, context.Compilation.GetSpecialType(SpecialType.System_Boolean)))
|
||||
{
|
||||
if (argumentSyntax.Expression is LiteralExpressionSyntax literalExpression && literalExpression.IsKind(SyntaxKind.TrueLiteralExpression))
|
||||
return;
|
||||
|
||||
resolution = "Omit ConfigureAwait, or use ConfigureAwait(true) to avoid CA2007.";
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
// First child node should be split into three pieces: "(some other code)", ".", and "ConfigureAwait"
|
||||
|
@ -85,7 +97,7 @@ public class DoNotUseConfigureAwait : XunitDiagnosticAnalyzer
|
|||
var textSpan = new TextSpan(methodCallChildren[2].SpanStart, length);
|
||||
var location = Location.Create(invocation.Syntax.SyntaxTree, textSpan);
|
||||
|
||||
context.ReportDiagnostic(Diagnostic.Create(Descriptors.X1030_DoNotUseConfigureAwait, location));
|
||||
context.ReportDiagnostic(Diagnostic.Create(Descriptors.X1030_DoNotUseConfigureAwait, location, argumentSyntax.ToFullString(), resolution));
|
||||
}, OperationKind.Invocation);
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче