diff options
Diffstat (limited to 'test/selfmount.py')
| -rw-r--r-- | test/selfmount.py | 46 |
1 files changed, 42 insertions, 4 deletions
diff --git a/test/selfmount.py b/test/selfmount.py index 55809740..86c32fa8 100644 --- a/test/selfmount.py +++ b/test/selfmount.py @@ -20,6 +20,7 @@ import signal import struct import sys import termios +import threading import time sys.path.insert(0, str(Path(__file__).resolve().parent)) @@ -127,17 +128,54 @@ def run(binary): return exits[-1].split()[2] time.sleep(.1) return 'timeout' + # Followed from before the burst: a fresh open of the log reads + # only what its 64 KiB ring still holds, which a burst this size + # outgrows, and a follower the ring outruns reads `lost N`, so + # every line is either heard once, in order, or a loss is said. + heard = [] + + def follow(): + with Client(sock) as follower: + fid = follower.open('/log', 2) + start = len(follower.read_fid(fid)) + follower.rpc(118, struct.pack('<IQI', fid, 0, len(b'follow new\n')) + b'follow new\n') + ready.set() + deadline = time.monotonic() + 60 + while time.monotonic() < deadline: + record = follower.read_fid(fid, start).decode() + heard.append(record) + if 'mnt-0999' in record: + return + ready = threading.Event() + reader = threading.Thread(target=follow, daemon=True) + reader.start() + check(ready.wait(10), 'a follower of the log is waiting') 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') + reader.join(60) + for r in heard: + if r.startswith(('err ', 'exit ')): + print(' heard: ' + r.rstrip()) + said = [r.rstrip('\n').split(' ', 2)[2] for r in heard if r.startswith('msg ') and 'mnt-' in r] + lost = [r for r in heard if r.startswith('lost ')] + numbers = [int(s[4:]) for s in said] + check(numbers == list(range(numbers[0] if numbers else 0, 1000)), + f'each line it heard ran once, whole, in order, through the last ({len(said)} heard)') + check(len(said) == 1000 or (lost and heard.index(lost[0]) < heard.index(next(r for r in heard if 'mnt-' in r))), + f'no line was lost unsaid ({len(said)} heard, {lost!r})') + check(not any('exit 127' in r for r in heard), '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)') + # Forty event reads held through the mount, a follower each, and + # the mount still answers the rest: 9ns keeps workers past what + # the editor holds (128), so `cat layout` is not queued behind + # them for ever. + held = run(f"for i in $(seq 40); do cat {mount}/pane/{first}/event > /dev/null & done; " + f"sleep 1; timeout 10 cat {mount}/layout > /dev/null; st=$?; kill $(jobs -p); exit $st\n".encode()) + check(held == '0', f'cat layout answers beside 40 held event reads ({held!r})') 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') |
