Show failure details in GitHub Actions step-summary collapsible sections - #10633
Show failure details in GitHub Actions step-summary collapsible sections#10633Azat Mukhametshin (azat-msft) wants to merge 1 commit into
Conversation
Each failed test in the GitHub Actions job summary is now expanded into a collapsible <details> section carrying its failure message, exception type, resolved source location and stack trace, instead of only its name. - Capture failure diagnostics in GitHubActionsSummaryReporter, resolving the source location the same way the annotation reporter does (exception call site, falling back to TestFileLocationProperty). - Propagate the diagnostics through the CI summary fragments so aggregated multi-module dotnet test runs render them too. - Bound the output twice (per value and per section) and state every truncation explicitly, so the summary stays well under GitHub's 1 MiB cap. - HTML-encode test-provided values in <summary> and pick a code fence longer than any backtick run in the body, so a hostile message cannot break out. - Add --report-gh-failure-details on|off to keep the previous compact list. Fixes #10591 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 110eb208-0496-4c66-be51-46dc51b16db5
There was a problem hiding this comment.
Pull request overview
Adds actionable failure diagnostics to GitHub Actions job summaries, including aggregated multi-module runs.
Changes:
- Captures and renders failure details in collapsible, injection-safe sections.
- Adds
--report-gh-failure-details on|offand output-size controls. - Updates tests, documentation, API baselines, and localization resources.
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
test/UnitTests/Microsoft.Testing.Extensions.UnitTests/GitHubActionsSummaryReporterTests.cs |
Tests failure-detail rendering and limits. |
test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs |
Updates CLI help expectations. |
test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/GitHubActionsReportTests.cs |
Adds end-to-end summary tests. |
src/Platform/SharedExtensionHelpers/SummaryReporterHelpers.cs |
Adds failure diagnostics to test records. |
src/Platform/SharedExtensionHelpers/CiRunSummaryAggregation.cs |
Persists diagnostics through aggregation. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.zh-Hant.xlf |
Adds Traditional Chinese localization entries. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.zh-Hans.xlf |
Adds Simplified Chinese localization entries. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.tr.xlf |
Adds Turkish localization entries. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.ru.xlf |
Adds Russian localization entries. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.pt-BR.xlf |
Adds Brazilian Portuguese localization entries. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.pl.xlf |
Adds Polish localization entries. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.ko.xlf |
Adds Korean localization entries. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.ja.xlf |
Adds Japanese localization entries. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.it.xlf |
Adds Italian localization entries. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.fr.xlf |
Adds French localization entries. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.es.xlf |
Adds Spanish localization entries. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.de.xlf |
Adds German localization entries. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.cs.xlf |
Adds Czech localization entries. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/GitHubActionsResources.resx |
Defines new localized messages. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/PACKAGE.md |
Documents the new option. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/InternalAPI/InternalAPI.Unshipped.txt |
Updates GitHub reporter API baseline. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/GitHubActionsSummaryReporter.cs |
Captures and renders failure diagnostics. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/GitHubActionsSummaryArtifactPostProcessor.cs |
Applies the option during aggregation. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/GitHubActionsFailureDetails.cs |
Implements bounded collapsible rendering. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/GitHubActionsCommandLineProvider.cs |
Registers and validates the option. |
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/GitHubActionsCommandLineOptions.cs |
Defines the option name. |
src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/InternalAPI/InternalAPI.Unshipped.txt |
Updates shared internal API baseline. |
docs/glossary.md |
Documents detailed failure summaries. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| /// Maximum characters of expanded failure detail rendered per module section, leaving ample room under | ||
| /// GitHub's 1 MiB job-summary limit for the other sections and for sibling test assemblies. | ||
| /// </summary> | ||
| internal const int MaxTotalDetailsLength = 60_000; |
| : GitHubActionsAnnotationReporter.TryResolveDeclaredLocation(testNode, repoRoot, _fileSystem); | ||
|
|
||
| return new TestFailureDetails( | ||
| GitHubActionsFailureDetails.Clip(failure.Value.Explanation ?? exception?.Message, GitHubActionsFailureDetails.MaxMessageLength), |
| if (includeFailureDetails && record.Failure is { IsEmpty: false } failure) | ||
| { | ||
| test.ErrorMessage = failure.Message; | ||
| test.ErrorType = failure.ExceptionType; | ||
| test.StackTrace = failure.StackTrace; | ||
| test.FilePath = failure.FilePath; | ||
| test.LineNumber = failure.LineNumber > 0 ? failure.LineNumber : null; |
| new CommandLineOption(GitHubActionsCommandLineOptions.GitHubActionsGroups, GitHubActionsResources.GroupsOptionDescription, ArgumentArity.ExactlyOne, false), | ||
| new CommandLineOption(GitHubActionsCommandLineOptions.GitHubActionsAnnotations, GitHubActionsResources.AnnotationsOptionDescription, ArgumentArity.ExactlyOne, false), | ||
| new CommandLineOption(GitHubActionsCommandLineOptions.GitHubActionsStepSummary, GitHubActionsResources.StepSummaryOptionDescription, ArgumentArity.ExactlyOne, false), | ||
| new CommandLineOption(GitHubActionsCommandLineOptions.GitHubActionsFailureDetails, GitHubActionsResources.FailureDetailsOptionDescription, ArgumentArity.ExactlyOne, false), |
Validation in real GitHub Actions runsValidated end-to-end in azat-msft/gh-report-validation with this build packed into that repo's local feed (extension
The headline number for the size concern: in #4, 31 failures each carrying a ~6 KB message and a 40-frame stack trace produce a 76 KB summary — roughly 7% of GitHub's 1 MiB job-summary limit — with both truncation notes rendered: Those validation PRs also fix a pre-existing bug in that repo's workflow, unrelated to this change: it passed |
Fourth validation run: the failure-count axisAdded azat-msft/gh-report-validation#5, which applies the opposite pressure from the oversized-details run: 5,000 failing tests with tiny diagnostics rather than a few with enormous ones. 5,000 failures produce a 27 KB summary — about 2.6% of GitHub's 1 MiB limit. Varying only the failure count (measured locally):
The size is flat; the 55-byte delta is just the wider count in the text. Notable result: I could not construct a summary that overflows purely from failure count. Both reporters bound their own sections — this one at 20 failures (12,750 B), TUnit's own block at a 50-row table (4,848 B). So failure count cannot push a run past the 1 MiB limit; only per-failure size can, which is exactly what the per-value clips and the per-section budget exist to contain. The two runs bracket the design: #4 shows the size axis is bounded at runtime, #5 shows the count axis is bounded by construction. One design question before this leaves draft
|
Fixes #10591
What
The GitHub Actions job summary previously listed only the fully-qualified name of each failed test, so investigating a failure meant leaving the summary page for the Annotations tab (which has no stack trace) or the raw workflow log.
Each failed test is now expanded into a collapsible
<details>section:The summary line reuses the
test name — durationpresentation and duration formatting of the existing "Slowest tests" section, so the two are visually consistent.How
GitHubActionsSummaryReporternow records the failure explanation (or exception message), exception type, stack trace and source location for each failing node. The location is resolved exactly the wayGitHubActionsAnnotationReporterresolves it: prefer the exception's call site, then fall back to the test's ownTestFileLocationProperty, so frameworks without a usable stack trace still get a location.CiRunSummaryTestfragment model, so aggregated multi-moduledotnet testruns render them too. They are attached only to the failures list, never to slowest-tests entries, so fragments don't carry duplicate stack traces.--report-gh-failure-details on|off, defaulting toon, restores the previous compact list.Bounding the output
Jakub Jareš (@nohwnd)'s point on the issue about the job-summary size limit is handled with two independent bounds, both stated explicitly in the rendered output rather than silently applied:
[... truncated]appended[... truncated]appended[!NOTE]callout saying how many were omitted[!NOTE]callout now statesShowing the first 20 of N failed testsClipping happens at capture time, not render time, so an enormous stack trace never reaches the fragment written to disk. A failure that carries no diagnostics at all falls back to the compact line instead of rendering an empty disclosure.
Injection safety
<summary>are HTML-encoded — a generic test name likeT.Map<string,int>would otherwise be parsed as a tag and swallow the rest of the line.Existing GitHub error/warning annotations are unchanged.
Testing
GitHubActionsSummaryReporterTestscovering the rendered section, the off-switch, the no-details fallback, HTML encoding, fence escaping, both truncation paths,Clip, and the aggregate/fragment path.GitHubActionsReportTestsdriving a real MTP session with an exception-carrying failure, asserting the collapsible section end-to-end and that--report-gh-failure-details offkeeps the compact list.HelpInfoAllExtensionsTests--help/--infoexpectations updated for the new option.Microsoft.Testing.Extensions.UnitTestssuite passes (1,100 tests).Docs (
PACKAGE.md,docs/glossary.md) and the.xlflocalization files are updated.Left as draft pending validation of the rendered output in real workflow runs (see linked validation PRs).