Skip to content

Initialize headless terminal dimensions in Testbench and unit tests - #71

Merged
binaryfire merged 8 commits into
0.4from
fix/test-terminal-dimensions
Oct 24, 2026
Merged

binaryfire merged 8 commits into
0.4from
fix/test-terminal-dimensions

Conversation

@binaryfire

@binaryfire binaryfire commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

Fixes hypervel/components#686.

Testbench's manual application bootstrap omitted ConfigureTerminalDimensions, even though the normal HTTP and console kernels run it. In a headless process, Symfony does not cache failed terminal-size probes, so repeated console calls can repeatedly spawn stty and enter Swoole's affected subprocess-wait path.

The bootstrapper now fills Symfony Terminal's missing dimension-cache entries with 80×24 when stdout is headless. It leaves real terminals and already populated cache entries alone. Symfony's own accessors continue to prefer explicit COLUMNS and LINES values, including values loaded after initialization.

The fallback never writes to the environment. This avoids masking later environment files, changing values restored by temporary-environment helpers, or requiring cleanup between tests. The earlier environment-ownership tracking and subscriber cleanup have been removed entirely.

Symfony exposes no dimension-cache setter, so this integration uses reflection in one place. Regression tests check both cache entries and the public terminal behavior, making changes to the dependency's private properties visible during upgrades.

Testbench invokes the initializer during application creation before configuration and provider execution. The shared test-invocation path also initializes every PHPUnit/ParaTest process, including application-free tests and tests that manage their own coroutines. Applications and packages do not need terminal settings in PHPUnit XML for this protection.

The components suite retains its existing XML dimensions solely to stabilize console-output wrapping assertions. Their comment now explains that purpose. This change prevents unnecessary terminal probes; it does not repair Swoole's general subprocess-wait defect, tracked in swoole/swoole-src#6328.

The new isolated tests also exposed an existing test-order failure. Building parallel test-command arguments called Options::fromConsoleInput(), which replaced PHPUnit's active configuration with the command's configuration. Later isolated tests then tried to require a temporary Testbench autoloader that another test had removed. The shared command now reads the cache setting without initializing a runner; the parallel-runner test fixture also preserves the active configuration. Both paths have explanatory comments and regression assertions. Default XML discovery now follows PHPUnit's filename order, including phpunit.dist.xml, and explicit relative and absolute configuration paths are preserved.

Explicit configuration directories use the same filename order, and missing explicit paths are passed to PHPUnit to reject instead of silently selecting the default config. Both test commands recognize -c as --configuration and remove the original selector before forwarding arguments, so it cannot override the generated profile configuration or select a different file for the cache check. Testbench's unused --custom-argument flag is removed; this difference is documented beside the signature and in the Testbench and porting documentation.

The suite sends displayed PHP errors to stderr so PHPUnit includes isolated-child startup errors in its failure messages. The original setting sent them to stdout, hiding the missing-bootstrap fatal behind “ended unexpectedly.” This was reproduced and verified using the exact failing CI image.

Regression coverage includes:

  • Headless 80×24 cache initialization with the environment left unset. The cache is inspected before calling Symfony's accessors so its native width fallback cannot conceal another failed probe.
  • Explicit and individually supplied dimensions, plus values temporarily removed and restored through the framework's environment helper.
  • testEnvironmentFileOverridesEarlierTerminalDefaults: a later environment file wins after fallbacks have already been initialized.
  • Testbench application creation and application-free tests in both coroutine modes.
  • A real PTY child whose dimension cache remains untouched.
  • Initialization before provider registration in both framework kernels.
  • PHPUnit configuration preservation when preparing a parallel command or runner fixture, plus cache-option forwarding, default XML discovery, profiling, and explicit configuration paths.

