Skip to content

Silence-driven progress heartbeat renderer for SimpleAnsi/NoAnsi terminal modes #9139

@Evangelink

Description

@Evangelink

Summary

Implement the silence-driven progress heartbeat renderer for the SimpleAnsi and NoAnsi terminal modes. This is the core implementation of #9125 and the natural successor to #9138.

Background

Today, TerminalOutputDevice.cs:212-216 force-disables progress for SimpleAnsi/NoAnsi, leaving CI / piped / file-redirected runs with zero progress signal between the banner and the summary. Previously the platform emitted a per-DLL summary every 3 seconds, which was rejected as spam (#6753). The fix is event-driven, not time-driven.

Design

The terminal reporter picks a renderer based on capability:

AnsiMode Renderer
AnsiIfPossible / ForceAnsi existing cursor-redraw renderer (unchanged)
SimpleAnsi / NoAnsi NEW silence-driven heartbeat renderer

Three independent emission rules. Each produces single lines, no cursor tricks, no duplicate timestamps (CIs already prepend their own):

E1 — silence heartbeat

Emit one summary line only when no test completion event has occurred for N seconds (default 30s). A healthy fast suite emits zero heartbeat lines.

running … 312/3200 completed, 2 failed | active: SlowTest.Foo

E2 — slow-test surfacing

When a single test exceeds a per-test threshold (default 60s), emit one line with exponential backoff (60s → 2m → 4m → 8m …):

[slow] still running after 60s: SlowTest.IntegrationFoo (MyAcceptance, net9.0)

E2 fires in all modes (including cursor) because it leaves a durable scrollback line; cursor frames are transient.

E3 — failures inline

Verify failures continue to be printed at the moment of failure for SimpleAnsi/NoAnsi. Already true in cursor mode; mostly a verification item.

Gating

Knobs (env vars only, no new CLI flags)

  • MTP_PROGRESS_SILENCE_SECONDS (default 30; 0 disables E1)
  • MTP_PROGRESS_SLOW_TEST_SECONDS (default 60; 0 disables E2)

Extension hook

Introduce a minimal IProgressEnricher (name TBD) hook with:

  • OnAssemblyStart(asm) / OnAssemblyEnd(asm) — per-CI extensions emit group markers here.
  • OnFailure(test, …) — per-CI extensions emit native annotations.
  • OnSlowTestThreshold(test) — extensions can lower/raise per-test E2 threshold (powers the AzDO-history extension).
  • OnSlowTestEmit(test, currentDuration) — extensions can decorate the emitted line (e.g. with historical p95 = 2s).

The hook contract is shipped here so per-CI extensions can iterate independently.

Touchpoints

  • src/Platform/Microsoft.Testing.Platform/OutputDevice/TerminalOutputDevice.cs — collapse the if (noProgress || ansiMode is NoAnsi or SimpleAnsi) branch to just if (noProgress); renderer picked by AnsiMode.
  • src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TestProgressStateAwareTerminal.cs — extract renderer abstraction.
  • New: SilenceDrivenHeartbeatRenderer.cs (or equivalent).
  • New: IProgressEnricher hook + glue.
  • Tests: heartbeat fires only on silence, slow-test backoff math, failure inline, --progress off suppression, controller suppression, env-var overrides.
  • Acceptance: re-run HelpInfoTests / HelpInfoAllExtensionsTests (no new flag here, but verify nothing regressed).

Out of scope (deliberately)

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels
    No fields configured for Feature.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions