Skip to content

The suite drives /bin/sh at 98 sites, so every test depends on the host shell and none can run where there is none #249

Description

@vyncint

Today — the integration suite spawns /bin/sh (or sh) at 98 sites across 17 of its 20 test files, each with a printf script as the program under test — 208 printf invocations in all. Measured against 0.9.0:

$ grep -c 'spawn("/bin/sh")\|spawn("sh")' crates/termlens/tests/*.rs | awk -F: '{s+=$2} END {print s}'
98

Every escape-sequence test is written as printf '\033(0lqk'; every "print, then hold the terminal open" is printf …; read _. The five fixtures in fixtures/ cover only the shapes that need a real event loop: crossterm input, resize, images, width.

Why it is worth fixing — three reasons, in order of weight.

The shell is a variable in every test that is not about the shell. Which sh runs — dash on Debian, bash in POSIX mode on macOS, busybox elsewhere — decides how printf treats \033 and %s, what read _ does at EOF, and how fast while :; do …; sleep 0.01; done iterates. The stress workflow caught exactly this in #243: a test's premise held only until the loop's sleep took longer to spawn than the stillness the test was measuring. The shell's process model leaked into a wait-semantics test.

It is why the suite cannot run where there is no /bin/sh. Windows is the obvious case — #149 names this as the first of its four steps — but so is any minimal container image.

And it is a barrier for contributors who think in Rust: printf '\033)0\016lqk\017 lqk \016x\017' asks its reader to decode octal escapes and shell quoting before the terminal semantics the test is about.

Fix — one more fixture binary, fixtures/emit (name open), replacing the sh -c 'printf …; read _' idiom with a small argument language and no shell in between:

emit 'ESC ( 0' lqk NL 'ESC ( B' --wait      # print, then hold stdin open until Enter
emit --raw 'caf\xe9' ' done' NL             # raw bytes, for the non-UTF-8 cases
emit first --sleep 2s ' second' --wait      # timed output, for the wait tests

Concretely: an enum Step { Text, Esc, Csi, Raw, Sleep, Wait, Exit(code) } parsed from argv, written to stdout with one write_all per step and a flush, and --wait reading one line from stdin the way every fixture already does (docs/DESIGN.md §2, the instant-exit caveat). Then a tests/common helper emit(steps) -> Terminal, and a mechanical migration of the sh(...) helpers one test file per commit, so a reviewer diffs one file's scripts against their emit form. Tests that are about a shell (stty, a read loop that echoes) keep /bin/sh, with a comment saying why.

Two rules for the fixture's //! header: no timing inside the binary except the explicit --sleep, and std only, so the fixture cannot become a second thing the suite tests.

Done when — every printf-only script in crates/termlens/tests/ goes through the fixture; grep -c 'spawn("/bin/sh")' crates/termlens/tests/*.rs counts only tests that are about a shell, each with a comment saying so; the fixture is std-only with its steps documented in its header; and the stress workflow passes on the migrated suite.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is needed

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions