From d7161448250a9f2ece844713dd422cfedf65c126 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Oct 2025 18:27:31 +0000 Subject: [PATCH 01/10] Initial plan From d4997e48a349d07415886a3cfcc9b63999719293 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Oct 2025 18:43:12 +0000 Subject: [PATCH 02/10] Add TaskRunTestMethodAttribute and update analyzer/fixer - Created TaskRunTestMethodAttribute that wraps test execution in Task.Run - Updated UseCooperativeCancellationForTimeoutAnalyzer resources to mention TaskRunTestMethod - Updated UseCooperativeCancellationForTimeoutFixer to offer TaskRunTestMethod as alternative - Added test for new code fix option - Updated PublicAPI.Unshipped.txt with new public API - Updated all XLF localization files Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com> --- .../CodeFixResources.resx | 3 + ...eCooperativeCancellationForTimeoutFixer.cs | 57 ++++++++++++- .../xlf/CodeFixResources.cs.xlf | 25 ++++++ .../xlf/CodeFixResources.de.xlf | 25 ++++++ .../xlf/CodeFixResources.es.xlf | 25 ++++++ .../xlf/CodeFixResources.fr.xlf | 25 ++++++ .../xlf/CodeFixResources.it.xlf | 25 ++++++ .../xlf/CodeFixResources.ja.xlf | 25 ++++++ .../xlf/CodeFixResources.ko.xlf | 25 ++++++ .../xlf/CodeFixResources.pl.xlf | 25 ++++++ .../xlf/CodeFixResources.pt-BR.xlf | 25 ++++++ .../xlf/CodeFixResources.ru.xlf | 25 ++++++ .../xlf/CodeFixResources.tr.xlf | 25 ++++++ .../xlf/CodeFixResources.zh-Hans.xlf | 25 ++++++ .../xlf/CodeFixResources.zh-Hant.xlf | 25 ++++++ src/Analyzers/MSTest.Analyzers/Resources.resx | 6 +- .../MSTest.Analyzers/xlf/Resources.cs.xlf | 12 +-- .../MSTest.Analyzers/xlf/Resources.de.xlf | 12 +-- .../MSTest.Analyzers/xlf/Resources.es.xlf | 12 +-- .../MSTest.Analyzers/xlf/Resources.fr.xlf | 12 +-- .../MSTest.Analyzers/xlf/Resources.it.xlf | 12 +-- .../MSTest.Analyzers/xlf/Resources.ja.xlf | 12 +-- .../MSTest.Analyzers/xlf/Resources.ko.xlf | 12 +-- .../MSTest.Analyzers/xlf/Resources.pl.xlf | 12 +-- .../MSTest.Analyzers/xlf/Resources.pt-BR.xlf | 12 +-- .../MSTest.Analyzers/xlf/Resources.ru.xlf | 12 +-- .../MSTest.Analyzers/xlf/Resources.tr.xlf | 12 +-- .../xlf/Resources.zh-Hans.xlf | 12 +-- .../xlf/Resources.zh-Hant.xlf | 12 +-- .../TestMethod/TaskRunTestMethodAttribute.cs | 85 +++++++++++++++++++ .../PublicAPI/PublicAPI.Unshipped.txt | 4 + ...tiveCancellationForTimeoutAnalyzerTests.cs | 38 +++++++++ 32 files changed, 591 insertions(+), 83 deletions(-) create mode 100644 src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx b/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx index 569baca6de..38e70f5be7 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/CodeFixResources.resx @@ -180,6 +180,9 @@ Use 'CooperativeCancellation = true' + + Use '[TaskRunTestMethod]' + Use 'Assert.{0}' instead of 'StringAssert' diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/UseCooperativeCancellationForTimeoutFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/UseCooperativeCancellationForTimeoutFixer.cs index a3052a78f1..26e4a525ca 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/UseCooperativeCancellationForTimeoutFixer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/UseCooperativeCancellationForTimeoutFixer.cs @@ -48,13 +48,25 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) return; } - // Register a code action that will invoke the fix + // Register code fix to add CooperativeCancellation = true context.RegisterCodeFix( CodeAction.Create( title: CodeFixResources.UseCooperativeCancellationForTimeoutFix, createChangedDocument: c => AddCooperativeCancellationAsync(context.Document, attributeSyntax, c), - equivalenceKey: nameof(UseCooperativeCancellationForTimeoutFixer)), + equivalenceKey: $"{nameof(UseCooperativeCancellationForTimeoutFixer)}.AddCooperativeCancellation"), diagnostic); + + // Register code fix to replace [TestMethod] with [TaskRunTestMethod] + // Find the test method to check if it uses [TestMethod] attribute + if (attributeSyntax.Parent?.Parent is MethodDeclarationSyntax methodDeclaration) + { + context.RegisterCodeFix( + CodeAction.Create( + title: CodeFixResources.UseTaskRunTestMethodFix, + createChangedDocument: c => ReplaceWithTaskRunTestMethodAsync(context.Document, methodDeclaration, c), + equivalenceKey: $"{nameof(UseCooperativeCancellationForTimeoutFixer)}.UseTaskRunTestMethod"), + diagnostic); + } } private static async Task AddCooperativeCancellationAsync(Document document, AttributeSyntax attributeSyntax, CancellationToken cancellationToken) @@ -118,4 +130,45 @@ private static async Task AddCooperativeCancellationAsync(Document doc return editor.GetChangedDocument(); } + + private static async Task ReplaceWithTaskRunTestMethodAsync(Document document, MethodDeclarationSyntax methodDeclaration, CancellationToken cancellationToken) + { + DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + + // Find the TestMethod attribute + AttributeSyntax? testMethodAttribute = null; + foreach (AttributeListSyntax attributeList in methodDeclaration.AttributeLists) + { + foreach (AttributeSyntax attribute in attributeList.Attributes) + { + string attributeName = attribute.Name.ToString(); + if (attributeName is "TestMethod" or "TestMethodAttribute") + { + testMethodAttribute = attribute; + break; + } + } + + if (testMethodAttribute is not null) + { + break; + } + } + + if (testMethodAttribute is null) + { + // No TestMethod attribute found, return unchanged document + return document; + } + + // Create the new TaskRunTestMethod attribute preserving any arguments + AttributeSyntax newAttribute = SyntaxFactory.Attribute( + SyntaxFactory.IdentifierName("TaskRunTestMethod"), + testMethodAttribute.ArgumentList); + + // Replace the TestMethod attribute with TaskRunTestMethod + editor.ReplaceNode(testMethodAttribute, newAttribute); + + return editor.GetChangedDocument(); + } } diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf index 8f9a0a8393..3c61950443 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.cs.xlf @@ -138,8 +138,33 @@ + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Use 'CooperativeCancellation = true' + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Použijte „CooperativeCancellation = true“ + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf index 374b6d3bde..8ddb440d14 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.de.xlf @@ -138,8 +138,33 @@ + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Use 'CooperativeCancellation = true' + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Verwenden Sie „CooperativeCancellation = true“. + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf index 66742db404..f4f4227367 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.es.xlf @@ -138,8 +138,33 @@ + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Use 'CooperativeCancellation = true' + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Usa "CooperativeCancellation = true" + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf index ec66aaf4da..e6b7432578 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.fr.xlf @@ -138,8 +138,33 @@ + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Use 'CooperativeCancellation = true' + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Utilisez 'CooperativeCancellation = true' + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf index fad6511ba2..8ebcf86586 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.it.xlf @@ -138,8 +138,33 @@ + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Use 'CooperativeCancellation = true' + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Usa "CooperativeCancellation = true" + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf index 422dd62c24..d5dc334ed9 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ja.xlf @@ -138,8 +138,33 @@ + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Use 'CooperativeCancellation = true' + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + 'CooperativeCancellation = true' を使用する + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf index 2bd87cf1af..a9b98227fc 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ko.xlf @@ -138,8 +138,33 @@ + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Use 'CooperativeCancellation = true' + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + 'CooperativeCancellation = true'를 사용하세요. + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf index 3ce2f0eda3..d1358104cf 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pl.xlf @@ -138,8 +138,33 @@ + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Use 'CooperativeCancellation = true' + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Użyj opcji „CooperativeCancellation = true” + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf index 3e7f19fb87..c04f99db77 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.pt-BR.xlf @@ -138,8 +138,33 @@ + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Use 'CooperativeCancellation = true' + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Usar “CooperativoCancellation = true” + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf index e442c31ee5..18c84c357f 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.ru.xlf @@ -138,8 +138,33 @@ + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Use 'CooperativeCancellation = true' + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Использовать "CooperativeCancellation = true" + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf index 28c764a1ea..0b130079be 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.tr.xlf @@ -138,8 +138,33 @@ + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Use 'CooperativeCancellation = true' + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + 'CooperativeCancellation = true' kullanın + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf index 6563ec971c..415845ae21 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hans.xlf @@ -138,8 +138,33 @@ + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Use 'CooperativeCancellation = true' + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + 使用 'CooperativeCancellation = true' + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf index bca5bd3526..bfa0092e03 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/xlf/CodeFixResources.zh-Hant.xlf @@ -138,8 +138,33 @@ + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + Use 'CooperativeCancellation = true' + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + 使用 'CooperativeCancellation = true' + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' + + + + + Use '[TaskRunTestMethod]' + Use '[TaskRunTestMethod]' diff --git a/src/Analyzers/MSTest.Analyzers/Resources.resx b/src/Analyzers/MSTest.Analyzers/Resources.resx index 0c8a17d969..e50ed6c081 100644 --- a/src/Analyzers/MSTest.Analyzers/Resources.resx +++ b/src/Analyzers/MSTest.Analyzers/Resources.resx @@ -589,13 +589,13 @@ The type declaring these methods should also respect the following rules: 'DataTestMethodAttribute' is obsolete and provides no additional functionality over 'TestMethodAttribute'. Use 'TestMethodAttribute' for all test methods, including parameterized tests. - Use 'CooperativeCancellation = true' with '[Timeout]' + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' - Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior and avoid dangling tasks - Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' or using '[TaskRunTestMethod]' is discouraged as it can lead to dangling tasks. Use '[TaskRunTestMethod]' to run tests in Task.Run with proper timeout handling, or set 'CooperativeCancellation = true' to enable cooperative cancellation behavior. TestContext property cannot be accessed in this context diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf index ba62836f66..e0aabea84f 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf @@ -900,18 +900,18 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: - Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. - Použití „[Timeout]“ bez explicitního nastavení „CooperativeCancellation = true“ se nedoporučuje. V budoucí verzi se kooperativní zrušení stane výchozím chováním. Nastavte „CooperativeCancellation = true“, abyste zapnuli doporučené chování a vyhnuli se změnám, které by mohly způsobit problémy. + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' or using '[TaskRunTestMethod]' is discouraged as it can lead to dangling tasks. Use '[TaskRunTestMethod]' to run tests in Task.Run with proper timeout handling, or set 'CooperativeCancellation = true' to enable cooperative cancellation behavior. + Použití „[Timeout]“ bez explicitního nastavení „CooperativeCancellation = true“ se nedoporučuje. V budoucí verzi se kooperativní zrušení stane výchozím chováním. Nastavte „CooperativeCancellation = true“, abyste zapnuli doporučené chování a vyhnuli se změnám, které by mohly způsobit problémy. - Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior - Použijte „CooperativeCancellation = true“ s „[Timeout]“, abyste povolili chování kooperativního zrušení + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior and avoid dangling tasks + Použijte „CooperativeCancellation = true“ s „[Timeout]“, abyste povolili chování kooperativního zrušení - Use 'CooperativeCancellation = true' with '[Timeout]' - Použijte „CooperativeCancellation = true“ s „[Timeout]“ + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' + Použijte „CooperativeCancellation = true“ s „[Timeout]“ diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf index 76737e3938..627eec058a 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf @@ -901,18 +901,18 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte - Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. - Es wird davon abgeraten, „[Timeout]“ ohne die explizite Festlegung von „CooperativeCancellation = true“ zu verwenden. In einer zukünftigen Version wird der kooperative Abbruch das Standardverhalten sein. Legen Sie „CooperativeCancellation = true“ fest, um das empfohlene Verhalten zu aktivieren und Breaking Changes zu vermeiden. + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' or using '[TaskRunTestMethod]' is discouraged as it can lead to dangling tasks. Use '[TaskRunTestMethod]' to run tests in Task.Run with proper timeout handling, or set 'CooperativeCancellation = true' to enable cooperative cancellation behavior. + Es wird davon abgeraten, „[Timeout]“ ohne die explizite Festlegung von „CooperativeCancellation = true“ zu verwenden. In einer zukünftigen Version wird der kooperative Abbruch das Standardverhalten sein. Legen Sie „CooperativeCancellation = true“ fest, um das empfohlene Verhalten zu aktivieren und Breaking Changes zu vermeiden. - Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior - Verwenden Sie „CooperativeCancellation = true“ mit „[Timeout]“, um das kooperative Abbruchverhalten zu aktivieren. + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior and avoid dangling tasks + Verwenden Sie „CooperativeCancellation = true“ mit „[Timeout]“, um das kooperative Abbruchverhalten zu aktivieren. - Use 'CooperativeCancellation = true' with '[Timeout]' - Verwenden Sie „CooperativeCancellation = true“ mit „[Timeout]“. + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' + Verwenden Sie „CooperativeCancellation = true“ mit „[Timeout]“. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf index 3e7bb27bc0..e860405726 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf @@ -900,18 +900,18 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: - Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. - No se recomienda usar "[Timeout]" sin establecer explícitamente "CooperativeCancellation = true". En una versión futura, la cancelación cooperativa se convertirá en el comportamiento predeterminado. Establece "CooperativeCancellation = true" para participar en el comportamiento recomendado y evitar cambios importantes. + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' or using '[TaskRunTestMethod]' is discouraged as it can lead to dangling tasks. Use '[TaskRunTestMethod]' to run tests in Task.Run with proper timeout handling, or set 'CooperativeCancellation = true' to enable cooperative cancellation behavior. + No se recomienda usar "[Timeout]" sin establecer explícitamente "CooperativeCancellation = true". En una versión futura, la cancelación cooperativa se convertirá en el comportamiento predeterminado. Establece "CooperativeCancellation = true" para participar en el comportamiento recomendado y evitar cambios importantes. - Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior - Usa "CooperativeCancellation = true" con "[Timeout]" para habilitar el comportamiento de cancelación cooperativa. + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior and avoid dangling tasks + Usa "CooperativeCancellation = true" con "[Timeout]" para habilitar el comportamiento de cancelación cooperativa. - Use 'CooperativeCancellation = true' with '[Timeout]' - Usa "CooperativeCancellation = true" con "[Timeout]" + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' + Usa "CooperativeCancellation = true" con "[Timeout]" diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf index 8b30fdd14e..d422f79a0a 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf @@ -900,18 +900,18 @@ Le type doit être une classe - Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. - L’utilisation de '[Timeout]' sans définir explicitement 'CooperativeCancellation = true' est déconseillée. Dans une future version, l’annulation coopérative deviendra le comportement par défaut. Définissez 'CooperativeCancellation = true' pour adopter le comportement recommandé et éviter les changements incompatibles. + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' or using '[TaskRunTestMethod]' is discouraged as it can lead to dangling tasks. Use '[TaskRunTestMethod]' to run tests in Task.Run with proper timeout handling, or set 'CooperativeCancellation = true' to enable cooperative cancellation behavior. + L’utilisation de '[Timeout]' sans définir explicitement 'CooperativeCancellation = true' est déconseillée. Dans une future version, l’annulation coopérative deviendra le comportement par défaut. Définissez 'CooperativeCancellation = true' pour adopter le comportement recommandé et éviter les changements incompatibles. - Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior - Utilisez 'CooperativeCancellation = true' avec '[Timeout]' pour activer le comportement d’annulation coopératif + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior and avoid dangling tasks + Utilisez 'CooperativeCancellation = true' avec '[Timeout]' pour activer le comportement d’annulation coopératif - Use 'CooperativeCancellation = true' with '[Timeout]' - Utiliser 'CooperativeCancellation = true' avec '[Timeout]' + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' + Utiliser 'CooperativeCancellation = true' avec '[Timeout]' diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf index 06dd23cd42..8e0c6c630d 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf @@ -900,18 +900,18 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: - Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. - L'uso di "[Timeout]" senza impostare esplicitamente "CooperativeCancellation = true" è sconsigliato. In una versione futura l'annullamento cooperativo diventerà il comportamento predefinito. Impostare "CooperativeCancellation = true" per acconsentire esplicitamente al comportamento consigliato ed evitare modifiche che causano un'interruzione. + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' or using '[TaskRunTestMethod]' is discouraged as it can lead to dangling tasks. Use '[TaskRunTestMethod]' to run tests in Task.Run with proper timeout handling, or set 'CooperativeCancellation = true' to enable cooperative cancellation behavior. + L'uso di "[Timeout]" senza impostare esplicitamente "CooperativeCancellation = true" è sconsigliato. In una versione futura l'annullamento cooperativo diventerà il comportamento predefinito. Impostare "CooperativeCancellation = true" per acconsentire esplicitamente al comportamento consigliato ed evitare modifiche che causano un'interruzione. - Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior - Usare "CooperativeCancellation = true" con "[Timeout]" per abilitare il comportamento di annullamento cooperativo + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior and avoid dangling tasks + Usare "CooperativeCancellation = true" con "[Timeout]" per abilitare il comportamento di annullamento cooperativo - Use 'CooperativeCancellation = true' with '[Timeout]' - Usa "CooperativeCancellation = true" con "[Timeout]" + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' + Usa "CooperativeCancellation = true" con "[Timeout]" diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf index ab704cbb51..d3cf1a9a89 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf @@ -900,18 +900,18 @@ The type declaring these methods should also respect the following rules: - Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. - 'CooperativeCancellation = true' を明示的にを設定せずに '[Timeout]' を使用することは推奨されません。将来のバージョンでは、協調的キャンセルが既定の動作になります。推奨される動作をオプトインし、破壊的な変更を避けるために、'CooperativeCancellation = true' を設定します。 + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' or using '[TaskRunTestMethod]' is discouraged as it can lead to dangling tasks. Use '[TaskRunTestMethod]' to run tests in Task.Run with proper timeout handling, or set 'CooperativeCancellation = true' to enable cooperative cancellation behavior. + 'CooperativeCancellation = true' を明示的にを設定せずに '[Timeout]' を使用することは推奨されません。将来のバージョンでは、協調的キャンセルが既定の動作になります。推奨される動作をオプトインし、破壊的な変更を避けるために、'CooperativeCancellation = true' を設定します。 - Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior - '[Timeout]' と共に 'CooperativeCancellation = true' を使用して、協調的キャンセルの動作を有効にします + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior and avoid dangling tasks + '[Timeout]' と共に 'CooperativeCancellation = true' を使用して、協調的キャンセルの動作を有効にします - Use 'CooperativeCancellation = true' with '[Timeout]' - '[Timeout]' と共に 'CooperativeCancellation = true' を使用する + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' + '[Timeout]' と共に 'CooperativeCancellation = true' を使用する diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf index e1579bc142..c68103bc37 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf @@ -900,18 +900,18 @@ The type declaring these methods should also respect the following rules: - Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. - 'CooperativeCancellation = true'를 명시적으로 설정하지 않고 '[Timeout]'을 사용하는 것은 권장되지 않습니다. 향후 버전에서는 협동 취소가 기본 동작으로 설정될 것입니다. 권장 동작을 선택하고 변경 사항으로 인한 문제를 피하려면 'CooperativeCancellation = true'를 설정하세요. + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' or using '[TaskRunTestMethod]' is discouraged as it can lead to dangling tasks. Use '[TaskRunTestMethod]' to run tests in Task.Run with proper timeout handling, or set 'CooperativeCancellation = true' to enable cooperative cancellation behavior. + 'CooperativeCancellation = true'를 명시적으로 설정하지 않고 '[Timeout]'을 사용하는 것은 권장되지 않습니다. 향후 버전에서는 협동 취소가 기본 동작으로 설정될 것입니다. 권장 동작을 선택하고 변경 사항으로 인한 문제를 피하려면 'CooperativeCancellation = true'를 설정하세요. - Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior - '[Timeout]'와 함께 'CooperativeCancellation = true'를 사용하여 협동 취소 동작을 활성화하세요. + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior and avoid dangling tasks + '[Timeout]'와 함께 'CooperativeCancellation = true'를 사용하여 협동 취소 동작을 활성화하세요. - Use 'CooperativeCancellation = true' with '[Timeout]' - '[Timeout]'와 함께 'CooperativeCancellation = true'를 사용하세요. + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' + '[Timeout]'와 함께 'CooperativeCancellation = true'를 사용하세요. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf index 8b72c8d6e2..fc4b1280e6 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf @@ -900,18 +900,18 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu - Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. - Nie rekomenduje się używania elementu „[Timeout]” bez jawnego ustawiania wartości „CooperativeCancellation = true”. W przyszłej wersji anulowanie trybu współpracy będzie zachowaniem domyślnym. Ustaw opcję „CooperativeCancellation = true”, aby włączyć rekomendowane zachowanie i uniknąć zmian powodujących niezgodność. + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' or using '[TaskRunTestMethod]' is discouraged as it can lead to dangling tasks. Use '[TaskRunTestMethod]' to run tests in Task.Run with proper timeout handling, or set 'CooperativeCancellation = true' to enable cooperative cancellation behavior. + Nie rekomenduje się używania elementu „[Timeout]” bez jawnego ustawiania wartości „CooperativeCancellation = true”. W przyszłej wersji anulowanie trybu współpracy będzie zachowaniem domyślnym. Ustaw opcję „CooperativeCancellation = true”, aby włączyć rekomendowane zachowanie i uniknąć zmian powodujących niezgodność. - Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior - Użyj opcji „CooperativeCancellation = true” z limitem czasu „[Timeout]”, aby włączyć zachowanie anulowania trybu współpracy + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior and avoid dangling tasks + Użyj opcji „CooperativeCancellation = true” z limitem czasu „[Timeout]”, aby włączyć zachowanie anulowania trybu współpracy - Use 'CooperativeCancellation = true' with '[Timeout]' - Użyj opcji „CooperativeCancellation = true” w limitem czasu „[Timeout]” + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' + Użyj opcji „CooperativeCancellation = true” w limitem czasu „[Timeout]” diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf index 3e9f3a2913..ab4c7a09fc 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf @@ -900,18 +900,18 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: - Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. - Usar '\[Timeout]' sem definir explicitamente 'CooperativeCancellation = true' não é recomendado. Em uma versão futura, o cancelamento cooperativo se tornará o comportamento padrão. Defina 'CooperativeCancellation = true' para optar pelo comportamento recomendado e evitar quebras. + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' or using '[TaskRunTestMethod]' is discouraged as it can lead to dangling tasks. Use '[TaskRunTestMethod]' to run tests in Task.Run with proper timeout handling, or set 'CooperativeCancellation = true' to enable cooperative cancellation behavior. + Usar '\[Timeout]' sem definir explicitamente 'CooperativeCancellation = true' não é recomendado. Em uma versão futura, o cancelamento cooperativo se tornará o comportamento padrão. Defina 'CooperativeCancellation = true' para optar pelo comportamento recomendado e evitar quebras. - Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior - Use 'CooperativeCancellation = true' com '\[Timeout]' para habilitar o comportamento de cancelamento cooperativo + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior and avoid dangling tasks + Use 'CooperativeCancellation = true' com '\[Timeout]' para habilitar o comportamento de cancelamento cooperativo - Use 'CooperativeCancellation = true' with '[Timeout]' - Use 'CooperativeCancellation = true' com '\[Timeout]' + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' + Use 'CooperativeCancellation = true' com '\[Timeout]' diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf index 2a32db08a3..64f17d2cbb 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf @@ -912,18 +912,18 @@ The type declaring these methods should also respect the following rules: - Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. - Использование "[Timeout]" без явной настройки "CooperativeCancellation = true" не рекомендуется. В будущей версии кооперативная отмена станет поведением по умолчанию. Настройте "CooperativeCancellation = true", чтобы согласиться с рекомендуемым поведением и избежать критических изменений. + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' or using '[TaskRunTestMethod]' is discouraged as it can lead to dangling tasks. Use '[TaskRunTestMethod]' to run tests in Task.Run with proper timeout handling, or set 'CooperativeCancellation = true' to enable cooperative cancellation behavior. + Использование "[Timeout]" без явной настройки "CooperativeCancellation = true" не рекомендуется. В будущей версии кооперативная отмена станет поведением по умолчанию. Настройте "CooperativeCancellation = true", чтобы согласиться с рекомендуемым поведением и избежать критических изменений. - Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior - Используйте "CooperativeCancellation = true" с "[Timeout]", чтобы включить поведение кооперативной отмены + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior and avoid dangling tasks + Используйте "CooperativeCancellation = true" с "[Timeout]", чтобы включить поведение кооперативной отмены - Use 'CooperativeCancellation = true' with '[Timeout]' - Использовать "CooperativeCancellation = true" с "[Timeout]" + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' + Использовать "CooperativeCancellation = true" с "[Timeout]" diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf index 4c959ae0e5..c31db20b29 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf @@ -902,18 +902,18 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: - Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. - '[Timeout]' ayarını 'CooperativeCancellation = true' olarak açıkça ayarlamadan kullanmak önerilmez. Gelecek bir sürümde, birlikte iptal etme varsayılan davranış haline gelecektir. Önerilen davranışı kabul etmek ve uyumsuzlukları önlemek için 'CooperativeCancellation = true' değerini ayarlayın. + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' or using '[TaskRunTestMethod]' is discouraged as it can lead to dangling tasks. Use '[TaskRunTestMethod]' to run tests in Task.Run with proper timeout handling, or set 'CooperativeCancellation = true' to enable cooperative cancellation behavior. + '[Timeout]' ayarını 'CooperativeCancellation = true' olarak açıkça ayarlamadan kullanmak önerilmez. Gelecek bir sürümde, birlikte iptal etme varsayılan davranış haline gelecektir. Önerilen davranışı kabul etmek ve uyumsuzlukları önlemek için 'CooperativeCancellation = true' değerini ayarlayın. - Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior - '[Timeout]' ile 'CooperativeCancellation = true' kullanarak birlikte iptal etme davranışını etkinleştirin + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior and avoid dangling tasks + '[Timeout]' ile 'CooperativeCancellation = true' kullanarak birlikte iptal etme davranışını etkinleştirin - Use 'CooperativeCancellation = true' with '[Timeout]' - '[Timeout]' ile 'CooperativeCancellation = true' kullanın + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' + '[Timeout]' ile 'CooperativeCancellation = true' kullanın diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf index 2058347afa..f44eff9922 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf @@ -900,18 +900,18 @@ The type declaring these methods should also respect the following rules: - Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. - 不建议在不显式设置 'CooperativeCancellation = true' 的情况下使用 '[Timeout]'。在将来的版本中,协作取消将成为默认行为。设置 'CooperativeCancellation = true' 以选择加入建议的行为,并避免中断性变更。 + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' or using '[TaskRunTestMethod]' is discouraged as it can lead to dangling tasks. Use '[TaskRunTestMethod]' to run tests in Task.Run with proper timeout handling, or set 'CooperativeCancellation = true' to enable cooperative cancellation behavior. + 不建议在不显式设置 'CooperativeCancellation = true' 的情况下使用 '[Timeout]'。在将来的版本中,协作取消将成为默认行为。设置 'CooperativeCancellation = true' 以选择加入建议的行为,并避免中断性变更。 - Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior - 将 'CooperativeCancellation = true' 与 '[Timeout]' 配合使用以启用协作取消行为 + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior and avoid dangling tasks + 将 'CooperativeCancellation = true' 与 '[Timeout]' 配合使用以启用协作取消行为 - Use 'CooperativeCancellation = true' with '[Timeout]' - 将 'CooperativeCancellation = true' 与 '[Timeout]' 配合使用 + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' + 将 'CooperativeCancellation = true' 与 '[Timeout]' 配合使用 diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf index 228d78acdd..5ffc34a291 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf @@ -900,18 +900,18 @@ The type declaring these methods should also respect the following rules: - Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' is discouraged. In a future version, cooperative cancellation will become the default behavior. Set 'CooperativeCancellation = true' to opt into the recommended behavior and avoid breaking changes. - 在未明確設定 'CooperativeCancellation = true' 的情況下,不建議使用 '[Timeout]'。在未來的版本中,合作取消將成為預設行為。請設定 'CooperativeCancellation = true' 以選擇加入推薦的行為,並避免破壞性變更。 + Using '[Timeout]' without explicitly setting 'CooperativeCancellation = true' or using '[TaskRunTestMethod]' is discouraged as it can lead to dangling tasks. Use '[TaskRunTestMethod]' to run tests in Task.Run with proper timeout handling, or set 'CooperativeCancellation = true' to enable cooperative cancellation behavior. + 在未明確設定 'CooperativeCancellation = true' 的情況下,不建議使用 '[Timeout]'。在未來的版本中,合作取消將成為預設行為。請設定 'CooperativeCancellation = true' 以選擇加入推薦的行為,並避免破壞性變更。 - Use 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior - 使用 'CooperativeCancellation = true' 搭配 '[Timeout]' 以啟用合作取消行為 + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' to enable cooperative cancellation behavior and avoid dangling tasks + 使用 'CooperativeCancellation = true' 搭配 '[Timeout]' 以啟用合作取消行為 - Use 'CooperativeCancellation = true' with '[Timeout]' - 使用 'CellCancellation = true' 搭配 '[Timeout]' + Use '[TaskRunTestMethod]' or 'CooperativeCancellation = true' with '[Timeout]' + 使用 'CellCancellation = true' 搭配 '[Timeout]' diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs new file mode 100644 index 0000000000..3414fbdf41 --- /dev/null +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestTools.UnitTesting; + +/// +/// The TaskRun test method attribute. +/// +/// +/// +/// This attribute is designed to handle test method execution with timeout by running the test code within a . +/// This allows the test runner to stop watching the task in case of timeout, preventing dangling tasks that can lead to +/// confusion or errors because the test method is still running in the background. +/// +/// +/// When a timeout occurs: +/// +/// The test is marked as timed out. +/// The cancellation token from is canceled. +/// The test runner stops awaiting the test task, allowing it to complete in the background. +/// +/// +/// +/// For best results, test methods should observe the cancellation token and cancel cooperatively. +/// If the test method does not handle cancellation properly, the task may continue running after the timeout, +/// which can still lead to issues, but the test runner will not block waiting for it to complete. +/// +/// +[AttributeUsage(AttributeTargets.Method)] +public class TaskRunTestMethodAttribute : TestMethodAttribute +{ + private readonly TestMethodAttribute? _testMethodAttribute; + + /// + /// Initializes a new instance of the class. + /// + public TaskRunTestMethodAttribute([CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = -1) + : base(callerFilePath, callerLineNumber) + { + } + + /// + /// Initializes a new instance of the class. + /// This constructor is intended to be called by test class attributes to wrap an existing test method attribute. + /// + /// The wrapped test method. + public TaskRunTestMethodAttribute(TestMethodAttribute testMethodAttribute) + : base(testMethodAttribute.DeclaringFilePath, testMethodAttribute.DeclaringLineNumber ?? -1) + => _testMethodAttribute = testMethodAttribute; + + /// + /// Executes a test method by wrapping it in a to allow timeout handling. + /// + /// The test method to execute. + /// An array of TestResult objects that represent the outcome(s) of the test. + public override async Task ExecuteAsync(ITestMethod testMethod) + { + if (_testMethodAttribute is not null) + { + return await ExecuteWithTaskRunAsync(() => _testMethodAttribute.ExecuteAsync(testMethod)).ConfigureAwait(false); + } + + return await ExecuteWithTaskRunAsync(() => testMethod.InvokeAsync(null)).ConfigureAwait(false); + } + + private static async Task ExecuteWithTaskRunAsync(Func> executeFunc) + { + // Run the test method in Task.Run so that we can stop awaiting it on timeout + // while allowing it to complete in the background + Task testTask = Task.Run(executeFunc); + + TestResult result = await testTask.ConfigureAwait(false); + return [result]; + } + + private static async Task ExecuteWithTaskRunAsync(Func> executeFunc) + { + // Run the test method in Task.Run so that we can stop awaiting it on timeout + // while allowing it to complete in the background + Task testTask = Task.Run(executeFunc); + + TestResult[] results = await testTask.ConfigureAwait(false); + return results; + } +} diff --git a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt index 7dc5c58110..1050aa7571 100644 --- a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt @@ -1 +1,5 @@ #nullable enable +Microsoft.VisualStudio.TestTools.UnitTesting.TaskRunTestMethodAttribute +Microsoft.VisualStudio.TestTools.UnitTesting.TaskRunTestMethodAttribute.ExecuteAsync(Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod testMethod) -> System.Threading.Tasks.Task! +Microsoft.VisualStudio.TestTools.UnitTesting.TaskRunTestMethodAttribute.TaskRunTestMethodAttribute(Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute! testMethodAttribute) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.TaskRunTestMethodAttribute.TaskRunTestMethodAttribute(string! callerFilePath = "", int callerLineNumber = -1) -> void diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseCooperativeCancellationForTimeoutAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseCooperativeCancellationForTimeoutAnalyzerTests.cs index 5d090b4bae..067f8973a7 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseCooperativeCancellationForTimeoutAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseCooperativeCancellationForTimeoutAnalyzerTests.cs @@ -440,4 +440,42 @@ public static void MyAssemblyCleanup() await VerifyCS.VerifyCodeFixAsync(code, fixedCode); } + + [TestMethod] + public async Task WhenTimeoutAttributeWithoutCooperativeCancellation_CodeFixOffersTaskRunTestMethod() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + [{|#0:Timeout(5000)|}] + public void MyTestMethod() + { + } + } + """; + + string fixedCodeWithTaskRunTestMethod = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TaskRunTestMethod] + [{|#0:Timeout(5000)|}] + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync( + code, + VerifyCS.Diagnostic().WithLocation(0), + fixedCodeWithTaskRunTestMethod, + codeFixIndex: 1); // Second code fix option + } } From f9d1af13cf4ffbca39b13c07cd0c010e47ddd208 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Oct 2025 18:45:18 +0000 Subject: [PATCH 03/10] Fix TaskRunTestMethodAttribute implementation - Simplified ExecuteAsync to avoid duplicate helper methods - Fixed logic for wrapping test invocation in Task.Run Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com> --- .../Attributes/TestMethod/TaskRunTestMethodAttribute.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs index 3414fbdf41..ebd2a0cbf5 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs @@ -60,15 +60,9 @@ public override async Task ExecuteAsync(ITestMethod testMethod) return await ExecuteWithTaskRunAsync(() => _testMethodAttribute.ExecuteAsync(testMethod)).ConfigureAwait(false); } - return await ExecuteWithTaskRunAsync(() => testMethod.InvokeAsync(null)).ConfigureAwait(false); - } - - private static async Task ExecuteWithTaskRunAsync(Func> executeFunc) - { // Run the test method in Task.Run so that we can stop awaiting it on timeout // while allowing it to complete in the background - Task testTask = Task.Run(executeFunc); - + Task testTask = Task.Run(() => testMethod.InvokeAsync(null)); TestResult result = await testTask.ConfigureAwait(false); return [result]; } From 7dc7c3dc10aff04f76495f0632eae2d13cdb9d8d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Oct 2025 18:47:17 +0000 Subject: [PATCH 04/10] Refactor TaskRunTestMethodAttribute to use consistent helper pattern - Use ExecuteWithTaskRunAsync helper for both code paths to reduce duplication - Ensures consistent Task.Run wrapping logic Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com> --- .../TestMethod/TaskRunTestMethodAttribute.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs index ebd2a0cbf5..5089f07564 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs @@ -60,11 +60,11 @@ public override async Task ExecuteAsync(ITestMethod testMethod) return await ExecuteWithTaskRunAsync(() => _testMethodAttribute.ExecuteAsync(testMethod)).ConfigureAwait(false); } - // Run the test method in Task.Run so that we can stop awaiting it on timeout - // while allowing it to complete in the background - Task testTask = Task.Run(() => testMethod.InvokeAsync(null)); - TestResult result = await testTask.ConfigureAwait(false); - return [result]; + return await ExecuteWithTaskRunAsync(async () => + { + TestResult result = await testMethod.InvokeAsync(null).ConfigureAwait(false); + return new[] { result }; + }).ConfigureAwait(false); } private static async Task ExecuteWithTaskRunAsync(Func> executeFunc) @@ -72,7 +72,6 @@ private static async Task ExecuteWithTaskRunAsync(Func testTask = Task.Run(executeFunc); - TestResult[] results = await testTask.ConfigureAwait(false); return results; } From 2edb2b4aafa8e1cc9d26fdddca5ef01a60a32e08 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Oct 2025 18:50:28 +0000 Subject: [PATCH 05/10] Update analyzer to not warn when TaskRunTestMethodAttribute is used - Modified UseCooperativeCancellationForTimeoutAnalyzer to check for TaskRunTestMethodAttribute - When TaskRunTestMethodAttribute is present, no diagnostic is reported since it provides Task.Run behavior - Added test to verify TaskRunTestMethodAttribute with Timeout doesn't trigger diagnostic - Fixed test to remove diagnostic marker from fixed code Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com> --- ...operativeCancellationForTimeoutAnalyzer.cs | 14 ++++++++++-- ...tiveCancellationForTimeoutAnalyzerTests.cs | 22 ++++++++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/Analyzers/MSTest.Analyzers/UseCooperativeCancellationForTimeoutAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseCooperativeCancellationForTimeoutAnalyzer.cs index ee452b0af7..02fa6bcacb 100644 --- a/src/Analyzers/MSTest.Analyzers/UseCooperativeCancellationForTimeoutAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/UseCooperativeCancellationForTimeoutAnalyzer.cs @@ -57,17 +57,27 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo var methodSymbol = (IMethodSymbol)context.Symbol; AttributeData? timeoutAttribute = null; + bool hasTaskRunTestMethodAttribute = false; + foreach (AttributeData attribute in methodSymbol.GetAttributes()) { if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, timeoutAttributeSymbol)) { timeoutAttribute = attribute; - break; + } + + // Check if the method uses TaskRunTestMethodAttribute + string? attributeName = attribute.AttributeClass?.Name; + if (attributeName is "TaskRunTestMethodAttribute" or "TaskRunTestMethod") + { + hasTaskRunTestMethodAttribute = true; } } - // Report diagnostic if CooperativeCancellation is not explicitly set to true + // Don't report diagnostic if using TaskRunTestMethodAttribute (it provides Task.Run behavior) + // or if CooperativeCancellation is explicitly set to true if (timeoutAttribute is not null + && !hasTaskRunTestMethodAttribute && !timeoutAttribute.NamedArguments.Any(x => x.Key == "CooperativeCancellation" && x.Value.Value is bool boolValue && boolValue)) { if (timeoutAttribute.ApplicationSyntaxReference?.GetSyntax() is { } syntax) diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseCooperativeCancellationForTimeoutAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseCooperativeCancellationForTimeoutAnalyzerTests.cs index 067f8973a7..606230283d 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseCooperativeCancellationForTimeoutAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseCooperativeCancellationForTimeoutAnalyzerTests.cs @@ -465,7 +465,7 @@ public void MyTestMethod() public class MyTestClass { [TaskRunTestMethod] - [{|#0:Timeout(5000)|}] + [Timeout(5000)] public void MyTestMethod() { } @@ -478,4 +478,24 @@ await VerifyCS.VerifyCodeFixAsync( fixedCodeWithTaskRunTestMethod, codeFixIndex: 1); // Second code fix option } + + [TestMethod] + public async Task WhenTaskRunTestMethodAttributeWithTimeout_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TaskRunTestMethod] + [Timeout(5000)] + public void MyTestMethod() + { + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } } From bebad3ee061141fb8eff7ff6f030634961f2b7af Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Oct 2025 18:52:39 +0000 Subject: [PATCH 06/10] Use proper symbol comparison in analyzer - Added TaskRunTestMethodAttribute to WellKnownTypeNames - Updated analyzer to use SymbolEqualityComparer instead of string comparison - Supports inheritance from TaskRunTestMethodAttribute Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com> --- .../Helpers/WellKnownTypeNames.cs | 1 + ...operativeCancellationForTimeoutAnalyzer.cs | 23 ++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs b/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs index f2ed45a0f9..81e3665fed 100644 --- a/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs +++ b/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs @@ -32,6 +32,7 @@ internal static class WellKnownTypeNames public const string MicrosoftVisualStudioTestToolsUnitTestingPriorityAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.PriorityAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingRetryBaseAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.RetryBaseAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingStringAssert = "Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert"; + public const string MicrosoftVisualStudioTestToolsUnitTestingTaskRunTestMethodAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.TaskRunTestMethodAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingTestClassAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingTestCleanupAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.TestCleanupAttribute"; public const string MicrosoftVisualStudioTestToolsUnitTestingTestContext = "Microsoft.VisualStudio.TestTools.UnitTesting.TestContext"; diff --git a/src/Analyzers/MSTest.Analyzers/UseCooperativeCancellationForTimeoutAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseCooperativeCancellationForTimeoutAnalyzer.cs index 02fa6bcacb..c6ad1c0ded 100644 --- a/src/Analyzers/MSTest.Analyzers/UseCooperativeCancellationForTimeoutAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/UseCooperativeCancellationForTimeoutAnalyzer.cs @@ -43,16 +43,21 @@ public override void Initialize(AnalysisContext context) context.RegisterCompilationStartAction(context => { - if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTimeoutAttribute, out INamedTypeSymbol? timeoutAttributeSymbol)) + if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTimeoutAttribute, out INamedTypeSymbol? timeoutAttributeSymbol)) { - context.RegisterSymbolAction( - context => AnalyzeSymbol(context, timeoutAttributeSymbol), - SymbolKind.Method); + return; } + + // Try to get TaskRunTestMethodAttribute symbol (it's a new attribute so it might not exist in older versions) + context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTaskRunTestMethodAttribute, out INamedTypeSymbol? taskRunTestMethodAttributeSymbol); + + context.RegisterSymbolAction( + context => AnalyzeSymbol(context, timeoutAttributeSymbol, taskRunTestMethodAttributeSymbol), + SymbolKind.Method); }); } - private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol timeoutAttributeSymbol) + private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol timeoutAttributeSymbol, INamedTypeSymbol? taskRunTestMethodAttributeSymbol) { var methodSymbol = (IMethodSymbol)context.Symbol; @@ -66,9 +71,11 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo timeoutAttribute = attribute; } - // Check if the method uses TaskRunTestMethodAttribute - string? attributeName = attribute.AttributeClass?.Name; - if (attributeName is "TaskRunTestMethodAttribute" or "TaskRunTestMethod") + // Check if the method uses TaskRunTestMethodAttribute or any derived type + if (taskRunTestMethodAttributeSymbol is not null && + attribute.AttributeClass is not null && + (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, taskRunTestMethodAttributeSymbol) || + attribute.AttributeClass.InheritsFromOrEquals(taskRunTestMethodAttributeSymbol))) { hasTaskRunTestMethodAttribute = true; } From e0e03a93fab8f0d09d06058420ad8d1c9d207e16 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 26 Oct 2025 18:54:18 +0000 Subject: [PATCH 07/10] Refactor analyzer for better readability - Improved comment about TaskRunTestMethodAttribute symbol handling - Extracted IsTaskRunTestMethodAttribute helper method for cleaner code - Better separation of concerns in the analyzer logic Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com> --- ...operativeCancellationForTimeoutAnalyzer.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Analyzers/MSTest.Analyzers/UseCooperativeCancellationForTimeoutAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/UseCooperativeCancellationForTimeoutAnalyzer.cs index c6ad1c0ded..60d9930b4f 100644 --- a/src/Analyzers/MSTest.Analyzers/UseCooperativeCancellationForTimeoutAnalyzer.cs +++ b/src/Analyzers/MSTest.Analyzers/UseCooperativeCancellationForTimeoutAnalyzer.cs @@ -48,7 +48,8 @@ public override void Initialize(AnalysisContext context) return; } - // Try to get TaskRunTestMethodAttribute symbol (it's a new attribute so it might not exist in older versions) + // Try to get TaskRunTestMethodAttribute symbol. It might not exist in older versions, + // but that's OK - the analyzer will still work, just without the suppression logic. context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTaskRunTestMethodAttribute, out INamedTypeSymbol? taskRunTestMethodAttributeSymbol); context.RegisterSymbolAction( @@ -72,10 +73,7 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo } // Check if the method uses TaskRunTestMethodAttribute or any derived type - if (taskRunTestMethodAttributeSymbol is not null && - attribute.AttributeClass is not null && - (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, taskRunTestMethodAttributeSymbol) || - attribute.AttributeClass.InheritsFromOrEquals(taskRunTestMethodAttributeSymbol))) + if (IsTaskRunTestMethodAttribute(attribute, taskRunTestMethodAttributeSymbol)) { hasTaskRunTestMethodAttribute = true; } @@ -93,4 +91,15 @@ attribute.AttributeClass is not null && } } } + + private static bool IsTaskRunTestMethodAttribute(AttributeData attribute, INamedTypeSymbol? taskRunTestMethodAttributeSymbol) + { + if (taskRunTestMethodAttributeSymbol is null || attribute.AttributeClass is null) + { + return false; + } + + return SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, taskRunTestMethodAttributeSymbol) + || attribute.AttributeClass.InheritsFromOrEquals(taskRunTestMethodAttributeSymbol); + } } From 2e87e1faca9a1ba695a55e7edf48f9a93236d115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Mon, 27 Oct 2025 08:20:01 +0100 Subject: [PATCH 08/10] Update src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs Co-authored-by: Youssef Victor --- .../Attributes/TestMethod/TaskRunTestMethodAttribute.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs index 5089f07564..bd76be2a2d 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs @@ -26,7 +26,6 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// which can still lead to issues, but the test runner will not block waiting for it to complete. /// /// -[AttributeUsage(AttributeTargets.Method)] public class TaskRunTestMethodAttribute : TestMethodAttribute { private readonly TestMethodAttribute? _testMethodAttribute; From cb78ce5f0aa6f75b71c3ced143b6a7554ca8a346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Mon, 27 Oct 2025 08:23:37 +0100 Subject: [PATCH 09/10] Update src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs Co-authored-by: Youssef Victor --- .../Attributes/TestMethod/TaskRunTestMethodAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs index bd76be2a2d..90753e292b 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs @@ -26,7 +26,7 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// which can still lead to issues, but the test runner will not block waiting for it to complete. /// /// -public class TaskRunTestMethodAttribute : TestMethodAttribute +public sealed class TaskRunTestMethodAttribute : TestMethodAttribute { private readonly TestMethodAttribute? _testMethodAttribute; From ac903bb7389abb5f2a5c946a0de0b5fac535ebf9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Oct 2025 07:49:26 +0000 Subject: [PATCH 10/10] Add timeout parameter to TaskRunTestMethodAttribute - Added timeout parameter to TaskRunTestMethodAttribute constructors - Implemented timeout handling with Task.WhenAny for non-cooperative timeout - Added STA thread handling to respect thread apartment state - Updated code fix to extract timeout from Timeout attribute and pass to TaskRunTestMethod - Updated tests to use TaskRunTestMethod(5000) instead of separate Timeout attribute - Updated PublicAPI.Unshipped.txt with new signatures Addresses comments from @Youssef1313 and @Evangelink about needing timeout parameter and STA thread handling. Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com> --- ...eCooperativeCancellationForTimeoutFixer.cs | 50 +++++-- .../TestMethod/TaskRunTestMethodAttribute.cs | 133 ++++++++++++++---- .../PublicAPI/PublicAPI.Unshipped.txt | 5 +- ...tiveCancellationForTimeoutAnalyzerTests.cs | 10 +- 4 files changed, 153 insertions(+), 45 deletions(-) diff --git a/src/Analyzers/MSTest.Analyzers.CodeFixes/UseCooperativeCancellationForTimeoutFixer.cs b/src/Analyzers/MSTest.Analyzers.CodeFixes/UseCooperativeCancellationForTimeoutFixer.cs index 26e4a525ca..ee55e7247c 100644 --- a/src/Analyzers/MSTest.Analyzers.CodeFixes/UseCooperativeCancellationForTimeoutFixer.cs +++ b/src/Analyzers/MSTest.Analyzers.CodeFixes/UseCooperativeCancellationForTimeoutFixer.cs @@ -134,40 +134,68 @@ private static async Task AddCooperativeCancellationAsync(Document doc private static async Task ReplaceWithTaskRunTestMethodAsync(Document document, MethodDeclarationSyntax methodDeclaration, CancellationToken cancellationToken) { DocumentEditor editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - // Find the TestMethod attribute + if (root is null) + { + return document; + } + + // Find the TestMethod and Timeout attributes AttributeSyntax? testMethodAttribute = null; + AttributeSyntax? timeoutAttribute = null; + int timeoutValue = 0; + foreach (AttributeListSyntax attributeList in methodDeclaration.AttributeLists) { foreach (AttributeSyntax attribute in attributeList.Attributes) { string attributeName = attribute.Name.ToString(); + if (attributeName is "TestMethod" or "TestMethodAttribute") { testMethodAttribute = attribute; - break; } - } - - if (testMethodAttribute is not null) - { - break; + else if (attributeName is "Timeout" or "TimeoutAttribute") + { + timeoutAttribute = attribute; + + // Extract timeout value from the first argument + if (attribute.ArgumentList?.Arguments.Count > 0) + { + var firstArg = attribute.ArgumentList.Arguments[0]; + if (firstArg.Expression is LiteralExpressionSyntax literalExpr && + literalExpr.Token.Value is int value) + { + timeoutValue = value; + } + } + } } } - if (testMethodAttribute is null) + if (testMethodAttribute is null || timeoutAttribute is null) { - // No TestMethod attribute found, return unchanged document + // Can't apply the fix without both attributes return document; } - // Create the new TaskRunTestMethod attribute preserving any arguments + // Create the new TaskRunTestMethod attribute with timeout parameter + AttributeArgumentSyntax timeoutArg = SyntaxFactory.AttributeArgument( + SyntaxFactory.LiteralExpression( + SyntaxKind.NumericLiteralExpression, + SyntaxFactory.Literal(timeoutValue))); + AttributeSyntax newAttribute = SyntaxFactory.Attribute( SyntaxFactory.IdentifierName("TaskRunTestMethod"), - testMethodAttribute.ArgumentList); + SyntaxFactory.AttributeArgumentList( + SyntaxFactory.SingletonSeparatedList(timeoutArg))); // Replace the TestMethod attribute with TaskRunTestMethod editor.ReplaceNode(testMethodAttribute, newAttribute); + + // Remove the Timeout attribute + editor.RemoveNode(timeoutAttribute); return editor.GetChangedDocument(); } diff --git a/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs b/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs index 90753e292b..8eca350387 100644 --- a/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs +++ b/src/TestFramework/TestFramework/Attributes/TestMethod/TaskRunTestMethodAttribute.cs @@ -4,28 +4,20 @@ namespace Microsoft.VisualStudio.TestTools.UnitTesting; /// -/// The TaskRun test method attribute. +/// Test method attribute that runs tests in a Task.Run with non-cooperative timeout handling. /// /// /// -/// This attribute is designed to handle test method execution with timeout by running the test code within a . -/// This allows the test runner to stop watching the task in case of timeout, preventing dangling tasks that can lead to -/// confusion or errors because the test method is still running in the background. +/// This attribute runs test methods in and implements non-cooperative timeout handling. +/// When a timeout occurs, the test is marked as timed out and the test runner stops awaiting the task, +/// allowing it to complete in the background. This prevents blocking but may lead to dangling tasks. /// /// -/// When a timeout occurs: -/// -/// The test is marked as timed out. -/// The cancellation token from is canceled. -/// The test runner stops awaiting the test task, allowing it to complete in the background. -/// -/// -/// -/// For best results, test methods should observe the cancellation token and cancel cooperatively. -/// If the test method does not handle cancellation properly, the task may continue running after the timeout, -/// which can still lead to issues, but the test runner will not block waiting for it to complete. +/// For cooperative timeout handling where tests are awaited until completion, use +/// with CooperativeCancellation = true instead. /// /// +[AttributeUsage(AttributeTargets.Method, Inherited = false)] public sealed class TaskRunTestMethodAttribute : TestMethodAttribute { private readonly TestMethodAttribute? _testMethodAttribute; @@ -33,9 +25,11 @@ public sealed class TaskRunTestMethodAttribute : TestMethodAttribute /// /// Initializes a new instance of the class. /// - public TaskRunTestMethodAttribute([CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = -1) + /// The timeout in milliseconds. If not specified or 0, no timeout is applied. + public TaskRunTestMethodAttribute(int timeout = 0, [CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = -1) : base(callerFilePath, callerLineNumber) { + Timeout = timeout; } /// @@ -43,12 +37,21 @@ public TaskRunTestMethodAttribute([CallerFilePath] string callerFilePath = "", [ /// This constructor is intended to be called by test class attributes to wrap an existing test method attribute. /// /// The wrapped test method. - public TaskRunTestMethodAttribute(TestMethodAttribute testMethodAttribute) + /// The timeout in milliseconds. If not specified or 0, no timeout is applied. + public TaskRunTestMethodAttribute(TestMethodAttribute testMethodAttribute, int timeout = 0) : base(testMethodAttribute.DeclaringFilePath, testMethodAttribute.DeclaringLineNumber ?? -1) - => _testMethodAttribute = testMethodAttribute; + { + _testMethodAttribute = testMethodAttribute; + Timeout = timeout; + } /// - /// Executes a test method by wrapping it in a to allow timeout handling. + /// Gets the timeout in milliseconds. + /// + public int Timeout { get; } + + /// + /// Executes a test method with non-cooperative timeout handling. /// /// The test method to execute. /// An array of TestResult objects that represent the outcome(s) of the test. @@ -56,22 +59,96 @@ public override async Task ExecuteAsync(ITestMethod testMethod) { if (_testMethodAttribute is not null) { - return await ExecuteWithTaskRunAsync(() => _testMethodAttribute.ExecuteAsync(testMethod)).ConfigureAwait(false); + return await ExecuteWithTimeoutAsync(() => _testMethodAttribute.ExecuteAsync(testMethod), testMethod).ConfigureAwait(false); } - return await ExecuteWithTaskRunAsync(async () => + return await ExecuteWithTimeoutAsync(async () => { TestResult result = await testMethod.InvokeAsync(null).ConfigureAwait(false); return new[] { result }; - }).ConfigureAwait(false); + }, testMethod).ConfigureAwait(false); } - private static async Task ExecuteWithTaskRunAsync(Func> executeFunc) + private async Task ExecuteWithTimeoutAsync(Func> executeFunc, ITestMethod testMethod) { - // Run the test method in Task.Run so that we can stop awaiting it on timeout - // while allowing it to complete in the background - Task testTask = Task.Run(executeFunc); - TestResult[] results = await testTask.ConfigureAwait(false); - return results; + if (Timeout <= 0) + { + // No timeout, run directly with Task.Run + return await RunOnThreadPoolOrCustomThreadAsync(executeFunc).ConfigureAwait(false); + } + + // Run with timeout + Task testTask = RunOnThreadPoolOrCustomThreadAsync(executeFunc); + Task completedTask = await Task.WhenAny(testTask, Task.Delay(Timeout)).ConfigureAwait(false); + + if (completedTask == testTask) + { + // Test completed before timeout + return await testTask.ConfigureAwait(false); + } + + // Timeout occurred - return timeout result and let task continue in background + return + [ + new TestResult + { + Outcome = UnitTestOutcome.Timeout, + TestFailureException = new TestFailedException( + UnitTestOutcome.Timeout, + string.Format( + CultureInfo.InvariantCulture, + "Test '{0}.{1}' exceeded timeout of {2}ms.", + testMethod.TestClassName, + testMethod.TestMethodName, + Timeout)), + }, + ]; + } + + private static Task RunOnThreadPoolOrCustomThreadAsync(Func> executeFunc) + { + // Check if we need to handle STA threading + // If current thread is STA and we're on Windows, create a new STA thread + // Otherwise, use Task.Run (thread pool) +#if NETFRAMEWORK + if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) + { + return RunOnSTAThreadAsync(executeFunc); + } +#else + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && + Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) + { + return RunOnSTAThreadAsync(executeFunc); + } +#endif + + // Use thread pool for non-STA scenarios + return Task.Run(executeFunc); + } + + private static Task RunOnSTAThreadAsync(Func> executeFunc) + { + var tcs = new TaskCompletionSource(); + var thread = new Thread(() => + { + try + { + TestResult[] result = executeFunc().GetAwaiter().GetResult(); + tcs.SetResult(result); + } + catch (Exception ex) + { + tcs.SetException(ex); + } + }) + { + Name = "TaskRunTestMethodAttribute STA thread", + }; + + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + + return tcs.Task; } } diff --git a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt index 1050aa7571..a312657052 100644 --- a/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/TestFramework/TestFramework/PublicAPI/PublicAPI.Unshipped.txt @@ -1,5 +1,6 @@ #nullable enable Microsoft.VisualStudio.TestTools.UnitTesting.TaskRunTestMethodAttribute Microsoft.VisualStudio.TestTools.UnitTesting.TaskRunTestMethodAttribute.ExecuteAsync(Microsoft.VisualStudio.TestTools.UnitTesting.ITestMethod testMethod) -> System.Threading.Tasks.Task! -Microsoft.VisualStudio.TestTools.UnitTesting.TaskRunTestMethodAttribute.TaskRunTestMethodAttribute(Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute! testMethodAttribute) -> void -Microsoft.VisualStudio.TestTools.UnitTesting.TaskRunTestMethodAttribute.TaskRunTestMethodAttribute(string! callerFilePath = "", int callerLineNumber = -1) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.TaskRunTestMethodAttribute.TaskRunTestMethodAttribute(int timeout = 0, string! callerFilePath = "", int callerLineNumber = -1) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.TaskRunTestMethodAttribute.TaskRunTestMethodAttribute(Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute! testMethodAttribute, int timeout = 0) -> void +Microsoft.VisualStudio.TestTools.UnitTesting.TaskRunTestMethodAttribute.Timeout.get -> int diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/UseCooperativeCancellationForTimeoutAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/UseCooperativeCancellationForTimeoutAnalyzerTests.cs index 606230283d..38f9f7e9ab 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/UseCooperativeCancellationForTimeoutAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/UseCooperativeCancellationForTimeoutAnalyzerTests.cs @@ -464,8 +464,7 @@ public void MyTestMethod() [TestClass] public class MyTestClass { - [TaskRunTestMethod] - [Timeout(5000)] + [TaskRunTestMethod(5000)] public void MyTestMethod() { } @@ -488,8 +487,7 @@ public async Task WhenTaskRunTestMethodAttributeWithTimeout_NoDiagnostic() [TestClass] public class MyTestClass { - [TaskRunTestMethod] - [Timeout(5000)] + [TaskRunTestMethod(5000)] public void MyTestMethod() { } @@ -498,4 +496,8 @@ public void MyTestMethod() await VerifyCS.VerifyCodeFixAsync(code, code); } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } }