8dc6a014
test: the scrollback fixture types only after both resizes have landed
a73x 2026-09-03 19:25
Commit message
src/server/server_test_attach.zig
| Old | New | ||
|---|---|---|---|
| @@ -539,6 +539,18 @@ test "Server: scrollback fetch is per-client and independent" { | |||
| 539 | const b = try dial.dialAttach(td.sock_path, 100, 30); | 539 | const b = try dial.dialAttach(td.sock_path, 100, 30); |
| 540 | defer b.close(); | 540 | defer b.close(); |
| 541 | 541 | ||
| 542 | // Both attaches must have REACHED the pty before anything is typed. | ||
| 543 | // `seatClient` resizes before it answers, so a state frame on each fd is | ||
| 544 | // proof that the SIGWINCH for that attach is already out. Without the | ||
| 545 | // wait, macOS's /bin/sh is bash and its readline takes the resize MID-ECHO | ||
| 546 | // and redraws the input line over itself — carriage return, erase to end | ||
| 547 | // of line, prompt again — so the history row this test selects from lost | ||
| 548 | // its first characters and every column after them moved. Linux's dash has | ||
| 549 | // no readline and redraws nothing, which is why the race only ever showed | ||
| 550 | // on the Mac. | ||
| 551 | _ = try h.firstStateFrame(alloc, a.handle, 10_000); | ||
| 552 | _ = try h.firstStateFrame(alloc, b.handle, 10_000); | ||
| 553 | |||
| 542 | // Typed by B; both clients see the history it produces. | 554 | // Typed by B; both clients see the history it produces. |
| 543 | try proto.writeFrame(b.handle, .input, "seq 1 100\n"); | 555 | try proto.writeFrame(b.handle, .input, "seq 1 100\n"); |
| 544 | 556 | ||
| @@ -1386,12 +1398,16 @@ test "Server: an observer that never reads its replies is dropped, not allowed t | |||
| 1386 | defer peer.close(); | 1398 | defer peer.close(); |
| 1387 | try td.srv.pumpOnce(20); | 1399 | try td.srv.pumpOnce(20); |
| 1388 | // Every reply is up to sessions_text_len bytes and the peer never reads | 1400 | // Every reply is up to sessions_text_len bytes and the peer never reads |
| 1389 | // one, so a small send buffer is what an unread socket looks like a few | 1401 | // one, so a small buffer is what an unread socket looks like a few frames |
| 1390 | // frames sooner. Set on the DAEMON's end: for AF_UNIX it is the sender's | 1402 | // sooner. Set on BOTH ends, because the two kernels put the bytes in |
| 1391 | // buffer that fills. | 1403 | // different places: for AF_UNIX Linux holds them against the SENDER's |
| 1404 | // send buffer, while Darwin hands them to the peer and charges the | ||
| 1405 | // RECEIVER's receive buffer, so capping only the daemon's end left the | ||
| 1406 | // Mac with room for the whole burst and no drop to grade. | ||
| 1392 | const fd = td.srv.observers[0].?.fd; | 1407 | const fd = td.srv.observers[0].?.fd; |
| 1393 | const sndbuf: c_int = 4096; | 1408 | const small: c_int = 4096; |
| 1394 | try std.posix.setsockopt(fd, std.posix.SOL.SOCKET, std.posix.SO.SNDBUF, std.mem.asBytes(&sndbuf)); | 1409 | try std.posix.setsockopt(fd, std.posix.SOL.SOCKET, std.posix.SO.SNDBUF, std.mem.asBytes(&small)); |
| 1410 | try std.posix.setsockopt(peer.handle, std.posix.SOL.SOCKET, std.posix.SO.RCVBUF, std.mem.asBytes(&small)); | ||
| 1395 | 1411 | ||
| 1396 | // One write, hundreds of frames: they fit in a single 64 KB read, which | 1412 | // One write, hundreds of frames: they fit in a single 64 KB read, which |
| 1397 | // is what makes the whole burst one uninterrupted drain. | 1413 | // is what makes the whole burst one uninterrupted drain. |