Validation completed sequentially:

  • composer fix passed for the terminal-cache change: formatting, source and type-fixture PHPStan, the full parallel framework suite, Testbench package mode, and both dogfood suites.
  • Full framework suite after the configuration and stderr fixes: 52,899 tests, 188,711 assertions; 4,180 skips.
  • Testbench package-mode suite: 647 tests, 2,103 assertions; 3 skips.
  • Testbench dogfood: 6 tests, 13 assertions. AI dogfood: 12 tests, 376 assertions.
  • The three terminal suites also passed with --no-configuration --bootstrap vendor/autoload.php, without PHPUnit XML or the cleanup extension.
  • Focused PHPUnit and formatting checks were rerun after the final test refinements; git diff --check is clean.
  • After the final configuration-shortcut fix: source and type-fixture PHPStan passed; Testing passed with 696 tests and 3,017 assertions; Testbench package mode passed with 647 tests, 2,103 assertions, and 3 skips.
  • The five-test sequence reproducing the CI failure passes in the exact failing CI image. Both configuration-preservation assertions were first observed failing against the old implementations.

The environment-mutation regressions were observed failing against the previous implementation before the cache change. No dependency manifests or lockfiles are changed.

Note

Initialize headless terminal dimensions via Symfony cache instead of env vars

  • ConfigureTerminalDimensions::configure now sets Symfony Console's cached width/height fallbacks through reflection when output is headless. It no longer writes COLUMNS/LINES into the process environment, and existing cached values are preserved.
  • The bootstrapper runs during Testbench application creation and before provider registration, so tests get deterministic terminal dimensions without env vars. CreatesApplication.php
  • Test commands now resolve PHPUnit configuration from explicit files or directories, search default filenames in a defined order, and accept -c as a shortcut for --configuration. Forwarded args and ParaTest construction can no longer override the selected configuration. TestCommandBase.php
  • Behavioral Change: terminal fallbacks live in Symfony's cache, so restoring or setting COLUMNS/LINES env vars still wins; package:test no longer declares the custom-argument option, and child PHP errors go to stderr per phpunit.xml.dist.

Macroscope summarized 191c811.

Summary by CodeRabbit

  • New Features
    • Test commands now accept -c as a shortcut for --configuration. Configuration can be selected by file or directory, with default PHPUnit configuration files searched in order.
  • Bug Fixes
    • Headless test runs now use fallback terminal dimensions of 80×24 when dimensions are unavailable, while preserving dimensions provided by the environment.
    • Test configuration and cache-directory options are handled more consistently, including when running tests in parallel or profiling.
  • Documentation
    • Updated package-testing guidance to explain configuration options and remove an unused argument.

…tion boot

Testbench creates applications through its own bootstrap sequence and its
console kernel has no bootstrappers. As a result, package tests missed the
terminal initialization that normal HTTP and console applications already
receive. Programmatic console calls could repeatedly spawn stty inside
Swoole's hooked subprocess wait when no terminal dimensions were exported.

Run ConfigureTerminalDimensions after environment loading and before
configuration, matching the normal kernels. This also initializes console
output before database test traits run commands during setup and teardown.
Preserve dimensions loaded from the application's environment file.

Add isolated regressions that clear inherited dimensions before refreshing
the application and verify both defaults and environment-file values. The
missing-default regression failed before the bootstrap call was added.

Refs #686
Reuse the existing headless terminal policy through an application-free
configure() method. Invoke it before test execution enters either the
framework coroutine or a test-managed coroutine, so unit tests that never
boot an application receive the same protection as application tests.
Symfony Process does not reliably forward putenv-only values, so each test
process establishes its own defaults without requiring PHPUnit XML pins.

Keep explicit dimensions and real TTY behavior unchanged. Document why
failed Symfony dimension probes repeat, their exposure to Swoole's child
process wait bug, and the process-wide lifetime of environment defaults.

Add isolated regressions for both coroutine modes, partial dimension
configuration, and a real pseudo-terminal. The unit-test regressions failed
before initialization was added and pass without PHPUnit configuration.
Retain the components suite's forced XML dimensions solely for consistent
console-output assertions and replace the old hang-prevention comment.

