|
8 | 8 | import type { Kernel, Permissions } from "../src/types.js"; |
9 | 9 | import { FILETYPE_PIPE, FILETYPE_CHARACTER_DEVICE } from "../src/types.js"; |
10 | 10 | import { filterEnv, wrapFileSystem } from "../src/permissions.js"; |
11 | | -import { MAX_CANON } from "../src/pty.js"; |
| 11 | +import { MAX_CANON, MAX_PTY_BUFFER_BYTES } from "../src/pty.js"; |
12 | 12 |
|
13 | 13 | describe("kernel + MockRuntimeDriver integration", () => { |
14 | 14 | let kernel: Kernel; |
@@ -3141,6 +3141,58 @@ describe("kernel + MockRuntimeDriver integration", () => { |
3141 | 3141 | proc.kill(); |
3142 | 3142 | }); |
3143 | 3143 |
|
| 3144 | + it("echo buffer exhaustion — fdWrite throws EAGAIN when output buffer is full", async () => { |
| 3145 | + const driver = new MockRuntimeDriver(["proc"], { |
| 3146 | + proc: { neverExit: true }, |
| 3147 | + }); |
| 3148 | + ({ kernel } = await createTestKernel({ drivers: [driver] })); |
| 3149 | + |
| 3150 | + const ki = driver.kernelInterface!; |
| 3151 | + const proc = kernel.spawn("proc", []); |
| 3152 | + const { masterFd, slaveFd } = ki.openpty(proc.pid); |
| 3153 | + |
| 3154 | + // Default termios: canonical + echo on. Fill output buffer via slave write. |
| 3155 | + const chunk = new Uint8Array(MAX_PTY_BUFFER_BYTES); |
| 3156 | + ki.fdWrite(proc.pid, slaveFd, chunk); |
| 3157 | + |
| 3158 | + // Master write with echo enabled — echo can't fit in full output buffer → EAGAIN |
| 3159 | + expect(() => |
| 3160 | + ki.fdWrite(proc.pid, masterFd, new Uint8Array([0x41])), // 'A' |
| 3161 | + ).toThrow(expect.objectContaining({ code: "EAGAIN" })); |
| 3162 | + |
| 3163 | + proc.kill(); |
| 3164 | + }); |
| 3165 | + |
| 3166 | + it("echo buffer exhaustion recovery — drain buffer, verify echo resumes", async () => { |
| 3167 | + const driver = new MockRuntimeDriver(["proc"], { |
| 3168 | + proc: { neverExit: true }, |
| 3169 | + }); |
| 3170 | + ({ kernel } = await createTestKernel({ drivers: [driver] })); |
| 3171 | + |
| 3172 | + const ki = driver.kernelInterface!; |
| 3173 | + const proc = kernel.spawn("proc", []); |
| 3174 | + const { masterFd, slaveFd } = ki.openpty(proc.pid); |
| 3175 | + |
| 3176 | + // Fill output buffer via slave write |
| 3177 | + const chunk = new Uint8Array(MAX_PTY_BUFFER_BYTES); |
| 3178 | + ki.fdWrite(proc.pid, slaveFd, chunk); |
| 3179 | + |
| 3180 | + // Confirm echo is blocked |
| 3181 | + expect(() => |
| 3182 | + ki.fdWrite(proc.pid, masterFd, new Uint8Array([0x41])), |
| 3183 | + ).toThrow(expect.objectContaining({ code: "EAGAIN" })); |
| 3184 | + |
| 3185 | + // Drain output buffer via master read |
| 3186 | + await ki.fdRead(proc.pid, masterFd, MAX_PTY_BUFFER_BYTES); |
| 3187 | + |
| 3188 | + // Echo should now work — write input, read echo back from master |
| 3189 | + ki.fdWrite(proc.pid, masterFd, new Uint8Array([0x42])); // 'B' |
| 3190 | + const echo = await ki.fdRead(proc.pid, masterFd, 1024); |
| 3191 | + expect(echo[0]).toBe(0x42); // 'B' echoed back |
| 3192 | + |
| 3193 | + proc.kill(); |
| 3194 | + }); |
| 3195 | + |
3144 | 3196 | it("tcgetattr on non-PTY FD throws EBADF", async () => { |
3145 | 3197 | const driver = new MockRuntimeDriver(["proc"], { |
3146 | 3198 | proc: { neverExit: true }, |
|
0 commit comments