diff options
| author | user <user@clank> | 2026-08-17 14:13:44 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-17 14:13:44 +0200 |
| commit | e2eac8ef074d15cbff4844b956ddbe1031200dc8 (patch) | |
| tree | bb7eef6e3f543e38ced30675b350e22e50b03001 /tests | |
| parent | sl0ppty: a wrapped line is one line when you copy it (diff) | |
| download | slosh-e2eac8ef074d15cbff4844b956ddbe1031200dc8.tar.gz slosh-e2eac8ef074d15cbff4844b956ddbe1031200dc8.tar.xz slosh-e2eac8ef074d15cbff4844b956ddbe1031200dc8.zip | |
sl0ppty: a dead pane says what it ran
```
│help: find a package to install `watch` with `cargo search cargo-watch` │
│ │
│[ran: cargo watch -x test] │
│[process exited: status 101] │
╰ exited: status 101 ─────────────────────────────────[re-run]─[close]─╯
```
`[re-run]` is one button on a pane whose program is gone, and nothing on the pane
said what it would run. The command was never anywhere: not in the frame, not in
the backlog, not in the exit note. Worst in a tab a project's layout built, where
the command was never typed into that pane and so cannot be scrolled back to
either -- which is now the common case, since that is what `open-workspace` does.
So the death note gains a line above it. A pane running the session's shell has no
command to name and gets the exit line alone, as before: `[ran: /bin/sh]` would be
noise dressed as information.
One note rather than two calls. `pane_note` puts a blank line either side of what
it writes, so two of them would space the two halves apart as if they were about
different things, when they are two facts about one run.
Re-running keeps the previous run above it (D14), so the pair turns a pane you
re-run into a log of what ran rather than a pile of identical epitaphs.
Not put in the frame footer, which is the other place the question gets asked. It
already carries the exit words and competes with the buttons for width, so a long
command would arrive truncated in the one place that is always visible -- and the
note is on screen anyway in the case that matters, because a pane whose program
just exited is scrolled to the bottom.
**Three existing tests were counting a string that now appears twice.** `DIES_CMD`
is `echo mark-one; exit 3`, and they waited for `screen().count("mark-one") == 2`
to mean "it ran twice" -- but the note contains the command, so every run now
matches twice and the wait never finished. They ask `output_runs()` instead, which
counts lines holding the marker that are not the `[ran:` line. Per line rather
than by trimming the text, because every row still has the pane's frame border on
the end of it. That is a fair correction and not a weakening: the assertion is
still "the output appeared twice", which is what they meant.
Verified: 56 checks in `tests/test_dead.py`, 1801 overall, all green. Three new:
the command is named and sits *above* the exit line (asserted by row order, not by
reading the two strings out of one screen); a pane with no command names none; and
two runs produce two of each note. Then a real session, `command="cargo watch -x
test"` with cargo-watch not installed, which is how this was noticed.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_dead.py | 74 |
1 files changed, 66 insertions, 8 deletions
diff --git a/tests/test_dead.py b/tests/test_dead.py index e42a965..6569013 100644 --- a/tests/test_dead.py +++ b/tests/test_dead.py @@ -63,6 +63,17 @@ def hit(snap, action): return None +def output_runs(snap, marker="mark-one"): + """How many times the command's *output* appears. + + A dead pane now writes the command it ran into its own backlog, and + `echo mark-one` contains `mark-one` -- so a substring count over the screen + finds every run twice. The note is the line carrying `[ran:`; every other line + holding the marker is output. (Counted per line rather than by trimming the + text: each row still has the pane's frame border on the end of it.)""" + return sum(1 for l in snap.text if marker in l and "[ran:" not in l) + + def test_it_stays_and_says_why(): with Session(SHELL, cols=60, rows=12, layout=commanded(DIES_CMD)) as s: snap = s.until_text("[process exited") @@ -80,6 +91,55 @@ def test_it_stays_and_says_why(): check("and no signal", pane["exit_signal"] == 0, str(pane)) +def test_it_says_what_it_ran(): + """`[re-run]` is one button on a pane whose program is gone. Without the + command written down, pressing it is a guess -- and in a tab somebody else's + layout built, the command was never typed here to be scrolled back to.""" + with Session(SHELL, cols=70, rows=12, layout=commanded(DIES_CMD)) as s: + snap = s.until_text("[process exited") + screen = snap.screen() + check("the backlog names the command", "[ran: " in screen, screen) + check("and it is the command that was given", + "echo mark-one; exit 3" in screen, screen) + + # Above, not below: the exit line is the last word on the run. + lines = [i for i, l in enumerate(snap.text) if "[ran: " in l] + exits = [i for i, l in enumerate(snap.text) if "[process exited" in l] + check("it sits above how it ended", lines and exits and lines[0] < exits[0], + "%s vs %s" % (lines, exits)) + + +def test_a_pane_with_no_command_has_none_to_name(): + """A shell that exited ran what the session runs. `[ran: /bin/sh]` would be + noise dressed as information.""" + with Session(SHELL, cols=60, rows=10, config=cfg('keep_dead "all"\n')) as s: + s.settle() + s.raw(DONE) + snap = s.until_text("[process exited") + check("it still says it exited", "[process exited" in snap.screen(), + snap.screen()) + check("and says nothing about a command", "[ran: " not in snap.screen(), + snap.screen()) + + +def test_each_run_says_what_it_was(): + """Re-running keeps the previous run above (D14), so the two notes turn the + scrollback into a log of what ran rather than a pile of identical epitaphs.""" + with Session(SHELL, cols=70, rows=16, layout=commanded(DIES_CMD)) as s: + s.until_text("[process exited") + s.api("rerun", id=0) + s.settle(120) + for _ in range(100): + snap = s.snapshot() + if snap.screen().count("[ran: ") >= 2: + break + s.settle(30) + check("both runs are named", snap.screen().count("[ran: ") == 2, + snap.screen()) + check("and both endings are", snap.screen().count("[process exited") == 2, + snap.screen()) + + def test_a_clean_exit_is_not_dressed_up_as_a_failure(): with Session(SHELL, cols=60, rows=12, layout=commanded("true")) as s: snap = s.until_text("[process exited") @@ -137,9 +197,8 @@ def test_rerun_runs_it_again_on_top_of_what_it_left(): x, y = hit(snap, f"rerun:{pane['id']}") s.click(x, y) - snap = s.until(lambda sn: sn.screen().count("mark-one") == 2) - check("the command ran again", - snap.screen().count("mark-one") == 2, snap.screen()) + snap = s.until(lambda sn: output_runs(sn) == 2) + check("the command ran again", output_runs(snap) == 2, snap.screen()) check("the run that ended is still above it", "[process exited: status 3]" in snap.screen(), snap.screen()) check("the pane kept its id", s.pane()["id"] == pane["id"]) @@ -157,9 +216,8 @@ def test_rerun_over_the_control_api(): pane = s.pane() reply = s.api("rerun", id=pane["id"]) check("the api runs it again", reply.get("ok") is True, str(reply)) - snap = s.until(lambda sn: sn.screen().count("mark-one") == 2) - check("and it really ran", snap.screen().count("mark-one") == 2, - snap.screen()) + snap = s.until(lambda sn: output_runs(sn) == 2) + check("and it really ran", output_runs(snap) == 2, snap.screen()) reply = s.api("rerun", id=9999) check("an unknown pane is refused", reply.get("ok") is False, str(reply)) @@ -169,8 +227,8 @@ def test_the_keyboard_can_do_it_too(): with Session(SHELL, cols=60, rows=20, layout=commanded(DIES_CMD)) as s: s.until_text("[process exited") s.key("r") - snap = s.until(lambda sn: sn.screen().count("mark-one") == 2) - check("C-a r runs it again", snap.screen().count("mark-one") == 2, + snap = s.until(lambda sn: output_runs(sn) == 2) + check("C-a r runs it again", output_runs(snap) == 2, snap.screen()) |
