diff options
| author | Gabriel Schneider <[email protected]> | 2026-10-02 00:50:41 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-02 01:43:04 -0300 |
| commit | 97c939b981bc7fbf1a58511a9d83d9f1e12a8f16 (patch) | |
| tree | 168f1c1cb7005d4b684be312a36be205e642f7df /test/selfmount.py | |
| parent | d65ec3394ff619118d9208718477469c673a5598 (diff) | |
| download | pardes-97c939b981bc7fbf1a58511a9d83d9f1e12a8f16.tar.gz pardes-97c939b981bc7fbf1a58511a9d83d9f1e12a8f16.zip | |
An Edit filter that can never be reaped no longer holds the Edit's answer: at the limit the command's group is killed and reaped off the answer's path, the stdin writer owning its input, so a filter writing its own ctl with > or >> answers EIO at 10 s instead of hanging; a running Edit keeps its own copies of the names it reports, and the panes it changes refuse Del, delete, rmdir, Undo, Redo, Get and Zerox (busy, EBUSY); e and r say unreadable files in Get's words; the reference says both sides of a filter's own ctl write need <>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'test/selfmount.py')
| -rw-r--r-- | test/selfmount.py | 35 |
1 files changed, 34 insertions, 1 deletions
diff --git a/test/selfmount.py b/test/selfmount.py index 5aa9749a..61d32523 100644 --- a/test/selfmount.py +++ b/test/selfmount.py @@ -372,8 +372,41 @@ def run(binary): check(codes == ['124', '0'] and client.read(f'/pane/{edited}/body') == b'aPPle\npear\napple\npear\n', f'an interrupted Edit changes nothing, and the next runs ({codes!r}, {client.read(f"/pane/{edited}/body")!r})') client.write('/exec', b'Msg still here\n') - text = ''.join(cell[0] for cell in client.screen()['cells']) + # The terminal is read as it is waited on: a frame the editor + # cannot write out (its pty full) is a frame /screen does not have. + deadline = time.monotonic() + 8 + while 'still here' not in (text := ''.join(cell[0] for cell in client.screen()['cells'])) and time.monotonic() < deadline: + drain() + time.sleep(.1) check('still here' in text, 'the editor answers after all of that') + # A filter that writes its own pane's ctl through the mount. Both + # sides with `<>` run at once; a `>` or `>>` open on either side + # waits in the kernel for the other write, and the command is + # stopped at its 10 s limit: the Edit's write is answered then, + # never held by a command it cannot kill. + loop_pane = env_pane # the screen has no room for another + client.write(f'/pane/{loop_pane}/body', b'x\n', truncate=True) + inner = Path(f'/tmp/{name}.inner') + timed = Path(f'/tmp/{name}.timed') + for outer_open, inner_open, quick in [('3<>', '1<>', True), ('>', '>', False), ('>>', '>', False), + ('3<>', '>>', False), ('>', '>>', False)]: + # `clean`, which says nothing: a stopped filter's write still + # lands once the Edit's is answered. + inner.write_text(f'echo clean {inner_open} "$PARDES_MOUNT/pane/$winid/ctl"\n') + edit = f'Edit 1 > sh {inner}' + write = (f"exec 3<>{mount}/pane/{loop_pane}/ctl; echo '{edit}' >&3; rc=$?; exec 3>&-" + if outer_open == '3<>' else f"echo '{edit}' {outer_open} {mount}/pane/{loop_pane}/ctl; rc=$?") + ran = run((f"s=$(date +%s%N); {write}; e=$(date +%s%N); " + f"echo $rc $(( (e - s) / 1000000 )) > {timed}\n").encode()) + rc_ms = timed.read_text().split() if timed.exists() else ['?', '-1'] + timed.unlink(missing_ok=True) + ms = int(rc_ms[1]) + ok = ms >= 0 and (ms < 2000 if quick else ms < 13000) + check(ran == '0' and ok, f'outer {outer_open}, filter {inner_open} on its own ctl: answered in {ms} ms (rc {rc_ms[0]})') + inner.unlink(missing_ok=True) + client.write('/exec', b'Msg after the stopped filters\n') + check('msg ' in client.read('/log').decode().split('after the stopped filters')[0][-40:], + 'and the editor answers after them') finally: os.kill(pid, signal.SIGTERM) time.sleep(.2) |