Refs #686
Related: #454
A unit test can initialize COLUMNS and LINES before an application test
loads its environment file. Dotenv treats those process values as external,
so the later application's configured dimensions are ignored. This makes
console formatting depend on test order even with normal framework state
cleanup enabled.

Track only the two variable names populated by ConfigureTerminalDimensions
and remove them through the existing after-each-test subscriber. Values
provided before initialization, including shell and PHPUnit XML settings,
are never owned or removed. Clear the ownership set after cleanup so a
subsequent test's explicit dimensions survive later cleanup passes.

Add an isolated regression that failed before the fix and verifies both
owned-default removal and explicit-value preservation. Also verify the
unit-before-application reproduction now honors the application's env-file
width without terminal settings in PHPUnit XML.

Refs #686
@coderabbitai

coderabbitai Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 2de4209 and 191c811.

Files selected for processing: 16
  • phpunit.xml.dist
  • src/docs/porting-from-laravel.md
  • src/docs/testbench.md
  • src/foundation/src/Bootstrap/ConfigureTerminalDimensions.php
  • src/foundation/src/Testing/Concerns/RunTestsInCoroutine.php
  • src/testbench/README.md
  • src/testbench/src/Concerns/CreatesApplication.php
  • src/testbench/src/Foundation/Console/TestCommand.php
  • src/testing/src/Console/TestCommand.php
  • src/testing/src/Console/TestCommandBase.php
  • tests/Foundation/Bootstrap/ConfigureTerminalDimensionsTest.php
  • tests/Foundation/Fixtures/configure-terminal-dimensions.php
  • tests/Testbench/TerminalDimensionsTest.php
  • tests/Testing/Console/TestCommandTest.php
  • tests/Testing/ParallelRunnerTest.php
  • tests/Testing/TerminalDimensionsTest.php

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

The PR changes terminal sizing for non-TTY test processes and wires that setup into test execution and Testbench application creation. It also updates test-command configuration aliases, file selection, argument forwarding, ParaTest cache handling, documentation, and related tests.

Changes: Headless terminal dimensions

Layer / File(s) Summary
Configure Symfony terminal dimensions
src/foundation/src/Bootstrap/ConfigureTerminalDimensions.php
The bootstrapper adds configure(). For non-TTY output, it sets Symfony Terminal’s cached width and height to 80 and 24 only when each cached value is null.
Invoke terminal setup during tests and application creation
src/foundation/src/Testing/Concerns/RunTestsInCoroutine.php, src/testbench/src/Concerns/CreatesApplication.php
Test invocations configure terminal dimensions before coroutine handling or PHPUnit delegation. Testbench application creation invokes the bootstrapper after environment resolution and before configuration.
Validate terminal sizing across test environments
tests/Foundation/Bootstrap/ConfigureTerminalDimensionsTest.php, tests/Foundation/Fixtures/configure-terminal-dimensions.php, tests/Testbench/TerminalDimensionsTest.php, tests/Testing/TerminalDimensionsTest.php, phpunit.xml.dist
Tests cover cached and environment dimensions, PTY behavior, bootstrap order, coroutine modes, and Testbench environment loading. phpunit.xml.dist changes display_errors to stderr; the terminal-dimensions values remain unchanged.

Test-command configuration handling

Layer / File(s) Summary
Add the configuration shortcut and update guidance
src/testing/src/Console/TestCommand.php, src/testbench/src/Foundation/Console/TestCommand.php, src/docs/porting-from-laravel.md, src/docs/testbench.md, src/testbench/README.md
Both test commands use -c as the shortcut for --configuration. The documentation describes the alias and removes references to the unused --custom-argument option.
Resolve and forward PHPUnit configuration
src/testing/src/Console/TestCommandBase.php
Configuration lookup supports ordered default filenames, explicit paths, and directories. Argument filtering handles the configuration shortcut. ParaTest cache handling checks explicit options and the selected XML configuration.
Test configuration selection and runner options
tests/Testing/Console/TestCommandTest.php, tests/Testing/ParallelRunnerTest.php
Tests cover configuration option forms, path resolution, default precedence, profiling, cache-directory options, and preserving active PHPUnit configuration when constructing ParaTest options.

