Skip to content

Add GitHub Actions test history - #10778

Open
Amaury Levé (Evangelink) wants to merge 1 commit into
mainfrom
dev/amauryleve/github-test-history
Open

Add GitHub Actions test history#10778
Amaury Levé (Evangelink) wants to merge 1 commit into
mainfrom
dev/amauryleve/github-test-history

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Summary

  • add a versioned, bounded local test-history snapshot to the GitHub Actions report extension
  • preserve history configuration through multi-module and retry artifact post-processing
  • annotate failures with prior-run failure and flakiness context scoped by assembly, target framework, architecture, and runner OS
  • document a GitHub Actions artifact workflow that keeps GitHub credentials outside the test process

Validation

  • warning-free net8.0 and net462 builds
  • 192 focused GitHub Actions report unit tests
  • full repository pack
  • 9 help/info acceptance cases across net462, net8.0, and net10.0 test assets
  • three expert code-review rounds with all confirmed findings resolved

Fixes #10770

Persist bounded test history through workflow-managed artifacts and use prior run outcomes to annotate failures and flakiness.

Fixes #10770

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 777e8acd-fc17-4ed7-a588-c973cc686388
Copilot AI balanced review requested due to automatic review settings August 26, 2026 16:10
private readonly IClock _clock;
private readonly ILogger _logger;
private readonly GitHubActionsHistoryScope _scope;
private IReadOnlyDictionary<string, GitHubActionsHistoryStats> _statsByTest = EmptyStats;

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds bounded, workflow-managed test history to the GitHub Actions reporting extension.

Changes:

  • Adds history CLI options, persistence, scoped statistics, and failure annotations.
  • Preserves history through retry and multi-module processing with unit coverage.
  • Updates help output, localization resources, package guidance, glossary, and changelog.
Show a summary per file
File Description
test/UnitTests/Microsoft.Testing.Extensions.UnitTests/GitHubActionsSummaryReporterTests.cs Tests history post-processing and retries.
test/UnitTests/Microsoft.Testing.Extensions.UnitTests/GitHubActionsHistoryTests.cs Tests storage, aggregation, and metadata.
test/UnitTests/Microsoft.Testing.Extensions.UnitTests/GitHubActionsCommandLineProviderTests.cs Tests history option validation.
test/UnitTests/Microsoft.Testing.Extensions.UnitTests/GitHubActionsAnnotationReporterTests.cs Tests historical annotation context.
test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs Updates help/info expectations.
src/Platform/SharedExtensionHelpers/CiRunSummaryModels.cs Adds history fragment models and settings.
src/Platform/SharedExtensionHelpers/CiRunSummaryAggregateReader.cs Validates history fragment data.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.zh-Hant.xlf Adds localized resource units.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.zh-Hans.xlf Adds localized resource units.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.tr.xlf Adds localized resource units.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.ru.xlf Adds localized resource units.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.pt-BR.xlf Adds localized resource units.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.pl.xlf Adds localized resource units.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.ko.xlf Adds localized resource units.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.ja.xlf Adds localized resource units.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.it.xlf Adds localized resource units.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.fr.xlf Adds localized resource units.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.es.xlf Adds localized resource units.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.de.xlf Adds localized resource units.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/xlf/GitHubActionsResources.cs.xlf Adds localized resource units.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/Resources/GitHubActionsResources.resx Defines history messages and descriptions.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/PACKAGE.md Documents artifact-based persistence.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/InternalAPI/InternalAPI.Unshipped.txt Updates tracked internal API signatures.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/GitHubActionsSummaryReporter.cs Collects and writes history results.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/GitHubActionsSummaryArtifactPostProcessor.cs Merges retry and module history.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/GitHubActionsHistory.cs Implements history storage and aggregation.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/GitHubActionsExtensions.cs Registers the history service.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/GitHubActionsCommandLineProvider.cs Exposes and validates history options.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/GitHubActionsCommandLineOptions.cs Defines option names and defaults.
src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/GitHubActionsAnnotationReporter.cs Appends historical failure context.
docs/glossary.md Documents the history feature.
docs/Changelog-Platform.md Records the feature addition.

