Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d556b81
Add deadline-aware cancellation prototype
nohwnd Jul 16, 2026
30fe9a1
Merge remote-tracking branch 'origin/main' into nohwnd-deadline-aware…
nohwnd Jul 16, 2026
1ddfd5a
Address PR review on deadline-aware cancellation
nohwnd Jul 16, 2026
1cbf4df
Merge remote-tracking branch 'origin/main' into nohwnd-deadline-aware…
nohwnd Jul 20, 2026
91d2de2
Address second review round on deadline-aware cancellation
nohwnd Jul 20, 2026
14d6b88
Address the re-review comments on deadline-aware cancellation
nohwnd Jul 22, 2026
aa73e62
Merge remote-tracking branch 'origin/main' into nohwnd-deadline-aware…
nohwnd Aug 4, 2026
e7786b2
Address deadline-aware cancellation review (exit code, server mode, l…
nohwnd Aug 4, 2026
f445901
Merge remote-tracking branch 'origin/main' into nohwnd-deadline-aware…
nohwnd Aug 4, 2026
f110c3d
Close a disposal race in AbortAtDeadlineExtension
nohwnd Aug 4, 2026
dba2383
Bound the pre-dump in-progress-test query with a short timeout
nohwnd Aug 4, 2026
c693499
Disarm the deadline once test execution completes
nohwnd Aug 4, 2026
4de04d2
Force an async boundary before deadline handler work
nohwnd Aug 4, 2026
e3beadd
Merge remote-tracking branch 'origin/main' into nohwnd-deadline-aware…
nohwnd Aug 17, 2026
f484a19
Address the deadline-cancellation review comments
nohwnd Aug 18, 2026
492ec13
Close the deadline-verdict race and query in-progress tests once per …
nohwnd Aug 18, 2026
7581ebf
Cover the deadline exit-code reason and the one-query-per-dump rule
nohwnd Aug 18, 2026
bf11a67
Keep the deadline verdict separate from the one-shot callbacks
nohwnd Aug 18, 2026
1a452e5
Commit the deadline verdict without awaiting after the claim
nohwnd Aug 18, 2026
a9072f8
Bound the deadline diagnostics and keep the dump's empty-list fallback
nohwnd Aug 18, 2026
7794fe0
Disarm the deadline when the test framework invoker returns
nohwnd Aug 18, 2026
3b0c9c3
Don't report a rejected graceful stop as a deadline truncation
nohwnd Aug 18, 2026
40e3b35
Query the in-progress tests once per dump, not once per process
nohwnd Aug 18, 2026
a0567fe
Don't let a failing diagnostic skip the process tree kill
nohwnd Aug 18, 2026
5a9655c
Merge deadline cancellation review fixes
nohwnd Aug 18, 2026
a9b514e
Merge latest deadline branch update
nohwnd Aug 18, 2026
6acb5fa
Register IStopPoliciesService in the CommonHost request tests
nohwnd Aug 19, 2026
c49b3ac
Request the graceful stop before writing the deadline message
nohwnd Aug 20, 2026
c40c246
Address static analysis findings from the review
nohwnd Aug 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ static Microsoft.Testing.Extensions.AzureDevOpsReport.AzureDevOpsSummaryReporter
*REMOVED*Microsoft.Testing.Extensions.AzureDevOpsReport.AzureDevOpsSummaryReporter.AzureDevOpsSummaryReporter(Microsoft.Testing.Platform.CommandLine.ICommandLineOptions! commandLineOptions, Microsoft.Testing.Platform.Configurations.IConfiguration! configuration, Microsoft.Testing.Platform.Helpers.IEnvironment! environment, Microsoft.Testing.Platform.Helpers.IFileSystem! fileSystem, Microsoft.Testing.Platform.OutputDevice.IOutputDevice! outputDevice, Microsoft.Testing.Platform.Services.ITestApplicationModuleInfo! testApplicationModuleInfo, Microsoft.Testing.Platform.Logging.ILoggerFactory! loggerFactory) -> void
*REMOVED*abstract Microsoft.Testing.Extensions.SlowTestReporterBase.EmitSlowTestAsync(string! testName, System.TimeSpan elapsed, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task!
Microsoft.Testing.Platform.Helpers.ExitCode.CoverageThresholdFailed = 14 -> Microsoft.Testing.Platform.Helpers.ExitCode
Microsoft.Testing.Platform.Helpers.ExitCode.TestExecutionStoppedAtDeadline = 15 -> Microsoft.Testing.Platform.Helpers.ExitCode
static Microsoft.Testing.Extensions.AzureDevOpsReport.AzureDevOpsTestResultsClient.CreateHttpClientHandler() -> System.Net.Http.HttpClientHandler!
static Microsoft.Testing.Extensions.AzureDevOpsReport.AzureDevOpsTestResultsClient.ShouldOptInToAutomaticDecompression(System.Net.Http.HttpClientHandler! handler) -> bool
Microsoft.Testing.Extensions.AzureDevOpsReport.AzureDevOpsTestResultsPublisher.AzureDevOpsTestResultsPublisher(Microsoft.Testing.Platform.CommandLine.ICommandLineOptions! commandLineOptions, Microsoft.Testing.Platform.Configurations.IConfiguration! configuration, Microsoft.Testing.Platform.Helpers.IEnvironment! environment, Microsoft.Testing.Platform.Helpers.IFileSystem! fileSystem, Microsoft.Testing.Platform.OutputDevice.IOutputDevice! outputDevice, Microsoft.Testing.Platform.Services.ITestApplicationModuleInfo! testApplicationModuleInfo, Microsoft.Testing.Platform.Services.ITestApplicationProcessExitCode! testApplicationProcessExitCode, Microsoft.Testing.Extensions.AzureDevOpsReport.IAzureDevOpsTestResultsClient! client, Microsoft.Testing.Platform.Helpers.ITask! task, Microsoft.Testing.Platform.Helpers.IClock! clock, Microsoft.Testing.Platform.Logging.ILogger! logger, Microsoft.Testing.Extensions.AzureDevOpsReport.AzureDevOpsTestResultsPublisherOptions! options) -> void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ static Microsoft.Testing.Extensions.CtrfReport.CtrfReportMerger.MergeToFileAsync
static Microsoft.Testing.Platform.Resources.PlatformResources.NamedPipeDirectoryNotWritableErrorMessage.get -> string!
static Microsoft.Testing.Platform.Resources.PlatformResources.NamedPipePathTooLongErrorMessage.get -> string!
Microsoft.Testing.Platform.Helpers.ExitCode.CoverageThresholdFailed = 14 -> Microsoft.Testing.Platform.Helpers.ExitCode
Microsoft.Testing.Platform.Helpers.ExitCode.TestExecutionStoppedAtDeadline = 15 -> Microsoft.Testing.Platform.Helpers.ExitCode
Comment thread
nohwnd marked this conversation as resolved.
static Microsoft.Testing.Platform.Services.ArtifactNamingHelper.ResolveAndSanitize(string! template, string! processName, string! processId, System.DateTimeOffset timestamp, System.Func<string!, string!>! sanitizeLeafFileName) -> string!
Microsoft.Testing.Extensions.MergeOutputFileHelper
static Microsoft.Testing.Extensions.MergeOutputFileHelper.BuildCaseFoldedProbePath(string! directory, string! probeFileName) -> string!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public static string GetReason(int exitCode)
(int)ExitCode.IncompatibleProtocolVersion => GitHubActionsResources.ExitCodeReasonIncompatibleProtocolVersion,
(int)ExitCode.TestExecutionStoppedForMaxFailedTests => GitHubActionsResources.ExitCodeReasonTestExecutionStoppedForMaxFailedTests,
(int)ExitCode.CoverageThresholdFailed => GitHubActionsResources.ExitCodeReasonCoverageThresholdFailed,
(int)ExitCode.TestExecutionStoppedAtDeadline => GitHubActionsResources.ExitCodeReasonTestExecutionStoppedAtDeadline,
_ => GitHubActionsResources.ExitCodeReasonUnknown,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ static Microsoft.Testing.Extensions.TestNodeIdentity.GetDisplayLabel(Microsoft.T
*REMOVED*static Microsoft.Testing.Extensions.GitHubActionsReport.GitHubActionsSlowTestReporter.BuildNoticeLine(string! testName, System.TimeSpan elapsed) -> string!
*REMOVED*static Microsoft.Testing.Extensions.GitHubActionsReport.GitHubActionsSummaryReporter.BuildMarkdown(System.Collections.Generic.IReadOnlyList<Microsoft.Testing.Extensions.TestRecord>! records, string! assemblyName, string! targetFrameworkMoniker) -> string!
Microsoft.Testing.Platform.Helpers.ExitCode.CoverageThresholdFailed = 14 -> Microsoft.Testing.Platform.Helpers.ExitCode
Microsoft.Testing.Platform.Helpers.ExitCode.TestExecutionStoppedAtDeadline = 15 -> Microsoft.Testing.Platform.Helpers.ExitCode
Microsoft.Testing.Extensions.GitHubActionsReport.GitHubActionsSourceLocation
Microsoft.Testing.Extensions.GitHubActionsReport.GitHubActionsSourceLocation.GitHubActionsSourceLocation() -> void
Microsoft.Testing.Extensions.GitHubActionsReport.GitHubActionsSourceLocation.GitHubActionsSourceLocation(string! relativeNormalizedPath, int lineNumber) -> void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ dotnet add package Microsoft.Testing.Extensions.GitHubActionsReport
This package extends Microsoft.Testing.Platform with:

- **Per-assembly log groups**: emits `::group::` / `::endgroup::` workflow commands so each test assembly's output is collapsed by default in the runner UI
- **Failure annotations**: emits an `::error` workflow command for each failing test so failures appear in the workflow Annotations tab and, when the source location can be resolved, on the pull request's "Files changed" diff gutter. Skipped tests are surfaced as `::warning` annotations so they are visible in the Annotations tab too. When the test session completes with a non-test-result failure — a `--minimum-expected-tests` violation, a run that discovered zero tests, a `--maximum-failed-tests` stop, or a test-adapter session failure — a single run-level `::error` is emitted describing the [Microsoft.Testing.Platform exit code](https://learn.microsoft.com/dotnet/core/testing/microsoft-testing-platform-troubleshooting#exit-codes)
- **Failure annotations**: emits an `::error` workflow command for each failing test so failures appear in the workflow Annotations tab and, when the source location can be resolved, on the pull request's "Files changed" diff gutter. Skipped tests are surfaced as `::warning` annotations so they are visible in the Annotations tab too. When the test session completes with a non-test-result failure — a `--minimum-expected-tests` violation, a run that discovered zero tests, a `--maximum-failed-tests` stop, a deadline-triggered early stop, or a test-adapter session failure — a single run-level `::error` is emitted describing the [Microsoft.Testing.Platform exit code](https://learn.microsoft.com/dotnet/core/testing/microsoft-testing-platform-troubleshooting#exit-codes)
- **Job summary**: writes one markdown roll-up (totals, failures, slowest tests) to the file pointed to by `GITHUB_STEP_SUMMARY`, which GitHub renders on the workflow run summary page. With an SDK that supports required artifact post-processing, multi-module `dotnet test` runs produce one authoritative overall section using the SDK's outer duration and exit verdict, with deterministic per-assembly details underneath. Older SDKs preserve the per-assembly sections. A non-test-result failure exit code is called out so a failure is not hidden behind a green ✅
- **Slow-test notices**: emits a `::notice` workflow command for any test still running past a threshold (default 60 seconds)

> [!NOTE]
> The exit-code callout and run-level annotation only cover outcomes the extension can observe once the in-process test session has finished. Those are: `ZeroTests` (8), `MinimumExpectedTestsPolicyViolation` (9), `TestAdapterTestSessionFailure` (10), and `TestExecutionStoppedForMaxFailedTests` (13). `AtLeastOneTestFailed` (2) is already conveyed by the per-test failures, so it gets no separate callout. A hard abort/cancellation (`TestSessionAborted`, 3) short-circuits end-of-session reporting, and codes raised before or after the session — e.g. `InvalidCommandLine` (5) or `TestHostProcessExitedNonGracefully` (7) — occur outside the extension's reach, so none of those are surfaced here.
> The exit-code callout and run-level annotation only cover outcomes the extension can observe once the in-process test session has finished. Those are: `ZeroTests` (8), `MinimumExpectedTestsPolicyViolation` (9), `TestAdapterTestSessionFailure` (10), `TestExecutionStoppedForMaxFailedTests` (13), and `TestExecutionStoppedAtDeadline` (15). `AtLeastOneTestFailed` (2) is already conveyed by the per-test failures, so it gets no separate callout. A hard abort/cancellation (`TestSessionAborted`, 3) short-circuits end-of-session reporting, and codes raised before or after the session — e.g. `InvalidCommandLine` (5) or `TestHostProcessExitedNonGracefully` (7) — occur outside the extension's reach, so none of those are surfaced here.
>
> Cross-module aggregation is negotiated with `dotnet test`. If the SDK does not provide the authoritative run-summary context, the extension keeps its standalone behavior; a manually invoked post-processor labels totals as observed and leaves overall duration and exit verdict unavailable rather than reconstructing them.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@
<data name="ExitCodeReasonCoverageThresholdFailed" xml:space="preserve">
<value>One or more code coverage thresholds were not met.</value>
</data>
<data name="ExitCodeReasonTestExecutionStoppedAtDeadline" xml:space="preserve">
<value>Test execution was stopped early because a CI-imposed deadline was approaching.</value>
</data>
<data name="ExitCodeReasonUnknown" xml:space="preserve">
<value>The test run reported a non-success exit code.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@
<target state="translated">Testovací adaptér oznámil selhání testovací relace.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedAtDeadline">
<source>Test execution was stopped early because a CI-imposed deadline was approaching.</source>
<target state="new">Test execution was stopped early because a CI-imposed deadline was approaching.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedForMaxFailedTests">
<source>Test execution stopped after reaching the limit set by --maximum-failed-tests.</source>
<target state="translated">Spouštění testů se zastavilo po dosažení limitu nastaveného parametrem --maximum-failed-tests.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@
<target state="translated">Der Testadapter hat einen Fehler in der Testsitzung gemeldet.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedAtDeadline">
<source>Test execution was stopped early because a CI-imposed deadline was approaching.</source>
<target state="new">Test execution was stopped early because a CI-imposed deadline was approaching.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedForMaxFailedTests">
<source>Test execution stopped after reaching the limit set by --maximum-failed-tests.</source>
<target state="translated">Die Testausführung wurde beendet, nachdem das von --maximum-failed-tests festgelegte Limit erreicht wurde.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@
<target state="translated">El adaptador de prueba notificó un error de sesión de prueba.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedAtDeadline">
<source>Test execution was stopped early because a CI-imposed deadline was approaching.</source>
<target state="new">Test execution was stopped early because a CI-imposed deadline was approaching.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedForMaxFailedTests">
<source>Test execution stopped after reaching the limit set by --maximum-failed-tests.</source>
<target state="translated">La ejecución de pruebas se detuvo al alcanzar el límite establecido por --maximum-failed-tests.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@
<target state="translated">L’adaptateur de test a signalé l’échec d’une session de test.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedAtDeadline">
<source>Test execution was stopped early because a CI-imposed deadline was approaching.</source>
<target state="new">Test execution was stopped early because a CI-imposed deadline was approaching.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedForMaxFailedTests">
<source>Test execution stopped after reaching the limit set by --maximum-failed-tests.</source>
<target state="translated">L’exécution de tests s’est arrêtée après avoir atteint la limite définie par --maximum-failed-tests.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@
<target state="translated">L'adattatore di test ha segnalato un errore della sessione di test.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedAtDeadline">
<source>Test execution was stopped early because a CI-imposed deadline was approaching.</source>
<target state="new">Test execution was stopped early because a CI-imposed deadline was approaching.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedForMaxFailedTests">
<source>Test execution stopped after reaching the limit set by --maximum-failed-tests.</source>
<target state="translated">L'esecuzione dei test è stata interrotta dopo aver raggiunto il limite impostato da --maximum-failed-tests.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@
<target state="translated">テスト アダプターから、テスト セッションの失敗が報告されました。</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedAtDeadline">
<source>Test execution was stopped early because a CI-imposed deadline was approaching.</source>
<target state="new">Test execution was stopped early because a CI-imposed deadline was approaching.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedForMaxFailedTests">
<source>Test execution stopped after reaching the limit set by --maximum-failed-tests.</source>
<target state="translated">--maximum-failed-tests で設定された上限に達したため、テストの実行が停止しました。</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@
<target state="translated">테스트 어댑터에서 테스트 세션 실패를 보고했습니다.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedAtDeadline">
<source>Test execution was stopped early because a CI-imposed deadline was approaching.</source>
<target state="new">Test execution was stopped early because a CI-imposed deadline was approaching.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedForMaxFailedTests">
<source>Test execution stopped after reaching the limit set by --maximum-failed-tests.</source>
<target state="translated">--maximum-failed-tests에서 설정한 한도에 도달해 테스트 실행이 중지되었습니다.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@
<target state="translated">Adapter testowy zgłosił błąd sesji testowej.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedAtDeadline">
<source>Test execution was stopped early because a CI-imposed deadline was approaching.</source>
<target state="new">Test execution was stopped early because a CI-imposed deadline was approaching.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedForMaxFailedTests">
<source>Test execution stopped after reaching the limit set by --maximum-failed-tests.</source>
<target state="translated">Wykonanie testów zostało zatrzymane po osiągnięciu limitu ustawionego za pomocą opcji --maximum-failed-tests.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@
<target state="translated">O adaptador de teste relatou uma falha na sessão de teste.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedAtDeadline">
<source>Test execution was stopped early because a CI-imposed deadline was approaching.</source>
<target state="new">Test execution was stopped early because a CI-imposed deadline was approaching.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedForMaxFailedTests">
<source>Test execution stopped after reaching the limit set by --maximum-failed-tests.</source>
<target state="translated">A execução de teste foi interrompida após atingir o limite definido por --maximum-failed-tests.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@
<target state="translated">Адаптер тестирования сообщил о сбое тестового сеанса.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedAtDeadline">
<source>Test execution was stopped early because a CI-imposed deadline was approaching.</source>
<target state="new">Test execution was stopped early because a CI-imposed deadline was approaching.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedForMaxFailedTests">
<source>Test execution stopped after reaching the limit set by --maximum-failed-tests.</source>
<target state="translated">Выполнение тестов остановлено после достижения предела, заданного параметром --maximum-failed-tests.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@
<target state="translated">Test bağdaştırıcısı bir test oturumu hatası bildirdi.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedAtDeadline">
<source>Test execution was stopped early because a CI-imposed deadline was approaching.</source>
<target state="new">Test execution was stopped early because a CI-imposed deadline was approaching.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedForMaxFailedTests">
<source>Test execution stopped after reaching the limit set by --maximum-failed-tests.</source>
<target state="translated">--maximum-failed-tests tarafından belirlenen sınıra ulaşıldıktan sonra test yürütme durduruldu.</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@
<target state="translated">测试适配器报告了测试会话失败。</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedAtDeadline">
<source>Test execution was stopped early because a CI-imposed deadline was approaching.</source>
<target state="new">Test execution was stopped early because a CI-imposed deadline was approaching.</target>
<note />
</trans-unit>
<trans-unit id="ExitCodeReasonTestExecutionStoppedForMaxFailedTests">
<source>Test execution stopped after reaching the limit set by --maximum-failed-tests.</source>
<target state="translated">测试执行在达到 --maximum-failed-tests 设置的限制后停止。</target>
Expand Down
Loading