Priority: Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix · Severity of issue fixed: Medium

Merge Risk: Minimal · up to 191c8

The terminal initialization and its regression test cover the supported test workflow; no identified issue needs resolution before merge.

Pre-merge checks: Passed 2 · Failed 3

Failed Failed checks: 3 warnings

Check name Status Explanation Resolution
Linked Issues check Warning #686 requires headless Testbench applications to provide COLUMNS=80 and LINES=24, as shown by its expected behavior and reproduction. ConfigureTerminalDimensions::configure() now writes only Sym… Either update the linked issue's requirements to accept Symfony cache initialization as the replacement behavior, or set the required missing COLUMNS and LINES environment variables in the headless Testbench path and retain regression c…
Out of Scope Changes check Warning The terminal bootstrap changes, Testbench integration, per-process initialization, and related regression tests directly support #686. The changes to PHPUnit configuration selection and forwarding in … Remove the independent PHPUnit/Testbench command behavior changes from this pull request, or track them in a separate linked issue and pull request. Keep only changes required to initialize headless terminal dimensions and to test that beha…
Docstring Coverage Warning Docstring coverage is 45.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 40 functions across 12 files. (4 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 2 passed
Check name Status Explanation
Title check Passed The title clearly summarizes the primary change: initializing headless terminal dimensions in Testbench and unit tests.
Description check Passed The description clearly explains the problem, implementation, behavior, regression coverage, related scope, and extensive verification results. It does not explicitly select a contribution type or mar…
Full details: Description check

Explanation

The description clearly explains the problem, implementation, behavior, regression coverage, related scope, and extensive verification results. It does not explicitly select a contribution type or mark the submission checklist, but the required technical content is otherwise substantially complete.

Full details: Linked Issues check

Explanation

#686 requires headless Testbench applications to provide COLUMNS=80 and LINES=24, as shown by its expected behavior and reproduction. ConfigureTerminalDimensions::configure() now writes only Symfony Terminal::$width and Terminal::$height through reflection. It never writes those environment variables. The implementation does satisfy the no-stty goal, preserves populated cache entries, runs after environment loading in CreatesApplication, and initializes application-free test processes, but it does not meet the issue's explicit environment-variable behavior.

Resolution

Either update the linked issue's requirements to accept Symfony cache initialization as the replacement behavior, or set the required missing COLUMNS and LINES environment variables in the headless Testbench path and retain regression coverage for that behavior.

Full details: Out of Scope Changes check

Explanation

The terminal bootstrap changes, Testbench integration, per-process initialization, and related regression tests directly support #686. The changes to PHPUnit configuration selection and forwarding in TestCommandBase, removal of Testbench's --custom-argument, parallel-runner configuration preservation, and the general isolated-child display_errors change address separate test-command behavior. The supplied evidence describes these as fixes for an existing test-order and error-reporting problem, not as requirements for terminal-dimension initialization.

Resolution

Remove the independent PHPUnit/Testbench command behavior changes from this pull request, or track them in a separate linked issue and pull request. Keep only changes required to initialize headless terminal dimensions and to test that behavior.

Full details: Docstring Coverage

Explanation

Docstring coverage is 45.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 40 functions across 12 files. (4 skipped: 4 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Initialize headless terminal dimensions in Testbench and unit tests

🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Initialize headless dimensions during Testbench boot and test invocation to avoid repeated
 terminal probes under Swoole.
• Preserve explicit dimensions and clear only framework-supplied defaults after each test.
• Add regressions for environment-file precedence, coroutine modes, cleanup, and real terminals.
Diagram

graph TD
  TB["Testbench boot"] --> Load["Environment load"] --> Policy["Dimension policy"] --> Vars[("Process environment")] --> Terminal["Symfony Terminal"]
  Invoke["Test invocation"] --> Policy
  Cleanup["After-test cleanup"] -->|"flush defaults"| Policy
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Rely on PHPUnit XML dimensions
  • ➕ Keeps initialization out of the test lifecycle.
  • ➖ Does not protect downstream suites without those settings.
  • ➖ Forced values can conceal application environment-file dimensions.
2. Initialize solely in a PHPUnit extension
  • ➕ Provides one test-process entry point.
  • ➖ Early defaults could precede and mask Testbench environment loading.
  • ➖ Application boot outside that entry point would still need protection.

Recommendation: Keep the shared, application-free policy and invoke it at both lifecycle boundaries. This protects unit tests and Testbench boot without requiring XML settings, while tracked cleanup allows a later application to load its own dimensions.

Files changed (10) +254 / -8

Bug fix (4) +45 / -2
ConfigureTerminalDimensions.phpExpose and track headless dimension initialization +37/-2

Expose and track headless dimension initialization

• Extracts the bootstrap behavior into an application-free static method that supplies only missing dimensions outside a real TTY. Tracks supplied variables for selective cleanup and documents the repeated Symfony probes and Swoole wait issue.

src/foundation/src/Bootstrap/ConfigureTerminalDimensions.php

RunTestsInCoroutine.phpInitialize dimensions before test-method invocation +5/-0

Initialize dimensions before test-method invocation

• Calls the shared dimension policy in each test process before either the framework coroutine path or direct invocation. This also covers tests that start their own coroutines without booting an application.

src/foundation/src/Testing/Concerns/RunTestsInCoroutine.php

CreatesApplication.phpRun terminal bootstrapper during Testbench application creation +2/-0

Run terminal bootstrapper during Testbench application creation

• Runs the existing bootstrapper after application environment loading and before configuration. Testbench applications now receive headless defaults without overriding dimensions supplied by their environment files.

src/testbench/src/Concerns/CreatesApplication.php

AfterEachTestSubscriber.phpClear framework-supplied dimensions after each test +1/-0

Clear framework-supplied dimensions after each test

• Adds terminal-dimension state flushing to the existing framework cleanup sequence. Explicit process dimensions remain outside the bootstrapper’s tracked defaults.

src/testing/src/PHPUnit/AfterEachTestSubscriber.php

Tests (5) +207 / -0
ConfigureTerminalDimensionsTest.phpCover partial dimensions and real-terminal behavior +42/-0

Cover partial dimensions and real-terminal behavior

• Adds separate-process cases verifying that either explicit dimension is preserved independently. A PTY-backed child test verifies that initialization leaves an actual terminal’s dimensions unset.

tests/Foundation/Bootstrap/ConfigureTerminalDimensionsTest.php

configure-terminal-dimensions.phpProvide a PTY child for terminal detection +11/-0

Provide a PTY child for terminal detection

• Invokes the application-free initializer and reports TTY status and resulting dimensions. The bootstrap test uses it to check real-terminal behavior outside Swoole’s hooked subprocess wait.

tests/Foundation/Fixtures/configure-terminal-dimensions.php

TerminalDimensionsTest.phpRegress Testbench defaults and environment precedence +69/-0

Regress Testbench defaults and environment precedence

• Creates isolated Testbench applications with inherited dimensions removed to verify headless defaults. A second case loads dimensions from a temporary application .env file and checks that they prevail.

tests/Testbench/TerminalDimensionsTest.php

AfterEachTestSubscriberTest.phpVerify selective terminal-default cleanup +37/-0

Verify selective terminal-default cleanup

• Exercises framework cleanup after setting one explicit dimension and one framework default. Confirms cleanup removes the default, preserves the explicit value, and does not remove a later explicit value.

tests/Testing/PHPUnit/AfterEachTestSubscriberTest.php

TerminalDimensionsTest.phpRegress application-free initialization in both test modes +48/-0

Regress application-free initialization in both test modes

• Clears inherited dimensions before invocation and verifies 80×24 defaults in a unit test without an application. Runs the check with and without the framework-managed coroutine.

tests/Testing/TerminalDimensionsTest.php

Documentation (1) +2 / -6
phpunit.xml.distClarify why suite dimensions remain pinned +2/-6

Clarify why suite dimensions remain pinned

• Rewords the comment to identify stable console-output assertions as the reason for the existing 80×24 XML values. The settings themselves are unchanged.

phpunit.xml.dist

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Explicit dimensions disappear after tests ⊘ Outdated
Description
ConfigureTerminalDimensions::flushState() unsets every tracked variable without checking whether
its framework default has since been replaced. When configuration supplies COLUMNS=80 and a test
or application subsequently sets COLUMNS=200, after-test cleanup removes the explicit value as
well.
Code

src/foundation/src/Bootstrap/ConfigureTerminalDimensions.php[R63-64]

+        foreach (array_keys(static::$configuredVariables) as $name) {
+            putenv($name);
Evidence
Configuration records only variable names when writing defaults, while cleanup unsets those names
unconditionally. The after-test subscriber calls that cleanup, and its new regression test changes
an explicit value only after the first flush has already cleared the tracking.

src/foundation/src/Bootstrap/ConfigureTerminalDimensions.php[46-67]
src/testing/src/PHPUnit/AfterEachTestSubscriber.php[192-196]
tests/Testing/PHPUnit/AfterEachTestSubscriberTest.php[349-365]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Cleanup removes an explicit dimension that replaced a framework-supplied default before the test ended.
## Fix Focus Areas
- src/foundation/src/Bootstrap/ConfigureTerminalDimensions.php[46-67]
## Recommended Fix
Before unsetting a tracked variable, verify that its current value is still the default the framework supplied; preserve replacements and then clear the tracking state.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Testbench ignores configured dimensions ✓ Resolved
Description
createApplication() invokes the terminal bootstrapper after the Testbench application has disabled
the putenv adapter, so getenv() cannot see dimensions loaded from its .env or environment
array. When those settings exist only in Testbench’s environment repository, the bootstrapper
installs 80 and 24 in the process environment instead, which is what terminal consumers then
receive.
Code

src/testbench/src/Concerns/CreatesApplication.php[271]

+            $app->make(ConfigureTerminalDimensions::class)->bootstrap($app);
Evidence
The public Testbench container creates a Foundation application using this bootstrap sequence. Its
environment resolver disables putenv before loading file and array values, whereas the added
bootstrapper checks only getenv() and writes defaults there.

src/testbench/src/functions.php[54-87]
src/testbench/src/Foundation/Application.php[359-376]
src/testbench/src/Foundation/Bootstrap/LoadEnvironmentVariablesFromArray.php[25-40]
src/support/src/Env.php[104-151]
src/foundation/src/Bootstrap/ConfigureTerminalDimensions.php[40-54]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Testbench application creation supplies process defaults even when its environment repository contains explicit terminal dimensions.
## Fix Focus Areas
- src/testbench/src/Concerns/CreatesApplication.php[268-272]
- src/testbench/src/Foundation/Application.php[363-376]
- src/foundation/src/Bootstrap/ConfigureTerminalDimensions.php[40-54]
## Recommended Fix
Before supplying defaults on this Testbench path, account for dimensions loaded without the `putenv` adapter and make explicit values available to terminal consumers. Add coverage using the public Testbench application resolver with configured dimensions and no inherited process dimensions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can choose which labels appear on a finding, and whether they show icons or text

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/foundation/src/Bootstrap/ConfigureTerminalDimensions.php Outdated
Comment thread src/testbench/src/Concerns/CreatesApplication.php
@greptile-apps

greptile-apps Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5 Tier: plus

[Medium impact] The PR appears safe to merge; both previous findings are addressed, and no new actionable issues were found.

Summary

The PR initializes headless terminal dimensions in Testbench and application-free tests.

  • Headless test runs get cached terminal dimensions before console work.
  • Test commands accept -c and carry the chosen PHPUnit configuration into test runs.

No new actionable issues were found. git diff --check passed. Tests were not run during this review.

Reviews (2) · Last reviewed commit: "test: report isolated child startup erro..." · Reviewed by Greptile

Comment thread src/foundation/src/Testing/Concerns/RunTestsInCoroutine.php
Comment thread src/foundation/src/Bootstrap/ConfigureTerminalDimensions.php Outdated
Writing fallback COLUMNS and LINES into the process environment makes
immutable dotenv loading mistake framework defaults for explicit settings.
Tracking variable names for cleanup also removes values restored by test
environment helpers. Those values share a channel, so cleanup cannot
reliably infer which code owns them.

Seed only the missing width and height entries in Symfony Terminal's
process-local cache when stdout is headless. Symfony continues to prefer
explicit environment values on every lookup, including values loaded after
initialization. Existing cached dimensions and real terminals are left
alone. No environment writes, ownership state, or test cleanup are needed.

Use reflection at this single boundary because Symfony exposes no cache
setter. Document the reason and test both cache entries before accessing
terminal dimensions, so Symfony's native width fallback cannot conceal a
failed probe or a dependency change. Remove the obsolete cleanup hook and
its test, and retain per-process initialization for Testbench and unit tests.

Cover missing, partial, explicit, temporarily restored, and later dotenv
values, real PTY behavior, both coroutine modes, and initialization before
provider registration. Terminal regressions pass without PHPUnit XML or
the cleanup extension. Full composer fix passed, including formatting,
source and type-fixture analysis, framework tests, Testbench package mode,
and both dogfood suites. Focused checks passed after the final test-only
refinements.

Refs #686
The provider-order regression already verifies that both kernels include
the terminal bootstrapper before comparing its position with provider
registration. Remove the separate inclusion test, which checks the same
condition without covering another behavior.

The focused bootstrap suite passes with 7 tests and 37 assertions.
Targeted formatting and git diff --check are clean.
Building parallel command arguments called ParaTest's Options factory only
to inspect its cache setting. That factory replaces PHPUnit's active
configuration, causing later isolated tests to load the command fixture's
bootstrap instead of the suite bootstrap. Test order determined whether
the temporary autoloader still existed, producing intermittent CI failures.

Inspect the CLI cache option and selected XML directly, leaving runner
initialization to the child process. Preserve explicit cache paths and
explain the configuration side effect beside the argument-building code.

Use PHPUnit's default configuration filename order, search explicit
configuration directories, preserve relative and absolute paths, and
forward missing explicit paths for PHPUnit to reject instead of silently
running a different configuration.

Make -c the configuration shortcut for both application and package tests.
Remove the original short or long selector before forwarding so it cannot
override the generated profile configuration. Remove Testbench's unused
custom-argument flag and document that difference in the source, README,
command reference, and porting guide.

Regression coverage preserves PHPUnit configuration identity and exercises
cache options, discovery, missing paths, profiling, and real Symfony input
for both commands. The relevant failures were reproduced before fixing
them. Validation: command tests (31 tests, 165 assertions), Testing suite
(696 tests, 3017 assertions), Testbench package mode (647 tests, 2103
assertions, 3 skips), PHPStan source/type fixtures, and changed-file style
checks all pass. The isolated-test CI failure sequence also passes.
Construct ParaTest Options directly with PHPUnit's active configuration
instead of calling the CLI factory, which replaces the process-wide
configuration and emits a runner-configuration event during a test.

Keep the requested process count and fixture settings explicit through
named constructor arguments. Explain both the avoided global side effect
and the dependency on ParaTest's internal constructor beside the fixture.

Add an identity assertion to the existing process-token test so returning
to the mutating factory fails immediately. The assertion failed before
the change. Validation: all 12 parallel-runner tests (48 assertions) and
the affected Testing and Testbench package-mode suites pass; style and
static analysis checks are clean.
PHPUnit sets display_errors to stderr before booting isolated children,
but the suite XML changed it back to on, which sends errors to stdout.
PHPUnit does not include that stdout in an unexpected-child-exit error.
With error logging disabled in CI, a missing bootstrap therefore produced
only "Test was run in child process and ended unexpectedly".

Keep displayed errors on stderr and explain why in the suite configuration.
This exposes the actual startup error without changing terminal settings
or adding custom child-process reporting code.

Verified with the exact failing CI image: the old source and a five-test
sequence produce two generic child-exit errors with display_errors=on;
changing only this setting exposes both missing-autoloader fatal messages.
The complete fixed sequence passes. The full four-worker framework suite
with stderr enabled passes: 52899 tests, 188711 assertions, 4180 skips.
@binaryfire

Copy link
Copy Markdown
Member Author

Follow-up to the review and summary: the final implementation is pushed at 191c811, and both inline findings have individual replies.

The cleanup finding was correct. Terminal defaults now live only in Symfony's cache; environment writes, ownership tracking, and cleanup are removed. Regression coverage verifies that restored explicit dimensions survive and that later environment files can supply dimensions.

The proposed bridge from a Testbench environment repository with putenv disabled into the process environment is not appropriate: disabling that adapter deliberately keeps those channels separate, while Symfony reads getenv(). The new initializer does not write process defaults, and explicit process dimensions still take precedence.

The PR description now includes the final design, the isolated-process CI root cause and fix, and verification results.

@binaryfire

Copy link
Copy Markdown
Member Author

Both findings in the Greptile review were confirmed and fixed in 4d447c6, now included in the pushed head 191c811. Each inline finding has an individual reply.

Fallback dimensions are stored in Symfony Terminal's cache without changing the environment. This removes both causes: later dotenv loads are no longer masked, and there is no ownership cleanup that can delete restored explicit values. Regression tests exercise both sequences. No additional environment-restoration mechanism is needed.

@binaryfire

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@binaryfire

Copy link
Copy Markdown
Member Author

Reviewed the final pre-merge warnings:

  • The cache-only behavior is intentional. The issue is the blocking terminal probe in headless tests; setting Symfony’s fallback dimensions fixes that without exporting defaults into the process environment or masking dimensions loaded later. Applications and packages need no XML workaround. The PR description and regression tests document this behavior.
  • The test-command changes fix the PHPUnit configuration corruption that caused the isolated child-process failures discovered while validating this PR, together with the affected configuration selection and forwarding paths. The unused package flag removal and shared -c behavior are deliberate, approved changes. Sending PHP errors to stderr preserves the child failure diagnostics. These fixes will remain together as documented in the PR description.
  • We do not add docblocks to test methods merely to meet a coverage percentage; the repository explicitly excludes test methods from the method-docblock requirement.

No source changes are needed for these three warnings.

@binaryfire

Copy link
Copy Markdown
Member Author

The remaining PHP 8.5 failure is a pre-existing coroutine timeout race, unrelated to this PR. The timeout test and enforceCoroutineTestTimeLimit() are unchanged from 0.4.

The coroutine deadline can enter its timeout catch block just before PHPUnit's native SIGALRM arrives. That signal then throws from pcntl_alarm(0) inside the catch block, outside the protected try, terminating the child process instead of reporting the normal timeout.

I reproduced the same failure on the base commit 2de420919 in the exact PHP 8.5 CI image, using diagnostic timing controls to make the coroutine deadline win and widen the cancellation window. Forty ordinary base runs passed, consistent with the timing-dependent CI result. Both PHP versions passed on the primary PR, and PHP 8.4 passed here.

This PR is being merged with that unrelated failure understood. The timeout fix will be handled on the maintenance branch.

@binaryfire
binaryfire merged commit ea62d26 into 0.4 Oct 24, 2026
52 of 53 checks passed
Sign up for free to 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.

Testbench never runs ConfigureTerminalDimensions, so package tests spawn stty on every console call

1 participant