diff options
| author | user <user@clank> | 2026-08-16 00:52:42 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-16 00:52:42 +0200 |
| commit | 40edef803e1b162ddc259e74aadd3688631ce1f2 (patch) | |
| tree | db86f33266d3f3e8085a5b9b69da8b3c4231b3f2 /tests | |
| parent | sl0ppty: one shader entry, one parser (diff) | |
| download | slosh-40edef803e1b162ddc259e74aadd3688631ce1f2.tar.gz slosh-40edef803e1b162ddc259e74aadd3688631ce1f2.tar.xz slosh-40edef803e1b162ddc259e74aadd3688631ce1f2.zip | |
sl0ppty: a reply is never a request
`hello` was answered with the verb `hello`. Any pane that echoes what it is
sent -- `cat`, `tee`, a shell with echo on, a REPL waiting for a line --
therefore answered the answer, and the session answered that. Measured
before the fix: a pane running `tee` was sent 4,303,832 bytes and 165,532
hellos in a second and a half. Afterwards, 32 bytes and one.
The fix is the rule rather than the instance: every message the session
sends *to* a program ends its verb in `-reply`, no request verb may, and
`on_osc5577` drops one on sight instead of relying on no handler happening
to exist. So this cannot come back when the next verb gets an answer --
which is the commit after this one, and how I found it.
`hello`'s reply is now `hello-reply;sl0ppty;1`. That is our own extension
to the fork's protocol, not one of the five compatible verbs, so nothing
written against sl0ppi notices. The fork's `click` goes to a program too
and keeps its name: it predates the rule and is safe for the weaker reason
that nothing answers a click.
The suite was hiding this. `emit()` builds its pane out of `cat -v`, so the
existing `hello` test had a pane trading megabytes with the session while
it asserted -- and passed, because the assertion was `in`, and the first
reply was in there along with all the rest. It now counts.
Carries `pane_touch`, two lines, unused until the next commit: a pane whose
shaders changed has to be repainted, and nothing about its cells changed to
say so.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_osc5577.py | 23 |
1 files changed, 22 insertions, 1 deletions
diff --git a/tests/test_osc5577.py b/tests/test_osc5577.py index 3ccd4f7..e215fdd 100644 --- a/tests/test_osc5577.py +++ b/tests/test_osc5577.py @@ -6,6 +6,7 @@ for the existing pi extensions. The hostile cases matter as much as the happy ones: a pane's own output is untrusted input. """ import sys +import time from harness import Session, check, report @@ -248,7 +249,26 @@ def test_hello_handshake(): s.settle() out = s.snapshot().pane_text(s.pane()) check("hello is answered with an implementation and a version", - "5577;1;hello;sl0ppty;1" in out, repr(out[:120])) + "5577;1;hello-reply;sl0ppty;1" in out, repr(out[:120])) + + +def test_a_reply_is_not_a_request(): + """The pane in `emit()` runs `cat -v`, so everything the session sends it + goes straight back out again. That is the ordinary case -- a shell with echo + on, a REPL waiting for a line -- and it used to be a loop: `hello` was + answered with the verb `hello`, which parsed as another request. A pane + running `tee` traded four megabytes with the session in a second and a half. + + So: exactly one answer, however loudly the pane echoes it. + """ + with Session(emit(osc("1;hello;")), cols=60, rows=12) as s: + s.settle() + time.sleep(0.5) # real time: a loop needs none of our help to run + out = s.snapshot().pane_text(s.pane()) + check("a reply the pane echoes back is not answered again", + out.count("hello-reply") == 1, repr(out[:200])) + check("and the pane is still usable afterwards", + len(out) < 400, "%d chars of screen" % len(out)) if __name__ == "__main__": @@ -260,4 +280,5 @@ if __name__ == "__main__": test_split_across_reads() test_pi_extension_compat() test_hello_handshake() + test_a_reply_is_not_a_request() sys.exit(report()) |
