summaryrefslogtreecommitdiff
path: root/test/selfmount.py
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 02:11:15 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:15 -0300
commit3db73735e9a8d88878bc9ee343f226c033320913 (patch)
tree93c52cd0d4d2008d765c862651464684e15006d6 /test/selfmount.py
parentb29de1d4e78466a15f5d3dc0916410cba1445c5f (diff)
downloadpardes-3db73735e9a8d88878bc9ee343f226c033320913.tar.gz
pardes-3db73735e9a8d88878bc9ee343f226c033320913.zip
A command line cut across writes runs once whole, and look/exec answer per open
A mount cuts a write at its message size, and each piece ran on its own: a line cut at the boundary ran as shell commands and a 50 KB Edit block broke. An open of look, exec, tagexec, any ctl or a column's exec now keeps an unfinished last line, or an Edit block still open, until its next write or its release, and never runs a fragment. The same open record holds what its last write touched, which a read on it answers (as /net/tcp/clone does); an open that never wrote reads the session's last as of its open. A release that runs a held line waits for quiet and counts as a change; a clicked line with a control character is still refused at its write. Tested in unit tests, fs.py (python client, 8 KiB) and selfmount.py (the kernel mount, a 1000-line seq burst and a 50 KB Edit block). Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'test/selfmount.py')
-rw-r--r--test/selfmount.py33
1 files changed, 33 insertions, 0 deletions
diff --git a/test/selfmount.py b/test/selfmount.py
index 90960bed..55809740 100644
--- a/test/selfmount.py
+++ b/test/selfmount.py
@@ -105,6 +105,39 @@ def run(binary):
check(client.read(f'/pane/{first}/body') == b'HELLO initial\n', f'Save wrote back through the mount into pane {first}')
text = ''.join(cell[0] for cell in client.screen()['cells'])
check('saved ' + mount in text, 'the editor reported the save')
+ # Through the kernel mount a write is cut at its msize: a
+ # 1000-line burst and a 50 KB Edit block each still run whole,
+ # line by line, and no piece of a line runs as a shell command.
+ # sh: bash's and fish's prompt files are not reachable in 9ns's
+ # namespace here, and a command pane needs no prompt marks.
+ client.write('/ctl', b'Shell sh\n')
+ # From the fixture's directory: a pane on the mount has one a
+ # shell cannot enter.
+ client.write('/focus', f'{first}\n'.encode())
+
+ def run(line):
+ """A command pane runs the line; answers its exit status."""
+ seen = client.read('/log').decode().count('\nexit ')
+ client.write('/exec', line)
+ deadline = time.monotonic() + 20
+ while time.monotonic() < deadline:
+ drain()
+ exits = [row for row in client.read('/log').decode().splitlines() if row.startswith('exit ')]
+ if len(exits) > seen:
+ return exits[-1].split()[2]
+ time.sleep(.1)
+ return 'timeout'
+ got = run(f"seq -f 'Msg mnt-%04g' 0 999 > {mount}/exec\n".encode())
+ check(got == '0', f'a 1000-line burst written through the mount ({got!r})')
+ log = client.read('/log').decode()
+ said = [line.split(' ', 2)[2] for line in log.splitlines() if line.startswith('msg ') and 'mnt-' in line]
+ check(said == ['mnt-%04d' % i for i in range(1000)], f'each line of it ran once, whole ({len(said)} said)')
+ check('exit 127' not in log, 'no piece of a line ran as a command')
+ scratch = int(client.read('/pane/new'))
+ maker = "import sys; sys.stdout.write('Edit ,a\\n' + ''.join('line %05d of the block\\n' % i for i in range(2500)) + '.\\n')"
+ check(run(f'python3 -c "{maker}" > {mount}/pane/{scratch}/ctl\n'.encode()) == '0', 'a 50 KB Edit block written through the mount')
+ body = client.read(f'/pane/{scratch}/body').decode().splitlines()
+ check(body == ['line %05d of the block' % i for i in range(2500)], f'it ran once, whole ({len(body)} lines)')
client.write('/exec', b'Msg still here\n')
text = ''.join(cell[0] for cell in client.screen()['cells'])
check('still here' in text, 'the editor answers after all of that')