Skip to content

Add CI-friendly textual progress heartbeat to MTP terminal reporter (raw log progress signal) #9125

@Evangelink

Description

@Evangelink

Summary

When running dotnet test (MTP) in AzDO (and any non-ANSI CI), users currently have a binary choice in the raw console log:

Mode Per-test "passed" lines Progress in raw log
--output normal (default) ❌ none (good) ❌ silent until summary (bad)
--output detailed ✅ one per test (noisy) ❌ still no time-based progress

--report-azdo-progress only updates the AzDO timeline view via ##vso[task.logdetail …] commands; it does not print anything human-readable to the raw build log, which is what you stare at when investigating a CI run.

We currently work around this in our own pipelines with --output detailed, which produces hundreds of lines like:

passed UnsupportedRunSettingsEntriesAreFlagged ("net10.0") (3s 768ms)
  from D:\a\_work\1\s\artifacts\bin\MSTest.Acceptance.IntegrationTests\Debug\net11.0\MSTest.Acceptance.IntegrationTests.dll (net11.0|x64)
passed UnsupportedRunSettingsEntriesAreFlagged_Localization ("fr-FR","it-IT",null,"fr-FR") (3s 756ms)
…

That's a lot of noise (failures get buried) and it still does not convey time-based progress — only completion order.

Motivation

The maintainer experience in CI is:

  • "Did the test run hang?" requires scrolling 10k lines of passed-test names, or staring at the timeline view in a separate panel.
  • Dropping --output detailed gives a clean log but no signal at all between the banner and the summary — for a 15‑minute test run that feels broken.
  • The cursor-redraw progress (SimpleAnsi/AnsiIfPossible) is intentionally disabled in CI because raw log files can't display cursor movement, so today there is no progress at all in raw logs.

Proposed feature: CI-friendly textual progress heartbeat

Emit a single-line plain-text status line periodically (default ~30s) in SimpleAnsi and NoAnsi modes. Each line is appended to the log (no cursor magic), so it's readable in AzDO/GitHub Actions/Jenkins raw logs.

Example shape (subject to bikeshed):

[00:30] running … 124/? completed, 2 failed (active: MyClass.MyTest, MyOther.OtherTest)
[01:00] running … 312/? completed, 2 failed (active: SlowTest.Foo)
[01:30] running … 451/? completed, 5 failed (active: SlowTest.Foo, Bar.Baz)

Behavior:

  • Default: enabled in CI (SimpleAnsi) and when --no-progress is not specified.
  • Configurable: --ci-progress-interval <seconds> (or env var); --no-progress continues to suppress everything.
  • Independent of --output: works whether or not passed tests are shown.
  • Single line per emission so log filters / regex grep stay clean.
  • Per-assembly variant when running multiple assemblies (matches existing ShowAssemblyStartAndComplete pattern).

Scope

This needs coordinated changes in two repos:

  1. microsoft/testfx — MTP's TerminalTestReporter / SimpleAnsiTerminal / TerminalOutputDevice for the single-exe --no-build and dotnet run scenarios. Touchpoints:
    • src/Platform/Microsoft.Testing.Platform/OutputDevice/TerminalOutputDevice.cs:212-216 (currently forces ShowProgress=false for SimpleAnsi/NoAnsi).
    • src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporterOptions.cs (new option).
    • src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/SimpleAnsiTerminal.cs (heartbeat timer + line emission).
    • TerminalTestReporterCommandLineOptionsProvider + acceptance-test expectations (HelpInfoTests, HelpInfoAllExtensionsTests, MSBuild.KnownExtensionRegistration).
  2. dotnet/sdk — the forked reporter under src/Cli/dotnet/Commands/Test/MTP/Terminal/ for the dotnet test (build → MTP) path. The SDK's TerminalTestReporter ctor force-disables showProgress when AnsiMode == SimpleAnsi; same logic gap.

Short-term mitigation (this repo only)

We can drop --output detailed from _CommonReleaseTestArgs in eng/pipelines/variables/test-args.yml:67 (plus the inline copies in azure-pipelines.yml:100 and eng/pipelines/steps/test-non-windows.yml:19) after the heartbeat lands, so our CI logs immediately benefit from the new signal without losing visibility.

Related work / prior art

Open questions

  1. Default emission interval: 15s, 30s, 60s?
  2. Include active-test names in the line? (Privacy/security implications minimal here, but worth confirming.)
  3. New CLI flag name vs. reusing --no-progress semantics: a single --no-progress should still suppress heartbeats.
  4. Should the heartbeat be enabled by default outside CI when ANSI cursor progress is unavailable (e.g., output redirected to a file)? Probably yes.
  5. Should each assembly emit its own heartbeat in multi-assembly runs (SDK path), or one aggregate heartbeat?

cc @Evangelink @nohwnd

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