Review details

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 32/32 changed files
  • Comments generated: 5
  • Review effort level: Balanced

run: dotnet test --report-gh --report-gh-history .test-history/history.json

- name: Publish test history
if: always() && github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
Comment on lines +13 to +16
const Microsoft.Testing.Extensions.GitHubActionsReport.GitHubActionsCommandLineOptions.GitHubActionsHistory = "report-gh-history" -> string!
const Microsoft.Testing.Extensions.GitHubActionsReport.GitHubActionsCommandLineOptions.GitHubActionsHistoryWindow = "report-gh-history-window" -> string!
const Microsoft.Testing.Extensions.GitHubActionsReport.GitHubActionsCommandLineOptions.GitHubActionsStepSummarySections = "report-gh-step-summary-sections" -> string!
const Microsoft.Testing.Extensions.GitHubActionsReport.GitHubActionsCommandLineOptions.HistoryWindowDefaultDays = 30 -> int

The extension intentionally reads and writes only a local snapshot. The workflow owns GitHub authentication and artifact transfer. This keeps `GITHUB_TOKEN` out of the test process and allows pull request runs to consume history without granting them write access. Snapshots retain at most 1,000 samples per test and the latest 10,000 samples overall, in addition to the configured age window.

The following steps restore the newest default-branch snapshot, run tests, and publish the updated snapshot only from the default branch:
Comment on lines +169 to +171
GitHubActionsHistorySample[] samples =
[
.. configuredGroup.SelectMany(module => module.HistoryTests.Select(test => new GitHubActionsHistorySample
Comment on lines +122 to +123
GitHubActionsHistorySample otherAssembly = CreateSample("Tests.Flaky", GitHubActionsHistoryOutcome.Failed, Now);
otherAssembly.AssemblyName = "OtherTests";
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Expert test review — PR #10778

No new or modified test methods were identified in the changed regions of this PR. Nothing to review.

Re-run with /review-tests.

🤖 Automated content by GitHub Copilot. Generated by the Test Reviewer on PR (on open / sync) workflow. · auto · 34.4 AIC · ⌖ 1.07 AIC · ⊞ 16.9K · [◷]( · )

@github-actions

Copy link
Copy Markdown
Contributor

🧵 Parallel-safety audit — PR #10778

Parallelization — audited assembly:

Test assembly Scope Workers Analyzer coverage
Microsoft.Testing.Extensions.UnitTests MethodLevel ([assembly: Parallelize(Scope = ExecutionScope.MethodLevel, Workers = 0)] in Program.cs) 0 (CPU count) coverable once the parallel-safety analyzers ship (attribute-based opt-in; MSTEST0074–0077 are in flight, not yet on main)

No .runsettings or testconfig.json overrides the opt-in for this project, and this PR does not touch the assembly's parallelization configuration — [DeploymentItem]/attribute state is unchanged.

Findings: A (global-state) 0 · B (paths) 0 · C (declaration) 0 · D (over-serialization) 0 — by severity: Critical 0 · High 0 · Warning 0 · Info 0.

I audited every changed/added test method across the four changed unit-test files (GitHubActionsAnnotationReporterTests.cs, GitHubActionsCommandLineProviderTests.cs, GitHubActionsHistoryTests.cs — new, GitHubActionsSummaryReporterTests.cs) plus the changed help-text assertions in HelpInfoAllExtensionsTests.cs (integration project, string-literal comparisons only, no runtime side effects). Findings:

  • All filesystem I/O uses a fresh, per-test Path.Combine(Path.GetTempPath(), $"...-{Guid.NewGuid():N}") directory (or a caller-supplied unique temp path in GitHubActionsHistoryTests.ReadAsync_ReturnsEmptySnapshot_WhenFileDoesNotExist), created and deleted (finally) within the same test — no relative paths, no shared constants, no cross-test path collisions. This is the elimination pattern the audit prefers; nothing to lock.
  • No changed test mutates process-global state: Environment, ILoggerFactory, IFileSystem, IOutputDevice, ITestApplicationProcessExitCode, and the new IGitHubActionsHistoryService are all supplied via Moq mocks or hand-written fakes (FakeHistoryService, CapturingHistoryService) scoped to the single test method — no shared mutable statics.
  • No changed or removed [ResourceLock] / [DoNotParallelize] / [Parallelize] declaration in this diff (existing usages in AzureFoundryChatClientProviderTests.cs, StackTraceSourceLocationResolverTests.cs, TrxLongPathHelperTests.cs are untouched and out of scope).
  • No new lifecycle members ([TestInitialize]/[ClassInitialize]/constructors) were added or modified by this PR.

Nothing to flag for parallel-safety.

Advisory only — heuristic, non-blocking. Re-run with /parallel-audit. This audit answers "is it parallel-safe?"; for testability, smells, or flakiness see the detect-static-dependencies / test-smell-detection / test-anti-patterns analyses.

🤖 Automated content by GitHub Copilot. Generated by the Parallel-safety audit on PR (on open / sync) workflow. · auto · 110.7 AIC · ⌖ 1.28 AIC · ⊞ 24.8K · [◷]( · )

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

Review Summary

Well-structured feature addition implementing bounded GitHub Actions test-history snapshots with proper file locking, retry logic, and atomic writes. The design correctly separates concerns between the history service, the store, and the consumers (annotation reporter, summary reporter, artifact post-processor).

# Dimension Verdict
1 Algorithmic Correctness ✅ Clean — merge deduplication and bounded retention are correct
2 Threading & Concurrency ✅ Clean — Volatile.Read/Write for _statsByTest, file-level advisory locking
3 Security & IPC Contract Safety ✅ N/A — paths come from user CLI args; user already has FS access
4 Public API & Binary Compatibility ✅ Clean — all types are internal; no public API surface added
5 Performance & Allocations ⚠️ Minor — GetIdentity()/GetRunIdentity() allocate per-call in LINQ chains (see inline)
6 Cross-TFM Compatibility ✅ Clean — uses System.Text.Json source gen, no TFM-gated APIs
7 Error Handling ✅ Clean — retryable exceptions with exponential backoff; corrupt files reset gracefully
8 Localization ✅ Clean — new strings in .resx, XLF files regenerated
9 Test Coverage ✅ Good — dedicated GitHubActionsHistoryTests covers merge, aggregation, window, and boundary cases
10 Configuration & CLI ✅ Clean — validation, dependency enforcement (--report-gh-history-window requires --report-gh-history)
11 Documentation ✅ Clean — changelog, glossary, and PACKAGE.md updated
12 Naming & Conventions ⚠️ Minor — indentation inconsistency in CreateHistoryService (see inline)
13–22 Remaining dimensions ✅ N/A or clean

Overall: No blocking issues. Two minor items flagged inline. The PR is ready for human sign-off.

}

private static GitHubActionsHistoryService CreateHistoryService(IServiceProvider serviceProvider)
=> new(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style (SA1028): This line uses 7-space indentation for the => body instead of the expected alignment. The .editorconfig enforces consistent indentation.

Suggested change
=> new(
=> new(

Comment on lines +417 to +419
.. samples
.GroupBy(
static sample => $"{sample.GetIdentity()}\0{sample.GetRunIdentity()}",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance (§5): GetIdentity() and GetRunIdentity() allocate interpolated strings on every invocation within GroupBy. For large history snapshots (up to MaxTotalSamples = 10,000 samples), this creates significant GC pressure during the merge operation. Consider caching the identity per sample (e.g. computed once and stored in a transient field or local dictionary) or using a composite key tuple instead of string concatenation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add GitHub Actions test history and flaky-test reporting

2 participants