Skip to content

[Touch.Unit] Preserve console output without retaining Console.Out - #26646

Open
rolfbjarne wants to merge 3 commits into
mainfrom
dev/rolf/console-is-gone
Open

rolfbjarne wants to merge 3 commits into
mainfrom
dev/rolf/console-is-gone

Conversation

@rolfbjarne

@rolfbjarne rolfbjarne commented Sep 16, 2026

Copy link
Copy Markdown
Member

Summary

Restores runner console output on Mac Catalyst app-bundle launches while keeping the lock-order deadlock fix from #26580.

#26580 avoided a deadlock by writing runner output through a StreamWriter over Console.OpenStandardOutput (). That worked around the lock cycle, but in Mac Catalyst app-bundle launches it bypassed the platform-visible Console.Out sink, so Console.WriteLine and Console.Error.WriteLine output disappeared entirely.

This PR reverts that implementation and replaces it with ConsoleTextWriter, a proxy that resolves Console.Out on every write. Because it never stores a reference to the writer, output stays visible in the app bundle without retaining the original synchronized writer that participates in NUnit's Console.SetOut lock-order cycle.

Changes

  • ConsoleTextWriter: a proxy TextWriter that looks up Console.Out at write time instead of capturing it.
  • XML fallback: now explicitly uses ConsoleTextWriter when there is no network or default writer, so NUnitOutputTextWriter neither retains nor closes the original Console.Out.
  • HttpTextWriter: no longer independently echoes to Console.Out. TouchRunner supplies the console sink instead, which prevents duplicate output when HTTP transport is enabled without XML.
  • Test: added a non-parallel unit test, ConsoleTextWriterTests.UsesCurrentConsoleWriter, which redirects Console.Out twice and verifies that each write lands in the writer installed at that moment.

Final diff: 6 files, 73 insertions, 12 deletions.

Validation

  1. The focused unit test passed: 1 passed, 0 failed.
  2. make clean build run-bare -C tests/introspection/dotnet/MacCatalyst completed the test run and restored visible output: 1 runner header, 39 PASS lines, and 4 FAIL lines, matching the known baseline. The command exited nonzero only because of two unrelated known introspection failures, which were explicitly ignored for this investigation.

Review feedback

The previous Copilot review asked for fixes in three areas — XML fallback retaining Console.Out, duplicate output over HTTP, and focused regression coverage. All three are addressed above.

🤖 Pull request created by Copilot

rolfbjarne and others added 2 commits September 16, 2026 16:45
… and NUnit's console capture. Fixes #26537. (#26580)"

This reverts commit 3c675e0.
Resolve Console.Out at write time so app-bundle output continues through the platform-visible console writer while NUnit capture is active. This avoids retaining the original synchronized writer, preserving the lock ordering fix for concurrent NUnit and callback output.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings September 16, 2026 14:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Unresolved writer-retention and duplicate-output paths remain, and focused regression coverage is missing.

Pull request overview

Updates Touch.Unit console routing to avoid retaining the original Console.Out while preserving platform-visible output.

Changes:

  • Removes the persistent standard-output writer.
  • Adds dynamic console forwarding.
  • Adjusts runner output handling.
File summaries
File Summary
tests/common/Touch.Unit/Touch.Client/Runner/TouchRunner.cs Replaces retained console forwarding with dynamic forwarding; XML fallback and HTTP duplicate-output paths still require fixes, along with regression coverage.
Review details

Suppressed comments (3)

tests/common/Touch.Unit/Touch.Client/Runner/TouchRunner.cs:418

  • ⚠️ Testing — This changes the synchronization-sensitive writer used for every test-run message, but there is no regression test for the fix. Please add a focused test that redirects Console.Out, writes through the runner writer, changes the redirect, and verifies the later output reaches the new writer without retaining the original; this would catch a future reintroduction of the deadlock/output-loss behavior.
				writers.Add (new ConsoleTextWriter ());

tests/common/Touch.Unit/Touch.Client/Runner/TouchRunner.cs:418

  • bug — This removes the captured writer only for the standalone console branch. If XML reporting is enabled and network setup fails but execution continues, defaultWriter remains null, so NUnitOutputTextWriter above falls back to Console.Out and retains the original synchronized writer (and later closes it). That path can still reintroduce the lock cycle and close the platform console; pass defaultWriter ?? new ConsoleTextWriter () to that writer as well.
				writers.Add (new ConsoleTextWriter ());

tests/common/Touch.Unit/Touch.Client/Runner/TouchRunner.cs:418

  • bug — With the HTTP transport and XML disabled, defaultWriter is HttpTextWriter, whose Write/WriteLine methods already echo every message to Console.Out (HttpTextWriter.cs:107-124). MultiplexedTextWriter now sends the same message to that writer and to ConsoleTextWriter, so runner output is emitted twice (the old independent standard-output stream did not share this path). Avoid adding a second console sink for this transport, or remove the HTTP writer's console echo, and cover the combination with a regression test.
				writers.Add (new ConsoleTextWriter ());
  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Lite

@vs-mobiletools-engineering-service2

This comment has been minimized.

Use the dynamic console proxy for XML fallback output so NUnit never retains or closes the original Console.Out writer. Remove HttpTextWriter's implicit console echo because the runner now provides the console sink explicitly, preventing duplicated HTTP transport output.

