diff --git a/packages/code-runtime/code-runtime-python/tests/runtime.spec.ts b/packages/code-runtime/code-runtime-python/tests/runtime.spec.ts index 1af772036c..e3805d44d8 100644 --- a/packages/code-runtime/code-runtime-python/tests/runtime.spec.ts +++ b/packages/code-runtime/code-runtime-python/tests/runtime.spec.ts @@ -751,22 +751,31 @@ describe('PythonCodeRuntime — programs and bindings', () => { // `_LogStream` (the child's sys.stdout wrapper) buffers newline-free writes // and early-flushes once the pending tail can no longer fit the ledger. // Charging that trigger by CHARACTER count undercharged a control-char flood - // by up to 6x: 30M NUL chars stay under a 50 MB char-count trigger yet - // serialize to ~180 MB, which the settlement flush then allocated at once — - // breaching a 64 MB RLIMIT_AS and surfacing as worker-exit instead of the - // truncation marker. Driving the flood through sys.stdout.write (not - // os.write, which bypasses the wrapper into host stray capture) exercises the - // in-child stream. On Linux CI the pre-fix trigger dies on RLIMIT_AS; the - // serialized-cost trigger flushes while running, so the run completes and - // ends at the marker. (RLIMIT_AS is skipped on Darwin — bootstrap.py — so the - // worker-exit repro is Linux-only; locally this asserts the happy path.) - const { runtime } = await setup({ maxLogBytes: 50_000_000, addressSpaceMb: 64, maxWallMs: 20_000 }) + // by up to 6x: NUL chars stay under a char-count trigger yet serialize to ~6x + // as many bytes, which the settlement "".join + encode then allocated at once. + // The program writes the flood in 1 MiB chunks (so no single argument str is + // itself the allocation under test) with no newline; the serialized-cost + // trigger flushes while running, keeping the pending tail bounded, so the run + // completes at the truncation marker. Pre-fix, the char-count trigger stayed + // dormant until ~200 MiB of chars accumulated, and the settlement encode of + // their ~1.2 GiB serialized form breached the 512 MiB RLIMIT_AS as a + // worker-exit. Driven through sys.stdout.write (not os.write, which bypasses + // the wrapper into host stray capture) to exercise the in-child stream. + // RLIMIT_AS is skipped on Darwin, so the worker-exit repro is Linux-only; + // on macOS this asserts the happy path, matching the control-char cases. + const { runtime } = await setup({ maxLogBytes: 20_000_000, addressSpaceMb: 512, maxWallMs: 30_000 }) const result = await runtime.run({ - program: ['import sys', 'sys.stdout.write("\\x00" * 30_000_000)', 'return None'].join('\n'), + program: [ + 'import sys', + 'chunk = "\\x00" * (1024 * 1024)', + 'for _ in range(200):', + ' sys.stdout.write(chunk)', + 'return None', + ].join('\n'), bindings: [], }) expect(result.error).toBeUndefined() - expect(result.logs.at(-1)).toBe(logTruncationMarker(50_000_000)) + expect(result.logs.at(-1)).toBe(logTruncationMarker(20_000_000)) }) it('bounds an illegal-UTF-8 native residual by its U+FFFD-decoded cost', async () => {