Add Azure DevOps flaky-history annotations and quarantine awareness - #8298

Merged
Amaury Levé (Evangelink) merged 6 commits into
mainfrom
dev/amauryleve/azdo-flaky-history
May 19, 2026
Merged

Add Azure DevOps flaky-history annotations and quarantine awareness#8298
Amaury Levé (Evangelink) merged 6 commits into
mainfrom
dev/amauryleve/azdo-flaky-history

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Part 2 of the brainstorm in #5951 — adds opt-in flaky-test history annotations and quarantine awareness to Microsoft.Testing.Extensions.AzureDevOpsReport. Decorates AzDO log issues with historical flake context and lets known-noisy failures be downgraded so PR gates aren't blocked.

One of three PRs derived from the #5951 brainstorm. The others:

See issue comment for the broader plan.

Why

Today every failure in an AzDO log looks equally bad. There's no way for the build to say "this test failed 4/20 times in the last 14 days — known noise" vs "this test had zero failures in 14 days and just broke — likely regression". Teams that have moved to MTP currently mark failures as warnings manually with a separate task or live with red PR checks for known-flaky tests.

What

Three new opt-in CLI options on Microsoft.Testing.Extensions.AzureDevOpsReport:

OptionTypePurpose
--report-azdo-flaky-history <days>int (1–90)Query AzDO REST history for the last N days and annotate failures with [flaky: failed K/N in last Md] or [REGRESSION] (only when ≥5 prior samples).
--report-azdo-quarantine-file <path>stringPath to a text file (one FQN/glob per line, # comments allowed) listing tests considered quarantined. Their failures are demoted to warning and tagged [quarantined]; emits ##vso[build.addbuildtag]has-quarantined-test-failure exactly once.
--report-azdo-demote-known-flakyzero-arityTogether with --report-azdo-flaky-history, auto-demote failures whose flake-rate ≥25% in the window to warning. Default OFF (annotate-only). Requires --report-azdo-flaky-history.

All three are opt-in; missing AzDO env vars (SYSTEM_ACCESSTOKEN/SYSTEM_COLLECTIONURI/SYSTEM_TEAMPROJECT/BUILD_DEFINITIONID) → log warning and no-op.

How it works

  • AuthAuthorization: Basic base64(":<SYSTEM_ACCESSTOKEN>") (no Microsoft.TeamFoundationServer.Client dependency; just HttpClient + a source-generated JsonSerializerContext so it's AOT-safe).
  • History queryGET {project}/_apis/test/Runs?definitions={pipelineDefinitionId}&minLastUpdatedDate=…&maxLastUpdatedDate=…&automated=true&$top=200 paginated with $skip up to MaxRunsToInspect = 200. Per run, GET …/results?api-version=7.1&outcomes=Failed,Passed paged with continuation token. Aggregated into Dictionary<automatedTestName, FlakyStats>.
  • Bounded session start — history load has a wall-clock budget (default 30 s). If exceeded, log info and degrade to empty stats; tests start immediately.
  • Resilience — REST calls retry on transient errors (3 attempts, exponential backoff, 429 honors Retry-After). Response bodies truncated to 500 chars in error messages. All callbacks catch everything except OperationCanceledException; history/quarantine failures never fail the test run.
  • Quarantine file — line-based (# comments), case-sensitive ordinal matching against FQN, glob patterns (*, ?) compiled to a single alternation regex. Capped at 10 000 patterns / 4 KB per pattern with a logged warning.
  • Build tag##vso[build.addbuildtag]has-quarantined-test-failure guarded by Interlocked.Exchange so concurrent failure events emit it exactly once.

Highlights from the expert-reviewer round

Implementation went through one full round of expert-reviewer. Critical issues addressed:

  • C1 (feature was broken): the runs query was using buildIds=<pipelineDefinitionId> (the wrong AzDO parameter — buildIds filters by individual build run id, not pipeline). Switched to definitions=<pipelineDefinitionId> so the query actually returns data. Unit test now asserts the URL contains definitions=.
  • C2: history load was synchronous & serial — up to 25 000 sequential HTTP requests blocking session start. Bounded with a 30 s wall-clock budget + cancellation; degrades to empty stats on exceed.
  • C3: $top=501 was silently capped server-side. Now properly pages runs via $skip until MaxRunsToInspect is reached.
  • C4: [REGRESSION] previously fired on a single prior sample, generating false positives everywhere. Now requires TotalCount >= 5 (configurable via MinSamplesForRegressionAnnotation).

Major items also addressed: User-Agent/Accept headers added (prevents AzDO WAF-side 403s), source-generated JsonSerializerContext (AOT-safe), error-body truncation, inner-exception preservation on retry exhaustion, quarantine pattern caps, ordinal-case-sensitive matching, IAzureDevOpsHistoryService interface + proper DI registration, guard-clause CLI validation, acceptance tests for invalid-value error paths.

Tests

546 unit tests pass. New coverage:

  • AzureDevOpsHistoryServiceTests.cs — history aggregation, paging, time budget, regression boundary (4 vs 5 samples), quarantine tag-emitted-once.
  • AzureDevOpsHistoryClientTests.cs — URL composition (definitions= parameter), User-Agent/Accept headers, 429 retry honoring Retry-After.
  • AzureDevOpsCommandLineProviderTests.cs — cross-product validation (e.g. --demote-known-flaky without --flaky-history, --quarantine-file without --report-azdo).
  • AzureDevOpsCommandLineTests.cs — acceptance-style coverage.

HelpInfoAllExtensionsTests expectations updated for the new options (both --help and --info blocks, alphabetical order preserved).

Build status (local)

  • .\.dotnet\dotnet.exe build src\Platform\Microsoft.Testing.Extensions.AzureDevOpsReport\Microsoft.Testing.Extensions.AzureDevOpsReport.csproj -c Debug0 warnings, 0 errors.
  • .\.dotnet\dotnet.exe test test\UnitTests\Microsoft.Testing.Extensions.UnitTests\Microsoft.Testing.Extensions.UnitTests.csproj546/546 passed.
  • .\build.cmd -pack0 warnings, 0 errors.

Out of scope (deliberate)

Checklist

  • Critical & Major review findings addressed
  • Localized via resx + xlf (regenerated with /t:UpdateXlf, not hand-edited)
  • Help/info acceptance test expectations updated
  • No new public API (besides one internal IAzureDevOpsHistoryService)
  • .\build.cmd green (0 warnings, 0 errors)
  • PR feedback addressed

Refs #5951

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 16, 2026 19:53

CopilotAI 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 opt-in Azure DevOps flaky-history annotations and quarantine awareness to the Microsoft.Testing.Extensions.AzureDevOpsReport extension. Failures can now be annotated with historical flake context, demoted from error to warning when known-flaky, and demoted with a [quarantined] tag when listed in a quarantine file (which also emits a one-shot ##vso[build.addbuildtag]has-quarantined-test-failure).

Changes:

  • New CLI options --report-azdo-flaky-history, --report-azdo-quarantine-file, --report-azdo-demote-known-flaky with cross-option validation in AzureDevOpsCommandLineProvider.
  • New AzureDevOpsHistoryService + AzureDevOpsHistoryClient (AOT-safe JsonSerializerContext) that queries the AzDO REST Runs/Results APIs under a 30 s wall-clock budget, with retries, 429 Retry-After honoring, paging caps, and a regression-annotation min-sample threshold.
  • AzureDevOpsReporter now annotates errors with [flaky: failed K/N in last Md] / [REGRESSION] / [quarantined], and demotes severity per the quarantine file and known-flaky rule.
Show a summary per file
FileDescription
src/.../AzureDevOpsCommandLineOptions.csAdds 3 new option name constants.
src/.../AzureDevOpsCommandLineProvider.csRegisters new options and adds cross-option validation.
src/.../AzureDevOpsExtensions.csWires AzureDevOpsHistoryService as data consumer + session lifetime handler.
src/.../AzureDevOpsHistoryClient.csNew REST client (auth, paging, retries, AOT JSON).
src/.../AzureDevOpsHistoryClientJsonContext.csSource-generated JSON context for DTOs.
src/.../AzureDevOpsHistoryService.csLoads/aggregates flaky stats with a bounded budget; exposes TryGetStats/IsLikelyFlaky.
src/.../AzureDevOpsReporter.csAdds annotation suffix building, severity demotion, one-shot quarantine build tag.
src/.../FlakyStats.csStruct holding pass/fail counts and failure rate.
src/.../IAzureDevOpsHistoryService.csInternal abstraction over the history service.
src/.../QuarantineFile.csParses quarantine file (globs, # comments, caps) into regex matchers.
src/.../Microsoft.Testing.Extensions.AzureDevOpsReport.csprojAdds System.Text.Json dependency and DynamicProxyGenAssembly2 IVT for Moq.
Directory.Packages.propsPins System.Text.Json version.
src/.../Resources/AzureDevOpsResources.resxNew strings for options, warnings, and annotation templates; fixes prior Eanble/AzureDev Ops typos.
src/.../Resources/xlf/*.xlf (12 locales)Regenerated XLFs for new strings; Description/OptionDescription flipped to needs-review-translation after the English typo fix.
test/.../AzureDevOpsHistoryClientTests.csAsserts URL composition (definitions=), headers, and run-paging behavior.
test/.../AzureDevOpsHistoryServiceTests.csCovers aggregation, paging, time-budget timeout, regression threshold, demote, quarantine tag-once.
test/.../AzureDevOpsCommandLineProviderTests.csValidates cross-option error messages.
test/.../AzureDevOpsCommandLineTests.csAcceptance-style test for invalid CLI argument errors.
test/.../HelpInfoAllExtensionsTests.csUpdates --help / --info expectations for new options.

Copilot's findings

  • Files reviewed: 31/31 changed files
  • Comments generated: 0

@Evangelink
Amaury Levé (Evangelink) marked this pull request as ready for review May 17, 2026 19:08
Aligns the failure message with the convention used by Assert.Contains
(which uses GetType().Name) and fixes the unit test expectations in
AssertTests.AreAll.cs that expected the short type name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 18, 2026 07:14

CopilotAI 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.

Copilot's findings

  • Files reviewed: 32/32 changed files
  • Comments generated: 4

CopilotAI added 2 commits May 18, 2026 22:03
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Revert unrelated merge-resolution changes that regressed main (Assert.AreEqual updates from RFC 012, AreAllDistinct, PreferAsyncAssertion, Arcade/Versions, eng/common)
- Format DemoteKnownFlakyOptionDescription with the threshold percentage at runtime to keep the resource string and KnownFlakyFailureRateThreshold constant in sync
- Regenerate XLF resources via UpdateXlf
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 19, 2026 04:49
@Evangelink

Copy link
Copy Markdown
MemberAuthor

Addressed the open review feedback in 0a2cdb5:

  1. Assert.AreAllDistinct.cs (unrelated change) — Reverted. The bad merge resolution in 0dac227 had also accidentally regressed several other unrelated files merged from main (the RFC 012 changes in Assert.AreEqual*, the MSTEST0064 follow-up in PreferAsyncAssertionFixer/PreferAsyncAssertionAnalyzerTests, and eng/Version.Details.xml / eng/Versions.props / eng/common/* / global.json). All of these were restored from origin/main so the PR diff is now scoped to AzDO files only.
  2. AzureDevOpsExtensions.cs (hard cast to ServiceProvider) — Already fixed in 0dac227 (history service is now created lazily via closure shared by the data consumer and the lifetime handler, no cast to the concrete ServiceProvider).
  3. AzureDevOpsReporter.cs constant/resource drift (25%) — Switched to runtime formatting. KnownFlakyFailureRateThreshold is now internal const; DemoteKnownFlakyOptionDescription carries a {0}% placeholder and AzureDevOpsCommandLineProvider formats it once at startup with KnownFlakyFailureRateThreshold * 100. XLF files regenerated via UpdateXlf; the rendered help text is identical to before, so HelpInfoAllExtensionsTests expectations remain valid.
  4. AzureDevOpsHistoryService.cs budget-task leak — Already fixed in 0dac227 (the budget CancellationTokenSource is canceled as soon as the load task wins the race, so the Task.Delay registration is released promptly).

Verified locally:

  • dotnet build src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport0 warnings, 0 errors.
  • AzureDevOps unit tests (32 of them in Microsoft.Testing.Extensions.UnitTests) — all pass.
  • Full Microsoft.Testing.Extensions.UnitTests151/153 pass, 2 skipped (the two crash-report tests skipped on Windows, as on main).
  • AreAllDistinct tests in TestFramework.UnitTests (23 tests) — all pass.

CopilotAI 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.

Copilot's findings

  • Files reviewed: 58/58 changed files
  • Comments generated: 1

CopilotAI review requested due to automatic review settings May 19, 2026 06:41
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/azdo-flaky-history branch from 6abe725 to 0a2cdb5CompareMay 19, 2026 06:41

CopilotAI 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.

Copilot's findings

  • Files reviewed: 58/58 changed files
  • Comments generated: 0 new

Resolve XLF conflicts by taking main's translations (the AzDO PR should not modify FrameworkMessages localizations).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto 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.

3 participants

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Add Azure DevOps flaky-history annotations and quarantine awareness - #8298

Merged
Amaury Levé (Evangelink) merged 6 commits into
mainfrom
dev/amauryleve/azdo-flaky-history
May 19, 2026
Merged

Add Azure DevOps flaky-history annotations and quarantine awareness#8298
Amaury Levé (Evangelink) merged 6 commits into
mainfrom
dev/amauryleve/azdo-flaky-history

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Part 2 of the brainstorm in #5951 — adds opt-in flaky-test history annotations and quarantine awareness to Microsoft.Testing.Extensions.AzureDevOpsReport. Decorates AzDO log issues with historical flake context and lets known-noisy failures be downgraded so PR gates aren't blocked.

One of three PRs derived from the #5951 brainstorm. The others:

See issue comment for the broader plan.

Why

Today every failure in an AzDO log looks equally bad. There's no way for the build to say "this test failed 4/20 times in the last 14 days — known noise" vs "this test had zero failures in 14 days and just broke — likely regression". Teams that have moved to MTP currently mark failures as warnings manually with a separate task or live with red PR checks for known-flaky tests.

What

Three new opt-in CLI options on Microsoft.Testing.Extensions.AzureDevOpsReport:

OptionTypePurpose
--report-azdo-flaky-history <days>int (1–90)Query AzDO REST history for the last N days and annotate failures with [flaky: failed K/N in last Md] or [REGRESSION] (only when ≥5 prior samples).
--report-azdo-quarantine-file <path>stringPath to a text file (one FQN/glob per line, # comments allowed) listing tests considered quarantined. Their failures are demoted to warning and tagged [quarantined]; emits ##vso[build.addbuildtag]has-quarantined-test-failure exactly once.
--report-azdo-demote-known-flakyzero-arityTogether with --report-azdo-flaky-history, auto-demote failures whose flake-rate ≥25% in the window to warning. Default OFF (annotate-only). Requires --report-azdo-flaky-history.

All three are opt-in; missing AzDO env vars (SYSTEM_ACCESSTOKEN/SYSTEM_COLLECTIONURI/SYSTEM_TEAMPROJECT/BUILD_DEFINITIONID) → log warning and no-op.

How it works

  • AuthAuthorization: Basic base64(":<SYSTEM_ACCESSTOKEN>") (no Microsoft.TeamFoundationServer.Client dependency; just HttpClient + a source-generated JsonSerializerContext so it's AOT-safe).
  • History queryGET {project}/_apis/test/Runs?definitions={pipelineDefinitionId}&minLastUpdatedDate=…&maxLastUpdatedDate=…&automated=true&$top=200 paginated with $skip up to MaxRunsToInspect = 200. Per run, GET …/results?api-version=7.1&outcomes=Failed,Passed paged with continuation token. Aggregated into Dictionary<automatedTestName, FlakyStats>.
  • Bounded session start — history load has a wall-clock budget (default 30 s). If exceeded, log info and degrade to empty stats; tests start immediately.
  • Resilience — REST calls retry on transient errors (3 attempts, exponential backoff, 429 honors Retry-After). Response bodies truncated to 500 chars in error messages. All callbacks catch everything except OperationCanceledException; history/quarantine failures never fail the test run.
  • Quarantine file — line-based (# comments), case-sensitive ordinal matching against FQN, glob patterns (*, ?) compiled to a single alternation regex. Capped at 10 000 patterns / 4 KB per pattern with a logged warning.
  • Build tag##vso[build.addbuildtag]has-quarantined-test-failure guarded by Interlocked.Exchange so concurrent failure events emit it exactly once.

Highlights from the expert-reviewer round

Implementation went through one full round of expert-reviewer. Critical issues addressed:

  • C1 (feature was broken): the runs query was using buildIds=<pipelineDefinitionId> (the wrong AzDO parameter — buildIds filters by individual build run id, not pipeline). Switched to definitions=<pipelineDefinitionId> so the query actually returns data. Unit test now asserts the URL contains definitions=.
  • C2: history load was synchronous & serial — up to 25 000 sequential HTTP requests blocking session start. Bounded with a 30 s wall-clock budget + cancellation; degrades to empty stats on exceed.
  • C3: $top=501 was silently capped server-side. Now properly pages runs via $skip until MaxRunsToInspect is reached.
  • C4: [REGRESSION] previously fired on a single prior sample, generating false positives everywhere. Now requires TotalCount >= 5 (configurable via MinSamplesForRegressionAnnotation).

Major items also addressed: User-Agent/Accept headers added (prevents AzDO WAF-side 403s), source-generated JsonSerializerContext (AOT-safe), error-body truncation, inner-exception preservation on retry exhaustion, quarantine pattern caps, ordinal-case-sensitive matching, IAzureDevOpsHistoryService interface + proper DI registration, guard-clause CLI validation, acceptance tests for invalid-value error paths.

Tests

546 unit tests pass. New coverage:

  • AzureDevOpsHistoryServiceTests.cs — history aggregation, paging, time budget, regression boundary (4 vs 5 samples), quarantine tag-emitted-once.
  • AzureDevOpsHistoryClientTests.cs — URL composition (definitions= parameter), User-Agent/Accept headers, 429 retry honoring Retry-After.
  • AzureDevOpsCommandLineProviderTests.cs — cross-product validation (e.g. --demote-known-flaky without --flaky-history, --quarantine-file without --report-azdo).
  • AzureDevOpsCommandLineTests.cs — acceptance-style coverage.

HelpInfoAllExtensionsTests expectations updated for the new options (both --help and --info blocks, alphabetical order preserved).

Build status (local)

  • .\.dotnet\dotnet.exe build src\Platform\Microsoft.Testing.Extensions.AzureDevOpsReport\Microsoft.Testing.Extensions.AzureDevOpsReport.csproj -c Debug0 warnings, 0 errors.
  • .\.dotnet\dotnet.exe test test\UnitTests\Microsoft.Testing.Extensions.UnitTests\Microsoft.Testing.Extensions.UnitTests.csproj546/546 passed.
  • .\build.cmd -pack0 warnings, 0 errors.

Out of scope (deliberate)

Checklist

  • Critical & Major review findings addressed
  • Localized via resx + xlf (regenerated with /t:UpdateXlf, not hand-edited)
  • Help/info acceptance test expectations updated
  • No new public API (besides one internal IAzureDevOpsHistoryService)
  • .\build.cmd green (0 warnings, 0 errors)
  • PR feedback addressed

Refs #5951

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 16, 2026 19:53

CopilotAI 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 opt-in Azure DevOps flaky-history annotations and quarantine awareness to the Microsoft.Testing.Extensions.AzureDevOpsReport extension. Failures can now be annotated with historical flake context, demoted from error to warning when known-flaky, and demoted with a [quarantined] tag when listed in a quarantine file (which also emits a one-shot ##vso[build.addbuildtag]has-quarantined-test-failure).

Changes:

  • New CLI options --report-azdo-flaky-history, --report-azdo-quarantine-file, --report-azdo-demote-known-flaky with cross-option validation in AzureDevOpsCommandLineProvider.
  • New AzureDevOpsHistoryService + AzureDevOpsHistoryClient (AOT-safe JsonSerializerContext) that queries the AzDO REST Runs/Results APIs under a 30 s wall-clock budget, with retries, 429 Retry-After honoring, paging caps, and a regression-annotation min-sample threshold.
  • AzureDevOpsReporter now annotates errors with [flaky: failed K/N in last Md] / [REGRESSION] / [quarantined], and demotes severity per the quarantine file and known-flaky rule.
Show a summary per file
FileDescription
src/.../AzureDevOpsCommandLineOptions.csAdds 3 new option name constants.
src/.../AzureDevOpsCommandLineProvider.csRegisters new options and adds cross-option validation.
src/.../AzureDevOpsExtensions.csWires AzureDevOpsHistoryService as data consumer + session lifetime handler.
src/.../AzureDevOpsHistoryClient.csNew REST client (auth, paging, retries, AOT JSON).
src/.../AzureDevOpsHistoryClientJsonContext.csSource-generated JSON context for DTOs.
src/.../AzureDevOpsHistoryService.csLoads/aggregates flaky stats with a bounded budget; exposes TryGetStats/IsLikelyFlaky.
src/.../AzureDevOpsReporter.csAdds annotation suffix building, severity demotion, one-shot quarantine build tag.
src/.../FlakyStats.csStruct holding pass/fail counts and failure rate.
src/.../IAzureDevOpsHistoryService.csInternal abstraction over the history service.
src/.../QuarantineFile.csParses quarantine file (globs, # comments, caps) into regex matchers.
src/.../Microsoft.Testing.Extensions.AzureDevOpsReport.csprojAdds System.Text.Json dependency and DynamicProxyGenAssembly2 IVT for Moq.
Directory.Packages.propsPins System.Text.Json version.
src/.../Resources/AzureDevOpsResources.resxNew strings for options, warnings, and annotation templates; fixes prior Eanble/AzureDev Ops typos.
src/.../Resources/xlf/*.xlf (12 locales)Regenerated XLFs for new strings; Description/OptionDescription flipped to needs-review-translation after the English typo fix.
test/.../AzureDevOpsHistoryClientTests.csAsserts URL composition (definitions=), headers, and run-paging behavior.
test/.../AzureDevOpsHistoryServiceTests.csCovers aggregation, paging, time-budget timeout, regression threshold, demote, quarantine tag-once.
test/.../AzureDevOpsCommandLineProviderTests.csValidates cross-option error messages.
test/.../AzureDevOpsCommandLineTests.csAcceptance-style test for invalid CLI argument errors.
test/.../HelpInfoAllExtensionsTests.csUpdates --help / --info expectations for new options.

Copilot's findings

  • Files reviewed: 31/31 changed files
  • Comments generated: 0

@Evangelink
Amaury Levé (Evangelink) marked this pull request as ready for review May 17, 2026 19:08
Aligns the failure message with the convention used by Assert.Contains
(which uses GetType().Name) and fixes the unit test expectations in
AssertTests.AreAll.cs that expected the short type name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 18, 2026 07:14

CopilotAI 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.

Copilot's findings

  • Files reviewed: 32/32 changed files
  • Comments generated: 4

CopilotAI added 2 commits May 18, 2026 22:03
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Revert unrelated merge-resolution changes that regressed main (Assert.AreEqual updates from RFC 012, AreAllDistinct, PreferAsyncAssertion, Arcade/Versions, eng/common)
- Format DemoteKnownFlakyOptionDescription with the threshold percentage at runtime to keep the resource string and KnownFlakyFailureRateThreshold constant in sync
- Regenerate XLF resources via UpdateXlf
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 19, 2026 04:49
@Evangelink

Copy link
Copy Markdown
MemberAuthor

Addressed the open review feedback in 0a2cdb5:

  1. Assert.AreAllDistinct.cs (unrelated change) — Reverted. The bad merge resolution in 0dac227 had also accidentally regressed several other unrelated files merged from main (the RFC 012 changes in Assert.AreEqual*, the MSTEST0064 follow-up in PreferAsyncAssertionFixer/PreferAsyncAssertionAnalyzerTests, and eng/Version.Details.xml / eng/Versions.props / eng/common/* / global.json). All of these were restored from origin/main so the PR diff is now scoped to AzDO files only.
  2. AzureDevOpsExtensions.cs (hard cast to ServiceProvider) — Already fixed in 0dac227 (history service is now created lazily via closure shared by the data consumer and the lifetime handler, no cast to the concrete ServiceProvider).
  3. AzureDevOpsReporter.cs constant/resource drift (25%) — Switched to runtime formatting. KnownFlakyFailureRateThreshold is now internal const; DemoteKnownFlakyOptionDescription carries a {0}% placeholder and AzureDevOpsCommandLineProvider formats it once at startup with KnownFlakyFailureRateThreshold * 100. XLF files regenerated via UpdateXlf; the rendered help text is identical to before, so HelpInfoAllExtensionsTests expectations remain valid.
  4. AzureDevOpsHistoryService.cs budget-task leak — Already fixed in 0dac227 (the budget CancellationTokenSource is canceled as soon as the load task wins the race, so the Task.Delay registration is released promptly).

Verified locally:

  • dotnet build src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport0 warnings, 0 errors.
  • AzureDevOps unit tests (32 of them in Microsoft.Testing.Extensions.UnitTests) — all pass.
  • Full Microsoft.Testing.Extensions.UnitTests151/153 pass, 2 skipped (the two crash-report tests skipped on Windows, as on main).
  • AreAllDistinct tests in TestFramework.UnitTests (23 tests) — all pass.

CopilotAI 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.

Copilot's findings

  • Files reviewed: 58/58 changed files
  • Comments generated: 1

CopilotAI review requested due to automatic review settings May 19, 2026 06:41
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/azdo-flaky-history branch from 6abe725 to 0a2cdb5CompareMay 19, 2026 06:41

CopilotAI 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.

Copilot's findings

  • Files reviewed: 58/58 changed files
  • Comments generated: 0 new

Resolve XLF conflicts by taking main's translations (the AzDO PR should not modify FrameworkMessages localizations).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto 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.

3 participants

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add Azure DevOps flaky-history annotations and quarantine awareness - #8298

Merged
Amaury Levé (Evangelink) merged 6 commits into
mainfrom
dev/amauryleve/azdo-flaky-history
May 19, 2026
Merged

Add Azure DevOps flaky-history annotations and quarantine awareness#8298
Amaury Levé (Evangelink) merged 6 commits into
mainfrom
dev/amauryleve/azdo-flaky-history

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Part 2 of the brainstorm in #5951 — adds opt-in flaky-test history annotations and quarantine awareness to Microsoft.Testing.Extensions.AzureDevOpsReport. Decorates AzDO log issues with historical flake context and lets known-noisy failures be downgraded so PR gates aren't blocked.

One of three PRs derived from the #5951 brainstorm. The others:

See issue comment for the broader plan.

Why

Today every failure in an AzDO log looks equally bad. There's no way for the build to say "this test failed 4/20 times in the last 14 days — known noise" vs "this test had zero failures in 14 days and just broke — likely regression". Teams that have moved to MTP currently mark failures as warnings manually with a separate task or live with red PR checks for known-flaky tests.

What

Three new opt-in CLI options on Microsoft.Testing.Extensions.AzureDevOpsReport:

OptionTypePurpose
--report-azdo-flaky-history <days>int (1–90)Query AzDO REST history for the last N days and annotate failures with [flaky: failed K/N in last Md] or [REGRESSION] (only when ≥5 prior samples).
--report-azdo-quarantine-file <path>stringPath to a text file (one FQN/glob per line, # comments allowed) listing tests considered quarantined. Their failures are demoted to warning and tagged [quarantined]; emits ##vso[build.addbuildtag]has-quarantined-test-failure exactly once.
--report-azdo-demote-known-flakyzero-arityTogether with --report-azdo-flaky-history, auto-demote failures whose flake-rate ≥25% in the window to warning. Default OFF (annotate-only). Requires --report-azdo-flaky-history.

All three are opt-in; missing AzDO env vars (SYSTEM_ACCESSTOKEN/SYSTEM_COLLECTIONURI/SYSTEM_TEAMPROJECT/BUILD_DEFINITIONID) → log warning and no-op.

How it works

  • AuthAuthorization: Basic base64(":<SYSTEM_ACCESSTOKEN>") (no Microsoft.TeamFoundationServer.Client dependency; just HttpClient + a source-generated JsonSerializerContext so it's AOT-safe).
  • History queryGET {project}/_apis/test/Runs?definitions={pipelineDefinitionId}&minLastUpdatedDate=…&maxLastUpdatedDate=…&automated=true&$top=200 paginated with $skip up to MaxRunsToInspect = 200. Per run, GET …/results?api-version=7.1&outcomes=Failed,Passed paged with continuation token. Aggregated into Dictionary<automatedTestName, FlakyStats>.
  • Bounded session start — history load has a wall-clock budget (default 30 s). If exceeded, log info and degrade to empty stats; tests start immediately.
  • Resilience — REST calls retry on transient errors (3 attempts, exponential backoff, 429 honors Retry-After). Response bodies truncated to 500 chars in error messages. All callbacks catch everything except OperationCanceledException; history/quarantine failures never fail the test run.
  • Quarantine file — line-based (# comments), case-sensitive ordinal matching against FQN, glob patterns (*, ?) compiled to a single alternation regex. Capped at 10 000 patterns / 4 KB per pattern with a logged warning.
  • Build tag##vso[build.addbuildtag]has-quarantined-test-failure guarded by Interlocked.Exchange so concurrent failure events emit it exactly once.

Highlights from the expert-reviewer round

Implementation went through one full round of expert-reviewer. Critical issues addressed:

  • C1 (feature was broken): the runs query was using buildIds=<pipelineDefinitionId> (the wrong AzDO parameter — buildIds filters by individual build run id, not pipeline). Switched to definitions=<pipelineDefinitionId> so the query actually returns data. Unit test now asserts the URL contains definitions=.
  • C2: history load was synchronous & serial — up to 25 000 sequential HTTP requests blocking session start. Bounded with a 30 s wall-clock budget + cancellation; degrades to empty stats on exceed.
  • C3: $top=501 was silently capped server-side. Now properly pages runs via $skip until MaxRunsToInspect is reached.
  • C4: [REGRESSION] previously fired on a single prior sample, generating false positives everywhere. Now requires TotalCount >= 5 (configurable via MinSamplesForRegressionAnnotation).

Major items also addressed: User-Agent/Accept headers added (prevents AzDO WAF-side 403s), source-generated JsonSerializerContext (AOT-safe), error-body truncation, inner-exception preservation on retry exhaustion, quarantine pattern caps, ordinal-case-sensitive matching, IAzureDevOpsHistoryService interface + proper DI registration, guard-clause CLI validation, acceptance tests for invalid-value error paths.

Tests

546 unit tests pass. New coverage:

  • AzureDevOpsHistoryServiceTests.cs — history aggregation, paging, time budget, regression boundary (4 vs 5 samples), quarantine tag-emitted-once.
  • AzureDevOpsHistoryClientTests.cs — URL composition (definitions= parameter), User-Agent/Accept headers, 429 retry honoring Retry-After.
  • AzureDevOpsCommandLineProviderTests.cs — cross-product validation (e.g. --demote-known-flaky without --flaky-history, --quarantine-file without --report-azdo).
  • AzureDevOpsCommandLineTests.cs — acceptance-style coverage.

HelpInfoAllExtensionsTests expectations updated for the new options (both --help and --info blocks, alphabetical order preserved).

Build status (local)

  • .\.dotnet\dotnet.exe build src\Platform\Microsoft.Testing.Extensions.AzureDevOpsReport\Microsoft.Testing.Extensions.AzureDevOpsReport.csproj -c Debug0 warnings, 0 errors.
  • .\.dotnet\dotnet.exe test test\UnitTests\Microsoft.Testing.Extensions.UnitTests\Microsoft.Testing.Extensions.UnitTests.csproj546/546 passed.
  • .\build.cmd -pack0 warnings, 0 errors.

Out of scope (deliberate)

Checklist

  • Critical & Major review findings addressed
  • Localized via resx + xlf (regenerated with /t:UpdateXlf, not hand-edited)
  • Help/info acceptance test expectations updated
  • No new public API (besides one internal IAzureDevOpsHistoryService)
  • .\build.cmd green (0 warnings, 0 errors)
  • PR feedback addressed

Refs #5951

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 16, 2026 19:53

CopilotAI 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 opt-in Azure DevOps flaky-history annotations and quarantine awareness to the Microsoft.Testing.Extensions.AzureDevOpsReport extension. Failures can now be annotated with historical flake context, demoted from error to warning when known-flaky, and demoted with a [quarantined] tag when listed in a quarantine file (which also emits a one-shot ##vso[build.addbuildtag]has-quarantined-test-failure).

Changes:

  • New CLI options --report-azdo-flaky-history, --report-azdo-quarantine-file, --report-azdo-demote-known-flaky with cross-option validation in AzureDevOpsCommandLineProvider.
  • New AzureDevOpsHistoryService + AzureDevOpsHistoryClient (AOT-safe JsonSerializerContext) that queries the AzDO REST Runs/Results APIs under a 30 s wall-clock budget, with retries, 429 Retry-After honoring, paging caps, and a regression-annotation min-sample threshold.
  • AzureDevOpsReporter now annotates errors with [flaky: failed K/N in last Md] / [REGRESSION] / [quarantined], and demotes severity per the quarantine file and known-flaky rule.
Show a summary per file
FileDescription
src/.../AzureDevOpsCommandLineOptions.csAdds 3 new option name constants.
src/.../AzureDevOpsCommandLineProvider.csRegisters new options and adds cross-option validation.
src/.../AzureDevOpsExtensions.csWires AzureDevOpsHistoryService as data consumer + session lifetime handler.
src/.../AzureDevOpsHistoryClient.csNew REST client (auth, paging, retries, AOT JSON).
src/.../AzureDevOpsHistoryClientJsonContext.csSource-generated JSON context for DTOs.
src/.../AzureDevOpsHistoryService.csLoads/aggregates flaky stats with a bounded budget; exposes TryGetStats/IsLikelyFlaky.
src/.../AzureDevOpsReporter.csAdds annotation suffix building, severity demotion, one-shot quarantine build tag.
src/.../FlakyStats.csStruct holding pass/fail counts and failure rate.
src/.../IAzureDevOpsHistoryService.csInternal abstraction over the history service.
src/.../QuarantineFile.csParses quarantine file (globs, # comments, caps) into regex matchers.
src/.../Microsoft.Testing.Extensions.AzureDevOpsReport.csprojAdds System.Text.Json dependency and DynamicProxyGenAssembly2 IVT for Moq.
Directory.Packages.propsPins System.Text.Json version.
src/.../Resources/AzureDevOpsResources.resxNew strings for options, warnings, and annotation templates; fixes prior Eanble/AzureDev Ops typos.
src/.../Resources/xlf/*.xlf (12 locales)Regenerated XLFs for new strings; Description/OptionDescription flipped to needs-review-translation after the English typo fix.
test/.../AzureDevOpsHistoryClientTests.csAsserts URL composition (definitions=), headers, and run-paging behavior.
test/.../AzureDevOpsHistoryServiceTests.csCovers aggregation, paging, time-budget timeout, regression threshold, demote, quarantine tag-once.
test/.../AzureDevOpsCommandLineProviderTests.csValidates cross-option error messages.
test/.../AzureDevOpsCommandLineTests.csAcceptance-style test for invalid CLI argument errors.
test/.../HelpInfoAllExtensionsTests.csUpdates --help / --info expectations for new options.

Copilot's findings

  • Files reviewed: 31/31 changed files
  • Comments generated: 0

@Evangelink
Amaury Levé (Evangelink) marked this pull request as ready for review May 17, 2026 19:08
Aligns the failure message with the convention used by Assert.Contains
(which uses GetType().Name) and fixes the unit test expectations in
AssertTests.AreAll.cs that expected the short type name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 18, 2026 07:14

CopilotAI 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.

Copilot's findings

  • Files reviewed: 32/32 changed files
  • Comments generated: 4

CopilotAI added 2 commits May 18, 2026 22:03
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Revert unrelated merge-resolution changes that regressed main (Assert.AreEqual updates from RFC 012, AreAllDistinct, PreferAsyncAssertion, Arcade/Versions, eng/common)
- Format DemoteKnownFlakyOptionDescription with the threshold percentage at runtime to keep the resource string and KnownFlakyFailureRateThreshold constant in sync
- Regenerate XLF resources via UpdateXlf
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 19, 2026 04:49
@Evangelink

Copy link
Copy Markdown
MemberAuthor

Addressed the open review feedback in 0a2cdb5:

  1. Assert.AreAllDistinct.cs (unrelated change) — Reverted. The bad merge resolution in 0dac227 had also accidentally regressed several other unrelated files merged from main (the RFC 012 changes in Assert.AreEqual*, the MSTEST0064 follow-up in PreferAsyncAssertionFixer/PreferAsyncAssertionAnalyzerTests, and eng/Version.Details.xml / eng/Versions.props / eng/common/* / global.json). All of these were restored from origin/main so the PR diff is now scoped to AzDO files only.
  2. AzureDevOpsExtensions.cs (hard cast to ServiceProvider) — Already fixed in 0dac227 (history service is now created lazily via closure shared by the data consumer and the lifetime handler, no cast to the concrete ServiceProvider).
  3. AzureDevOpsReporter.cs constant/resource drift (25%) — Switched to runtime formatting. KnownFlakyFailureRateThreshold is now internal const; DemoteKnownFlakyOptionDescription carries a {0}% placeholder and AzureDevOpsCommandLineProvider formats it once at startup with KnownFlakyFailureRateThreshold * 100. XLF files regenerated via UpdateXlf; the rendered help text is identical to before, so HelpInfoAllExtensionsTests expectations remain valid.
  4. AzureDevOpsHistoryService.cs budget-task leak — Already fixed in 0dac227 (the budget CancellationTokenSource is canceled as soon as the load task wins the race, so the Task.Delay registration is released promptly).

Verified locally:

  • dotnet build src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport0 warnings, 0 errors.
  • AzureDevOps unit tests (32 of them in Microsoft.Testing.Extensions.UnitTests) — all pass.
  • Full Microsoft.Testing.Extensions.UnitTests151/153 pass, 2 skipped (the two crash-report tests skipped on Windows, as on main).
  • AreAllDistinct tests in TestFramework.UnitTests (23 tests) — all pass.

CopilotAI 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.

Copilot's findings

  • Files reviewed: 58/58 changed files
  • Comments generated: 1

CopilotAI review requested due to automatic review settings May 19, 2026 06:41
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/azdo-flaky-history branch from 6abe725 to 0a2cdb5CompareMay 19, 2026 06:41

CopilotAI 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.

Copilot's findings

  • Files reviewed: 58/58 changed files
  • Comments generated: 0 new

Resolve XLF conflicts by taking main's translations (the AzDO PR should not modify FrameworkMessages localizations).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto 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.

3 participants

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add Azure DevOps flaky-history annotations and quarantine awareness - #8298

Merged
Amaury Levé (Evangelink) merged 6 commits into
mainfrom
dev/amauryleve/azdo-flaky-history
May 19, 2026
Merged

Add Azure DevOps flaky-history annotations and quarantine awareness#8298
Amaury Levé (Evangelink) merged 6 commits into
mainfrom
dev/amauryleve/azdo-flaky-history

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Part 2 of the brainstorm in #5951 — adds opt-in flaky-test history annotations and quarantine awareness to Microsoft.Testing.Extensions.AzureDevOpsReport. Decorates AzDO log issues with historical flake context and lets known-noisy failures be downgraded so PR gates aren't blocked.

One of three PRs derived from the #5951 brainstorm. The others:

See issue comment for the broader plan.

Why

Today every failure in an AzDO log looks equally bad. There's no way for the build to say "this test failed 4/20 times in the last 14 days — known noise" vs "this test had zero failures in 14 days and just broke — likely regression". Teams that have moved to MTP currently mark failures as warnings manually with a separate task or live with red PR checks for known-flaky tests.

What

Three new opt-in CLI options on Microsoft.Testing.Extensions.AzureDevOpsReport:

OptionTypePurpose
--report-azdo-flaky-history <days>int (1–90)Query AzDO REST history for the last N days and annotate failures with [flaky: failed K/N in last Md] or [REGRESSION] (only when ≥5 prior samples).
--report-azdo-quarantine-file <path>stringPath to a text file (one FQN/glob per line, # comments allowed) listing tests considered quarantined. Their failures are demoted to warning and tagged [quarantined]; emits ##vso[build.addbuildtag]has-quarantined-test-failure exactly once.
--report-azdo-demote-known-flakyzero-arityTogether with --report-azdo-flaky-history, auto-demote failures whose flake-rate ≥25% in the window to warning. Default OFF (annotate-only). Requires --report-azdo-flaky-history.

All three are opt-in; missing AzDO env vars (SYSTEM_ACCESSTOKEN/SYSTEM_COLLECTIONURI/SYSTEM_TEAMPROJECT/BUILD_DEFINITIONID) → log warning and no-op.

How it works

  • AuthAuthorization: Basic base64(":<SYSTEM_ACCESSTOKEN>") (no Microsoft.TeamFoundationServer.Client dependency; just HttpClient + a source-generated JsonSerializerContext so it's AOT-safe).
  • History queryGET {project}/_apis/test/Runs?definitions={pipelineDefinitionId}&minLastUpdatedDate=…&maxLastUpdatedDate=…&automated=true&$top=200 paginated with $skip up to MaxRunsToInspect = 200. Per run, GET …/results?api-version=7.1&outcomes=Failed,Passed paged with continuation token. Aggregated into Dictionary<automatedTestName, FlakyStats>.
  • Bounded session start — history load has a wall-clock budget (default 30 s). If exceeded, log info and degrade to empty stats; tests start immediately.
  • Resilience — REST calls retry on transient errors (3 attempts, exponential backoff, 429 honors Retry-After). Response bodies truncated to 500 chars in error messages. All callbacks catch everything except OperationCanceledException; history/quarantine failures never fail the test run.
  • Quarantine file — line-based (# comments), case-sensitive ordinal matching against FQN, glob patterns (*, ?) compiled to a single alternation regex. Capped at 10 000 patterns / 4 KB per pattern with a logged warning.
  • Build tag##vso[build.addbuildtag]has-quarantined-test-failure guarded by Interlocked.Exchange so concurrent failure events emit it exactly once.

Highlights from the expert-reviewer round

Implementation went through one full round of expert-reviewer. Critical issues addressed:

  • C1 (feature was broken): the runs query was using buildIds=<pipelineDefinitionId> (the wrong AzDO parameter — buildIds filters by individual build run id, not pipeline). Switched to definitions=<pipelineDefinitionId> so the query actually returns data. Unit test now asserts the URL contains definitions=.
  • C2: history load was synchronous & serial — up to 25 000 sequential HTTP requests blocking session start. Bounded with a 30 s wall-clock budget + cancellation; degrades to empty stats on exceed.
  • C3: $top=501 was silently capped server-side. Now properly pages runs via $skip until MaxRunsToInspect is reached.
  • C4: [REGRESSION] previously fired on a single prior sample, generating false positives everywhere. Now requires TotalCount >= 5 (configurable via MinSamplesForRegressionAnnotation).

Major items also addressed: User-Agent/Accept headers added (prevents AzDO WAF-side 403s), source-generated JsonSerializerContext (AOT-safe), error-body truncation, inner-exception preservation on retry exhaustion, quarantine pattern caps, ordinal-case-sensitive matching, IAzureDevOpsHistoryService interface + proper DI registration, guard-clause CLI validation, acceptance tests for invalid-value error paths.

Tests

546 unit tests pass. New coverage:

  • AzureDevOpsHistoryServiceTests.cs — history aggregation, paging, time budget, regression boundary (4 vs 5 samples), quarantine tag-emitted-once.
  • AzureDevOpsHistoryClientTests.cs — URL composition (definitions= parameter), User-Agent/Accept headers, 429 retry honoring Retry-After.
  • AzureDevOpsCommandLineProviderTests.cs — cross-product validation (e.g. --demote-known-flaky without --flaky-history, --quarantine-file without --report-azdo).
  • AzureDevOpsCommandLineTests.cs — acceptance-style coverage.

HelpInfoAllExtensionsTests expectations updated for the new options (both --help and --info blocks, alphabetical order preserved).

Build status (local)

  • .\.dotnet\dotnet.exe build src\Platform\Microsoft.Testing.Extensions.AzureDevOpsReport\Microsoft.Testing.Extensions.AzureDevOpsReport.csproj -c Debug0 warnings, 0 errors.
  • .\.dotnet\dotnet.exe test test\UnitTests\Microsoft.Testing.Extensions.UnitTests\Microsoft.Testing.Extensions.UnitTests.csproj546/546 passed.
  • .\build.cmd -pack0 warnings, 0 errors.

Out of scope (deliberate)

Checklist

  • Critical & Major review findings addressed
  • Localized via resx + xlf (regenerated with /t:UpdateXlf, not hand-edited)
  • Help/info acceptance test expectations updated
  • No new public API (besides one internal IAzureDevOpsHistoryService)
  • .\build.cmd green (0 warnings, 0 errors)
  • PR feedback addressed

Refs #5951

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 16, 2026 19:53

CopilotAI 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 opt-in Azure DevOps flaky-history annotations and quarantine awareness to the Microsoft.Testing.Extensions.AzureDevOpsReport extension. Failures can now be annotated with historical flake context, demoted from error to warning when known-flaky, and demoted with a [quarantined] tag when listed in a quarantine file (which also emits a one-shot ##vso[build.addbuildtag]has-quarantined-test-failure).

Changes:

  • New CLI options --report-azdo-flaky-history, --report-azdo-quarantine-file, --report-azdo-demote-known-flaky with cross-option validation in AzureDevOpsCommandLineProvider.
  • New AzureDevOpsHistoryService + AzureDevOpsHistoryClient (AOT-safe JsonSerializerContext) that queries the AzDO REST Runs/Results APIs under a 30 s wall-clock budget, with retries, 429 Retry-After honoring, paging caps, and a regression-annotation min-sample threshold.
  • AzureDevOpsReporter now annotates errors with [flaky: failed K/N in last Md] / [REGRESSION] / [quarantined], and demotes severity per the quarantine file and known-flaky rule.
Show a summary per file
FileDescription
src/.../AzureDevOpsCommandLineOptions.csAdds 3 new option name constants.
src/.../AzureDevOpsCommandLineProvider.csRegisters new options and adds cross-option validation.
src/.../AzureDevOpsExtensions.csWires AzureDevOpsHistoryService as data consumer + session lifetime handler.
src/.../AzureDevOpsHistoryClient.csNew REST client (auth, paging, retries, AOT JSON).
src/.../AzureDevOpsHistoryClientJsonContext.csSource-generated JSON context for DTOs.
src/.../AzureDevOpsHistoryService.csLoads/aggregates flaky stats with a bounded budget; exposes TryGetStats/IsLikelyFlaky.
src/.../AzureDevOpsReporter.csAdds annotation suffix building, severity demotion, one-shot quarantine build tag.
src/.../FlakyStats.csStruct holding pass/fail counts and failure rate.
src/.../IAzureDevOpsHistoryService.csInternal abstraction over the history service.
src/.../QuarantineFile.csParses quarantine file (globs, # comments, caps) into regex matchers.
src/.../Microsoft.Testing.Extensions.AzureDevOpsReport.csprojAdds System.Text.Json dependency and DynamicProxyGenAssembly2 IVT for Moq.
Directory.Packages.propsPins System.Text.Json version.
src/.../Resources/AzureDevOpsResources.resxNew strings for options, warnings, and annotation templates; fixes prior Eanble/AzureDev Ops typos.
src/.../Resources/xlf/*.xlf (12 locales)Regenerated XLFs for new strings; Description/OptionDescription flipped to needs-review-translation after the English typo fix.
test/.../AzureDevOpsHistoryClientTests.csAsserts URL composition (definitions=), headers, and run-paging behavior.
test/.../AzureDevOpsHistoryServiceTests.csCovers aggregation, paging, time-budget timeout, regression threshold, demote, quarantine tag-once.
test/.../AzureDevOpsCommandLineProviderTests.csValidates cross-option error messages.
test/.../AzureDevOpsCommandLineTests.csAcceptance-style test for invalid CLI argument errors.
test/.../HelpInfoAllExtensionsTests.csUpdates --help / --info expectations for new options.

Copilot's findings

  • Files reviewed: 31/31 changed files
  • Comments generated: 0

@Evangelink
Amaury Levé (Evangelink) marked this pull request as ready for review May 17, 2026 19:08
Aligns the failure message with the convention used by Assert.Contains
(which uses GetType().Name) and fixes the unit test expectations in
AssertTests.AreAll.cs that expected the short type name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 18, 2026 07:14

CopilotAI 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.

Copilot's findings

  • Files reviewed: 32/32 changed files
  • Comments generated: 4

CopilotAI added 2 commits May 18, 2026 22:03
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Revert unrelated merge-resolution changes that regressed main (Assert.AreEqual updates from RFC 012, AreAllDistinct, PreferAsyncAssertion, Arcade/Versions, eng/common)
- Format DemoteKnownFlakyOptionDescription with the threshold percentage at runtime to keep the resource string and KnownFlakyFailureRateThreshold constant in sync
- Regenerate XLF resources via UpdateXlf
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 19, 2026 04:49
@Evangelink

Copy link
Copy Markdown
MemberAuthor

Addressed the open review feedback in 0a2cdb5:

  1. Assert.AreAllDistinct.cs (unrelated change) — Reverted. The bad merge resolution in 0dac227 had also accidentally regressed several other unrelated files merged from main (the RFC 012 changes in Assert.AreEqual*, the MSTEST0064 follow-up in PreferAsyncAssertionFixer/PreferAsyncAssertionAnalyzerTests, and eng/Version.Details.xml / eng/Versions.props / eng/common/* / global.json). All of these were restored from origin/main so the PR diff is now scoped to AzDO files only.
  2. AzureDevOpsExtensions.cs (hard cast to ServiceProvider) — Already fixed in 0dac227 (history service is now created lazily via closure shared by the data consumer and the lifetime handler, no cast to the concrete ServiceProvider).
  3. AzureDevOpsReporter.cs constant/resource drift (25%) — Switched to runtime formatting. KnownFlakyFailureRateThreshold is now internal const; DemoteKnownFlakyOptionDescription carries a {0}% placeholder and AzureDevOpsCommandLineProvider formats it once at startup with KnownFlakyFailureRateThreshold * 100. XLF files regenerated via UpdateXlf; the rendered help text is identical to before, so HelpInfoAllExtensionsTests expectations remain valid.
  4. AzureDevOpsHistoryService.cs budget-task leak — Already fixed in 0dac227 (the budget CancellationTokenSource is canceled as soon as the load task wins the race, so the Task.Delay registration is released promptly).

Verified locally:

  • dotnet build src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport0 warnings, 0 errors.
  • AzureDevOps unit tests (32 of them in Microsoft.Testing.Extensions.UnitTests) — all pass.
  • Full Microsoft.Testing.Extensions.UnitTests151/153 pass, 2 skipped (the two crash-report tests skipped on Windows, as on main).
  • AreAllDistinct tests in TestFramework.UnitTests (23 tests) — all pass.

CopilotAI 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.

Copilot's findings

  • Files reviewed: 58/58 changed files
  • Comments generated: 1

CopilotAI review requested due to automatic review settings May 19, 2026 06:41
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/azdo-flaky-history branch from 6abe725 to 0a2cdb5CompareMay 19, 2026 06:41

CopilotAI 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.

Copilot's findings

  • Files reviewed: 58/58 changed files
  • Comments generated: 0 new

Resolve XLF conflicts by taking main's translations (the AzDO PR should not modify FrameworkMessages localizations).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto 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.

3 participants

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Add Azure DevOps flaky-history annotations and quarantine awareness - #8298

Merged
Amaury Levé (Evangelink) merged 6 commits into
mainfrom
dev/amauryleve/azdo-flaky-history
May 19, 2026
Merged

Add Azure DevOps flaky-history annotations and quarantine awareness#8298
Amaury Levé (Evangelink) merged 6 commits into
mainfrom
dev/amauryleve/azdo-flaky-history

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Part 2 of the brainstorm in #5951 — adds opt-in flaky-test history annotations and quarantine awareness to Microsoft.Testing.Extensions.AzureDevOpsReport. Decorates AzDO log issues with historical flake context and lets known-noisy failures be downgraded so PR gates aren't blocked.

One of three PRs derived from the #5951 brainstorm. The others:

See issue comment for the broader plan.

Why

Today every failure in an AzDO log looks equally bad. There's no way for the build to say "this test failed 4/20 times in the last 14 days — known noise" vs "this test had zero failures in 14 days and just broke — likely regression". Teams that have moved to MTP currently mark failures as warnings manually with a separate task or live with red PR checks for known-flaky tests.

What

Three new opt-in CLI options on Microsoft.Testing.Extensions.AzureDevOpsReport:

OptionTypePurpose
--report-azdo-flaky-history <days>int (1–90)Query AzDO REST history for the last N days and annotate failures with [flaky: failed K/N in last Md] or [REGRESSION] (only when ≥5 prior samples).
--report-azdo-quarantine-file <path>stringPath to a text file (one FQN/glob per line, # comments allowed) listing tests considered quarantined. Their failures are demoted to warning and tagged [quarantined]; emits ##vso[build.addbuildtag]has-quarantined-test-failure exactly once.
--report-azdo-demote-known-flakyzero-arityTogether with --report-azdo-flaky-history, auto-demote failures whose flake-rate ≥25% in the window to warning. Default OFF (annotate-only). Requires --report-azdo-flaky-history.

All three are opt-in; missing AzDO env vars (SYSTEM_ACCESSTOKEN/SYSTEM_COLLECTIONURI/SYSTEM_TEAMPROJECT/BUILD_DEFINITIONID) → log warning and no-op.

How it works

  • AuthAuthorization: Basic base64(":<SYSTEM_ACCESSTOKEN>") (no Microsoft.TeamFoundationServer.Client dependency; just HttpClient + a source-generated JsonSerializerContext so it's AOT-safe).
  • History queryGET {project}/_apis/test/Runs?definitions={pipelineDefinitionId}&minLastUpdatedDate=…&maxLastUpdatedDate=…&automated=true&$top=200 paginated with $skip up to MaxRunsToInspect = 200. Per run, GET …/results?api-version=7.1&outcomes=Failed,Passed paged with continuation token. Aggregated into Dictionary<automatedTestName, FlakyStats>.
  • Bounded session start — history load has a wall-clock budget (default 30 s). If exceeded, log info and degrade to empty stats; tests start immediately.
  • Resilience — REST calls retry on transient errors (3 attempts, exponential backoff, 429 honors Retry-After). Response bodies truncated to 500 chars in error messages. All callbacks catch everything except OperationCanceledException; history/quarantine failures never fail the test run.
  • Quarantine file — line-based (# comments), case-sensitive ordinal matching against FQN, glob patterns (*, ?) compiled to a single alternation regex. Capped at 10 000 patterns / 4 KB per pattern with a logged warning.
  • Build tag##vso[build.addbuildtag]has-quarantined-test-failure guarded by Interlocked.Exchange so concurrent failure events emit it exactly once.

Highlights from the expert-reviewer round

Implementation went through one full round of expert-reviewer. Critical issues addressed:

  • C1 (feature was broken): the runs query was using buildIds=<pipelineDefinitionId> (the wrong AzDO parameter — buildIds filters by individual build run id, not pipeline). Switched to definitions=<pipelineDefinitionId> so the query actually returns data. Unit test now asserts the URL contains definitions=.
  • C2: history load was synchronous & serial — up to 25 000 sequential HTTP requests blocking session start. Bounded with a 30 s wall-clock budget + cancellation; degrades to empty stats on exceed.
  • C3: $top=501 was silently capped server-side. Now properly pages runs via $skip until MaxRunsToInspect is reached.
  • C4: [REGRESSION] previously fired on a single prior sample, generating false positives everywhere. Now requires TotalCount >= 5 (configurable via MinSamplesForRegressionAnnotation).

Major items also addressed: User-Agent/Accept headers added (prevents AzDO WAF-side 403s), source-generated JsonSerializerContext (AOT-safe), error-body truncation, inner-exception preservation on retry exhaustion, quarantine pattern caps, ordinal-case-sensitive matching, IAzureDevOpsHistoryService interface + proper DI registration, guard-clause CLI validation, acceptance tests for invalid-value error paths.

Tests

546 unit tests pass. New coverage:

  • AzureDevOpsHistoryServiceTests.cs — history aggregation, paging, time budget, regression boundary (4 vs 5 samples), quarantine tag-emitted-once.
  • AzureDevOpsHistoryClientTests.cs — URL composition (definitions= parameter), User-Agent/Accept headers, 429 retry honoring Retry-After.
  • AzureDevOpsCommandLineProviderTests.cs — cross-product validation (e.g. --demote-known-flaky without --flaky-history, --quarantine-file without --report-azdo).
  • AzureDevOpsCommandLineTests.cs — acceptance-style coverage.

HelpInfoAllExtensionsTests expectations updated for the new options (both --help and --info blocks, alphabetical order preserved).

Build status (local)

  • .\.dotnet\dotnet.exe build src\Platform\Microsoft.Testing.Extensions.AzureDevOpsReport\Microsoft.Testing.Extensions.AzureDevOpsReport.csproj -c Debug0 warnings, 0 errors.
  • .\.dotnet\dotnet.exe test test\UnitTests\Microsoft.Testing.Extensions.UnitTests\Microsoft.Testing.Extensions.UnitTests.csproj546/546 passed.
  • .\build.cmd -pack0 warnings, 0 errors.

Out of scope (deliberate)

Checklist

  • Critical & Major review findings addressed
  • Localized via resx + xlf (regenerated with /t:UpdateXlf, not hand-edited)
  • Help/info acceptance test expectations updated
  • No new public API (besides one internal IAzureDevOpsHistoryService)
  • .\build.cmd green (0 warnings, 0 errors)
  • PR feedback addressed

Refs #5951

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 16, 2026 19:53

CopilotAI 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 opt-in Azure DevOps flaky-history annotations and quarantine awareness to the Microsoft.Testing.Extensions.AzureDevOpsReport extension. Failures can now be annotated with historical flake context, demoted from error to warning when known-flaky, and demoted with a [quarantined] tag when listed in a quarantine file (which also emits a one-shot ##vso[build.addbuildtag]has-quarantined-test-failure).

Changes:

  • New CLI options --report-azdo-flaky-history, --report-azdo-quarantine-file, --report-azdo-demote-known-flaky with cross-option validation in AzureDevOpsCommandLineProvider.
  • New AzureDevOpsHistoryService + AzureDevOpsHistoryClient (AOT-safe JsonSerializerContext) that queries the AzDO REST Runs/Results APIs under a 30 s wall-clock budget, with retries, 429 Retry-After honoring, paging caps, and a regression-annotation min-sample threshold.
  • AzureDevOpsReporter now annotates errors with [flaky: failed K/N in last Md] / [REGRESSION] / [quarantined], and demotes severity per the quarantine file and known-flaky rule.
Show a summary per file
FileDescription
src/.../AzureDevOpsCommandLineOptions.csAdds 3 new option name constants.
src/.../AzureDevOpsCommandLineProvider.csRegisters new options and adds cross-option validation.
src/.../AzureDevOpsExtensions.csWires AzureDevOpsHistoryService as data consumer + session lifetime handler.
src/.../AzureDevOpsHistoryClient.csNew REST client (auth, paging, retries, AOT JSON).
src/.../AzureDevOpsHistoryClientJsonContext.csSource-generated JSON context for DTOs.
src/.../AzureDevOpsHistoryService.csLoads/aggregates flaky stats with a bounded budget; exposes TryGetStats/IsLikelyFlaky.
src/.../AzureDevOpsReporter.csAdds annotation suffix building, severity demotion, one-shot quarantine build tag.
src/.../FlakyStats.csStruct holding pass/fail counts and failure rate.
src/.../IAzureDevOpsHistoryService.csInternal abstraction over the history service.
src/.../QuarantineFile.csParses quarantine file (globs, # comments, caps) into regex matchers.
src/.../Microsoft.Testing.Extensions.AzureDevOpsReport.csprojAdds System.Text.Json dependency and DynamicProxyGenAssembly2 IVT for Moq.
Directory.Packages.propsPins System.Text.Json version.
src/.../Resources/AzureDevOpsResources.resxNew strings for options, warnings, and annotation templates; fixes prior Eanble/AzureDev Ops typos.
src/.../Resources/xlf/*.xlf (12 locales)Regenerated XLFs for new strings; Description/OptionDescription flipped to needs-review-translation after the English typo fix.
test/.../AzureDevOpsHistoryClientTests.csAsserts URL composition (definitions=), headers, and run-paging behavior.
test/.../AzureDevOpsHistoryServiceTests.csCovers aggregation, paging, time-budget timeout, regression threshold, demote, quarantine tag-once.
test/.../AzureDevOpsCommandLineProviderTests.csValidates cross-option error messages.
test/.../AzureDevOpsCommandLineTests.csAcceptance-style test for invalid CLI argument errors.
test/.../HelpInfoAllExtensionsTests.csUpdates --help / --info expectations for new options.

Copilot's findings

  • Files reviewed: 31/31 changed files
  • Comments generated: 0

@Evangelink
Amaury Levé (Evangelink) marked this pull request as ready for review May 17, 2026 19:08
Aligns the failure message with the convention used by Assert.Contains
(which uses GetType().Name) and fixes the unit test expectations in
AssertTests.AreAll.cs that expected the short type name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 18, 2026 07:14

CopilotAI 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.

Copilot's findings

  • Files reviewed: 32/32 changed files
  • Comments generated: 4

CopilotAI added 2 commits May 18, 2026 22:03
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Revert unrelated merge-resolution changes that regressed main (Assert.AreEqual updates from RFC 012, AreAllDistinct, PreferAsyncAssertion, Arcade/Versions, eng/common)
- Format DemoteKnownFlakyOptionDescription with the threshold percentage at runtime to keep the resource string and KnownFlakyFailureRateThreshold constant in sync
- Regenerate XLF resources via UpdateXlf
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 19, 2026 04:49
@Evangelink

Copy link
Copy Markdown
MemberAuthor

Addressed the open review feedback in 0a2cdb5:

  1. Assert.AreAllDistinct.cs (unrelated change) — Reverted. The bad merge resolution in 0dac227 had also accidentally regressed several other unrelated files merged from main (the RFC 012 changes in Assert.AreEqual*, the MSTEST0064 follow-up in PreferAsyncAssertionFixer/PreferAsyncAssertionAnalyzerTests, and eng/Version.Details.xml / eng/Versions.props / eng/common/* / global.json). All of these were restored from origin/main so the PR diff is now scoped to AzDO files only.
  2. AzureDevOpsExtensions.cs (hard cast to ServiceProvider) — Already fixed in 0dac227 (history service is now created lazily via closure shared by the data consumer and the lifetime handler, no cast to the concrete ServiceProvider).
  3. AzureDevOpsReporter.cs constant/resource drift (25%) — Switched to runtime formatting. KnownFlakyFailureRateThreshold is now internal const; DemoteKnownFlakyOptionDescription carries a {0}% placeholder and AzureDevOpsCommandLineProvider formats it once at startup with KnownFlakyFailureRateThreshold * 100. XLF files regenerated via UpdateXlf; the rendered help text is identical to before, so HelpInfoAllExtensionsTests expectations remain valid.
  4. AzureDevOpsHistoryService.cs budget-task leak — Already fixed in 0dac227 (the budget CancellationTokenSource is canceled as soon as the load task wins the race, so the Task.Delay registration is released promptly).

Verified locally:

  • dotnet build src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport0 warnings, 0 errors.
  • AzureDevOps unit tests (32 of them in Microsoft.Testing.Extensions.UnitTests) — all pass.
  • Full Microsoft.Testing.Extensions.UnitTests151/153 pass, 2 skipped (the two crash-report tests skipped on Windows, as on main).
  • AreAllDistinct tests in TestFramework.UnitTests (23 tests) — all pass.

CopilotAI 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.

Copilot's findings

  • Files reviewed: 58/58 changed files
  • Comments generated: 1

CopilotAI review requested due to automatic review settings May 19, 2026 06:41
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/azdo-flaky-history branch from 6abe725 to 0a2cdb5CompareMay 19, 2026 06:41

CopilotAI 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.

Copilot's findings

  • Files reviewed: 58/58 changed files
  • Comments generated: 0 new

Resolve XLF conflicts by taking main's translations (the AzDO PR should not modify FrameworkMessages localizations).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto 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.

3 participants

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add Azure DevOps flaky-history annotations and quarantine awareness - #8298

Merged
Amaury Levé (Evangelink) merged 6 commits into
mainfrom
dev/amauryleve/azdo-flaky-history
May 19, 2026
Merged

Add Azure DevOps flaky-history annotations and quarantine awareness#8298
Amaury Levé (Evangelink) merged 6 commits into
mainfrom
dev/amauryleve/azdo-flaky-history

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Part 2 of the brainstorm in #5951 — adds opt-in flaky-test history annotations and quarantine awareness to Microsoft.Testing.Extensions.AzureDevOpsReport. Decorates AzDO log issues with historical flake context and lets known-noisy failures be downgraded so PR gates aren't blocked.

One of three PRs derived from the #5951 brainstorm. The others:

See issue comment for the broader plan.

Why

Today every failure in an AzDO log looks equally bad. There's no way for the build to say "this test failed 4/20 times in the last 14 days — known noise" vs "this test had zero failures in 14 days and just broke — likely regression". Teams that have moved to MTP currently mark failures as warnings manually with a separate task or live with red PR checks for known-flaky tests.

What

Three new opt-in CLI options on Microsoft.Testing.Extensions.AzureDevOpsReport:

OptionTypePurpose
--report-azdo-flaky-history <days>int (1–90)Query AzDO REST history for the last N days and annotate failures with [flaky: failed K/N in last Md] or [REGRESSION] (only when ≥5 prior samples).
--report-azdo-quarantine-file <path>stringPath to a text file (one FQN/glob per line, # comments allowed) listing tests considered quarantined. Their failures are demoted to warning and tagged [quarantined]; emits ##vso[build.addbuildtag]has-quarantined-test-failure exactly once.
--report-azdo-demote-known-flakyzero-arityTogether with --report-azdo-flaky-history, auto-demote failures whose flake-rate ≥25% in the window to warning. Default OFF (annotate-only). Requires --report-azdo-flaky-history.

All three are opt-in; missing AzDO env vars (SYSTEM_ACCESSTOKEN/SYSTEM_COLLECTIONURI/SYSTEM_TEAMPROJECT/BUILD_DEFINITIONID) → log warning and no-op.

How it works

  • AuthAuthorization: Basic base64(":<SYSTEM_ACCESSTOKEN>") (no Microsoft.TeamFoundationServer.Client dependency; just HttpClient + a source-generated JsonSerializerContext so it's AOT-safe).
  • History queryGET {project}/_apis/test/Runs?definitions={pipelineDefinitionId}&minLastUpdatedDate=…&maxLastUpdatedDate=…&automated=true&$top=200 paginated with $skip up to MaxRunsToInspect = 200. Per run, GET …/results?api-version=7.1&outcomes=Failed,Passed paged with continuation token. Aggregated into Dictionary<automatedTestName, FlakyStats>.
  • Bounded session start — history load has a wall-clock budget (default 30 s). If exceeded, log info and degrade to empty stats; tests start immediately.
  • Resilience — REST calls retry on transient errors (3 attempts, exponential backoff, 429 honors Retry-After). Response bodies truncated to 500 chars in error messages. All callbacks catch everything except OperationCanceledException; history/quarantine failures never fail the test run.
  • Quarantine file — line-based (# comments), case-sensitive ordinal matching against FQN, glob patterns (*, ?) compiled to a single alternation regex. Capped at 10 000 patterns / 4 KB per pattern with a logged warning.
  • Build tag##vso[build.addbuildtag]has-quarantined-test-failure guarded by Interlocked.Exchange so concurrent failure events emit it exactly once.

Highlights from the expert-reviewer round

Implementation went through one full round of expert-reviewer. Critical issues addressed:

  • C1 (feature was broken): the runs query was using buildIds=<pipelineDefinitionId> (the wrong AzDO parameter — buildIds filters by individual build run id, not pipeline). Switched to definitions=<pipelineDefinitionId> so the query actually returns data. Unit test now asserts the URL contains definitions=.
  • C2: history load was synchronous & serial — up to 25 000 sequential HTTP requests blocking session start. Bounded with a 30 s wall-clock budget + cancellation; degrades to empty stats on exceed.
  • C3: $top=501 was silently capped server-side. Now properly pages runs via $skip until MaxRunsToInspect is reached.
  • C4: [REGRESSION] previously fired on a single prior sample, generating false positives everywhere. Now requires TotalCount >= 5 (configurable via MinSamplesForRegressionAnnotation).

Major items also addressed: User-Agent/Accept headers added (prevents AzDO WAF-side 403s), source-generated JsonSerializerContext (AOT-safe), error-body truncation, inner-exception preservation on retry exhaustion, quarantine pattern caps, ordinal-case-sensitive matching, IAzureDevOpsHistoryService interface + proper DI registration, guard-clause CLI validation, acceptance tests for invalid-value error paths.

Tests

546 unit tests pass. New coverage:

  • AzureDevOpsHistoryServiceTests.cs — history aggregation, paging, time budget, regression boundary (4 vs 5 samples), quarantine tag-emitted-once.
  • AzureDevOpsHistoryClientTests.cs — URL composition (definitions= parameter), User-Agent/Accept headers, 429 retry honoring Retry-After.
  • AzureDevOpsCommandLineProviderTests.cs — cross-product validation (e.g. --demote-known-flaky without --flaky-history, --quarantine-file without --report-azdo).
  • AzureDevOpsCommandLineTests.cs — acceptance-style coverage.

HelpInfoAllExtensionsTests expectations updated for the new options (both --help and --info blocks, alphabetical order preserved).

Build status (local)

  • .\.dotnet\dotnet.exe build src\Platform\Microsoft.Testing.Extensions.AzureDevOpsReport\Microsoft.Testing.Extensions.AzureDevOpsReport.csproj -c Debug0 warnings, 0 errors.
  • .\.dotnet\dotnet.exe test test\UnitTests\Microsoft.Testing.Extensions.UnitTests\Microsoft.Testing.Extensions.UnitTests.csproj546/546 passed.
  • .\build.cmd -pack0 warnings, 0 errors.

Out of scope (deliberate)

Checklist

  • Critical & Major review findings addressed
  • Localized via resx + xlf (regenerated with /t:UpdateXlf, not hand-edited)
  • Help/info acceptance test expectations updated
  • No new public API (besides one internal IAzureDevOpsHistoryService)
  • .\build.cmd green (0 warnings, 0 errors)
  • PR feedback addressed

Refs #5951

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 16, 2026 19:53

CopilotAI 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 opt-in Azure DevOps flaky-history annotations and quarantine awareness to the Microsoft.Testing.Extensions.AzureDevOpsReport extension. Failures can now be annotated with historical flake context, demoted from error to warning when known-flaky, and demoted with a [quarantined] tag when listed in a quarantine file (which also emits a one-shot ##vso[build.addbuildtag]has-quarantined-test-failure).

Changes:

  • New CLI options --report-azdo-flaky-history, --report-azdo-quarantine-file, --report-azdo-demote-known-flaky with cross-option validation in AzureDevOpsCommandLineProvider.
  • New AzureDevOpsHistoryService + AzureDevOpsHistoryClient (AOT-safe JsonSerializerContext) that queries the AzDO REST Runs/Results APIs under a 30 s wall-clock budget, with retries, 429 Retry-After honoring, paging caps, and a regression-annotation min-sample threshold.
  • AzureDevOpsReporter now annotates errors with [flaky: failed K/N in last Md] / [REGRESSION] / [quarantined], and demotes severity per the quarantine file and known-flaky rule.
Show a summary per file
FileDescription
src/.../AzureDevOpsCommandLineOptions.csAdds 3 new option name constants.
src/.../AzureDevOpsCommandLineProvider.csRegisters new options and adds cross-option validation.
src/.../AzureDevOpsExtensions.csWires AzureDevOpsHistoryService as data consumer + session lifetime handler.
src/.../AzureDevOpsHistoryClient.csNew REST client (auth, paging, retries, AOT JSON).
src/.../AzureDevOpsHistoryClientJsonContext.csSource-generated JSON context for DTOs.
src/.../AzureDevOpsHistoryService.csLoads/aggregates flaky stats with a bounded budget; exposes TryGetStats/IsLikelyFlaky.
src/.../AzureDevOpsReporter.csAdds annotation suffix building, severity demotion, one-shot quarantine build tag.
src/.../FlakyStats.csStruct holding pass/fail counts and failure rate.
src/.../IAzureDevOpsHistoryService.csInternal abstraction over the history service.
src/.../QuarantineFile.csParses quarantine file (globs, # comments, caps) into regex matchers.
src/.../Microsoft.Testing.Extensions.AzureDevOpsReport.csprojAdds System.Text.Json dependency and DynamicProxyGenAssembly2 IVT for Moq.
Directory.Packages.propsPins System.Text.Json version.
src/.../Resources/AzureDevOpsResources.resxNew strings for options, warnings, and annotation templates; fixes prior Eanble/AzureDev Ops typos.
src/.../Resources/xlf/*.xlf (12 locales)Regenerated XLFs for new strings; Description/OptionDescription flipped to needs-review-translation after the English typo fix.
test/.../AzureDevOpsHistoryClientTests.csAsserts URL composition (definitions=), headers, and run-paging behavior.
test/.../AzureDevOpsHistoryServiceTests.csCovers aggregation, paging, time-budget timeout, regression threshold, demote, quarantine tag-once.
test/.../AzureDevOpsCommandLineProviderTests.csValidates cross-option error messages.
test/.../AzureDevOpsCommandLineTests.csAcceptance-style test for invalid CLI argument errors.
test/.../HelpInfoAllExtensionsTests.csUpdates --help / --info expectations for new options.

Copilot's findings

  • Files reviewed: 31/31 changed files
  • Comments generated: 0

@Evangelink
Amaury Levé (Evangelink) marked this pull request as ready for review May 17, 2026 19:08
Aligns the failure message with the convention used by Assert.Contains
(which uses GetType().Name) and fixes the unit test expectations in
AssertTests.AreAll.cs that expected the short type name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 18, 2026 07:14

CopilotAI 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.

Copilot's findings

  • Files reviewed: 32/32 changed files
  • Comments generated: 4

CopilotAI added 2 commits May 18, 2026 22:03
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Revert unrelated merge-resolution changes that regressed main (Assert.AreEqual updates from RFC 012, AreAllDistinct, PreferAsyncAssertion, Arcade/Versions, eng/common)
- Format DemoteKnownFlakyOptionDescription with the threshold percentage at runtime to keep the resource string and KnownFlakyFailureRateThreshold constant in sync
- Regenerate XLF resources via UpdateXlf
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 19, 2026 04:49
@Evangelink

Copy link
Copy Markdown
MemberAuthor

Addressed the open review feedback in 0a2cdb5:

  1. Assert.AreAllDistinct.cs (unrelated change) — Reverted. The bad merge resolution in 0dac227 had also accidentally regressed several other unrelated files merged from main (the RFC 012 changes in Assert.AreEqual*, the MSTEST0064 follow-up in PreferAsyncAssertionFixer/PreferAsyncAssertionAnalyzerTests, and eng/Version.Details.xml / eng/Versions.props / eng/common/* / global.json). All of these were restored from origin/main so the PR diff is now scoped to AzDO files only.
  2. AzureDevOpsExtensions.cs (hard cast to ServiceProvider) — Already fixed in 0dac227 (history service is now created lazily via closure shared by the data consumer and the lifetime handler, no cast to the concrete ServiceProvider).
  3. AzureDevOpsReporter.cs constant/resource drift (25%) — Switched to runtime formatting. KnownFlakyFailureRateThreshold is now internal const; DemoteKnownFlakyOptionDescription carries a {0}% placeholder and AzureDevOpsCommandLineProvider formats it once at startup with KnownFlakyFailureRateThreshold * 100. XLF files regenerated via UpdateXlf; the rendered help text is identical to before, so HelpInfoAllExtensionsTests expectations remain valid.
  4. AzureDevOpsHistoryService.cs budget-task leak — Already fixed in 0dac227 (the budget CancellationTokenSource is canceled as soon as the load task wins the race, so the Task.Delay registration is released promptly).

Verified locally:

  • dotnet build src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport0 warnings, 0 errors.
  • AzureDevOps unit tests (32 of them in Microsoft.Testing.Extensions.UnitTests) — all pass.
  • Full Microsoft.Testing.Extensions.UnitTests151/153 pass, 2 skipped (the two crash-report tests skipped on Windows, as on main).
  • AreAllDistinct tests in TestFramework.UnitTests (23 tests) — all pass.

CopilotAI 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.

Copilot's findings

  • Files reviewed: 58/58 changed files
  • Comments generated: 1

CopilotAI review requested due to automatic review settings May 19, 2026 06:41
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/azdo-flaky-history branch from 6abe725 to 0a2cdb5CompareMay 19, 2026 06:41

CopilotAI 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.

Copilot's findings

  • Files reviewed: 58/58 changed files
  • Comments generated: 0 new

Resolve XLF conflicts by taking main's translations (the AzDO PR should not modify FrameworkMessages localizations).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto 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.

3 participants

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add Azure DevOps flaky-history annotations and quarantine awareness - #8298

Merged
Amaury Levé (Evangelink) merged 6 commits into
mainfrom
dev/amauryleve/azdo-flaky-history
May 19, 2026
Merged

Add Azure DevOps flaky-history annotations and quarantine awareness#8298
Amaury Levé (Evangelink) merged 6 commits into
mainfrom
dev/amauryleve/azdo-flaky-history

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Part 2 of the brainstorm in #5951 — adds opt-in flaky-test history annotations and quarantine awareness to Microsoft.Testing.Extensions.AzureDevOpsReport. Decorates AzDO log issues with historical flake context and lets known-noisy failures be downgraded so PR gates aren't blocked.

One of three PRs derived from the #5951 brainstorm. The others:

See issue comment for the broader plan.

Why

Today every failure in an AzDO log looks equally bad. There's no way for the build to say "this test failed 4/20 times in the last 14 days — known noise" vs "this test had zero failures in 14 days and just broke — likely regression". Teams that have moved to MTP currently mark failures as warnings manually with a separate task or live with red PR checks for known-flaky tests.

What

Three new opt-in CLI options on Microsoft.Testing.Extensions.AzureDevOpsReport:

OptionTypePurpose
--report-azdo-flaky-history <days>int (1–90)Query AzDO REST history for the last N days and annotate failures with [flaky: failed K/N in last Md] or [REGRESSION] (only when ≥5 prior samples).
--report-azdo-quarantine-file <path>stringPath to a text file (one FQN/glob per line, # comments allowed) listing tests considered quarantined. Their failures are demoted to warning and tagged [quarantined]; emits ##vso[build.addbuildtag]has-quarantined-test-failure exactly once.
--report-azdo-demote-known-flakyzero-arityTogether with --report-azdo-flaky-history, auto-demote failures whose flake-rate ≥25% in the window to warning. Default OFF (annotate-only). Requires --report-azdo-flaky-history.

All three are opt-in; missing AzDO env vars (SYSTEM_ACCESSTOKEN/SYSTEM_COLLECTIONURI/SYSTEM_TEAMPROJECT/BUILD_DEFINITIONID) → log warning and no-op.

How it works

  • AuthAuthorization: Basic base64(":<SYSTEM_ACCESSTOKEN>") (no Microsoft.TeamFoundationServer.Client dependency; just HttpClient + a source-generated JsonSerializerContext so it's AOT-safe).
  • History queryGET {project}/_apis/test/Runs?definitions={pipelineDefinitionId}&minLastUpdatedDate=…&maxLastUpdatedDate=…&automated=true&$top=200 paginated with $skip up to MaxRunsToInspect = 200. Per run, GET …/results?api-version=7.1&outcomes=Failed,Passed paged with continuation token. Aggregated into Dictionary<automatedTestName, FlakyStats>.
  • Bounded session start — history load has a wall-clock budget (default 30 s). If exceeded, log info and degrade to empty stats; tests start immediately.
  • Resilience — REST calls retry on transient errors (3 attempts, exponential backoff, 429 honors Retry-After). Response bodies truncated to 500 chars in error messages. All callbacks catch everything except OperationCanceledException; history/quarantine failures never fail the test run.
  • Quarantine file — line-based (# comments), case-sensitive ordinal matching against FQN, glob patterns (*, ?) compiled to a single alternation regex. Capped at 10 000 patterns / 4 KB per pattern with a logged warning.
  • Build tag##vso[build.addbuildtag]has-quarantined-test-failure guarded by Interlocked.Exchange so concurrent failure events emit it exactly once.

Highlights from the expert-reviewer round

Implementation went through one full round of expert-reviewer. Critical issues addressed:

  • C1 (feature was broken): the runs query was using buildIds=<pipelineDefinitionId> (the wrong AzDO parameter — buildIds filters by individual build run id, not pipeline). Switched to definitions=<pipelineDefinitionId> so the query actually returns data. Unit test now asserts the URL contains definitions=.
  • C2: history load was synchronous & serial — up to 25 000 sequential HTTP requests blocking session start. Bounded with a 30 s wall-clock budget + cancellation; degrades to empty stats on exceed.
  • C3: $top=501 was silently capped server-side. Now properly pages runs via $skip until MaxRunsToInspect is reached.
  • C4: [REGRESSION] previously fired on a single prior sample, generating false positives everywhere. Now requires TotalCount >= 5 (configurable via MinSamplesForRegressionAnnotation).

Major items also addressed: User-Agent/Accept headers added (prevents AzDO WAF-side 403s), source-generated JsonSerializerContext (AOT-safe), error-body truncation, inner-exception preservation on retry exhaustion, quarantine pattern caps, ordinal-case-sensitive matching, IAzureDevOpsHistoryService interface + proper DI registration, guard-clause CLI validation, acceptance tests for invalid-value error paths.

Tests

546 unit tests pass. New coverage:

  • AzureDevOpsHistoryServiceTests.cs — history aggregation, paging, time budget, regression boundary (4 vs 5 samples), quarantine tag-emitted-once.
  • AzureDevOpsHistoryClientTests.cs — URL composition (definitions= parameter), User-Agent/Accept headers, 429 retry honoring Retry-After.
  • AzureDevOpsCommandLineProviderTests.cs — cross-product validation (e.g. --demote-known-flaky without --flaky-history, --quarantine-file without --report-azdo).
  • AzureDevOpsCommandLineTests.cs — acceptance-style coverage.

HelpInfoAllExtensionsTests expectations updated for the new options (both --help and --info blocks, alphabetical order preserved).

Build status (local)

  • .\.dotnet\dotnet.exe build src\Platform\Microsoft.Testing.Extensions.AzureDevOpsReport\Microsoft.Testing.Extensions.AzureDevOpsReport.csproj -c Debug0 warnings, 0 errors.
  • .\.dotnet\dotnet.exe test test\UnitTests\Microsoft.Testing.Extensions.UnitTests\Microsoft.Testing.Extensions.UnitTests.csproj546/546 passed.
  • .\build.cmd -pack0 warnings, 0 errors.

Out of scope (deliberate)

Checklist

  • Critical & Major review findings addressed
  • Localized via resx + xlf (regenerated with /t:UpdateXlf, not hand-edited)
  • Help/info acceptance test expectations updated
  • No new public API (besides one internal IAzureDevOpsHistoryService)
  • .\build.cmd green (0 warnings, 0 errors)
  • PR feedback addressed

Refs #5951

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 16, 2026 19:53

CopilotAI 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 opt-in Azure DevOps flaky-history annotations and quarantine awareness to the Microsoft.Testing.Extensions.AzureDevOpsReport extension. Failures can now be annotated with historical flake context, demoted from error to warning when known-flaky, and demoted with a [quarantined] tag when listed in a quarantine file (which also emits a one-shot ##vso[build.addbuildtag]has-quarantined-test-failure).

Changes:

  • New CLI options --report-azdo-flaky-history, --report-azdo-quarantine-file, --report-azdo-demote-known-flaky with cross-option validation in AzureDevOpsCommandLineProvider.
  • New AzureDevOpsHistoryService + AzureDevOpsHistoryClient (AOT-safe JsonSerializerContext) that queries the AzDO REST Runs/Results APIs under a 30 s wall-clock budget, with retries, 429 Retry-After honoring, paging caps, and a regression-annotation min-sample threshold.
  • AzureDevOpsReporter now annotates errors with [flaky: failed K/N in last Md] / [REGRESSION] / [quarantined], and demotes severity per the quarantine file and known-flaky rule.
Show a summary per file
FileDescription
src/.../AzureDevOpsCommandLineOptions.csAdds 3 new option name constants.
src/.../AzureDevOpsCommandLineProvider.csRegisters new options and adds cross-option validation.
src/.../AzureDevOpsExtensions.csWires AzureDevOpsHistoryService as data consumer + session lifetime handler.
src/.../AzureDevOpsHistoryClient.csNew REST client (auth, paging, retries, AOT JSON).
src/.../AzureDevOpsHistoryClientJsonContext.csSource-generated JSON context for DTOs.
src/.../AzureDevOpsHistoryService.csLoads/aggregates flaky stats with a bounded budget; exposes TryGetStats/IsLikelyFlaky.
src/.../AzureDevOpsReporter.csAdds annotation suffix building, severity demotion, one-shot quarantine build tag.
src/.../FlakyStats.csStruct holding pass/fail counts and failure rate.
src/.../IAzureDevOpsHistoryService.csInternal abstraction over the history service.
src/.../QuarantineFile.csParses quarantine file (globs, # comments, caps) into regex matchers.
src/.../Microsoft.Testing.Extensions.AzureDevOpsReport.csprojAdds System.Text.Json dependency and DynamicProxyGenAssembly2 IVT for Moq.
Directory.Packages.propsPins System.Text.Json version.
src/.../Resources/AzureDevOpsResources.resxNew strings for options, warnings, and annotation templates; fixes prior Eanble/AzureDev Ops typos.
src/.../Resources/xlf/*.xlf (12 locales)Regenerated XLFs for new strings; Description/OptionDescription flipped to needs-review-translation after the English typo fix.
test/.../AzureDevOpsHistoryClientTests.csAsserts URL composition (definitions=), headers, and run-paging behavior.
test/.../AzureDevOpsHistoryServiceTests.csCovers aggregation, paging, time-budget timeout, regression threshold, demote, quarantine tag-once.
test/.../AzureDevOpsCommandLineProviderTests.csValidates cross-option error messages.
test/.../AzureDevOpsCommandLineTests.csAcceptance-style test for invalid CLI argument errors.
test/.../HelpInfoAllExtensionsTests.csUpdates --help / --info expectations for new options.

Copilot's findings

  • Files reviewed: 31/31 changed files
  • Comments generated: 0

@Evangelink
Amaury Levé (Evangelink) marked this pull request as ready for review May 17, 2026 19:08
Aligns the failure message with the convention used by Assert.Contains
(which uses GetType().Name) and fixes the unit test expectations in
AssertTests.AreAll.cs that expected the short type name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 18, 2026 07:14

CopilotAI 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.

Copilot's findings

  • Files reviewed: 32/32 changed files
  • Comments generated: 4

CopilotAI added 2 commits May 18, 2026 22:03
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Revert unrelated merge-resolution changes that regressed main (Assert.AreEqual updates from RFC 012, AreAllDistinct, PreferAsyncAssertion, Arcade/Versions, eng/common)
- Format DemoteKnownFlakyOptionDescription with the threshold percentage at runtime to keep the resource string and KnownFlakyFailureRateThreshold constant in sync
- Regenerate XLF resources via UpdateXlf
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 19, 2026 04:49
@Evangelink

Copy link
Copy Markdown
MemberAuthor

Addressed the open review feedback in 0a2cdb5:

  1. Assert.AreAllDistinct.cs (unrelated change) — Reverted. The bad merge resolution in 0dac227 had also accidentally regressed several other unrelated files merged from main (the RFC 012 changes in Assert.AreEqual*, the MSTEST0064 follow-up in PreferAsyncAssertionFixer/PreferAsyncAssertionAnalyzerTests, and eng/Version.Details.xml / eng/Versions.props / eng/common/* / global.json). All of these were restored from origin/main so the PR diff is now scoped to AzDO files only.
  2. AzureDevOpsExtensions.cs (hard cast to ServiceProvider) — Already fixed in 0dac227 (history service is now created lazily via closure shared by the data consumer and the lifetime handler, no cast to the concrete ServiceProvider).
  3. AzureDevOpsReporter.cs constant/resource drift (25%) — Switched to runtime formatting. KnownFlakyFailureRateThreshold is now internal const; DemoteKnownFlakyOptionDescription carries a {0}% placeholder and AzureDevOpsCommandLineProvider formats it once at startup with KnownFlakyFailureRateThreshold * 100. XLF files regenerated via UpdateXlf; the rendered help text is identical to before, so HelpInfoAllExtensionsTests expectations remain valid.
  4. AzureDevOpsHistoryService.cs budget-task leak — Already fixed in 0dac227 (the budget CancellationTokenSource is canceled as soon as the load task wins the race, so the Task.Delay registration is released promptly).

Verified locally:

  • dotnet build src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport0 warnings, 0 errors.
  • AzureDevOps unit tests (32 of them in Microsoft.Testing.Extensions.UnitTests) — all pass.
  • Full Microsoft.Testing.Extensions.UnitTests151/153 pass, 2 skipped (the two crash-report tests skipped on Windows, as on main).
  • AreAllDistinct tests in TestFramework.UnitTests (23 tests) — all pass.

CopilotAI 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.

Copilot's findings

  • Files reviewed: 58/58 changed files
  • Comments generated: 1

CopilotAI review requested due to automatic review settings May 19, 2026 06:41
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/azdo-flaky-history branch from 6abe725 to 0a2cdb5CompareMay 19, 2026 06:41

CopilotAI 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.

Copilot's findings

  • Files reviewed: 58/58 changed files
  • Comments generated: 0 new

Resolve XLF conflicts by taking main's translations (the AzDO PR should not modify FrameworkMessages localizations).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto 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.

3 participants

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Add Azure DevOps flaky-history annotations and quarantine awareness - #8298

Merged
Amaury Levé (Evangelink) merged 6 commits into
mainfrom
dev/amauryleve/azdo-flaky-history
May 19, 2026
Merged

Add Azure DevOps flaky-history annotations and quarantine awareness#8298
Amaury Levé (Evangelink) merged 6 commits into
mainfrom
dev/amauryleve/azdo-flaky-history

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Part 2 of the brainstorm in #5951 — adds opt-in flaky-test history annotations and quarantine awareness to Microsoft.Testing.Extensions.AzureDevOpsReport. Decorates AzDO log issues with historical flake context and lets known-noisy failures be downgraded so PR gates aren't blocked.

One of three PRs derived from the #5951 brainstorm. The others:

See issue comment for the broader plan.

Why

Today every failure in an AzDO log looks equally bad. There's no way for the build to say "this test failed 4/20 times in the last 14 days — known noise" vs "this test had zero failures in 14 days and just broke — likely regression". Teams that have moved to MTP currently mark failures as warnings manually with a separate task or live with red PR checks for known-flaky tests.

What

Three new opt-in CLI options on Microsoft.Testing.Extensions.AzureDevOpsReport:

OptionTypePurpose
--report-azdo-flaky-history <days>int (1–90)Query AzDO REST history for the last N days and annotate failures with [flaky: failed K/N in last Md] or [REGRESSION] (only when ≥5 prior samples).
--report-azdo-quarantine-file <path>stringPath to a text file (one FQN/glob per line, # comments allowed) listing tests considered quarantined. Their failures are demoted to warning and tagged [quarantined]; emits ##vso[build.addbuildtag]has-quarantined-test-failure exactly once.
--report-azdo-demote-known-flakyzero-arityTogether with --report-azdo-flaky-history, auto-demote failures whose flake-rate ≥25% in the window to warning. Default OFF (annotate-only). Requires --report-azdo-flaky-history.

All three are opt-in; missing AzDO env vars (SYSTEM_ACCESSTOKEN/SYSTEM_COLLECTIONURI/SYSTEM_TEAMPROJECT/BUILD_DEFINITIONID) → log warning and no-op.

How it works

  • AuthAuthorization: Basic base64(":<SYSTEM_ACCESSTOKEN>") (no Microsoft.TeamFoundationServer.Client dependency; just HttpClient + a source-generated JsonSerializerContext so it's AOT-safe).
  • History queryGET {project}/_apis/test/Runs?definitions={pipelineDefinitionId}&minLastUpdatedDate=…&maxLastUpdatedDate=…&automated=true&$top=200 paginated with $skip up to MaxRunsToInspect = 200. Per run, GET …/results?api-version=7.1&outcomes=Failed,Passed paged with continuation token. Aggregated into Dictionary<automatedTestName, FlakyStats>.
  • Bounded session start — history load has a wall-clock budget (default 30 s). If exceeded, log info and degrade to empty stats; tests start immediately.
  • Resilience — REST calls retry on transient errors (3 attempts, exponential backoff, 429 honors Retry-After). Response bodies truncated to 500 chars in error messages. All callbacks catch everything except OperationCanceledException; history/quarantine failures never fail the test run.
  • Quarantine file — line-based (# comments), case-sensitive ordinal matching against FQN, glob patterns (*, ?) compiled to a single alternation regex. Capped at 10 000 patterns / 4 KB per pattern with a logged warning.
  • Build tag##vso[build.addbuildtag]has-quarantined-test-failure guarded by Interlocked.Exchange so concurrent failure events emit it exactly once.

Highlights from the expert-reviewer round

Implementation went through one full round of expert-reviewer. Critical issues addressed:

  • C1 (feature was broken): the runs query was using buildIds=<pipelineDefinitionId> (the wrong AzDO parameter — buildIds filters by individual build run id, not pipeline). Switched to definitions=<pipelineDefinitionId> so the query actually returns data. Unit test now asserts the URL contains definitions=.
  • C2: history load was synchronous & serial — up to 25 000 sequential HTTP requests blocking session start. Bounded with a 30 s wall-clock budget + cancellation; degrades to empty stats on exceed.
  • C3: $top=501 was silently capped server-side. Now properly pages runs via $skip until MaxRunsToInspect is reached.
  • C4: [REGRESSION] previously fired on a single prior sample, generating false positives everywhere. Now requires TotalCount >= 5 (configurable via MinSamplesForRegressionAnnotation).

Major items also addressed: User-Agent/Accept headers added (prevents AzDO WAF-side 403s), source-generated JsonSerializerContext (AOT-safe), error-body truncation, inner-exception preservation on retry exhaustion, quarantine pattern caps, ordinal-case-sensitive matching, IAzureDevOpsHistoryService interface + proper DI registration, guard-clause CLI validation, acceptance tests for invalid-value error paths.

Tests

546 unit tests pass. New coverage:

  • AzureDevOpsHistoryServiceTests.cs — history aggregation, paging, time budget, regression boundary (4 vs 5 samples), quarantine tag-emitted-once.
  • AzureDevOpsHistoryClientTests.cs — URL composition (definitions= parameter), User-Agent/Accept headers, 429 retry honoring Retry-After.
  • AzureDevOpsCommandLineProviderTests.cs — cross-product validation (e.g. --demote-known-flaky without --flaky-history, --quarantine-file without --report-azdo).
  • AzureDevOpsCommandLineTests.cs — acceptance-style coverage.

HelpInfoAllExtensionsTests expectations updated for the new options (both --help and --info blocks, alphabetical order preserved).

Build status (local)

  • .\.dotnet\dotnet.exe build src\Platform\Microsoft.Testing.Extensions.AzureDevOpsReport\Microsoft.Testing.Extensions.AzureDevOpsReport.csproj -c Debug0 warnings, 0 errors.
  • .\.dotnet\dotnet.exe test test\UnitTests\Microsoft.Testing.Extensions.UnitTests\Microsoft.Testing.Extensions.UnitTests.csproj546/546 passed.
  • .\build.cmd -pack0 warnings, 0 errors.

Out of scope (deliberate)

Checklist

  • Critical & Major review findings addressed
  • Localized via resx + xlf (regenerated with /t:UpdateXlf, not hand-edited)
  • Help/info acceptance test expectations updated
  • No new public API (besides one internal IAzureDevOpsHistoryService)
  • .\build.cmd green (0 warnings, 0 errors)
  • PR feedback addressed

Refs #5951

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 16, 2026 19:53

CopilotAI 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 opt-in Azure DevOps flaky-history annotations and quarantine awareness to the Microsoft.Testing.Extensions.AzureDevOpsReport extension. Failures can now be annotated with historical flake context, demoted from error to warning when known-flaky, and demoted with a [quarantined] tag when listed in a quarantine file (which also emits a one-shot ##vso[build.addbuildtag]has-quarantined-test-failure).

Changes:

  • New CLI options --report-azdo-flaky-history, --report-azdo-quarantine-file, --report-azdo-demote-known-flaky with cross-option validation in AzureDevOpsCommandLineProvider.
  • New AzureDevOpsHistoryService + AzureDevOpsHistoryClient (AOT-safe JsonSerializerContext) that queries the AzDO REST Runs/Results APIs under a 30 s wall-clock budget, with retries, 429 Retry-After honoring, paging caps, and a regression-annotation min-sample threshold.
  • AzureDevOpsReporter now annotates errors with [flaky: failed K/N in last Md] / [REGRESSION] / [quarantined], and demotes severity per the quarantine file and known-flaky rule.
Show a summary per file
FileDescription
src/.../AzureDevOpsCommandLineOptions.csAdds 3 new option name constants.
src/.../AzureDevOpsCommandLineProvider.csRegisters new options and adds cross-option validation.
src/.../AzureDevOpsExtensions.csWires AzureDevOpsHistoryService as data consumer + session lifetime handler.
src/.../AzureDevOpsHistoryClient.csNew REST client (auth, paging, retries, AOT JSON).
src/.../AzureDevOpsHistoryClientJsonContext.csSource-generated JSON context for DTOs.
src/.../AzureDevOpsHistoryService.csLoads/aggregates flaky stats with a bounded budget; exposes TryGetStats/IsLikelyFlaky.
src/.../AzureDevOpsReporter.csAdds annotation suffix building, severity demotion, one-shot quarantine build tag.
src/.../FlakyStats.csStruct holding pass/fail counts and failure rate.
src/.../IAzureDevOpsHistoryService.csInternal abstraction over the history service.
src/.../QuarantineFile.csParses quarantine file (globs, # comments, caps) into regex matchers.
src/.../Microsoft.Testing.Extensions.AzureDevOpsReport.csprojAdds System.Text.Json dependency and DynamicProxyGenAssembly2 IVT for Moq.
Directory.Packages.propsPins System.Text.Json version.
src/.../Resources/AzureDevOpsResources.resxNew strings for options, warnings, and annotation templates; fixes prior Eanble/AzureDev Ops typos.
src/.../Resources/xlf/*.xlf (12 locales)Regenerated XLFs for new strings; Description/OptionDescription flipped to needs-review-translation after the English typo fix.
test/.../AzureDevOpsHistoryClientTests.csAsserts URL composition (definitions=), headers, and run-paging behavior.
test/.../AzureDevOpsHistoryServiceTests.csCovers aggregation, paging, time-budget timeout, regression threshold, demote, quarantine tag-once.
test/.../AzureDevOpsCommandLineProviderTests.csValidates cross-option error messages.
test/.../AzureDevOpsCommandLineTests.csAcceptance-style test for invalid CLI argument errors.
test/.../HelpInfoAllExtensionsTests.csUpdates --help / --info expectations for new options.

Copilot's findings

  • Files reviewed: 31/31 changed files
  • Comments generated: 0

@Evangelink
Amaury Levé (Evangelink) marked this pull request as ready for review May 17, 2026 19:08
Aligns the failure message with the convention used by Assert.Contains
(which uses GetType().Name) and fixes the unit test expectations in
AssertTests.AreAll.cs that expected the short type name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 18, 2026 07:14

CopilotAI 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.

Copilot's findings

  • Files reviewed: 32/32 changed files
  • Comments generated: 4

CopilotAI added 2 commits May 18, 2026 22:03
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Revert unrelated merge-resolution changes that regressed main (Assert.AreEqual updates from RFC 012, AreAllDistinct, PreferAsyncAssertion, Arcade/Versions, eng/common)
- Format DemoteKnownFlakyOptionDescription with the threshold percentage at runtime to keep the resource string and KnownFlakyFailureRateThreshold constant in sync
- Regenerate XLF resources via UpdateXlf
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings May 19, 2026 04:49
@Evangelink

Copy link
Copy Markdown
MemberAuthor

Addressed the open review feedback in 0a2cdb5:

  1. Assert.AreAllDistinct.cs (unrelated change) — Reverted. The bad merge resolution in 0dac227 had also accidentally regressed several other unrelated files merged from main (the RFC 012 changes in Assert.AreEqual*, the MSTEST0064 follow-up in PreferAsyncAssertionFixer/PreferAsyncAssertionAnalyzerTests, and eng/Version.Details.xml / eng/Versions.props / eng/common/* / global.json). All of these were restored from origin/main so the PR diff is now scoped to AzDO files only.
  2. AzureDevOpsExtensions.cs (hard cast to ServiceProvider) — Already fixed in 0dac227 (history service is now created lazily via closure shared by the data consumer and the lifetime handler, no cast to the concrete ServiceProvider).
  3. AzureDevOpsReporter.cs constant/resource drift (25%) — Switched to runtime formatting. KnownFlakyFailureRateThreshold is now internal const; DemoteKnownFlakyOptionDescription carries a {0}% placeholder and AzureDevOpsCommandLineProvider formats it once at startup with KnownFlakyFailureRateThreshold * 100. XLF files regenerated via UpdateXlf; the rendered help text is identical to before, so HelpInfoAllExtensionsTests expectations remain valid.
  4. AzureDevOpsHistoryService.cs budget-task leak — Already fixed in 0dac227 (the budget CancellationTokenSource is canceled as soon as the load task wins the race, so the Task.Delay registration is released promptly).

Verified locally:

  • dotnet build src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport0 warnings, 0 errors.
  • AzureDevOps unit tests (32 of them in Microsoft.Testing.Extensions.UnitTests) — all pass.
  • Full Microsoft.Testing.Extensions.UnitTests151/153 pass, 2 skipped (the two crash-report tests skipped on Windows, as on main).
  • AreAllDistinct tests in TestFramework.UnitTests (23 tests) — all pass.

CopilotAI 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.

Copilot's findings

  • Files reviewed: 58/58 changed files
  • Comments generated: 1

CopilotAI review requested due to automatic review settings May 19, 2026 06:41
@Evangelink
Amaury Levé (Evangelink)force-pushed the dev/amauryleve/azdo-flaky-history branch from 6abe725 to 0a2cdb5CompareMay 19, 2026 06:41

CopilotAI 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.

Copilot's findings

  • Files reviewed: 58/58 changed files
  • Comments generated: 0 new

Resolve XLF conflicts by taking main's translations (the AzDO PR should not modify FrameworkMessages localizations).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto 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.

3 participants

@Evangelink