/** * Node regression tests for Web Serial readUntil buffer handling. * Run: node led-tool/static/web_serial_readuntil_test.mjs */ function bytesIndexOf(buf, suffix) { if (!suffix.length) return 0; if (buf.length < suffix.length) return -1; for (let i = 0; i <= buf.length - suffix.length; i += 1) { let ok = true; for (let j = 0; j < suffix.length; j += 1) { if (buf[i + j] !== suffix[j]) { ok = false; break; } } if (ok) return i; } return -1; } function readUntilConsume(rxBuf, suffixBytes) { const idx = bytesIndexOf(rxBuf, suffixBytes); if (idx < 0) return null; const end = idx + suffixBytes.length; const matched = rxBuf.slice(0, end); rxBuf.splice(0, end); return matched; } function enc(s) { return [...new TextEncoder().encode(s)]; } function assert(cond, msg) { if (!cond) throw new Error(msg); } const RAW = 'raw REPL; CTRL-B to exit\r\n'; const SOFT = 'soft reboot\r\n'; // Old bug: one chunk with soft reboot + banner; suffix-at-end + clear-all loses banner. { const rx = enc(`MPY: ${SOFT}${RAW}boot.py line\r\n`); const soft = enc(SOFT); const m1 = readUntilConsume(rx, soft); assert(m1 !== null, 'soft reboot should match'); const banner = enc(RAW); const m2 = readUntilConsume(rx, banner); assert(m2 !== null, 'banner must remain in buffer after soft reboot match'); assert(rx.length > 0, 'boot.py tail should remain after banner'); } // Banner anywhere in buffer, not only at end. { const rx = enc(`noise${RAW}>>> `); const banner = enc(RAW); const m = readUntilConsume(rx, banner); assert(m !== null, 'banner should match mid-buffer'); } // MPY: soft reboot marker { const rx = enc(`MPY: ${SOFT}`); const soft = enc('MPY: soft reboot\r\n'); assert(readUntilConsume(rx, soft) !== null, 'MPY soft reboot marker'); } console.log('web_serial_readuntil_test: ok');