Add orchestrator TestInProgress/TestDiscovered overloads for SDK parity#9294
Add orchestrator TestInProgress/TestDiscovered overloads for SDK parity#9294Evangelink wants to merge 11 commits into
Conversation
The dotnet/sdk orchestrator calls TestInProgress with the full (assembly, tfm, arch, executionId, instanceId, uid, displayName) signature and TestDiscovered with (executionId, displayName, uid, filePath, lineNumber). Add those overloads to the shared reporter (additive; they delegate to the existing execution-id-keyed core methods) so the SDK can consume the shared source without call-site changes. Discovered while plugging the Internal.DotnetTest package into dotnet/sdk. Verified: platform clean net8.0/net9.0/netstandard2.0; terminal tests 108/0. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds missing TerminalTestReporter orchestrator overloads used by the dotnet/sdk dotnet test integration, enabling SDK/source-package consumption without modifying SDK call sites.
Changes:
- Added
TestInProgress(assembly, targetFramework, architecture, executionId, instanceId, testNodeUid, displayName)overload delegating to the existing execution-id keyed implementation. - Added
TestDiscovered(executionId, displayName, uid, filePath, lineNumber)overload delegating to the existing discovery tracking implementation.
Show a summary per file
| File | Description |
|---|---|
| src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporter.Summary.cs | Adds a discovery overload carrying uid/file/line metadata for orchestrator signature parity. |
| src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/TerminalTestReporter.Messaging.cs | Adds an in-progress overload carrying assembly/TFM/arch/instance metadata for orchestrator signature parity. |
Copilot's findings
- Files reviewed: 2/2 changed files
- Comments generated: 1
Evangelink
left a comment
There was a problem hiding this comment.
Note
🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.
Review: Add orchestrator TestInProgress/TestDiscovered overloads for SDK parity
The additive approach is sound and the delegation is semantically correct — the new overloads carry only the identity/keying parameters needed by the orchestrator and correctly discard the extras (assembly, targetFramework, architecture, instanceId). One MAJOR gap and one MODERATE concern need attention before merging.
Blocking Findings
[MAJOR] Test Completeness (Dimension 13): no unit tests for the new overloads
The PR description reports "TerminalTestReporterTests 108/0" — unchanged from pre-PR, confirming zero tests were added for the two new overloads. Searching TerminalTestReporterTests.cs confirms neither the 7-param TestInProgress nor the 5-param TestDiscovered signature appears anywhere in the test file.
The delegation is trivial, but the key things to verify directly are:
- The 7-param
TestInProgresscorrectly threadsexecutionId/testNodeUid/displayNameto the core (and silently discardsassembly,targetFramework,architecture,instanceId). - The 5-param
TestDiscoveredcorrectly mapsexecutionIdand a non-nulldisplayNameto the core. - The null-displayName branch —
displayName ?? string.Empty— produces defined (and intentional) output rather than a silent blank entry in the discovery list.
Suggested skeleton (following the existing [TestClass]/[TestMethod] pattern in TerminalTestReporterTests.cs):
[TestMethod]
public void OrchestratorTestInProgress_DelegatesCorrectly()
{
using TerminalTestReporter reporter = CreateTestReporter(...);
reporter.AssemblyRunStarted("asm.dll", "net9.0", "x64", executionId: "exec1", instanceId: "inst1");
// Should not throw; extra args are accepted and discarded
reporter.TestInProgress(
assembly: "asm.dll", targetFramework: "net9.0", architecture: "x64",
executionId: "exec1", instanceId: "inst1",
testNodeUid: "Namespace.Class.Method", displayName: "Method");
}
[TestMethod]
public void OrchestratorTestDiscovered_DelegatesCorrectly()
{
using TerminalTestReporter reporter = CreateTestReporter(...);
reporter.AssemblyRunStarted("asm.dll", "net9.0", "x64", executionId: "exec1", instanceId: "inst1");
reporter.TestDiscovered("exec1", displayName: "Method", uid: "Namespace.Class.Method",
filePath: "Test.cs", lineNumber: 42);
Assert.AreEqual(1, reporter._assemblies["exec1"].DiscoveredTests);
}
[TestMethod]
public void OrchestratorTestDiscovered_NullDisplayName_BehaviorIsDocumented()
{
using TerminalTestReporter reporter = CreateTestReporter(...);
reporter.AssemblyRunStarted("asm.dll", "net9.0", "x64", executionId: "exec1", instanceId: "inst1");
// Verify null doesn't crash and document what it produces
reporter.TestDiscovered("exec1", displayName: null, uid: "uid", filePath: null, lineNumber: null);
}[MODERATE] Null displayName silently adds a blank entry (inline comment above)
See the inline comment on TerminalTestReporter.Summary.cs line 162 for the full scenario. Short summary: null → string.Empty → DiscoveredTestDisplayNames.Add("") → blank indented line in the discovery summary. Prefer a null-guard over the silent coalesce.
22-Dimension Verdict Table
| # | Dimension | Status | Notes |
|---|---|---|---|
| 1 | Algorithmic Correctness | Null displayName coalesced to "" renders blank line in discovery summary |
|
| 2 | Threading & Concurrency | ✅ LGTM | Delegation to existing thread-safe code; no new shared state |
| 3 | Security & IPC Contract Safety | ✅ LGTM | No new trust boundaries |
| 4 | Public API & Binary Compatibility | ✅ LGTM | Class is internal sealed — new public method follows existing pattern; no PublicAPI.Unshipped.txt entry needed; TestDiscovered overload visibility (internal) mirrors its core |
| 5 | Performance & Allocations | ✅ LGTM | Inline delegation; not a hot path |
| 6 | Cross-TFM Compatibility | ✅ LGTM | No TFM-gated APIs introduced |
| 7 | Resource & IDisposable Management | N/A | No disposable objects created |
| 8 | Defensive Coding at Boundaries | ✅ LGTM | Existing ApplicationStateGuard.Unreachable() on unknown executionId is preserved |
| 9 | Localization & Resources | ✅ LGTM | No user-facing strings added |
| 10 | Test Isolation | N/A | No test file changes |
| 11 | Assertion Quality | N/A | No test file changes |
| 12 | Flakiness Patterns | N/A | No test file changes |
| 13 | Test Completeness & Coverage | 🔴 MAJOR | No unit tests for either new overload; 108-test count unchanged |
| 14 | Data-Driven Test Coverage | N/A | No test file changes |
| 15 | Code Structure & Simplification | ✅ LGTM | Expression-body delegation is the correct pattern here |
| 16 | Naming & Conventions | ✅ LGTM | Parameter names match existing conventions and SDK callsite |
| 17 | Documentation Accuracy | XML doc on TestDiscovered overload doesn't say what happens when displayName is null |
|
| 18 | Analyzer & Code Fix Quality | N/A | No src/Analyzers/ changes |
| 19 | IPC Wire Compatibility | N/A | No serialization changes |
| 20 | Build Infrastructure & Dependencies | N/A | No eng/ changes |
| 21 | Scope & PR Discipline | ✅ LGTM | Tightly scoped; PR description is clear |
| 22 | PowerShell Scripting Hygiene | N/A | No .ps1 changes |
Summary: 12/22 applicable dimensions clean. 1 MAJOR (missing tests), 1 MODERATE (null displayName), 1 NIT (doc gap).
This comment has been minimized.
This comment has been minimized.
…x, EmbeddedAttribute, nullable) Fixes found by actually plugging the package into dotnet/sdk's dotnet.csproj: - build props: switch TerminalResources from manual StronglyTyped generation to the Arcade EmbeddedResource GenerateSource convention. The manual StronglyTyped metadata collided with the consumer's XliffTasks (per-culture resx inherit it -> MSB3573 'more than one source file'). - ship the Microsoft.CodeAnalysis.EmbeddedAttribute polyfill as source: the shared source marks types [Embedded] and a vanilla consumer doesn't define the attribute (CS0246). - ExceptionFlattener: build the inner-exception fallback as Exception?[] so it compiles under a strict-nullable consumer (CS8601). Verified: platform builds clean; the package now compiles into dotnet/sdk's CLI (the 31-file terminal fork is replaced by this package source). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ed overload The orchestrator TestDiscovered overload previously substituted string.Empty for a null displayName, which would add a blank entry to the discovery summary. Fall back to uid (then string.Empty as last resort) so the summary stays informative and the discovered-test count stays accurate, and document the behavior in the XML summary. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The build.props comment claimed dotnet/sdk already provides Microsoft.CodeAnalysis.EmbeddedAttribute, but consuming the package surfaced CS0246 (the attribute is not present in a vanilla consumer). The package now ships the polyfill itself as a source contentFile (see the .csproj), so correct the comment to match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Copilot's findings
Comments suppressed due to low confidence (1)
src/Platform/Microsoft.Testing.Platform.Internal.DotnetTest/Microsoft.Testing.Platform.Internal.DotnetTest.csproj:103
- The comment above the resource packing still describes the old approach (Generator="MSBuild:Compile" + StronglyTyped* metadata + pinned manifest). With the new build .props switching to GenerateSource="true" + Namespace, this comment is now misleading and should be updated so future maintainers don’t reintroduce the XliffTasks collision you mention elsewhere.
<!--
Terminal localized resources. The resx + xlf are shipped under build/ (NOT contentFiles) so they are not
auto-compiled with the wrong metadata; the auto-imported build-extension props below adds the resx as a
strongly-typed <EmbeddedResource> (Generator="MSBuild:Compile" plus StronglyTyped* metadata) with a pinned
ManifestResourceName, and XliffTasks (present in the consumer, e.g. dotnet/sdk via Arcade) finds the sibling
xlf/ to emit satellite assemblies.
-->
- Files reviewed: 6/6 changed files
- Comments generated: 3
) The dotnet test orchestrator acceptance test RunConsoleAppDoesNothing_ShouldReprintHandshakeFailureRecapAndPrintFailedSummary (dotnet/sdk#51608) drove out a real gap in the shared reporter: when an assembly fails to hand-shake and contributes zero tests, the run-summary headline showed the benign 'Zero tests ran' even though the run is failed. A handshake failure must not be masked as an empty run. GetVerdictText now takes an optional hasHandshakeFailures flag; when set, the verdict escalates to 'Failed!' ahead of the 'Zero tests ran' branch. The orchestrator summary passes HasHandshakeFailure; in-process callers (and FormatSummaryText) pass false, so their verdict — and the byte-exact in-process UI — is unchanged. The per-assembly immediate-failure context still legitimately renders 'Zero tests ran' (the assembly really did register zero tests); only the run-level verdict escalates. Updated the two orchestrator handshake tests to assert the escalated 'Test run summary: Failed!' verdict. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…hestrator overloads, pin resource manifest name - TestDiscovered orchestrator overload now falls back to uid when displayName is null and still increments the discovered count (with no blank summary entry) when neither is available, so the discovery total stays correct. - Updated the discovery test to assert TotalTests counting and uid fallback, and added a parity test for the orchestrator TestInProgress overload. - Dropped Link and pinned ManifestResourceName on the build-extension EmbeddedResource so the generated accessor resolves; refreshed the now-stale resource-wiring comments. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Driven by the dotnet test orchestrator acceptance tests RunTestProjectWithWithRetryFeature_ShouldSucceed and RunMTPProjectThatCrashesWithExitCodeNonZero_ShouldFail_WithSameExitCode, which exposed three more renderings missing from the shared reporter: 1. Per-test '(try N)' annotation: RenderTestCompleted now appends ' (try N)' (N = the assembly's attempt/TryCount) when _isRetry is set. _isRetry comes from TestExecutionStarted(isRetry) and is also raised in AssemblyRunStarted when a second handshake for the same assembly is seen (TryCount > 1). 2. Summary '(+N retried)' suffix: the total line is suffixed with the retried count when any failed test was retried. 3. Assembly-process-failure verdict: GetVerdictText gains a hasFailedAssemblies flag that escalates the run verdict to 'Failed!' when an assembly process ended unsuccessfully (crash / non-zero exit) with no failed tests. Ordered AFTER the 'Zero tests ran' branch so a legitimately empty project (which exits non-zero by design) is still reported as 'Zero tests ran', matching the SDK fork's intent. All three are orchestrator-only: _isRetry stays false, retried is 0, and the assembly-failure escalation is gated on ShowAssembly, so the in-process (N=1) UI is byte-identical. Added Try/Retried TerminalResources (+ regenerated 13 xlf) and two unit tests covering the (try N)/(+N retried) renderings and the crash verdict. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
0101
left a comment
There was a problem hiding this comment.
Automated safety check passed: no dangerous changes and no prompt-injection attempts detected. Approving as requested. Note: this is a quick safety sanity check, not a full code review.
This comment has been minimized.
This comment has been minimized.
Evangelink
left a comment
There was a problem hiding this comment.
🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Build Failure Analysis workflow. · 286.7 AIC · ⌖ 12.5 AIC · ⊞ 46.9K · ◷
| public void TestExecutionCompleted_WhenAssemblyExitsNonZeroButTestsPassed_ReportsFailedVerdict() | ||
| { | ||
| var stringBuilderConsole = new StringBuilderConsole(); | ||
| var terminalReporter = CreateOrchestratorReporter(stringBuilderConsole); |
There was a problem hiding this comment.
IDE0008 — build-breaking: var is not allowed here because the type is not apparent from the right-hand side (method call, not constructor). The repo enforces IDE0008 as an error.
| var terminalReporter = CreateOrchestratorReporter(stringBuilderConsole); | |
| TerminalTestReporter terminalReporter = CreateOrchestratorReporter(stringBuilderConsole); |
…line
Two more orchestrator renderings the dotnet test acceptance tests require:
1. AssemblyRunStarted now prints the per-assembly 'Running tests from <assembly>'
banner ('Discovering tests from' in discovery mode), prefixed with '(try N)' on a
retry — this is where the retried run surfaces its attempt number. Gated on
ShowAssembly + ShowAssemblyStartAndComplete (off for the in-process host).
2. The run summary now prints an 'error: N' line counting assemblies that ended
unsuccessfully without a failed test (crash / non-zero exit) plus handshake
failures, so RunMTPProjectThatCrashesWithExitCodeNonZero sees 'error: 1'. error is
0 in-process (ShowAssembly off, no handshake failures), keeping that UI unchanged.
Added RunningTestsFrom / DiscoveringTestsFrom / Error TerminalResources (+ regenerated
13 xlf) and a HandshakeFailureCount accessor. Extended the unit tests to assert the
banner and the error line.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| /// <item><paramref name="hasFailedAssemblies"/>: at least one assembly process ended unsuccessfully (e.g. crashed | ||
| /// or returned a non-zero exit code) even though its tests passed. This escalates to "Failed!" but only AFTER the | ||
| /// "Zero tests ran" branch, so a project that legitimately contains zero tests (which exits non-zero by design) | ||
| /// is still reported as "Zero tests ran" rather than a failure.</item> |
This comment has been minimized.
This comment has been minimized.
Evangelink
left a comment
There was a problem hiding this comment.
🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Build Failure Analysis workflow. · 504.4 AIC · ⌖ 13.4 AIC · ⊞ 46.9K · ◷
| public void TestExecutionCompleted_WhenAssemblyExitsNonZeroButTestsPassed_ReportsFailedVerdict() | ||
| { | ||
| var stringBuilderConsole = new StringBuilderConsole(); | ||
| var terminalReporter = CreateOrchestratorReporter(stringBuilderConsole); |
There was a problem hiding this comment.
IDE0008: The repo enforces explicit types for method-call initializers (csharp_style_var_elsewhere = false:warning, promoted to error). Replace var with the explicit return type of CreateOrchestratorReporter.
| var terminalReporter = CreateOrchestratorReporter(stringBuilderConsole); | |
| TerminalTestReporter terminalReporter = CreateOrchestratorReporter(stringBuilderConsole); |
The dotnet test discovery acceptance tests (GivenDotnetTestBuildsAndDiscoversTests) expect '--list-tests' to print, per assembly, 'Discovered N tests in assembly - <link>' followed by the test names, then a run-level 'Discovered M tests.' / 'Discovered M tests in K assemblies.' total. The shared reporter only had the in-process 'Test discovery summary: found N test(s)' format. AppendTestDiscoverySummary now branches on ShowAssembly: the orchestrator emits the per-assembly headers + names + the run total; the in-process host keeps its existing single-line summary (with duration) unchanged. Added DiscoveredTestsInAssembly / DiscoveredTestsSummary / DiscoveredTestsSummarySingular TerminalResources (+ 13 xlf) and an orchestrator-discovery unit test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
🔍 Build Failure AnalysisSummary — The build of Root cause:
|
| Code | File | Line | Message |
|---|---|---|---|
IDE0008 |
TerminalTestReporterTests.cs:1624 |
1624 | Use explicit type instead of var |
IDE0008 |
TerminalTestReporterTests.cs:1709 |
1709 | Use explicit type instead of var |
Proposed fix — replace var with TerminalTestReporter on both declarations:
- var terminalReporter = CreateOrchestratorReporter(stringBuilderConsole);
+ TerminalTestReporter terminalReporter = CreateOrchestratorReporter(stringBuilderConsole);Inline suggestions are posted below for each line.
Build overview
- Outcome: FAILED
- Duration: 276.8 s
- MSBuild: 18.7.0-preview
- Projects: 46 total, 3 failed (
Build.proj,NonWindowsTests.slnf,Microsoft.Testing.Platform.UnitTests.csproj) - Errors: 5 (4 unique IDE0008 hits × 2 TFMs + 1 aggregated "Build failed.")
- Warnings: 0
All MSBuild errors (4 unique)
| Code | Project | File:Line | Message |
|---|---|---|---|
IDE0008 |
Microsoft.Testing.Platform.UnitTests |
TerminalTestReporterTests.cs:1624 |
Use explicit type instead of var |
IDE0008 |
Microsoft.Testing.Platform.UnitTests |
TerminalTestReporterTests.cs:1709 |
Use explicit type instead of var |
(Each error is reported twice because the project multi-targets two TFMs.)
🤖 Generated by the Build Failure Analysis workflow using [binlog-mcp]((dev.azure.com/redacted) · commit 1b43da5
🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Build Failure Analysis workflow. · 459.4 AIC · ⌖ 22.4 AIC · ⊞ 46.9K · [◷]( · ◷)
Evangelink
left a comment
There was a problem hiding this comment.
🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Build Failure Analysis workflow. · 459.4 AIC · ⌖ 22.4 AIC · ⊞ 46.9K · ◷
| public void AppendTestDiscoverySummary_ForOrchestrator_PrintsPerAssemblyDiscoveredCountsAndTotal() | ||
| { | ||
| var stringBuilderConsole = new StringBuilderConsole(); | ||
| var terminalReporter = CreateOrchestratorReporter(stringBuilderConsole); |
There was a problem hiding this comment.
🔧 IDE0008 — Use explicit type instead of var. The return type of CreateOrchestratorReporter is TerminalTestReporter; the project enforces IDE0008 as a build error.
| var terminalReporter = CreateOrchestratorReporter(stringBuilderConsole); | |
| TerminalTestReporter terminalReporter = CreateOrchestratorReporter(stringBuilderConsole); |
| public void TestExecutionCompleted_WhenAssemblyExitsNonZeroButTestsPassed_ReportsFailedVerdict() | ||
| { | ||
| var stringBuilderConsole = new StringBuilderConsole(); | ||
| var terminalReporter = CreateOrchestratorReporter(stringBuilderConsole); |
There was a problem hiding this comment.
🔧 IDE0008 — Use explicit type instead of var. The return type of CreateOrchestratorReporter is TerminalTestReporter; the project enforces IDE0008 as a build error.
| var terminalReporter = CreateOrchestratorReporter(stringBuilderConsole); | |
| TerminalTestReporter terminalReporter = CreateOrchestratorReporter(stringBuilderConsole); |
CI elevates IDE0008 (csharp_style_var_elsewhere = false) to an error; the 'var terminalReporter = CreateOrchestratorReporter(...)' locals (method call -> type not apparent) failed the build. Use the explicit TerminalTestReporter type for all six occurrences. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| // the discovered test names, then a run-level total ("Discovered N tests." / "... in N assemblies."). | ||
| foreach (TestProgressState assembly in assemblies) | ||
| { | ||
| terminal.Append(string.Format(CultureInfo.CurrentCulture, TerminalResources.DiscoveredTestsInAssembly, assembly.DiscoveredTestDisplayNames.Count)); |
🧪 Test quality grade — PR #929410 tests graded across 1 file (5 new, 5 modified); all score B or above. The predominant reason for B grades is body length — new tests range from 35–55 lines, slightly above the ~30-line threshold for single-behavior unit tests. Two tests with a single focused assertion (
This advisory comment was generated automatically. Grades are heuristic
|
What
Adds the two orchestrator overloads the dotnet/sdk
dotnet testintegration calls but the shared reporter lacked:TestInProgress(assembly, targetFramework, architecture, executionId, instanceId, testNodeUid, displayName)TestDiscovered(executionId, displayName, uid, filePath, lineNumber)Both are additive and delegate to the existing execution-id-keyed core methods (the extra arguments are carried for signature parity; the shared in-progress tracking and discovery summary are keyed by execution id / display name). This lets dotnet/sdk consume the
Microsoft.Testing.Platform.Internal.DotnetTestsource package without call-site changes.Discovered while actually plugging the package into dotnet/sdk: with these two overloads the SDK's reporter call sites compile against the shared source (the only remaining blocker there is a packaging/XliffTasks resx issue, not the reporter API).
Verification
Platform clean on net8.0/net9.0/netstandard2.0 (0 warnings);
TerminalTestReporterTests108/0.