Skip to content

feat: add native launch options for test sessions - #286

Draft
Alexey Zhokhov (donbeave) wants to merge 10 commits into
microsoft:mainfrom
tailrocks:goal/u08-launch-options
Draft

Alexey Zhokhov (donbeave) wants to merge 10 commits into
microsoft:mainfrom
tailrocks:goal/u08-launch-options

Conversation

@donbeave

@donbeave Alexey Zhokhov (donbeave) commented Oct 25, 2026 •

Copy link
Copy Markdown

Title: feat: add native launch options for test sessions

Summary

Adds LaunchOptions (native program, arguments, environment control) and
Session::spawn_with, wiring native launches through the engine spawn and
restart paths so recorded sessions can start real binaries. The launch
probe used by tests is built on demand and asserts the real PATH.

The latest update on top of the original PR head:

  • Failed native spawns now carry the same structured failure metadata as
    the recorded path, via one shared finalization helper under the same
    panic boundary.
  • Session setup shares one initializer between open and spawn_with so
    timestamp stamping cannot diverge again.
  • Probe reuse is source-aware: a binary older than its fixture source or
    manifest rebuilds. Docs clarify the inspection snapshot type and which
    run options spawn_with ignores.

Head: 8b4450178025bb8976604ad10b830ce7f2ffac6c
(launch.rs, session.rs, engine.rs, terminal/pty.rs)

Testing

Focused suites, run locally with nextest (default features):

  • cargo nextest run -p tui-test-rs --test launch_options:
    15 tests run, 15 passed, 0 skipped
  • cargo nextest run -p tui-test-rs --test session_spawn_with:
    2 tests run, 2 passed, 0 skipped

Known limitations

  • Native-spawn and PATH behavior differ by OS; Windows and macOS results
    are pending upstream CI on this PR.
  • Merging needs upstream maintainer review and green CI.

…ronment

Private-field options with inherited/cleared modes, explicit removals, and overrides. resolve() applies inherited-or-empty, removals, the mandatory TERM default, then overrides, keeping native strings and validating keys, values, and args to typed usage errors.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Spawn with native arguments and a complete resolved environment. The transport base environment is cleared before the resolved values are filled, so cleared launches stay cleared apart from transport defaults.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Constructor next to Session::open. Spawns from a resolved launch without shell parsing or Unicode replacement; restart reuses the resolved values.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Add SpawnCommand::Launch carrying the resolved launch, public Engine::spawn_with and Engine::spawn_with_context following the recorded lifecycle protocol, and restart reuse of resolved values. RunOptions program, args, env, and cwd stay untouched and are ignored for launches.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Engine-driven PTY tests with a controlled probe binary for native arguments and dedicated driver processes for parent environment changes. The parallel runner environment is never changed.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
A bare cargo test --test selection never builds examples, and the shared target cache can drop the probe binary. The test now builds it into an isolated target directory when missing.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Move the harness-free probe to tests/fixtures/launch_probe.rs as a
[[test]] target (harness = false, test = false) so it no longer ships
as a user-facing example. The on-demand build keeps its isolated
target dir, own lock, and --offline; only the target selector changes
(--test) plus hash-suffixed artifact discovery. u08_j now asserts the
PATH precondition instead of returning silently.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
R10: add public Session::spawn_with delegating to
Engine::spawn_with_context with the session execution context, so
external consumers can launch natively without touching Engine.

R11: record the frozen absolute cwd on the resolved launch used for
each native spawn (including the default), so restart reuses the
first directory instead of re-resolving absent/relative cwds against
a moved parent.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
The native reader (Session::spawn_with) processed PTY bytes but never
recorded TermState::last_output, while the legacy reader did. Byte-quiet
waits reset their timer only on Some observations, so natively spawned
output was invisible to them: a readiness marker confirmed processed by
WaitLocator still left the observation timestamps at None, pre- and
post-restart.

Record the monotonic read time on every nonempty successful PTY read in
both readers, first-after-lock like the integrated tree, and serve it via
Session/Engine last_output_time for byte-quiet waits. Restart spawns a
fresh session, so the restarted generation re-stamps on its own output.

Regression test: native_spawn_stamps_output_observations_across_restart
spawns a marker-printing stdin-blocking child natively, proves processing
with the public WaitLocator wait (no shell timing), and asserts Some
timestamps on both generations.
spawn_with_context duplicated the executor bookkeeping but returned right
after operation_history.finish, so a failed native spawn carried no
structured failure metadata while the same failure through run did. Route
both paths through the shared finalize_request_error helper and run the
native spawn under the same panic boundary.

Share the reader/watcher/state init between Session::open and
Session::spawn_with (new_term_state + finish_session) so the timestamp
stamping cannot diverge again. Make probe reuse source-aware: a hashed
binary older than the fixture source or manifest rebuilds. Clarify that
ResolvedLaunch is an inspection snapshot the public spawn API does not
accept, and document the RunOptions fields spawn_with ignores.

Regression tests: u08_l asserts the configured operation name and
structured failure metadata on both entry points; the uses_session_context
test now proves the session context reaches the spawn request itself.
@donbeave Alexey Zhokhov (donbeave) changed the title Add explicit child launch settings feat: add native launch options for test sessions Oct 26, 2026
Alexey Zhokhov (donbeave) added a commit to tailrocks/tuiscotti that referenced this pull request Oct 27, 2026
Migrate tuiscotti onto the tui-test engine as its terminal runtime.