Add focused coverage that redirects Console.Out twice and verifies the proxy routes each write to the current writer rather than retaining the first one.

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

Copilot-Session: eea9570a-646e-4aac-9a5f-b06062484381

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The XML-only fallback can duplicate runner output, and targeted coverage for the affected routing paths is still missing.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (2)

tests/common/Touch.Unit/Touch.Client/Runner/TouchRunner.cs:404

  • Bug — When XML is enabled but no transport writer is created (for example, after TCP host selection fails), this proxy is used as NUnitOutputTextWriter.BaseWriter while another ConsoleTextWriter is added to the multiplexed list at line 418. NUnitOutputTextWriter buffers the same runner lines in extra_data and writes them to BaseWriter during Close, so the console sink emits each line once live and once again inside the XML comment/wrapper. Avoid using the same console sink for both paths, or suppress the live sink for this XML-only fallback.
							writers.Add (new NUnitOutputTextWriter (
								this, defaultWriter ?? new ConsoleTextWriter (), formatter, options.XmlMode));

tests/common/Touch.Unit/Touch.Client/Runner/TouchRunner.cs:404

  • ⚠️ test_coverage — The new regression only tests ConsoleTextWriter.WriteLine in isolation. It does not exercise this XML fallback through NUnitOutputTextWriter.Close(), which calls BaseWriter.Close() and is the path that previously retained/closed the captured Console.Out; a regression in this defaultWriter ?? ... wiring would still pass. Add a test for the no-default-writer XML path and verify that closing it leaves the installed console writer usable.
								this, defaultWriter ?? new ConsoleTextWriter (), formatter, options.XmlMode));
  • Files reviewed: 6/6 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread tests/common/Touch.Unit/Touch.Client/Runner/TouchRunner.cs
@vs-mobiletools-engineering-service2

Copy link
Copy Markdown
Collaborator

✅ API diff for current PR / commit

NET (empty diffs)

✅ API diff vs stable

NET (empty diffs)

ℹ️ Generator diff

Generator Diff: vsdrops (html) vsdrops (raw diff) gist (raw diff) - Please review changes)

Pipeline on Agent
Hash: a433c077ba059480bb2b8984e6a90800ad29b356 [PR build]

@vs-mobiletools-engineering-service2

Copy link
Copy Markdown
Collaborator

🔥 [CI Build #a433c07] Test results 🔥

Test results

❌ Tests failed on VSTS: test results

0 tests crashed, 2 tests failed, 262 tests passed.

Failures

❌ monotouch tests (iOS)

2 tests failed, 23 tests passed.

Failed tests

  • monotouch-test/iOS - simulator/Debug: LaunchTimedOut
  • monotouch-test/iOS - simulator/Release (trimmable static registrar, NativeAOT): TimedOut

Html Report (VSDrops) Download

Successes

✅ assembly-processing: All 1 tests passed. Html Report (VSDrops) Download
✅ cecil: All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (iOS): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (MacCatalyst): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (macOS): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (Multiple platforms): All 1 tests passed. Html Report (VSDrops) Download
✅ dotnettests (tvOS): All 1 tests passed. Html Report (VSDrops) Download
✅ framework: All 2 tests passed. Html Report (VSDrops) Download
✅ fsharp: All 4 tests passed. Html Report (VSDrops) Download
✅ generator: All 5 tests passed. Html Report (VSDrops) Download
✅ interdependent-binding-projects: All 4 tests passed. Html Report (VSDrops) Download
✅ introspection: All 7 tests passed. Html Report (VSDrops) Download
✅ linker (iOS): All 31 tests passed. Html Report (VSDrops) Download
✅ linker (MacCatalyst): All 31 tests passed. Html Report (VSDrops) Download
✅ linker (macOS): All 21 tests passed. Html Report (VSDrops) Download
✅ linker (tvOS): All 31 tests passed. Html Report (VSDrops) Download
✅ monotouch (MacCatalyst): All 25 tests passed. Html Report (VSDrops) Download
✅ monotouch (macOS): All 20 tests passed. Html Report (VSDrops) Download
✅ monotouch (tvOS): All 25 tests passed. Html Report (VSDrops) Download
✅ msbuild: All 2 tests passed. Html Report (VSDrops) Download
✅ sharpie: All 1 tests passed. Html Report (VSDrops) Download
✅ windows: All 3 tests passed. Html Report (VSDrops) Download
✅ xcframework: All 4 tests passed. Html Report (VSDrops) Download
✅ xtro: All 1 tests passed. Html Report (VSDrops) Download

macOS tests

✅ Tests on macOS Sonoma (14): All 5 tests passed. Html Report (VSDrops) Download
✅ Tests on macOS Sequoia (15): All 5 tests passed. Html Report (VSDrops) Download
✅ Tests on macOS Tahoe (26): All 5 tests passed. Html Report (VSDrops) Download
⚠️ Tests on macOS Golden Gate (27): Tests skipped, incorrect beta version. Html Report (VSDrops) Download

Linux Build Verification

Linux build succeeded

Pipeline on Agent
Hash: a433c077ba059480bb2b8984e6a90800ad29b356 [PR build]

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants