diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 18:16:13 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:17 -0300 |
| commit | 66500ac6e4f002a673e95e9788a4992178bf989a (patch) | |
| tree | 0816815c8865670269f9923b351bb25a4c45d1f5 /test/monkey9p.py | |
| parent | 893e12345c2458e70b333faedaaaaf8fbffb6932 (diff) | |
| download | pardes-66500ac6e4f002a673e95e9788a4992178bf989a.tar.gz pardes-66500ac6e4f002a673e95e9788a4992178bf989a.zip | |
The 9P monkey counts the lines a write runs as the tree does: a short write's unended tail is a line of its own
Since round 24 (uwzuqzxp), a write to a lines file shorter than a Twrite holds is whole, as acme takes each write, so a look line written a byte a Twrite is as many looks as bytes, each missing with its own err. one_failure_rule still split the data on newlines whole, allowed one err, and failed the fixed smoke seed (20260929 step 607, size 20 6 written in bytes to look: 7 errs). served_lines models the tree's write (tree.zig) and has a self-check run at start.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'test/monkey9p.py')
| -rw-r--r-- | test/monkey9p.py | 41 |
1 files changed, 36 insertions, 5 deletions
diff --git a/test/monkey9p.py b/test/monkey9p.py index 4c1732e5..6a8f27c1 100644 --- a/test/monkey9p.py +++ b/test/monkey9p.py @@ -640,12 +640,13 @@ def op_write(sess, op): timeout = SLOW_TIMEOUT if slow(data) else TIMEOUT ok, body = w.call(TOPEN, struct.pack('<IB', fid, mode), timeout) res.add(open_kind(path, mode), path, ok, None if ok else body) - allowances(res, path, data) + sizes = chunking(op.get('chunks', 'whole'), len(data), w.msize - 23) + allowances(res, path, data, sizes, w.msize - 24) opened = ok sent = data if ok: offset = 0 - for size in chunking(op.get('chunks', 'whole'), len(data), w.msize - 23): + for size in sizes: piece = data[offset:offset + size] ok, body = w.call(TWRITE, struct.pack('<IQI', fid, op.get('offset', offset), len(piece)) + piece, timeout) # A write on a fid opened for reading is 9P misuse, not a write @@ -663,13 +664,35 @@ def op_write(sess, op): return res -def allowances(res, path, data): +def served_lines(data, sizes, room): + """The lines a lines file runs for `data` written in pieces of `sizes` + (fs.md, tree.zig write): each ended line, and the unended tail of a piece + shorter than a Twrite holds (`room`, msize less 24), which is whole in + itself, as acme takes each write; a piece that fills its Twrite may go + on, and its tail waits for the next. A line written a byte a Twrite is + so as many lines as bytes.""" + lines, pending, at = [], b'', 0 + for size in sizes: + piece = data[at:at + size] + at += size + pending += piece + *done, pending = pending.split(b'\n') + lines += done + if pending and len(piece) < room: + lines.append(pending) + pending = b'' + if pending: + lines.append(pending) + return lines + + +def allowances(res, path, data, sizes, room): """Errs a successful write may log (fs.md): a look that finds nothing logs one, per line, whether written to look, written back as an event record (acme's xfid.c:842) or run as the Look builtin; an Edit block whose text never ended runs, and fails, at the close.""" base = path.rsplit('/', 1)[-1] - lines = [line for line in data.split(b'\n') if line.strip()] + lines = [line for line in served_lines(data, sizes, room) if line.strip()] if base in ('look', 'event'): n = len(lines) elif base in ('exec', 'ctl', 'tagexec'): @@ -692,6 +715,13 @@ def open_kind(path, mode): return 'open' +def check_served_lines(): + assert served_lines(b'a\nb\n', [4], 8000) == [b'a', b'b'] + assert served_lines(b'ab\n', [1, 1, 1], 8000) == [b'a', b'b', b''] + assert served_lines(b'abcd', [2, 2], 2) == [b'abcd'] + assert served_lines(b'ab\ncd', [3, 2], 8000) == [b'ab', b'cd'] + + def chunking(spec, n, cap): if n == 0: return [0] @@ -820,7 +850,7 @@ def op_hwrite(sess, op): fid, path, mode, off, _ = h data = dec(op['data'])[:sess.wire.msize - 23] # one Twrite h[4] = data - allowances(res, path, data) + allowances(res, path, data, [len(data)], sess.wire.msize - 24) ok, body = sess.wire.call(TWRITE, struct.pack('<IQI', fid, off[0], len(data)) + data, SLOW_TIMEOUT if slow(data) else TIMEOUT) res.add('write' if mode & 3 else 'write-badfid', path, ok, None if ok else body) @@ -1919,6 +1949,7 @@ def main(): ap.add_argument('--verbose', '-v', action='store_true', help='replay: print each request and log record') ap.add_argument('--out', default=DEFAULT_OUT) args = ap.parse_args() + check_served_lines() args.binary = os.path.abspath(args.binary) for key in [k for k in os.environ if k.startswith('PARDES_') or k in ('NINE_MOUNT', 'NAMESPACE')]: del os.environ[key] |