Upstream (`tui-test`) owns the terminal engine: child processes,
emulation, sessions, input, waiting, rendering, fonts, and pixel
comparison. Tuiscotti keeps the application testing layer: pure-view
capture, versioned samples, frozen baseline checks, explicit approval,
static reports, and the offline CLI (`check` / `accept` / `render` /
`report`, exit codes 0/1/2/3). The previous built-in live engine,
renderer, recorder, and second approval engine are removed, leaving the
two shipping crates (`tuiscotti`, `tuiscotti-cli`).

## Provisional fork dependency

The new `tui-test` APIs this PR needs are still unmerged upstream, so
this branch temporarily depends on a pinned fork via `[patch]`:

- `donbeave/tui-test`, branch `goal/qual-stack-r5`, pinned at
`81da26fa9dc29664a897dc802be816a4432c0618`

The pin is provisional. Once the upstream PRs below merge and a registry
release contains the new APIs, the `[patch]` is removed in favor of the
registry version.

## Upstream PRs (all 11, all open)

| PR | Change | Head SHA | Status |
|----|--------|----------|--------|
|
[microsoft/tui-test#276](microsoft/tui-test#276)
| Explicit read-only snapshot policy |
`fc0150160c25ea83532b18f9507f9f1a155b91e4` | Reworked + verified |
|
[microsoft/tui-test#277](microsoft/tui-test#277)
| RGBA image comparison for pixel equality |
`4fb316aa56c65cc659b4a43ee4921c45e33cecc0` | Approved as-is;
dependency-only, no rework |
|
[microsoft/tui-test#278](microsoft/tui-test#278)
| Owned terminal snapshot API |
`334c474d1c621c373333e9dc24e549623a133570` | Reworked + verified |
|
[microsoft/tui-test#279](microsoft/tui-test#279)
| Raw byte input for Rust sessions |
`3b1439c313e88d0a8867dfbcdadabb28624dc5b5` | Reworked + verified |
|
[microsoft/tui-test#280](microsoft/tui-test#280)
| Public rendering for offline snapshots |
`378036846a4e85174104e0f9c153b0a0f8f84161` | Reworked + verified |
|
[microsoft/tui-test#281](microsoft/tui-test#281)
| Configurable quiet waits and request cancellation |
`103d296494f251921f8cd61d3f6ce2f7e152690d` | Reworked + verified |
|
[microsoft/tui-test#282](microsoft/tui-test#282)
| Mode-aware programmatic input |
`212ba1db26987a45537708e98c82db77c90bb734` | Reworked + verified |
|
[microsoft/tui-test#283](microsoft/tui-test#283)
| Explicit underline colors in rendering |
`fff55c122ae210efbe9a68d1ac8683b1a8856d` | Reworked + verified |
|
[microsoft/tui-test#284](microsoft/tui-test#284)
| Explicit pinned-font rendering |
`2bdaa3f8d3692514a6cb9c1ab844e880ca8d0f23` | Reworked + verified |
|
[microsoft/tui-test#285](microsoft/tui-test#285)
| Combined and wide cell text rendering |
`edf2b525d6f8a5710cd56440545437ff4f146727` | Reworked + verified |
|
[microsoft/tui-test#286](microsoft/tui-test#286)
| Explicit child launch settings |
`d5d9bbab9ac4e8a5e0562175497661b7b1a4b158` | Reworked + verified |

"Reworked + verified" means the PR went through a rework round after
independent review, and its per-task suites plus the integrated stack
re-ran green. #277 needed no changes — its head is unchanged since
review — so it is listed as a pure dependency.

Upstream merge order: #278 before #280 and #281; #280 before #283 and
#284; #284 before #285. #276, #277, #279, #282, and #286 are independent
and can merge in any order.

## Verification (at PR head `a056f8f60bae1bf81429b575076c86aed3e6717b`)

- Tests: 209/209 passing in both the default-features and
no-default-features lanes.
- `cargo fmt --check` clean; `cargo clippy --all-targets -D warnings`
clean (default and no-default features).
- Downstream CI: [run
40408516574](https://github.com/tailrocks/tuiscotti/actions/runs/40408516574)
(in progress, at `a056f8f`); prior run
[40390626472](https://github.com/tailrocks/tuiscotti/actions/runs/40390626472)
success at `9ae97ff`.

## Known limitations

- Windows/macOS runner coverage is still pending; platform-specific
behavior (for example raw-byte PTY paths) is unverified there.
- The upstream PRs currently report only CLA checks; Rust CI on the
upstream side still needs maintainer workflow approval, so upstream-side
proof rests on the recorded per-task suites plus this downstream
integration.
- No crates.io release contains the new APIs yet, so registry adoption
is blocked until the upstream PRs merge and release. The provisional
fork pin above supports merging this PR only; it is not a release path.

## Depends-on / merge order

This PR depends on all eleven upstream PRs merging (in the order noted
above) plus a registry release; only then does the provisional `[patch]`
come out. Nothing in this PR can ship to crates.io before that. Upstream
maintainers: the stack is additive-only, and the pinned fork branch
tracks the exact heads listed above.

---------

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Signed-off-by: Muse Code <muse-code@meta.com>
Signed-off-by: Muse Code <muse@example.local>
Co-authored-by: Muse Code <muse-code@meta.com>
Co-authored-by: Muse Code <muse@example.local>

This branch has not been deployed

No deployments
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.

1 participant