d2cc2334
test: byte fidelity, non-vacuity, and the third sun_path victim
a73x 2026-08-08 14:08
Commit message
build.zig
| Old | New | ||
|---|---|---|---|
| @@ -90,6 +90,15 @@ pub fn build(b: *std.Build) void { | |||
| 90 | .link_libc = true, | 90 | .link_libc = true, |
| 91 | }); | 91 | }); |
| 92 | 92 | ||
| 93 | // Test-only: short temp paths for the tests that bind unix sockets. | ||
| 94 | // Imported by every module that has such a test, which is why it is a | ||
| 95 | // module rather than three copies. | ||
| 96 | const testtmp_mod = b.createModule(.{ | ||
| 97 | .root_source_file = b.path("src/testtmp.zig"), | ||
| 98 | .target = target, | ||
| 99 | .optimize = optimize, | ||
| 100 | }); | ||
| 101 | |||
| 93 | const server_mod = b.createModule(.{ | 102 | const server_mod = b.createModule(.{ |
| 94 | .root_source_file = b.path("src/server.zig"), | 103 | .root_source_file = b.path("src/server.zig"), |
| 95 | .target = target, | 104 | .target = target, |
| @@ -100,6 +109,7 @@ pub fn build(b: *std.Build) void { | |||
| 100 | server_mod.addImport("pty", pty_mod); | 109 | server_mod.addImport("pty", pty_mod); |
| 101 | server_mod.addImport("protocol", protocol_mod); | 110 | server_mod.addImport("protocol", protocol_mod); |
| 102 | server_mod.addImport("quic", quic_mod); | 111 | server_mod.addImport("quic", quic_mod); |
| 112 | server_mod.addImport("testtmp", testtmp_mod); | ||
| 103 | 113 | ||
| 104 | const client_mod = b.createModule(.{ | 114 | const client_mod = b.createModule(.{ |
| 105 | .root_source_file = b.path("src/client.zig"), | 115 | .root_source_file = b.path("src/client.zig"), |
| @@ -109,6 +119,7 @@ pub fn build(b: *std.Build) void { | |||
| 109 | }); | 119 | }); |
| 110 | client_mod.addImport("engine", engine_mod); | 120 | client_mod.addImport("engine", engine_mod); |
| 111 | client_mod.addImport("protocol", protocol_mod); | 121 | client_mod.addImport("protocol", protocol_mod); |
| 122 | client_mod.addImport("testtmp", testtmp_mod); | ||
| 112 | 123 | ||
| 113 | const mux_mod = b.createModule(.{ | 124 | const mux_mod = b.createModule(.{ |
| 114 | .root_source_file = b.path("src/mux_main.zig"), | 125 | .root_source_file = b.path("src/mux_main.zig"), |
| @@ -118,14 +129,17 @@ pub fn build(b: *std.Build) void { | |||
| 118 | }); | 129 | }); |
| 119 | mux_mod.addImport("client", client_mod); | 130 | mux_mod.addImport("client", client_mod); |
| 120 | 131 | ||
| 121 | // No imports, deliberately: the proxy is a byte pump that knows nothing | 132 | // No imports that teach it anything, deliberately: the proxy is a byte |
| 122 | // about the protocol it carries. | 133 | // pump that knows nothing about the protocol it carries. `testtmp` is |
| 134 | // the one exception and does not weaken that — it hands its tests a | ||
| 135 | // short directory to put a socket in and knows nothing about the bytes. | ||
| 123 | const proxy_mod = b.createModule(.{ | 136 | const proxy_mod = b.createModule(.{ |
| 124 | .root_source_file = b.path("src/proxy.zig"), | 137 | .root_source_file = b.path("src/proxy.zig"), |
| 125 | .target = target, | 138 | .target = target, |
| 126 | .optimize = optimize, | 139 | .optimize = optimize, |
| 127 | .link_libc = true, | 140 | .link_libc = true, |
| 128 | }); | 141 | }); |
| 142 | proxy_mod.addImport("testtmp", testtmp_mod); | ||
| 129 | 143 | ||
| 130 | const exe_mod = b.createModule(.{ | 144 | const exe_mod = b.createModule(.{ |
| 131 | .root_source_file = b.path("src/main.zig"), | 145 | .root_source_file = b.path("src/main.zig"), |
| @@ -161,7 +175,7 @@ pub fn build(b: *std.Build) void { | |||
| 161 | // absence here was a live hazard recorded in decisions.md — muxd's | 175 | // absence here was a live hazard recorded in decisions.md — muxd's |
| 162 | // entrypoint could grow tests that silently never ran, exactly as | 176 | // entrypoint could grow tests that silently never ran, exactly as |
| 163 | // mux_main.zig's five did before it was added. | 177 | // mux_main.zig's five did before it was added. |
| 164 | for ([_]*std.Build.Module{ protocol_mod, engine_mod, pty_mod, server_mod, client_mod, proxy_mod, mux_mod, quic_mod, exe_mod }) |mod| { | 178 | for ([_]*std.Build.Module{ protocol_mod, engine_mod, pty_mod, server_mod, client_mod, proxy_mod, mux_mod, quic_mod, exe_mod, testtmp_mod }) |mod| { |
| 165 | const t = b.addTest(.{ .root_module = mod }); | 179 | const t = b.addTest(.{ .root_module = mod }); |
| 166 | t.use_llvm = true; | 180 | t.use_llvm = true; |
| 167 | t.use_lld = true; | 181 | t.use_lld = true; |
src/client.zig
| Old | New | ||
|---|---|---|---|
| @@ -8,6 +8,7 @@ | |||
| 8 | const std = @import("std"); | 8 | const std = @import("std"); |
| 9 | const Engine = @import("engine").Engine; | 9 | const Engine = @import("engine").Engine; |
| 10 | const proto = @import("protocol"); | 10 | const proto = @import("protocol"); |
| 11 | const TmpDir = @import("testtmp").TmpDir; | ||
| 11 | 12 | ||
| 12 | var winch_flag = std.atomic.Value(bool).init(false); | 13 | var winch_flag = std.atomic.Value(bool).init(false); |
| 13 | 14 | ||
| @@ -690,10 +691,9 @@ fn renderScrollback( | |||
| 690 | test "Transport.close is idempotent: the abort path closes what reconnect already closed" { | 691 | test "Transport.close is idempotent: the abort path closes what reconnect already closed" { |
| 691 | const alloc = std.testing.allocator; | 692 | const alloc = std.testing.allocator; |
| 692 | 693 | ||
| 693 | var tmp = std.testing.tmpDir(.{}); | 694 | var tmp = try TmpDir.make(); |
| 694 | defer tmp.cleanup(); | 695 | defer tmp.cleanup(); |
| 695 | var path_buf: [256]u8 = undefined; | 696 | const dir_path = tmp.path(); |
| 696 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 697 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/t.sock", .{dir_path}); | 697 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/t.sock", .{dir_path}); |
| 698 | defer alloc.free(sock_path); | 698 | defer alloc.free(sock_path); |
| 699 | 699 | ||
src/proxy.zig
| Old | New | ||
|---|---|---|---|
| @@ -4,6 +4,7 @@ | |||
| 4 | //! pipe suffices to carry the protocol over SSH, transport is a swap, | 4 | //! pipe suffices to carry the protocol over SSH, transport is a swap, |
| 5 | //! not a redesign. Keep this file's import list empty of `protocol`. | 5 | //! not a redesign. Keep this file's import list empty of `protocol`. |
| 6 | const std = @import("std"); | 6 | const std = @import("std"); |
| 7 | const TmpDir = @import("testtmp").TmpDir; | ||
| 7 | 8 | ||
| 8 | /// `muxd proxy` proper: pump between this process's stdio and `sock_path`. | 9 | /// `muxd proxy` proper: pump between this process's stdio and `sock_path`. |
| 9 | pub fn run(sock_path: []const u8) !u8 { | 10 | pub fn run(sock_path: []const u8) !u8 { |
| @@ -197,10 +198,9 @@ fn readExactly(fd: std.posix.fd_t, buf: []u8) !void { | |||
| 197 | test "pump carries bytes both ways verbatim" { | 198 | test "pump carries bytes both ways verbatim" { |
| 198 | const alloc = std.testing.allocator; | 199 | const alloc = std.testing.allocator; |
| 199 | 200 | ||
| 200 | var tmp = std.testing.tmpDir(.{}); | 201 | var tmp = try TmpDir.make(); |
| 201 | defer tmp.cleanup(); | 202 | defer tmp.cleanup(); |
| 202 | var path_buf: [256]u8 = undefined; | 203 | const dir_path = tmp.path(); |
| 203 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 204 | var sock_buf: [280]u8 = undefined; | 204 | var sock_buf: [280]u8 = undefined; |
| 205 | const sock_path = try std.fmt.bufPrint(&sock_buf, "{s}/proxy.sock", .{dir_path}); | 205 | const sock_path = try std.fmt.bufPrint(&sock_buf, "{s}/proxy.sock", .{dir_path}); |
| 206 | 206 | ||
| @@ -253,10 +253,9 @@ test "pump carries bytes both ways verbatim" { | |||
| 253 | test "pump carries a large transfer verbatim under backpressure" { | 253 | test "pump carries a large transfer verbatim under backpressure" { |
| 254 | const alloc = std.testing.allocator; | 254 | const alloc = std.testing.allocator; |
| 255 | 255 | ||
| 256 | var tmp = std.testing.tmpDir(.{}); | 256 | var tmp = try TmpDir.make(); |
| 257 | defer tmp.cleanup(); | 257 | defer tmp.cleanup(); |
| 258 | var path_buf: [256]u8 = undefined; | 258 | const dir_path = tmp.path(); |
| 259 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 260 | var sock_buf: [280]u8 = undefined; | 259 | var sock_buf: [280]u8 = undefined; |
| 261 | const sock_path = try std.fmt.bufPrint(&sock_buf, "{s}/big.sock", .{dir_path}); | 260 | const sock_path = try std.fmt.bufPrint(&sock_buf, "{s}/big.sock", .{dir_path}); |
| 262 | 261 | ||
| @@ -311,10 +310,9 @@ test "pump carries a large transfer verbatim under backpressure" { | |||
| 311 | test "pump exits when the far side of its output pipe is gone" { | 310 | test "pump exits when the far side of its output pipe is gone" { |
| 312 | const alloc = std.testing.allocator; | 311 | const alloc = std.testing.allocator; |
| 313 | 312 | ||
| 314 | var tmp = std.testing.tmpDir(.{}); | 313 | var tmp = try TmpDir.make(); |
| 315 | defer tmp.cleanup(); | 314 | defer tmp.cleanup(); |
| 316 | var path_buf: [256]u8 = undefined; | 315 | const dir_path = tmp.path(); |
| 317 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 318 | var sock_buf: [280]u8 = undefined; | 316 | var sock_buf: [280]u8 = undefined; |
| 319 | const sock_path = try std.fmt.bufPrint(&sock_buf, "{s}/gone.sock", .{dir_path}); | 317 | const sock_path = try std.fmt.bufPrint(&sock_buf, "{s}/gone.sock", .{dir_path}); |
| 320 | 318 | ||
| @@ -347,10 +345,9 @@ test "pump exits when the far side of its output pipe is gone" { | |||
| 347 | } | 345 | } |
| 348 | 346 | ||
| 349 | test "pump reports a missing daemon socket instead of hanging" { | 347 | test "pump reports a missing daemon socket instead of hanging" { |
| 350 | var tmp = std.testing.tmpDir(.{}); | 348 | var tmp = try TmpDir.make(); |
| 351 | defer tmp.cleanup(); | 349 | defer tmp.cleanup(); |
| 352 | var path_buf: [256]u8 = undefined; | 350 | const dir_path = tmp.path(); |
| 353 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 354 | var sock_buf: [280]u8 = undefined; | 351 | var sock_buf: [280]u8 = undefined; |
| 355 | const sock_path = try std.fmt.bufPrint(&sock_buf, "{s}/absent.sock", .{dir_path}); | 352 | const sock_path = try std.fmt.bufPrint(&sock_buf, "{s}/absent.sock", .{dir_path}); |
| 356 | 353 | ||
src/quic_server.zig
| Old | New | ||
|---|---|---|---|
| @@ -1517,7 +1517,14 @@ test "Listener: PSK handshake, Retry, and a payload larger than the initial wind | |||
| 1517 | // production too, which is why it is worth a test rather than a comment. | 1517 | // production too, which is why it is worth a test rather than a comment. |
| 1518 | const payload = try alloc.alloc(u8, 3 * 1024 * 1024); | 1518 | const payload = try alloc.alloc(u8, 3 * 1024 * 1024); |
| 1519 | defer alloc.free(payload); | 1519 | defer alloc.free(payload); |
| 1520 | // A repeating byte pattern, checked as it arrives rather than counted | ||
| 1521 | // at the end. Counting alone cannot tell a working transport from one | ||
| 1522 | // that delivers the right NUMBER of the wrong bytes — reordered, | ||
| 1523 | // duplicated, or read back out of a buffer that had been recycled | ||
| 1524 | // underneath it, which is precisely the failure this file has already | ||
| 1525 | // had once. | ||
| 1520 | for (payload, 0..) |*b, i| b.* = @truncate(i); | 1526 | for (payload, 0..) |*b, i| b.* = @truncate(i); |
| 1527 | cl.verify = payload; | ||
| 1521 | cl.out = payload; | 1528 | cl.out = payload; |
| 1522 | cl.drain(); | 1529 | cl.drain(); |
| 1523 | 1530 | ||
| @@ -1534,6 +1541,7 @@ test "Listener: PSK handshake, Retry, and a payload larger than the initial wind | |||
| 1534 | } | 1541 | } |
| 1535 | try std.testing.expect(ok); | 1542 | try std.testing.expect(ok); |
| 1536 | try std.testing.expectEqual(@as(usize, 3 * 1024 * 1024), owner.received); | 1543 | try std.testing.expectEqual(@as(usize, 3 * 1024 * 1024), owner.received); |
| 1544 | try std.testing.expect(!cl.mismatch); | ||
| 1537 | } | 1545 | } |
| 1538 | 1546 | ||
| 1539 | test "Listener: a client holding the wrong key never completes a handshake" { | 1547 | test "Listener: a client holding the wrong key never completes a handshake" { |
| @@ -1562,6 +1570,16 @@ test "Listener: a client holding the wrong key never completes a handshake" { | |||
| 1562 | try std.testing.expect(!cl.handshake_done); | 1570 | try std.testing.expect(!cl.handshake_done); |
| 1563 | try std.testing.expectEqual(@as(usize, 0), owner.opened); | 1571 | try std.testing.expectEqual(@as(usize, 0), owner.opened); |
| 1564 | try std.testing.expectEqual(@as(usize, 0), owner.received); | 1572 | try std.testing.expectEqual(@as(usize, 0), owner.received); |
| 1573 | |||
| 1574 | // ...and it failed on the KEY, which is a stronger claim than "it | ||
| 1575 | // failed". A client refused earlier — a Retry token that never | ||
| 1576 | // validated, an Initial the server would not parse — would satisfy | ||
| 1577 | // every assertion above while proving nothing about authentication. | ||
| 1578 | // The listener only allocates a connection id once it has accepted a | ||
| 1579 | // token-bearing Initial, so an id having been handed out is the witness | ||
| 1580 | // that this client got all the way to the point where the PSK is the | ||
| 1581 | // only thing left to disagree about. | ||
| 1582 | try std.testing.expect(setup.l.next_id > 1); | ||
| 1565 | } | 1583 | } |
| 1566 | 1584 | ||
| 1567 | test "Listener: keepalive carries an idle connection past its idle timeout" { | 1585 | test "Listener: keepalive carries an idle connection past its idle timeout" { |
src/server.zig
| Old | New | ||
|---|---|---|---|
| @@ -10,6 +10,7 @@ const Engine = @import("engine").Engine; | |||
| 10 | const Pty = @import("pty").Pty; | 10 | const Pty = @import("pty").Pty; |
| 11 | const proto = @import("protocol"); | 11 | const proto = @import("protocol"); |
| 12 | const quic = @import("quic"); | 12 | const quic = @import("quic"); |
| 13 | const TmpDir = @import("testtmp").TmpDir; | ||
| 13 | 14 | ||
| 14 | const max_clients = 8; | 15 | const max_clients = 8; |
| 15 | const max_observers = 4; | 16 | const max_observers = 4; |
| @@ -1382,10 +1383,9 @@ fn applyFrame(alloc: std.mem.Allocator, replica: *Engine, frame: proto.Frame) !v | |||
| 1382 | test "Server: survives a client that dies without detaching; next attach works" { | 1383 | test "Server: survives a client that dies without detaching; next attach works" { |
| 1383 | const alloc = std.testing.allocator; | 1384 | const alloc = std.testing.allocator; |
| 1384 | 1385 | ||
| 1385 | var tmp = std.testing.tmpDir(.{}); | 1386 | var tmp = try TmpDir.make(); |
| 1386 | defer tmp.cleanup(); | 1387 | defer tmp.cleanup(); |
| 1387 | var path_buf: [256]u8 = undefined; | 1388 | const dir_path = tmp.path(); |
| 1388 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 1389 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/kill.sock", .{dir_path}); | 1389 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/kill.sock", .{dir_path}); |
| 1390 | defer alloc.free(sock_path); | 1390 | defer alloc.free(sock_path); |
| 1391 | 1391 | ||
| @@ -1431,10 +1431,9 @@ test "Server: survives a client that dies without detaching; next attach works" | |||
| 1431 | test "Server: serves scrollback chunks on request" { | 1431 | test "Server: serves scrollback chunks on request" { |
| 1432 | const alloc = std.testing.allocator; | 1432 | const alloc = std.testing.allocator; |
| 1433 | 1433 | ||
| 1434 | var tmp = std.testing.tmpDir(.{}); | 1434 | var tmp = try TmpDir.make(); |
| 1435 | defer tmp.cleanup(); | 1435 | defer tmp.cleanup(); |
| 1436 | var path_buf: [256]u8 = undefined; | 1436 | const dir_path = tmp.path(); |
| 1437 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 1438 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/sb.sock", .{dir_path}); | 1437 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/sb.sock", .{dir_path}); |
| 1439 | defer alloc.free(sock_path); | 1438 | defer alloc.free(sock_path); |
| 1440 | 1439 | ||
| @@ -1500,10 +1499,9 @@ test "Server: serves scrollback chunks on request" { | |||
| 1500 | test "Server: a full session refuses the next attach instead of displacing anyone" { | 1499 | test "Server: a full session refuses the next attach instead of displacing anyone" { |
| 1501 | const alloc = std.testing.allocator; | 1500 | const alloc = std.testing.allocator; |
| 1502 | 1501 | ||
| 1503 | var tmp = std.testing.tmpDir(.{}); | 1502 | var tmp = try TmpDir.make(); |
| 1504 | defer tmp.cleanup(); | 1503 | defer tmp.cleanup(); |
| 1505 | var path_buf: [256]u8 = undefined; | 1504 | const dir_path = tmp.path(); |
| 1506 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 1507 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/full.sock", .{dir_path}); | 1505 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/full.sock", .{dir_path}); |
| 1508 | defer alloc.free(sock_path); | 1506 | defer alloc.free(sock_path); |
| 1509 | 1507 | ||
| @@ -1578,10 +1576,9 @@ test "Server: a full session refuses the next attach instead of displacing anyon | |||
| 1578 | test "Server: two clients converge on one session" { | 1576 | test "Server: two clients converge on one session" { |
| 1579 | const alloc = std.testing.allocator; | 1577 | const alloc = std.testing.allocator; |
| 1580 | 1578 | ||
| 1581 | var tmp = std.testing.tmpDir(.{}); | 1579 | var tmp = try TmpDir.make(); |
| 1582 | defer tmp.cleanup(); | 1580 | defer tmp.cleanup(); |
| 1583 | var path_buf: [256]u8 = undefined; | 1581 | const dir_path = tmp.path(); |
| 1584 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 1585 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/two.sock", .{dir_path}); | 1582 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/two.sock", .{dir_path}); |
| 1586 | defer alloc.free(sock_path); | 1583 | defer alloc.free(sock_path); |
| 1587 | 1584 | ||
| @@ -1685,10 +1682,9 @@ test "Server: two clients converge on one session" { | |||
| 1685 | test "Server: a same-size join snapshots the joiner only" { | 1682 | test "Server: a same-size join snapshots the joiner only" { |
| 1686 | const alloc = std.testing.allocator; | 1683 | const alloc = std.testing.allocator; |
| 1687 | 1684 | ||
| 1688 | var tmp = std.testing.tmpDir(.{}); | 1685 | var tmp = try TmpDir.make(); |
| 1689 | defer tmp.cleanup(); | 1686 | defer tmp.cleanup(); |
| 1690 | var path_buf: [256]u8 = undefined; | 1687 | const dir_path = tmp.path(); |
| 1691 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 1692 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/join.sock", .{dir_path}); | 1688 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/join.sock", .{dir_path}); |
| 1693 | defer alloc.free(sock_path); | 1689 | defer alloc.free(sock_path); |
| 1694 | 1690 | ||
| @@ -1839,10 +1835,9 @@ fn shrinkSendBuf(fd: std.posix.fd_t) !void { | |||
| 1839 | test "Server: a stalled client does not block delivery to others" { | 1835 | test "Server: a stalled client does not block delivery to others" { |
| 1840 | const alloc = std.testing.allocator; | 1836 | const alloc = std.testing.allocator; |
| 1841 | 1837 | ||
| 1842 | var tmp = std.testing.tmpDir(.{}); | 1838 | var tmp = try TmpDir.make(); |
| 1843 | defer tmp.cleanup(); | 1839 | defer tmp.cleanup(); |
| 1844 | var path_buf: [256]u8 = undefined; | 1840 | const dir_path = tmp.path(); |
| 1845 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 1846 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/stall.sock", .{dir_path}); | 1841 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/stall.sock", .{dir_path}); |
| 1847 | defer alloc.free(sock_path); | 1842 | defer alloc.free(sock_path); |
| 1848 | 1843 | ||
| @@ -1897,10 +1892,9 @@ test "Server: a stalled client does not block delivery to others" { | |||
| 1897 | test "Server: a client exceeding the pending cap is dropped" { | 1892 | test "Server: a client exceeding the pending cap is dropped" { |
| 1898 | const alloc = std.testing.allocator; | 1893 | const alloc = std.testing.allocator; |
| 1899 | 1894 | ||
| 1900 | var tmp = std.testing.tmpDir(.{}); | 1895 | var tmp = try TmpDir.make(); |
| 1901 | defer tmp.cleanup(); | 1896 | defer tmp.cleanup(); |
| 1902 | var path_buf: [256]u8 = undefined; | 1897 | const dir_path = tmp.path(); |
| 1903 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 1904 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/cap.sock", .{dir_path}); | 1898 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/cap.sock", .{dir_path}); |
| 1905 | defer alloc.free(sock_path); | 1899 | defer alloc.free(sock_path); |
| 1906 | 1900 | ||
| @@ -1955,10 +1949,9 @@ test "Server: a client exceeding the pending cap is dropped" { | |||
| 1955 | test "Server: a writable backlog is flushed by poll, not mistaken for input" { | 1949 | test "Server: a writable backlog is flushed by poll, not mistaken for input" { |
| 1956 | const alloc = std.testing.allocator; | 1950 | const alloc = std.testing.allocator; |
| 1957 | 1951 | ||
| 1958 | var tmp = std.testing.tmpDir(.{}); | 1952 | var tmp = try TmpDir.make(); |
| 1959 | defer tmp.cleanup(); | 1953 | defer tmp.cleanup(); |
| 1960 | var path_buf: [256]u8 = undefined; | 1954 | const dir_path = tmp.path(); |
| 1961 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 1962 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/pollout.sock", .{dir_path}); | 1955 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/pollout.sock", .{dir_path}); |
| 1963 | defer alloc.free(sock_path); | 1956 | defer alloc.free(sock_path); |
| 1964 | 1957 | ||
| @@ -2007,10 +2000,9 @@ test "Server: a writable backlog is flushed by poll, not mistaken for input" { | |||
| 2007 | 2000 | ||
| 2008 | test "Server: a partially flushed queue delivers every byte exactly once" { | 2001 | test "Server: a partially flushed queue delivers every byte exactly once" { |
| 2009 | const alloc = std.testing.allocator; | 2002 | const alloc = std.testing.allocator; |
| 2010 | var tmp = std.testing.tmpDir(.{}); | 2003 | var tmp = try TmpDir.make(); |
| 2011 | defer tmp.cleanup(); | 2004 | defer tmp.cleanup(); |
| 2012 | var path_buf: [256]u8 = undefined; | 2005 | const dir_path = tmp.path(); |
| 2013 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 2014 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/pf.sock", .{dir_path}); | 2006 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/pf.sock", .{dir_path}); |
| 2015 | defer alloc.free(sock_path); | 2007 | defer alloc.free(sock_path); |
| 2016 | 2008 | ||
| @@ -2071,10 +2063,9 @@ test "Server: a partially flushed queue delivers every byte exactly once" { | |||
| 2071 | test "Server: the shell's exit status reaches an attached client" { | 2063 | test "Server: the shell's exit status reaches an attached client" { |
| 2072 | const alloc = std.testing.allocator; | 2064 | const alloc = std.testing.allocator; |
| 2073 | 2065 | ||
| 2074 | var tmp = std.testing.tmpDir(.{}); | 2066 | var tmp = try TmpDir.make(); |
| 2075 | defer tmp.cleanup(); | 2067 | defer tmp.cleanup(); |
| 2076 | var path_buf: [256]u8 = undefined; | 2068 | const dir_path = tmp.path(); |
| 2077 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 2078 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/exit.sock", .{dir_path}); | 2069 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/exit.sock", .{dir_path}); |
| 2079 | defer alloc.free(sock_path); | 2070 | defer alloc.free(sock_path); |
| 2080 | 2071 | ||
| @@ -2164,10 +2155,9 @@ const PeerDrainer = struct { | |||
| 2164 | test "Server: the exit drain delivers a backlog once the peer resumes reading" { | 2155 | test "Server: the exit drain delivers a backlog once the peer resumes reading" { |
| 2165 | const alloc = std.testing.allocator; | 2156 | const alloc = std.testing.allocator; |
| 2166 | 2157 | ||
| 2167 | var tmp = std.testing.tmpDir(.{}); | 2158 | var tmp = try TmpDir.make(); |
| 2168 | defer tmp.cleanup(); | 2159 | defer tmp.cleanup(); |
| 2169 | var path_buf: [256]u8 = undefined; | 2160 | const dir_path = tmp.path(); |
| 2170 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 2171 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/exitdrain.sock", .{dir_path}); | 2161 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/exitdrain.sock", .{dir_path}); |
| 2172 | defer alloc.free(sock_path); | 2162 | defer alloc.free(sock_path); |
| 2173 | 2163 | ||
| @@ -2216,10 +2206,9 @@ test "Server: the exit drain delivers a backlog once the peer resumes reading" { | |||
| 2216 | test "Server: the exit drain gives up on a peer that never reads" { | 2206 | test "Server: the exit drain gives up on a peer that never reads" { |
| 2217 | const alloc = std.testing.allocator; | 2207 | const alloc = std.testing.allocator; |
| 2218 | 2208 | ||
| 2219 | var tmp = std.testing.tmpDir(.{}); | 2209 | var tmp = try TmpDir.make(); |
| 2220 | defer tmp.cleanup(); | 2210 | defer tmp.cleanup(); |
| 2221 | var path_buf: [256]u8 = undefined; | 2211 | const dir_path = tmp.path(); |
| 2222 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 2223 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/drain.sock", .{dir_path}); | 2212 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/drain.sock", .{dir_path}); |
| 2224 | defer alloc.free(sock_path); | 2213 | defer alloc.free(sock_path); |
| 2225 | 2214 | ||
| @@ -2256,10 +2245,9 @@ test "Server: the exit drain gives up on a peer that never reads" { | |||
| 2256 | test "Server: broadcast stats count every send but the counterfactual once" { | 2245 | test "Server: broadcast stats count every send but the counterfactual once" { |
| 2257 | const alloc = std.testing.allocator; | 2246 | const alloc = std.testing.allocator; |
| 2258 | 2247 | ||
| 2259 | var tmp = std.testing.tmpDir(.{}); | 2248 | var tmp = try TmpDir.make(); |
| 2260 | defer tmp.cleanup(); | 2249 | defer tmp.cleanup(); |
| 2261 | var path_buf: [256]u8 = undefined; | 2250 | const dir_path = tmp.path(); |
| 2262 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 2263 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/stats.sock", .{dir_path}); | 2251 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/stats.sock", .{dir_path}); |
| 2264 | defer alloc.free(sock_path); | 2252 | defer alloc.free(sock_path); |
| 2265 | 2253 | ||
| @@ -2305,10 +2293,9 @@ test "Server: broadcast stats count every send but the counterfactual once" { | |||
| 2305 | test "Server: latest attacher's size wins; earlier client is resnapshotted at the new size" { | 2293 | test "Server: latest attacher's size wins; earlier client is resnapshotted at the new size" { |
| 2306 | const alloc = std.testing.allocator; | 2294 | const alloc = std.testing.allocator; |
| 2307 | 2295 | ||
| 2308 | var tmp = std.testing.tmpDir(.{}); | 2296 | var tmp = try TmpDir.make(); |
| 2309 | defer tmp.cleanup(); | 2297 | defer tmp.cleanup(); |
| 2310 | var path_buf: [256]u8 = undefined; | 2298 | const dir_path = tmp.path(); |
| 2311 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 2312 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/latest.sock", .{dir_path}); | 2299 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/latest.sock", .{dir_path}); |
| 2313 | defer alloc.free(sock_path); | 2300 | defer alloc.free(sock_path); |
| 2314 | 2301 | ||
| @@ -2410,10 +2397,9 @@ fn awaitSnapshotSize( | |||
| 2410 | test "Server: typing claims the grid for the typist (latest-wins on input)" { | 2397 | test "Server: typing claims the grid for the typist (latest-wins on input)" { |
| 2411 | const alloc = std.testing.allocator; | 2398 | const alloc = std.testing.allocator; |
| 2412 | 2399 | ||
| 2413 | var tmp = std.testing.tmpDir(.{}); | 2400 | var tmp = try TmpDir.make(); |
| 2414 | defer tmp.cleanup(); | 2401 | defer tmp.cleanup(); |
| 2415 | var path_buf: [256]u8 = undefined; | 2402 | const dir_path = tmp.path(); |
| 2416 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 2417 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/typing.sock", .{dir_path}); | 2403 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/typing.sock", .{dir_path}); |
| 2418 | defer alloc.free(sock_path); | 2404 | defer alloc.free(sock_path); |
| 2419 | 2405 | ||
| @@ -2508,10 +2494,9 @@ fn awaitMarkerWithoutSnapshot( | |||
| 2508 | test "Server: a size the grid refuses never becomes a claim" { | 2494 | test "Server: a size the grid refuses never becomes a claim" { |
| 2509 | const alloc = std.testing.allocator; | 2495 | const alloc = std.testing.allocator; |
| 2510 | 2496 | ||
| 2511 | var tmp = std.testing.tmpDir(.{}); | 2497 | var tmp = try TmpDir.make(); |
| 2512 | defer tmp.cleanup(); | 2498 | defer tmp.cleanup(); |
| 2513 | var path_buf: [256]u8 = undefined; | 2499 | const dir_path = tmp.path(); |
| 2514 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 2515 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/refused.sock", .{dir_path}); | 2500 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/refused.sock", .{dir_path}); |
| 2516 | defer alloc.free(sock_path); | 2501 | defer alloc.free(sock_path); |
| 2517 | 2502 | ||
| @@ -2587,10 +2572,9 @@ test "Server: a size the grid refuses never becomes a claim" { | |||
| 2587 | test "Server: scrollback fetch is per-client and independent" { | 2572 | test "Server: scrollback fetch is per-client and independent" { |
| 2588 | const alloc = std.testing.allocator; | 2573 | const alloc = std.testing.allocator; |
| 2589 | 2574 | ||
| 2590 | var tmp = std.testing.tmpDir(.{}); | 2575 | var tmp = try TmpDir.make(); |
| 2591 | defer tmp.cleanup(); | 2576 | defer tmp.cleanup(); |
| 2592 | var path_buf: [256]u8 = undefined; | 2577 | const dir_path = tmp.path(); |
| 2593 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 2594 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/percli.sock", .{dir_path}); | 2578 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/percli.sock", .{dir_path}); |
| 2595 | defer alloc.free(sock_path); | 2579 | defer alloc.free(sock_path); |
| 2596 | 2580 | ||
| @@ -2684,10 +2668,9 @@ test "Server: scrollback fetch is per-client and independent" { | |||
| 2684 | test "Server: replica rebuilt from snapshots matches the authoritative grid" { | 2668 | test "Server: replica rebuilt from snapshots matches the authoritative grid" { |
| 2685 | const alloc = std.testing.allocator; | 2669 | const alloc = std.testing.allocator; |
| 2686 | 2670 | ||
| 2687 | var tmp = std.testing.tmpDir(.{}); | 2671 | var tmp = try TmpDir.make(); |
| 2688 | defer tmp.cleanup(); | 2672 | defer tmp.cleanup(); |
| 2689 | var path_buf: [256]u8 = undefined; | 2673 | const dir_path = tmp.path(); |
| 2690 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 2691 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/m2.sock", .{dir_path}); | 2674 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/m2.sock", .{dir_path}); |
| 2692 | defer alloc.free(sock_path); | 2675 | defer alloc.free(sock_path); |
| 2693 | 2676 | ||
| @@ -2761,10 +2744,9 @@ test "Server: replica rebuilt from snapshots matches the authoritative grid" { | |||
| 2761 | test "Server: typing produces deltas, not snapshots; stats track both" { | 2744 | test "Server: typing produces deltas, not snapshots; stats track both" { |
| 2762 | const alloc = std.testing.allocator; | 2745 | const alloc = std.testing.allocator; |
| 2763 | 2746 | ||
| 2764 | var tmp = std.testing.tmpDir(.{}); | 2747 | var tmp = try TmpDir.make(); |
| 2765 | defer tmp.cleanup(); | 2748 | defer tmp.cleanup(); |
| 2766 | var path_buf: [256]u8 = undefined; | 2749 | const dir_path = tmp.path(); |
| 2767 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 2768 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/delta.sock", .{dir_path}); | 2750 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/delta.sock", .{dir_path}); |
| 2769 | defer alloc.free(sock_path); | 2751 | defer alloc.free(sock_path); |
| 2770 | 2752 | ||
| @@ -2857,10 +2839,9 @@ test "Server: typing produces deltas, not snapshots; stats track both" { | |||
| 2857 | test "Server: reattach needs a recent have_seq AND this daemon's epoch to get a delta" { | 2839 | test "Server: reattach needs a recent have_seq AND this daemon's epoch to get a delta" { |
| 2858 | const alloc = std.testing.allocator; | 2840 | const alloc = std.testing.allocator; |
| 2859 | 2841 | ||
| 2860 | var tmp = std.testing.tmpDir(.{}); | 2842 | var tmp = try TmpDir.make(); |
| 2861 | defer tmp.cleanup(); | 2843 | defer tmp.cleanup(); |
| 2862 | var path_buf: [256]u8 = undefined; | 2844 | const dir_path = tmp.path(); |
| 2863 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 2864 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/resync.sock", .{dir_path}); | 2845 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/resync.sock", .{dir_path}); |
| 2865 | defer alloc.free(sock_path); | 2846 | defer alloc.free(sock_path); |
| 2866 | 2847 | ||
| @@ -2953,10 +2934,9 @@ test "Server: reattach needs a recent have_seq AND this daemon's epoch to get a | |||
| 2953 | test "Server: a daemon restart invalidates have_seq even with the old epoch presented" { | 2934 | test "Server: a daemon restart invalidates have_seq even with the old epoch presented" { |
| 2954 | const alloc = std.testing.allocator; | 2935 | const alloc = std.testing.allocator; |
| 2955 | 2936 | ||
| 2956 | var tmp = std.testing.tmpDir(.{}); | 2937 | var tmp = try TmpDir.make(); |
| 2957 | defer tmp.cleanup(); | 2938 | defer tmp.cleanup(); |
| 2958 | var path_buf: [256]u8 = undefined; | 2939 | const dir_path = tmp.path(); |
| 2959 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 2960 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/restart.sock", .{dir_path}); | 2940 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/restart.sock", .{dir_path}); |
| 2961 | defer alloc.free(sock_path); | 2941 | defer alloc.free(sock_path); |
| 2962 | 2942 | ||
| @@ -3058,10 +3038,9 @@ fn expectInitRefused(alloc: std.mem.Allocator, path: []const u8, want: anyerror) | |||
| 3058 | test "Server: injected bytes reach frame handling, split anywhere" { | 3038 | test "Server: injected bytes reach frame handling, split anywhere" { |
| 3059 | const alloc = std.testing.allocator; | 3039 | const alloc = std.testing.allocator; |
| 3060 | 3040 | ||
| 3061 | var tmp = std.testing.tmpDir(.{}); | 3041 | var tmp = try TmpDir.make(); |
| 3062 | defer tmp.cleanup(); | 3042 | defer tmp.cleanup(); |
| 3063 | var path_buf: [256]u8 = undefined; | 3043 | const dir_path = tmp.path(); |
| 3064 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 3065 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/inject.sock", .{dir_path}); | 3044 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/inject.sock", .{dir_path}); |
| 3066 | defer alloc.free(sock_path); | 3045 | defer alloc.free(sock_path); |
| 3067 | 3046 | ||
| @@ -3107,10 +3086,9 @@ test "Server: injected bytes reach frame handling, split anywhere" { | |||
| 3107 | test "Server: a second daemon refuses a live socket instead of stealing it" { | 3086 | test "Server: a second daemon refuses a live socket instead of stealing it" { |
| 3108 | const alloc = std.testing.allocator; | 3087 | const alloc = std.testing.allocator; |
| 3109 | 3088 | ||
| 3110 | var tmp = std.testing.tmpDir(.{}); | 3089 | var tmp = try TmpDir.make(); |
| 3111 | defer tmp.cleanup(); | 3090 | defer tmp.cleanup(); |
| 3112 | var path_buf: [256]u8 = undefined; | 3091 | const dir_path = tmp.path(); |
| 3113 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 3114 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/live.sock", .{dir_path}); | 3092 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/live.sock", .{dir_path}); |
| 3115 | defer alloc.free(sock_path); | 3093 | defer alloc.free(sock_path); |
| 3116 | 3094 | ||
| @@ -3145,10 +3123,9 @@ test "Server: a second daemon refuses a live socket instead of stealing it" { | |||
| 3145 | test "Server: a dead daemon's leftover socket file is cleared and rebound" { | 3123 | test "Server: a dead daemon's leftover socket file is cleared and rebound" { |
| 3146 | const alloc = std.testing.allocator; | 3124 | const alloc = std.testing.allocator; |
| 3147 | 3125 | ||
| 3148 | var tmp = std.testing.tmpDir(.{}); | 3126 | var tmp = try TmpDir.make(); |
| 3149 | defer tmp.cleanup(); | 3127 | defer tmp.cleanup(); |
| 3150 | var path_buf: [256]u8 = undefined; | 3128 | const dir_path = tmp.path(); |
| 3151 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 3152 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/stale.sock", .{dir_path}); | 3129 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/stale.sock", .{dir_path}); |
| 3153 | defer alloc.free(sock_path); | 3130 | defer alloc.free(sock_path); |
| 3154 | 3131 | ||
| @@ -3183,10 +3160,9 @@ test "Server: a dead daemon's leftover socket file is cleared and rebound" { | |||
| 3183 | test "Server: a path that cannot be bound fails as AddressInUse" { | 3160 | test "Server: a path that cannot be bound fails as AddressInUse" { |
| 3184 | const alloc = std.testing.allocator; | 3161 | const alloc = std.testing.allocator; |
| 3185 | 3162 | ||
| 3186 | var tmp = std.testing.tmpDir(.{}); | 3163 | var tmp = try TmpDir.make(); |
| 3187 | defer tmp.cleanup(); | 3164 | defer tmp.cleanup(); |
| 3188 | var path_buf: [256]u8 = undefined; | 3165 | const dir_path = tmp.path(); |
| 3189 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 3190 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/dangling.sock", .{dir_path}); | 3166 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/dangling.sock", .{dir_path}); |
| 3191 | defer alloc.free(sock_path); | 3167 | defer alloc.free(sock_path); |
| 3192 | 3168 | ||
| @@ -3213,10 +3189,9 @@ test "Server: a path that cannot be bound fails as AddressInUse" { | |||
| 3213 | test "Server: a non-socket at the path is refused, not deleted" { | 3189 | test "Server: a non-socket at the path is refused, not deleted" { |
| 3214 | const alloc = std.testing.allocator; | 3190 | const alloc = std.testing.allocator; |
| 3215 | 3191 | ||
| 3216 | var tmp = std.testing.tmpDir(.{}); | 3192 | var tmp = try TmpDir.make(); |
| 3217 | defer tmp.cleanup(); | 3193 | defer tmp.cleanup(); |
| 3218 | var path_buf: [256]u8 = undefined; | 3194 | const dir_path = tmp.path(); |
| 3219 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 3220 | const file_path = try std.fmt.allocPrint(alloc, "{s}/notes.txt", .{dir_path}); | 3195 | const file_path = try std.fmt.allocPrint(alloc, "{s}/notes.txt", .{dir_path}); |
| 3221 | defer alloc.free(file_path); | 3196 | defer alloc.free(file_path); |
| 3222 | 3197 | ||
| @@ -3430,10 +3405,9 @@ fn attachOver(cl: *quic.TestClient, buf: *std.ArrayList(u8), alloc: std.mem.Allo | |||
| 3430 | 3405 | ||
| 3431 | test "Server: a QUIC client still receives the shell's exit status" { | 3406 | test "Server: a QUIC client still receives the shell's exit status" { |
| 3432 | const alloc = std.testing.allocator; | 3407 | const alloc = std.testing.allocator; |
| 3433 | var tmp = std.testing.tmpDir(.{}); | 3408 | var tmp = try TmpDir.make(); |
| 3434 | defer tmp.cleanup(); | 3409 | defer tmp.cleanup(); |
| 3435 | var path_buf: [256]u8 = undefined; | 3410 | const dir_path = tmp.path(); |
| 3436 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 3437 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/qexit.sock", .{dir_path}); | 3411 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/qexit.sock", .{dir_path}); |
| 3438 | defer alloc.free(sock_path); | 3412 | defer alloc.free(sock_path); |
| 3439 | 3413 | ||
| @@ -3484,10 +3458,9 @@ test "Server: a QUIC client still receives the shell's exit status" { | |||
| 3484 | 3458 | ||
| 3485 | test "Server: one QUIC client leaving does not disturb the other" { | 3459 | test "Server: one QUIC client leaving does not disturb the other" { |
| 3486 | const alloc = std.testing.allocator; | 3460 | const alloc = std.testing.allocator; |
| 3487 | var tmp = std.testing.tmpDir(.{}); | 3461 | var tmp = try TmpDir.make(); |
| 3488 | defer tmp.cleanup(); | 3462 | defer tmp.cleanup(); |
| 3489 | var path_buf: [256]u8 = undefined; | 3463 | const dir_path = tmp.path(); |
| 3490 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 3491 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/qtwo.sock", .{dir_path}); | 3464 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/qtwo.sock", .{dir_path}); |
| 3492 | defer alloc.free(sock_path); | 3465 | defer alloc.free(sock_path); |
| 3493 | 3466 | ||
| @@ -3555,10 +3528,9 @@ test "Server: one QUIC client leaving does not disturb the other" { | |||
| 3555 | 3528 | ||
| 3556 | test "Server: a QUIC client that stops reading is dropped by the cap, not tolerated" { | 3529 | test "Server: a QUIC client that stops reading is dropped by the cap, not tolerated" { |
| 3557 | const alloc = std.testing.allocator; | 3530 | const alloc = std.testing.allocator; |
| 3558 | var tmp = std.testing.tmpDir(.{}); | 3531 | var tmp = try TmpDir.make(); |
| 3559 | defer tmp.cleanup(); | 3532 | defer tmp.cleanup(); |
| 3560 | var path_buf: [256]u8 = undefined; | 3533 | const dir_path = tmp.path(); |
| 3561 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 3562 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/qcap.sock", .{dir_path}); | 3534 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/qcap.sock", .{dir_path}); |
| 3563 | defer alloc.free(sock_path); | 3535 | defer alloc.free(sock_path); |
| 3564 | 3536 | ||
| @@ -3612,10 +3584,9 @@ test "Server: a QUIC client that stops reading is dropped by the cap, not tolera | |||
| 3612 | 3584 | ||
| 3613 | test "Server: drainPending waits for a QUIC client's acks, not just its queue" { | 3585 | test "Server: drainPending waits for a QUIC client's acks, not just its queue" { |
| 3614 | const alloc = std.testing.allocator; | 3586 | const alloc = std.testing.allocator; |
| 3615 | var tmp = std.testing.tmpDir(.{}); | 3587 | var tmp = try TmpDir.make(); |
| 3616 | defer tmp.cleanup(); | 3588 | defer tmp.cleanup(); |
| 3617 | var path_buf: [256]u8 = undefined; | 3589 | const dir_path = tmp.path(); |
| 3618 | const dir_path = try tmp.dir.realpath(".", &path_buf); | ||
| 3619 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/qdrain.sock", .{dir_path}); | 3590 | const sock_path = try std.fmt.allocPrint(alloc, "{s}/qdrain.sock", .{dir_path}); |
| 3620 | defer alloc.free(sock_path); | 3591 | defer alloc.free(sock_path); |
| 3621 | 3592 | ||
src/testtmp.zig
| Old | New | ||
|---|---|---|---|
| @@ -0,0 +1,86 @@ | |||
| 1 | //! Temp directories with SHORT paths, for tests that bind unix sockets. | ||
| 2 | //! | ||
| 3 | //! `std.testing.tmpDir` puts its directory under `.zig-cache/tmp`, which | ||
| 4 | //! makes the path as long as wherever the repository happens to be checked | ||
| 5 | //! out. A unix socket address caps its path at 108 bytes (`sun_path`), so a | ||
| 6 | //! checkout a couple of directories deeper than usual turns every | ||
| 7 | //! socket-binding test in this project into a `NameTooLong` failure — and | ||
| 8 | //! that failure surfaces from inside `std.net`, so it reads like a bug in | ||
| 9 | //! the code under test rather than a fact about the checkout. It has now | ||
| 10 | //! cost three people a gate cycle each, which is three more than a hazard | ||
| 11 | //! that is this cheap to remove should cost anyone. | ||
| 12 | //! | ||
| 13 | //! Same shape as `std.testing.tmpDir` — `.dir` and `.cleanup()` — plus | ||
| 14 | //! `path()`, which is what socket paths get built from. A directory here is | ||
| 15 | //! ~21 characters regardless of where the repository lives. | ||
| 16 | const std = @import("std"); | ||
| 17 | |||
| 18 | pub const TmpDir = struct { | ||
| 19 | dir: std.fs.Dir, | ||
| 20 | buf: [32]u8 = undefined, | ||
| 21 | len: usize = 0, | ||
| 22 | |||
| 23 | pub fn make() !TmpDir { | ||
| 24 | var self: TmpDir = .{ .dir = undefined }; | ||
| 25 | while (true) { | ||
| 26 | const p = try std.fmt.bufPrint( | ||
| 27 | &self.buf, | ||
| 28 | "/tmp/mux-t{x:0>12}", | ||
| 29 | .{std.crypto.random.int(u48)}, | ||
| 30 | ); | ||
| 31 | self.len = p.len; | ||
| 32 | std.fs.cwd().makeDir(p) catch |err| switch (err) { | ||
| 33 | // Two tests in the same millisecond is ordinary; two that | ||
| 34 | // drew the same 48 bits is not, but it costs one retry. | ||
| 35 | error.PathAlreadyExists => continue, | ||
| 36 | else => return err, | ||
| 37 | }; | ||
| 38 | self.dir = try std.fs.cwd().openDir(p, .{ .iterate = true }); | ||
| 39 | return self; | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | pub fn path(self: *const TmpDir) []const u8 { | ||
| 44 | return self.buf[0..self.len]; | ||
| 45 | } | ||
| 46 | |||
| 47 | /// Idempotent, because the natural way to use this is a `defer` plus an | ||
| 48 | /// occasional early cleanup, and closing an already-closed directory | ||
| 49 | /// aborts rather than complaining. | ||
| 50 | pub fn cleanup(self: *TmpDir) void { | ||
| 51 | if (self.len == 0) return; | ||
| 52 | const p = self.path(); | ||
| 53 | self.dir.close(); | ||
| 54 | std.fs.cwd().deleteTree(p) catch {}; | ||
| 55 | self.len = 0; | ||
| 56 | } | ||
| 57 | }; | ||
| 58 | |||
| 59 | test "TmpDir: a path short enough to bind a socket in" { | ||
| 60 | var tmp = try TmpDir.make(); | ||
| 61 | defer tmp.cleanup(); | ||
| 62 | |||
| 63 | // The number that matters. sun_path is 108 bytes including the | ||
| 64 | // terminator, and this leaves room for a filename inside it. | ||
| 65 | try std.testing.expect(tmp.path().len < 32); | ||
| 66 | try std.testing.expect(std.mem.startsWith(u8, tmp.path(), "/tmp/")); | ||
| 67 | |||
| 68 | // It really is a directory, and it really is writable. | ||
| 69 | try tmp.dir.writeFile(.{ .sub_path = "probe", .data = "x" }); | ||
| 70 | |||
| 71 | // ...and a socket binds in it, which is the whole point. Built the way | ||
| 72 | // the tests build theirs, so the length being tested is the real one. | ||
| 73 | var buf: [128]u8 = undefined; | ||
| 74 | const sock_path = try std.fmt.bufPrint(&buf, "{s}/probe.sock", .{tmp.path()}); | ||
| 75 | const addr = try std.net.Address.initUnix(sock_path); | ||
| 76 | var server = try addr.listen(.{}); | ||
| 77 | server.deinit(); | ||
| 78 | |||
| 79 | const saved = try std.fmt.allocPrint(std.testing.allocator, "{s}", .{tmp.path()}); | ||
| 80 | defer std.testing.allocator.free(saved); | ||
| 81 | tmp.cleanup(); | ||
| 82 | try std.testing.expectError(error.FileNotFound, std.fs.cwd().access(saved, .{})); | ||
| 83 | // The `defer` above will call cleanup a second time. That has to be | ||
| 84 | // harmless, or every test using this would have to choose between an | ||
| 85 | // early cleanup and a defer — and closing a closed directory aborts. | ||
| 86 | } | ||