a73x

90c7c32f

The daemon's tests connect like a client: the dial sweep

a73x   2026-08-31 15:37

Commit message
The daemon's tests connect like a client: the dial sweep

All 175 hand-rolled `std.net.connectUnixSocket` sites across the eight
`server_test_*.zig` files now go through the `dial` module, so the suite
opens a daemon socket the way the CLI client and `mux a` do rather than
respelling the pair of calls at every site.

  61  dial.dialAttach       — fresh attaches at the default session. Sizes
                              pass through untouched: the 100x30, 1x1 and
                              0x0 sites are the tests' subjects and keep
                              their numbers.
  49  dial.dialAttachNamed  — fresh attaches at a named session.
  65  dial.dial             — the connection alone, no frame sent:
                              41 deliberately attach-free dials (observer
                              verbs, half-open and garbage probes, and the
                              agent-socket clients that dial `agent_path`),
                              15 dials whose attach is `attachOffering`'s,
                              and 9 watermark resumes, where the
                              hand-spelled `encodeAttach(have_seq,
                              have_epoch)` stays visible because the
                              watermark is what those tests are about.

`server_test_attach.zig`'s attach+resize-in-one-write keeps its
`appendFrame` block — folding it would destroy the property it pins — and
only its dial line changed spelling.

`h.attachNamed` stays: two genuine callers attach an fd that was not just
dialled — `attachOffering`, which seats a client its caller opened, and
the re-attach of one live connection to a second session in
`server_test_await.zig`. Its now-dead per-file aliases in the clipboard,
session and attach suites are gone; agent's and await's remain.

The harness spells `@import("dial")` once and re-exports it, which is what
spends the `daemon` row's new `test_imports` grant.

No test was added, removed or reworded: 1056 before, 1056 after.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

build.zig
Old New
@@ -187,7 +187,7 @@ const mod_table = [_]ModSpec{
187 // daemon itself when nobody handed it a --key — and for the shim 187 // daemon itself when nobody handed it a --key — and for the shim
188 // directory shell integration writes under the same 0700 policy. 188 // directory shell integration writes under the same 0700 policy.
189 // `pty` stays a row of its own: the ptyclient fixture consumes it. 189 // `pty` stays a row of its own: the ptyclient fixture consumes it.
190 .{ .name = "daemon", .path = "src/server/server.zig", .link_libc = true, .imports = &.{ "term", "pty", "sockpath", "quic", "xdg", "proxy" }, .test_imports = &.{"testtmp"}, .quic_tests = true }, 190 .{ .name = "daemon", .path = "src/server/server.zig", .link_libc = true, .imports = &.{ "term", "pty", "sockpath", "quic", "xdg", "proxy" }, .test_imports = &.{ "testtmp", "dial" }, .quic_tests = true },
191 // The agent-facing client. It speaks frames and owns no terminal, which 191 // The agent-facing client. It speaks frames and owns no terminal, which
192 // is the whole point — it attaches at 0x0 and never claims the grid. 192 // is the whole point — it attaches at 0x0 and never claims the grid.
193 // The transport modules are the CLI client's, minus everything that 193 // The transport modules are the CLI client's, minus everything that
src/server/server_test_agent.zig
Old New
@@ -4,6 +4,7 @@ const quic = @import("quic");
4 const quic_server = @import("quic_server.zig"); 4 const quic_server = @import("quic_server.zig");
5 const TmpDir = @import("testtmp").TmpDir; 5 const TmpDir = @import("testtmp").TmpDir;
6 const h = @import("server_test_harness.zig"); 6 const h = @import("server_test_harness.zig");
7 const dial = h.dial;
7 const srv_mod = @import("server.zig"); 8 const srv_mod = @import("server.zig");
8 const Server = srv_mod.Server; 9 const Server = srv_mod.Server;
9 const max_agent_chans = srv_mod.max_agent_chans; 10 const max_agent_chans = srv_mod.max_agent_chans;
@@ -106,9 +107,8 @@ test "Server: agent_offer flags the slot, and an unknown type leaves the client
106 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 107 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
107 defer srv.deinit(); 108 defer srv.deinit();
108 109
109 const c = try std.net.connectUnixSocket(sock_path); 110 const c = try dial.dialAttachNamed(sock_path, 80, 24, "");
110 defer c.close(); 111 defer c.close();
111 try attachNamed(c.handle, 80, 24, "");
112 var spun: usize = 0; 112 var spun: usize = 0;
113 while (spun < 200 and srv.clients[0] == null) : (spun += 1) try srv.pumpOnce(5); 113 while (spun < 200 and srv.clients[0] == null) : (spun += 1) try srv.pumpOnce(5);
114 try std.testing.expect(srv.clients[0] != null); 114 try std.testing.expect(srv.clients[0] != null);
@@ -221,9 +221,8 @@ test "Server: a session with no agent socket does not inherit the daemon's" {
221 ); 221 );
222 defer alloc.free(cmd); 222 defer alloc.free(cmd);
223 223
224 const c = try std.net.connectUnixSocket(sock_path); 224 const c = try dial.dialAttachNamed(sock_path, 80, 24, "nosock");
225 defer c.close(); 225 defer c.close();
226 try attachNamed(c.handle, 80, 24, "nosock");
227 var spun: usize = 0; 226 var spun: usize = 0;
228 while (spun < 200 and srv.clients[0] == null) : (spun += 1) try srv.pumpOnce(5); 227 while (spun < 200 and srv.clients[0] == null) : (spun += 1) try srv.pumpOnce(5);
229 const si = srv.clients[0].?.session orelse return error.ClientNeverSeated; 228 const si = srv.clients[0].?.session orelse return error.ClientNeverSeated;
@@ -256,7 +255,7 @@ test "Server: a full channel table refuses the newest dial and says so once" {
256 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 255 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
257 defer srv.deinit(); 256 defer srv.deinit();
258 257
259 const c = try std.net.connectUnixSocket(sock_path); 258 const c = try dial.dial(sock_path);
260 defer c.close(); 259 defer c.close();
261 try attachOffering(&srv, c.handle, 0, ""); 260 try attachOffering(&srv, c.handle, 0, "");
262 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket; 261 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket;
@@ -265,7 +264,7 @@ test "Server: a full channel table refuses the newest dial and says so once" {
265 // so a long-lived agent connection holding a slot is the field failure 264 // so a long-lived agent connection holding a slot is the field failure
266 // this counts — eight of them turn forwarding off for every session. 265 // this counts — eight of them turn forwarding off for every session.
267 var dials: [max_agent_chans + 1]std.net.Stream = undefined; 266 var dials: [max_agent_chans + 1]std.net.Stream = undefined;
268 for (&dials) |*d| d.* = try std.net.connectUnixSocket(path); 267 for (&dials) |*d| d.* = try dial.dial(path);
269 defer for (dials) |d| d.close(); 268 defer for (dials) |d| d.close();
270 269
271 var spun: usize = 0; 270 var spun: usize = 0;
@@ -293,7 +292,7 @@ test "Server: a full channel table refuses the newest dial and says so once" {
293 // and only a freed slot re-arms the line. ssh retries, so a log that 292 // and only a freed slot re-arms the line. ssh retries, so a log that
294 // scrolled would be its own outage. 293 // scrolled would be its own outage.
295 try std.testing.expect(srv.agents.full_said); 294 try std.testing.expect(srv.agents.full_said);
296 const again = try std.net.connectUnixSocket(path); 295 const again = try dial.dial(path);
297 defer again.close(); 296 defer again.close();
298 spun = 0; 297 spun = 0;
299 while (spun < 400 and srv.agents.refused_full < 2) : (spun += 1) try srv.pumpOnce(5); 298 while (spun < 400 and srv.agents.refused_full < 2) : (spun += 1) try srv.pumpOnce(5);
@@ -314,9 +313,8 @@ test "Server: an agent connection with nobody offering is refused fast" {
314 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 313 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
315 defer srv.deinit(); 314 defer srv.deinit();
316 315
317 const c = try std.net.connectUnixSocket(sock_path); 316 const c = try dial.dialAttachNamed(sock_path, 80, 24, "");
318 defer c.close(); 317 defer c.close();
319 try attachNamed(c.handle, 80, 24, "");
320 var spun: usize = 0; 318 var spun: usize = 0;
321 while (spun < 200 and srv.clients[0] == null) : (spun += 1) try srv.pumpOnce(5); 319 while (spun < 200 and srv.clients[0] == null) : (spun += 1) try srv.pumpOnce(5);
322 try std.testing.expect(srv.clients[0] != null); 320 try std.testing.expect(srv.clients[0] != null);
@@ -328,13 +326,13 @@ test "Server: an agent connection with nobody offering is refused fast" {
328 // exists so a dial can be attributed to one shell; an answerer taken 326 // exists so a dial can be attributed to one shell; an answerer taken
329 // from the daemon at large would hand this session's ssh the key of 327 // from the daemon at large would hand this session's ssh the key of
330 // somebody sitting in front of another one. 328 // somebody sitting in front of another one.
331 const other = try std.net.connectUnixSocket(sock_path); 329 const other = try dial.dial(sock_path);
332 defer other.close(); 330 defer other.close();
333 try attachOffering(&srv, other.handle, 1, "b"); 331 try attachOffering(&srv, other.handle, 1, "b");
334 try std.testing.expect(srv.clients[1].?.session != srv.clients[0].?.session); 332 try std.testing.expect(srv.clients[1].?.session != srv.clients[0].?.session);
335 333
336 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket; 334 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket;
337 const agent = try std.net.connectUnixSocket(path); 335 const agent = try dial.dial(path);
338 defer agent.close(); 336 defer agent.close();
339 337
340 // EOF, and quickly: ssh reads a closed agent socket as "agent refused 338 // EOF, and quickly: ssh reads a closed agent socket as "agent refused
@@ -357,12 +355,12 @@ test "Server: agent bytes pump both ways through a channel" {
357 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 355 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
358 defer srv.deinit(); 356 defer srv.deinit();
359 357
360 const c = try std.net.connectUnixSocket(sock_path); 358 const c = try dial.dial(sock_path);
361 defer c.close(); 359 defer c.close();
362 try attachOffering(&srv, c.handle, 0, ""); 360 try attachOffering(&srv, c.handle, 0, "");
363 361
364 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket; 362 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket;
365 const agent = try std.net.connectUnixSocket(path); 363 const agent = try dial.dial(path);
366 defer agent.close(); 364 defer agent.close();
367 365
368 // The daemon allocates the id and announces the channel; the client 366 // The daemon allocates the id and announces the channel; the client
@@ -414,10 +412,10 @@ test "Server: an agent connection is routed to the latest-active offerer" {
414 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 412 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
415 defer srv.deinit(); 413 defer srv.deinit();
416 414
417 const ca = try std.net.connectUnixSocket(sock_path); 415 const ca = try dial.dial(sock_path);
418 defer ca.close(); 416 defer ca.close();
419 try attachOffering(&srv, ca.handle, 0, ""); 417 try attachOffering(&srv, ca.handle, 0, "");
420 const cb = try std.net.connectUnixSocket(sock_path); 418 const cb = try dial.dial(sock_path);
421 defer cb.close(); 419 defer cb.close();
422 try attachOffering(&srv, cb.handle, 1, ""); 420 try attachOffering(&srv, cb.handle, 1, "");
423 421
@@ -435,7 +433,7 @@ test "Server: an agent connection is routed to the latest-active offerer" {
435 } 433 }
436 try std.testing.expect(srv.clients[1].?.activity > srv.clients[0].?.activity); 434 try std.testing.expect(srv.clients[1].?.activity > srv.clients[0].?.activity);
437 435
438 const first = try std.net.connectUnixSocket(path); 436 const first = try dial.dial(path);
439 defer first.close(); 437 defer first.close();
440 const to_b = (try awaitFrame(alloc, &srv, cb.handle, .agent_open, 200)) orelse 438 const to_b = (try awaitFrame(alloc, &srv, cb.handle, .agent_open, 200)) orelse
441 return error.NoAgentOpenForB; 439 return error.NoAgentOpenForB;
@@ -458,7 +456,7 @@ test "Server: an agent connection is routed to the latest-active offerer" {
458 } 456 }
459 try std.testing.expect(srv.clients[0].?.activity > srv.clients[1].?.activity); 457 try std.testing.expect(srv.clients[0].?.activity > srv.clients[1].?.activity);
460 458
461 const second = try std.net.connectUnixSocket(path); 459 const second = try dial.dial(path);
462 defer second.close(); 460 defer second.close();
463 const to_a = (try awaitFrame(alloc, &srv, ca.handle, .agent_open, 200)) orelse 461 const to_a = (try awaitFrame(alloc, &srv, ca.handle, .agent_open, 200)) orelse
464 return error.NoAgentOpenForA; 462 return error.NoAgentOpenForA;
@@ -482,16 +480,16 @@ test "Server: agent_data for an unknown or another client's channel is dropped"
482 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 480 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
483 defer srv.deinit(); 481 defer srv.deinit();
484 482
485 const ca = try std.net.connectUnixSocket(sock_path); 483 const ca = try dial.dial(sock_path);
486 defer ca.close(); 484 defer ca.close();
487 try attachOffering(&srv, ca.handle, 0, ""); 485 try attachOffering(&srv, ca.handle, 0, "");
488 const cb = try std.net.connectUnixSocket(sock_path); 486 const cb = try dial.dial(sock_path);
489 defer cb.close(); 487 defer cb.close();
490 try attachOffering(&srv, cb.handle, 1, ""); 488 try attachOffering(&srv, cb.handle, 1, "");
491 489
492 // B attached last, so the channel is B's. 490 // B attached last, so the channel is B's.
493 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket; 491 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket;
494 const agent = try std.net.connectUnixSocket(path); 492 const agent = try dial.dial(path);
495 defer agent.close(); 493 defer agent.close();
496 const open = (try awaitFrame(alloc, &srv, cb.handle, .agent_open, 200)) orelse 494 const open = (try awaitFrame(alloc, &srv, cb.handle, .agent_open, 200)) orelse
497 return error.NoAgentOpen; 495 return error.NoAgentOpen;
@@ -546,12 +544,12 @@ test "Server: an agent_data frame past the cap hangs the channel up" {
546 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 544 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
547 defer srv.deinit(); 545 defer srv.deinit();
548 546
549 const c = try std.net.connectUnixSocket(sock_path); 547 const c = try dial.dial(sock_path);
550 defer c.close(); 548 defer c.close();
551 try attachOffering(&srv, c.handle, 0, ""); 549 try attachOffering(&srv, c.handle, 0, "");
552 550
553 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket; 551 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket;
554 const agent = try std.net.connectUnixSocket(path); 552 const agent = try dial.dial(path);
555 defer agent.close(); 553 defer agent.close();
556 const open = (try awaitFrame(alloc, &srv, c.handle, .agent_open, 200)) orelse 554 const open = (try awaitFrame(alloc, &srv, c.handle, .agent_open, 200)) orelse
557 return error.NoAgentOpen; 555 return error.NoAgentOpen;
@@ -598,12 +596,12 @@ test "Server: a client closing a channel hangs up the agent connection without a
598 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 596 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
599 defer srv.deinit(); 597 defer srv.deinit();
600 598
601 const c = try std.net.connectUnixSocket(sock_path); 599 const c = try dial.dial(sock_path);
602 defer c.close(); 600 defer c.close();
603 try attachOffering(&srv, c.handle, 0, ""); 601 try attachOffering(&srv, c.handle, 0, "");
604 602
605 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket; 603 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket;
606 const agent = try std.net.connectUnixSocket(path); 604 const agent = try dial.dial(path);
607 defer agent.close(); 605 defer agent.close();
608 const open = (try awaitFrame(alloc, &srv, c.handle, .agent_open, 200)) orelse 606 const open = (try awaitFrame(alloc, &srv, c.handle, .agent_open, 200)) orelse
609 return error.NoAgentOpen; 607 return error.NoAgentOpen;
@@ -640,12 +638,12 @@ test "Server: an agent listener and the connections it accepts are close-on-exec
640 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 638 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
641 defer srv.deinit(); 639 defer srv.deinit();
642 640
643 const c = try std.net.connectUnixSocket(sock_path); 641 const c = try dial.dial(sock_path);
644 defer c.close(); 642 defer c.close();
645 try attachOffering(&srv, c.handle, 0, ""); 643 try attachOffering(&srv, c.handle, 0, "");
646 644
647 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket; 645 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket;
648 const agent = try std.net.connectUnixSocket(path); 646 const agent = try dial.dial(path);
649 defer agent.close(); 647 defer agent.close();
650 const open = (try awaitFrame(alloc, &srv, c.handle, .agent_open, 200)) orelse 648 const open = (try awaitFrame(alloc, &srv, c.handle, .agent_open, 200)) orelse
651 return error.NoAgentOpen; 649 return error.NoAgentOpen;
@@ -675,11 +673,11 @@ test "Server: a client's agent channels die with the client" {
675 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 673 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
676 defer srv.deinit(); 674 defer srv.deinit();
677 675
678 const c = try std.net.connectUnixSocket(sock_path); 676 const c = try dial.dial(sock_path);
679 try attachOffering(&srv, c.handle, 0, ""); 677 try attachOffering(&srv, c.handle, 0, "");
680 678
681 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket; 679 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket;
682 const agent = try std.net.connectUnixSocket(path); 680 const agent = try dial.dial(path);
683 defer agent.close(); 681 defer agent.close();
684 const open = (try awaitFrame(alloc, &srv, c.handle, .agent_open, 200)) orelse 682 const open = (try awaitFrame(alloc, &srv, c.handle, .agent_open, 200)) orelse
685 return error.NoAgentOpen; 683 return error.NoAgentOpen;
@@ -732,7 +730,7 @@ test "Server: a QUIC client's agent channels die with the client" {
732 if (srv.clients[0] == null or !srv.clients[0].?.agent_offer) return error.ClientNeverOffered; 730 if (srv.clients[0] == null or !srv.clients[0].?.agent_offer) return error.ClientNeverOffered;
733 731
734 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket; 732 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket;
735 const agent = try std.net.connectUnixSocket(path); 733 const agent = try dial.dial(path);
736 defer agent.close(); 734 defer agent.close();
737 try quicPump(&srv, &only, 10000, &cl, struct { 735 try quicPump(&srv, &only, 10000, &cl, struct {
738 fn f(t: *quic_server.TestPeer) bool { 736 fn f(t: *quic_server.TestPeer) bool {
@@ -790,12 +788,12 @@ test "Server: an offerer that never answers its first request is hung up on and
790 defer srv.deinit(); 788 defer srv.deinit();
791 srv.agents.answer_ms = 150; 789 srv.agents.answer_ms = 150;
792 790
793 const c = try std.net.connectUnixSocket(sock_path); 791 const c = try dial.dial(sock_path);
794 defer c.close(); 792 defer c.close();
795 try attachOffering(&srv, c.handle, 0, ""); 793 try attachOffering(&srv, c.handle, 0, "");
796 794
797 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket; 795 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket;
798 const agent = try std.net.connectUnixSocket(path); 796 const agent = try dial.dial(path);
799 defer agent.close(); 797 defer agent.close();
800 const id = try openAndAsk(alloc, &srv, c.handle, agent.handle); 798 const id = try openAndAsk(alloc, &srv, c.handle, agent.handle);
801 799
@@ -815,7 +813,7 @@ test "Server: an offerer that never answers its first request is hung up on and
815 // And it is no longer an offerer: the next dial must not route here 813 // And it is no longer an offerer: the next dial must not route here
816 // again, or every ssh pays the bound before falling through. 814 // again, or every ssh pays the bound before falling through.
817 try std.testing.expect(!srv.clients[0].?.agent_offer); 815 try std.testing.expect(!srv.clients[0].?.agent_offer);
818 const again = try std.net.connectUnixSocket(path); 816 const again = try dial.dial(path);
819 defer again.close(); 817 defer again.close();
820 try std.testing.expectEqual(@as(?usize, 0), try pumpUntilReadable(&srv, again.handle, &buf, 60)); 818 try std.testing.expectEqual(@as(?usize, 0), try pumpUntilReadable(&srv, again.handle, &buf, 60));
821 try std.testing.expectEqual(@as(u32, 1), srv.agents.refused_no_offer); 819 try std.testing.expectEqual(@as(u32, 1), srv.agents.refused_no_offer);
@@ -833,12 +831,12 @@ test "Server: a channel that has answered once is never timed out" {
833 defer srv.deinit(); 831 defer srv.deinit();
834 srv.agents.answer_ms = 150; 832 srv.agents.answer_ms = 150;
835 833
836 const c = try std.net.connectUnixSocket(sock_path); 834 const c = try dial.dial(sock_path);
837 defer c.close(); 835 defer c.close();
838 try attachOffering(&srv, c.handle, 0, ""); 836 try attachOffering(&srv, c.handle, 0, "");
839 837
840 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket; 838 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket;
841 const agent = try std.net.connectUnixSocket(path); 839 const agent = try dial.dial(path);
842 defer agent.close(); 840 defer agent.close();
843 const id = try openAndAsk(alloc, &srv, c.handle, agent.handle); 841 const id = try openAndAsk(alloc, &srv, c.handle, agent.handle);
844 842
@@ -876,12 +874,12 @@ test "Server: bytes a client sends before it was asked prove nothing" {
876 defer srv.deinit(); 874 defer srv.deinit();
877 srv.agents.answer_ms = 150; 875 srv.agents.answer_ms = 150;
878 876
879 const c = try std.net.connectUnixSocket(sock_path); 877 const c = try dial.dial(sock_path);
880 defer c.close(); 878 defer c.close();
881 try attachOffering(&srv, c.handle, 0, ""); 879 try attachOffering(&srv, c.handle, 0, "");
882 880
883 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket; 881 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket;
884 const agent = try std.net.connectUnixSocket(path); 882 const agent = try dial.dial(path);
885 defer agent.close(); 883 defer agent.close();
886 const open = (try awaitFrame(alloc, &srv, c.handle, .agent_open, 200)) orelse 884 const open = (try awaitFrame(alloc, &srv, c.handle, .agent_open, 200)) orelse
887 return error.NoAgentOpen; 885 return error.NoAgentOpen;
@@ -919,12 +917,12 @@ test "Server: a channel nobody has asked anything on is not timed out" {
919 defer srv.deinit(); 917 defer srv.deinit();
920 srv.agents.answer_ms = 150; 918 srv.agents.answer_ms = 150;
921 919
922 const c = try std.net.connectUnixSocket(sock_path); 920 const c = try dial.dial(sock_path);
923 defer c.close(); 921 defer c.close();
924 try attachOffering(&srv, c.handle, 0, ""); 922 try attachOffering(&srv, c.handle, 0, "");
925 923
926 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket; 924 const path = srv.ses(0).agent_path orelse return error.NoAgentSocket;
927 const agent = try std.net.connectUnixSocket(path); 925 const agent = try dial.dial(path);
928 defer agent.close(); 926 defer agent.close();
929 const open = (try awaitFrame(alloc, &srv, c.handle, .agent_open, 200)) orelse 927 const open = (try awaitFrame(alloc, &srv, c.handle, .agent_open, 200)) orelse
930 return error.NoAgentOpen; 928 return error.NoAgentOpen;
src/server/server_test_attach.zig
Old New
@@ -3,12 +3,12 @@ const Engine = @import("term").engine.Engine;
3 const proto = @import("term").protocol; 3 const proto = @import("term").protocol;
4 const TmpDir = @import("testtmp").TmpDir; 4 const TmpDir = @import("testtmp").TmpDir;
5 const h = @import("server_test_harness.zig"); 5 const h = @import("server_test_harness.zig");
6 const dial = h.dial;
6 const srv_mod = @import("server.zig"); 7 const srv_mod = @import("server.zig");
7 const Server = srv_mod.Server; 8 const Server = srv_mod.Server;
8 const installSignalHandlers = srv_mod.installSignalHandlers; 9 const installSignalHandlers = srv_mod.installSignalHandlers;
9 const max_clients = srv_mod.max_clients; 10 const max_clients = srv_mod.max_clients;
10 const applyFrame = h.applyFrame; 11 const applyFrame = h.applyFrame;
11 const attachNamed = h.attachNamed;
12 const awaitFrame = h.awaitFrame; 12 const awaitFrame = h.awaitFrame;
13 const connectedPair = h.connectedPair; 13 const connectedPair = h.connectedPair;
14 const firstStateFrame = h.firstStateFrame; 14 const firstStateFrame = h.firstStateFrame;
@@ -33,8 +33,7 @@ test "Server: survives a client that dies without detaching; next attach works"
33 defer stop.store(true, .release); 33 defer stop.store(true, .release);
34 34
35 // Client 1 attaches, provokes output, then vanishes without detach. 35 // Client 1 attaches, provokes output, then vanishes without detach.
36 const a = try std.net.connectUnixSocket(sock_path); 36 const a = try dial.dialAttach(sock_path, 80, 24);
37 try proto.writeFrame(a.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
38 try proto.writeFrame(a.handle, .input, "echo pre-kill\n"); 37 try proto.writeFrame(a.handle, .input, "echo pre-kill\n");
39 std.Thread.sleep(300 * std.time.ns_per_ms); 38 std.Thread.sleep(300 * std.time.ns_per_ms);
40 a.close(); // abrupt: no detach frame 39 a.close(); // abrupt: no detach frame
@@ -43,9 +42,8 @@ test "Server: survives a client that dies without detaching; next attach works"
43 std.Thread.sleep(300 * std.time.ns_per_ms); 42 std.Thread.sleep(300 * std.time.ns_per_ms);
44 43
45 // Daemon must still be serving: a fresh attach gets a snapshot. 44 // Daemon must still be serving: a fresh attach gets a snapshot.
46 const b = try std.net.connectUnixSocket(sock_path); 45 const b = try dial.dialAttach(sock_path, 80, 24);
47 defer b.close(); 46 defer b.close();
48 try proto.writeFrame(b.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
49 var got_snapshot = false; 47 var got_snapshot = false;
50 var deadline_ms: u64 = 5000; 48 var deadline_ms: u64 = 5000;
51 while (deadline_ms > 0 and !got_snapshot) { 49 while (deadline_ms > 0 and !got_snapshot) {
@@ -79,9 +77,8 @@ test "Server: serves scrollback chunks on request" {
79 defer th.join(); 77 defer th.join();
80 defer stop.store(true, .release); 78 defer stop.store(true, .release);
81 79
82 const c = try std.net.connectUnixSocket(sock_path); 80 const c = try dial.dialAttach(sock_path, 80, 24);
83 defer c.close(); 81 defer c.close();
84 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
85 try proto.writeFrame(c.handle, .input, "seq 1 100\n"); 82 try proto.writeFrame(c.handle, .input, "seq 1 100\n");
86 83
87 // Wait until an update reports enough history, then fetch the oldest 84 // Wait until an update reports enough history, then fetch the oldest
@@ -154,17 +151,15 @@ test "Server: a full session refuses the next attach instead of displacing anyon
154 var opened: usize = 0; 151 var opened: usize = 0;
155 defer for (streams[0..opened]) |s| s.close(); 152 defer for (streams[0..opened]) |s| s.close();
156 while (opened < max_clients) : (opened += 1) { 153 while (opened < max_clients) : (opened += 1) {
157 streams[opened] = try std.net.connectUnixSocket(sock_path); 154 streams[opened] = try dial.dialAttach(sock_path, 80, 24);
158 const fd = streams[opened].handle; 155 const fd = streams[opened].handle;
159 try proto.writeFrame(fd, .attach, &proto.encodeAttach(80, 24, 0, 0));
160 const first = try firstStateFrame(alloc, fd, 10_000); 156 const first = try firstStateFrame(alloc, fd, 10_000);
161 try std.testing.expect(first != null); 157 try std.testing.expect(first != null);
162 } 158 }
163 159
164 // The ninth attach is refused; nobody already attached is evicted. 160 // The ninth attach is refused; nobody already attached is evicted.
165 const extra = try std.net.connectUnixSocket(sock_path); 161 const extra = try dial.dialAttach(sock_path, 80, 24);
166 defer extra.close(); 162 defer extra.close();
167 try proto.writeFrame(extra.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
168 var refused = false; 163 var refused = false;
169 var deadline_ms: u64 = 5000; 164 var deadline_ms: u64 = 5000;
170 while (deadline_ms > 0 and !refused) { 165 while (deadline_ms > 0 and !refused) {
@@ -224,12 +219,10 @@ test "Server: two clients converge on one session" {
224 defer th.join(); 219 defer th.join();
225 defer stop.store(true, .release); 220 defer stop.store(true, .release);
226 221
227 const a = try std.net.connectUnixSocket(sock_path); 222 const a = try dial.dialAttach(sock_path, 80, 24);
228 defer a.close(); 223 defer a.close();
229 try proto.writeFrame(a.handle, .attach, &proto.encodeAttach(80, 24, 0, 0)); 224 const b = try dial.dialAttach(sock_path, 80, 24);
230 const b = try std.net.connectUnixSocket(sock_path);
231 defer b.close(); 225 defer b.close();
232 try proto.writeFrame(b.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
233 226
234 // Input through A must reach both replicas. 227 // Input through A must reach both replicas.
235 try proto.writeFrame(a.handle, .input, "echo both-see-this\n"); 228 try proto.writeFrame(a.handle, .input, "echo both-see-this\n");
@@ -330,9 +323,8 @@ test "Server: a same-size join snapshots the joiner only" {
330 defer th.join(); 323 defer th.join();
331 defer stop.store(true, .release); 324 defer stop.store(true, .release);
332 325
333 const a = try std.net.connectUnixSocket(sock_path); 326 const a = try dial.dialAttach(sock_path, 80, 24);
334 defer a.close(); 327 defer a.close();
335 try proto.writeFrame(a.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
336 328
337 var replica_a = try Engine.init(alloc, .{ .cols = 80, .rows = 24 }); 329 var replica_a = try Engine.init(alloc, .{ .cols = 80, .rows = 24 });
338 defer replica_a.deinit(); 330 defer replica_a.deinit();
@@ -362,9 +354,8 @@ test "Server: a same-size join snapshots the joiner only" {
362 354
363 // B joins at the same size: nothing about A's grid changed, so A must 355 // B joins at the same size: nothing about A's grid changed, so A must
364 // not be repainted. B, which has nothing, must be. 356 // not be repainted. B, which has nothing, must be.
365 const b = try std.net.connectUnixSocket(sock_path); 357 const b = try dial.dialAttach(sock_path, 80, 24);
366 defer b.close(); 358 defer b.close();
367 try proto.writeFrame(b.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
368 359
369 var b_snapshot = false; 360 var b_snapshot = false;
370 deadline_ms = 5000; 361 deadline_ms = 5000;
@@ -449,9 +440,8 @@ test "Server: latest attacher's size wins; earlier client is resnapshotted at th
449 th.join(); 440 th.join();
450 }; 441 };
451 442
452 const a = try std.net.connectUnixSocket(sock_path); 443 const a = try dial.dialAttach(sock_path, 80, 24);
453 defer a.close(); 444 defer a.close();
454 try proto.writeFrame(a.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
455 445
456 // A is attached once it has been answered; only then can B's attach be 446 // A is attached once it has been answered; only then can B's attach be
457 // the *later* event this test is about. 447 // the *later* event this test is about.
@@ -474,9 +464,8 @@ test "Server: latest attacher's size wins; earlier client is resnapshotted at th
474 } 464 }
475 try std.testing.expect(a_attached); 465 try std.testing.expect(a_attached);
476 466
477 const b = try std.net.connectUnixSocket(sock_path); 467 const b = try dial.dialAttach(sock_path, 100, 30);
478 defer b.close(); 468 defer b.close();
479 try proto.writeFrame(b.handle, .attach, &proto.encodeAttach(100, 30, 0, 0));
480 469
481 // The grid follows the newest attacher, and A is told about it. 470 // The grid follows the newest attacher, and A is told about it.
482 var a_resized = false; 471 var a_resized = false;
@@ -548,15 +537,13 @@ test "Server: typing claims the grid for the typist (latest-wins on input)" {
548 537
549 // A joins first and, being the only client, sets the grid: attach-wins 538 // A joins first and, being the only client, sets the grid: attach-wins
550 // is unchanged by any of this. 539 // is unchanged by any of this.
551 const a = try std.net.connectUnixSocket(sock_path); 540 const a = try dial.dialAttach(sock_path, 100, 30);
552 defer a.close(); 541 defer a.close();
553 try proto.writeFrame(a.handle, .attach, &proto.encodeAttach(100, 30, 0, 0));
554 try std.testing.expect(try awaitSnapshotSize(alloc, a.handle, 100, 30, 10_000)); 542 try std.testing.expect(try awaitSnapshotSize(alloc, a.handle, 100, 30, 10_000));
555 543
556 // B joins at a different size and takes the grid, which A is told about. 544 // B joins at a different size and takes the grid, which A is told about.
557 const b = try std.net.connectUnixSocket(sock_path); 545 const b = try dial.dialAttach(sock_path, 80, 24);
558 defer b.close(); 546 defer b.close();
559 try proto.writeFrame(b.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
560 try std.testing.expect(try awaitSnapshotSize(alloc, a.handle, 80, 24, 10_000)); 547 try std.testing.expect(try awaitSnapshotSize(alloc, a.handle, 80, 24, 10_000));
561 try std.testing.expect(try awaitSnapshotSize(alloc, b.handle, 80, 24, 10_000)); 548 try std.testing.expect(try awaitSnapshotSize(alloc, b.handle, 80, 24, 10_000));
562 549
@@ -646,9 +633,8 @@ test "Server: a size the grid refuses never becomes a claim" {
646 th.join(); 633 th.join();
647 }; 634 };
648 635
649 const a = try std.net.connectUnixSocket(sock_path); 636 const a = try dial.dialAttach(sock_path, 80, 24);
650 defer a.close(); 637 defer a.close();
651 try proto.writeFrame(a.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
652 try std.testing.expect(try awaitSnapshotSize(alloc, a.handle, 80, 24, 10_000)); 638 try std.testing.expect(try awaitSnapshotSize(alloc, a.handle, 80, 24, 10_000));
653 _ = try drainHeld(alloc, a.handle, 10_000); 639 _ = try drainHeld(alloc, a.handle, 10_000);
654 640
@@ -656,9 +642,8 @@ test "Server: a size the grid refuses never becomes a claim" {
656 // It still joins and is still served — it is only its *size* that is 642 // It still joins and is still served — it is only its *size* that is
657 // refused — but the grid must not move, and A must not be repainted for 643 // refused — but the grid must not move, and A must not be repainted for
658 // a resize that never happened. 644 // a resize that never happened.
659 const d = try std.net.connectUnixSocket(sock_path); 645 const d = try dial.dialAttach(sock_path, 1, 1);
660 defer d.close(); 646 defer d.close();
661 try proto.writeFrame(d.handle, .attach, &proto.encodeAttach(1, 1, 0, 0));
662 try std.testing.expect(try awaitSnapshotSize(alloc, d.handle, 80, 24, 10_000)); 647 try std.testing.expect(try awaitSnapshotSize(alloc, d.handle, 80, 24, 10_000));
663 648
664 // D types. Its slot holds no accepted size, so this claims nothing: A 649 // D types. Its slot holds no accepted size, so this claims nothing: A
@@ -725,12 +710,10 @@ test "Server: scrollback fetch is per-client and independent" {
725 defer th.join(); 710 defer th.join();
726 defer stop.store(true, .release); 711 defer stop.store(true, .release);
727 712
728 const a = try std.net.connectUnixSocket(sock_path); 713 const a = try dial.dialAttach(sock_path, 80, 24);
729 defer a.close(); 714 defer a.close();
730 try proto.writeFrame(a.handle, .attach, &proto.encodeAttach(80, 24, 0, 0)); 715 const b = try dial.dialAttach(sock_path, 100, 30);
731 const b = try std.net.connectUnixSocket(sock_path);
732 defer b.close(); 716 defer b.close();
733 try proto.writeFrame(b.handle, .attach, &proto.encodeAttach(100, 30, 0, 0));
734 717
735 // Typed by B; both clients see the history it produces. 718 // Typed by B; both clients see the history it produces.
736 try proto.writeFrame(b.handle, .input, "seq 1 100\n"); 719 try proto.writeFrame(b.handle, .input, "seq 1 100\n");
@@ -915,14 +898,13 @@ test "Server: replica rebuilt from snapshots matches the authoritative grid" {
915 defer th.join(); 898 defer th.join();
916 defer stop.store(true, .release); 899 defer stop.store(true, .release);
917 900
918 const stream = try std.net.connectUnixSocket(sock_path); 901 const stream = try dial.dialAttach(sock_path, 100, 30);
919 defer stream.close(); 902 defer stream.close();
920 const fd = stream.handle; 903 const fd = stream.handle;
921 904
922 var replica = try Engine.init(alloc, .{ .cols = 100, .rows = 30 }); 905 var replica = try Engine.init(alloc, .{ .cols = 100, .rows = 30 });
923 defer replica.deinit(); 906 defer replica.deinit();
924 907
925 try proto.writeFrame(fd, .attach, &proto.encodeAttach(100, 30, 0, 0));
926 try proto.writeFrame(fd, .input, "printf 'fidelity-%s\\n' ok\n"); 908 try proto.writeFrame(fd, .input, "printf 'fidelity-%s\\n' ok\n");
927 909
928 // Consume the attach snapshot and the deltas that follow it until the 910 // Consume the attach snapshot and the deltas that follow it until the
@@ -991,9 +973,8 @@ test "Server: typing produces deltas, not snapshots; stats track both" {
991 defer th.join(); 973 defer th.join();
992 defer stop.store(true, .release); 974 defer stop.store(true, .release);
993 975
994 const c = try std.net.connectUnixSocket(sock_path); 976 const c = try dial.dialAttach(sock_path, 80, 24);
995 defer c.close(); 977 defer c.close();
996 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
997 978
998 // The attach is answered with a full snapshot carrying the tracker seq. 979 // The attach is answered with a full snapshot carrying the tracker seq.
999 var attach_seq: u64 = 0; 980 var attach_seq: u64 = 0;
@@ -1091,9 +1072,8 @@ test "Server: reattach needs a recent have_seq AND this daemon's epoch to get a
1091 var last_seq: u64 = 0; 1072 var last_seq: u64 = 0;
1092 var epoch: u64 = 0; 1073 var epoch: u64 = 0;
1093 { 1074 {
1094 const c1 = try std.net.connectUnixSocket(sock_path); 1075 const c1 = try dial.dialAttach(sock_path, 80, 24);
1095 defer c1.close(); 1076 defer c1.close();
1096 try proto.writeFrame(c1.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
1097 // A resize is a discontinuity, so this pins reset_seq above 1 no 1077 // A resize is a discontinuity, so this pins reset_seq above 1 no
1098 // matter how the shell's first output raced the attach — session 3 1078 // matter how the shell's first output raced the attach — session 3
1099 // below needs have_seq=1 to be reliably stale. 1079 // below needs have_seq=1 to be reliably stale.
@@ -1113,7 +1093,7 @@ test "Server: reattach needs a recent have_seq AND this daemon's epoch to get a
1113 // instance. Those seqs describe a different history; honouring one would 1093 // instance. Those seqs describe a different history; honouring one would
1114 // paint this session's screen with another's rows. Snapshot. 1094 // paint this session's screen with another's rows. Snapshot.
1115 { 1095 {
1116 const c2 = try std.net.connectUnixSocket(sock_path); 1096 const c2 = try dial.dial(sock_path);
1117 defer c2.close(); 1097 defer c2.close();
1118 try proto.writeFrame(c2.handle, .attach, &proto.encodeAttach(80, 24, last_seq, epoch ^ 1)); 1098 try proto.writeFrame(c2.handle, .attach, &proto.encodeAttach(80, 24, last_seq, epoch ^ 1));
1119 const first = try firstStateFrame(alloc, c2.handle, 10_000); 1099 const first = try firstStateFrame(alloc, c2.handle, 10_000);
@@ -1130,7 +1110,7 @@ test "Server: reattach needs a recent have_seq AND this daemon's epoch to get a
1130 // Session 3: a real seq with have_epoch = 0 — a pre-epoch client, or one 1110 // Session 3: a real seq with have_epoch = 0 — a pre-epoch client, or one
1131 // hoping 0 means "any". It means "none", and none is never serviceable. 1111 // hoping 0 means "any". It means "none", and none is never serviceable.
1132 { 1112 {
1133 const c3 = try std.net.connectUnixSocket(sock_path); 1113 const c3 = try dial.dial(sock_path);
1134 defer c3.close(); 1114 defer c3.close();
1135 try proto.writeFrame(c3.handle, .attach, &proto.encodeAttach(80, 24, last_seq, 0)); 1115 try proto.writeFrame(c3.handle, .attach, &proto.encodeAttach(80, 24, last_seq, 0));
1136 const first = try firstStateFrame(alloc, c3.handle, 10_000); 1116 const first = try firstStateFrame(alloc, c3.handle, 10_000);
@@ -1143,7 +1123,7 @@ test "Server: reattach needs a recent have_seq AND this daemon's epoch to get a
1143 // Session 4: right seq, right epoch — we really are up to date, so the 1123 // Session 4: right seq, right epoch — we really are up to date, so the
1144 // daemon owes us a delta (possibly empty), never a full repaint. 1124 // daemon owes us a delta (possibly empty), never a full repaint.
1145 { 1125 {
1146 const c4 = try std.net.connectUnixSocket(sock_path); 1126 const c4 = try dial.dial(sock_path);
1147 defer c4.close(); 1127 defer c4.close();
1148 try proto.writeFrame(c4.handle, .attach, &proto.encodeAttach(80, 24, last_seq, epoch)); 1128 try proto.writeFrame(c4.handle, .attach, &proto.encodeAttach(80, 24, last_seq, epoch));
1149 const first = try firstStateFrame(alloc, c4.handle, 10_000); 1129 const first = try firstStateFrame(alloc, c4.handle, 10_000);
@@ -1155,7 +1135,7 @@ test "Server: reattach needs a recent have_seq AND this daemon's epoch to get a
1155 // Session 5: right epoch, but have_seq predates the last discontinuity, 1135 // Session 5: right epoch, but have_seq predates the last discontinuity,
1156 // so no delta can reconstruct our state — full snapshot. 1136 // so no delta can reconstruct our state — full snapshot.
1157 { 1137 {
1158 const c5 = try std.net.connectUnixSocket(sock_path); 1138 const c5 = try dial.dial(sock_path);
1159 defer c5.close(); 1139 defer c5.close();
1160 try proto.writeFrame(c5.handle, .attach, &proto.encodeAttach(80, 24, 1, epoch)); 1140 try proto.writeFrame(c5.handle, .attach, &proto.encodeAttach(80, 24, 1, epoch));
1161 const first = try firstStateFrame(alloc, c5.handle, 10_000); 1141 const first = try firstStateFrame(alloc, c5.handle, 10_000);
@@ -1184,9 +1164,8 @@ test "Server: a daemon restart invalidates have_seq even with the old epoch pres
1184 defer th_a.join(); 1164 defer th_a.join();
1185 defer stop_a.store(true, .release); 1165 defer stop_a.store(true, .release);
1186 1166
1187 const c = try std.net.connectUnixSocket(sock_path); 1167 const c = try dial.dialAttach(sock_path, 80, 24);
1188 defer c.close(); 1168 defer c.close();
1189 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
1190 try proto.writeFrame(c.handle, .input, "echo before-restart\n"); 1169 try proto.writeFrame(c.handle, .input, "echo before-restart\n");
1191 held_a = try drainHeld(alloc, c.handle, 10_000); 1170 held_a = try drainHeld(alloc, c.handle, 10_000);
1192 try std.testing.expect(held_a.seq > 0); 1171 try std.testing.expect(held_a.seq > 0);
@@ -1210,9 +1189,8 @@ test "Server: a daemon restart invalidates have_seq even with the old epoch pres
1210 // constant epoch would satisfy every other test in the file. 1189 // constant epoch would satisfy every other test in the file.
1211 var held_b: Held = undefined; 1190 var held_b: Held = undefined;
1212 { 1191 {
1213 const c = try std.net.connectUnixSocket(sock_path); 1192 const c = try dial.dialAttach(sock_path, 80, 24);
1214 defer c.close(); 1193 defer c.close();
1215 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
1216 held_b = try drainHeld(alloc, c.handle, 10_000); 1194 held_b = try drainHeld(alloc, c.handle, 10_000);
1217 try std.testing.expect(held_b.seq > 0); 1195 try std.testing.expect(held_b.seq > 0);
1218 try std.testing.expect(held_b.epoch != 0); 1196 try std.testing.expect(held_b.epoch != 0);
@@ -1224,7 +1202,7 @@ test "Server: a daemon restart invalidates have_seq even with the old epoch pres
1224 // about this attach is serviceable except the epoch, so a delta here 1202 // about this attach is serviceable except the epoch, so a delta here
1225 // would mean the epoch is not being checked at all. 1203 // would mean the epoch is not being checked at all.
1226 { 1204 {
1227 const c = try std.net.connectUnixSocket(sock_path); 1205 const c = try dial.dial(sock_path);
1228 defer c.close(); 1206 defer c.close();
1229 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, held_b.seq, held_a.epoch)); 1207 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, held_b.seq, held_a.epoch));
1230 const first = try firstStateFrame(alloc, c.handle, 10_000); 1208 const first = try firstStateFrame(alloc, c.handle, 10_000);
@@ -1239,7 +1217,7 @@ test "Server: a daemon restart invalidates have_seq even with the old epoch pres
1239 // held. B has no way to reconstruct that state, so: snapshot, stamped 1217 // held. B has no way to reconstruct that state, so: snapshot, stamped
1240 // with B's own epoch, which the client adopts in place of A's. 1218 // with B's own epoch, which the client adopts in place of A's.
1241 { 1219 {
1242 const c = try std.net.connectUnixSocket(sock_path); 1220 const c = try dial.dial(sock_path);
1243 defer c.close(); 1221 defer c.close();
1244 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, held_a.seq, held_a.epoch)); 1222 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, held_a.seq, held_a.epoch));
1245 const first = try firstStateFrame(alloc, c.handle, 10_000); 1223 const first = try firstStateFrame(alloc, c.handle, 10_000);
@@ -1412,9 +1390,8 @@ test "Server: a session is created at the attacher's size" {
1412 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 1390 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
1413 defer srv.deinit(); 1391 defer srv.deinit();
1414 1392
1415 const c = try std.net.connectUnixSocket(sock_path); 1393 const c = try dial.dialAttachNamed(sock_path, 100, 30, "big");
1416 defer c.close(); 1394 defer c.close();
1417 try attachNamed(c.handle, 100, 30, "big");
1418 const f = (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400)) orelse 1395 const f = (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400)) orelse
1419 return error.NoSnapshot; 1396 return error.NoSnapshot;
1420 defer f.deinit(alloc); 1397 defer f.deinit(alloc);
@@ -1443,9 +1420,8 @@ test "Server: a 0x0 attach joins but never creates" {
1443 // mux a send attaches at 0x0 — it makes no size claim. Against a name 1420 // mux a send attaches at 0x0 — it makes no size claim. Against a name
1444 // that does not exist, that must be a refusal, never a shell spawned 1421 // that does not exist, that must be a refusal, never a shell spawned
1445 // at a size nobody has. 1422 // at a size nobody has.
1446 const c1 = try std.net.connectUnixSocket(sock_path); 1423 const c1 = try dial.dialAttachNamed(sock_path, 0, 0, "b");
1447 defer c1.close(); 1424 defer c1.close();
1448 try attachNamed(c1.handle, 0, 0, "b");
1449 const f1 = (try awaitFrame(alloc, &srv, c1.handle, .exit_status, 400)) orelse 1425 const f1 = (try awaitFrame(alloc, &srv, c1.handle, .exit_status, 400)) orelse
1450 return error.NoRefusal; 1426 return error.NoRefusal;
1451 defer f1.deinit(alloc); 1427 defer f1.deinit(alloc);
@@ -1453,18 +1429,16 @@ test "Server: a 0x0 attach joins but never creates" {
1453 try std.testing.expect(srv.sessions.table[1] == null); 1429 try std.testing.expect(srv.sessions.table[1] == null);
1454 1430
1455 // Create it properly, at a real size... 1431 // Create it properly, at a real size...
1456 const c2 = try std.net.connectUnixSocket(sock_path); 1432 const c2 = try dial.dialAttachNamed(sock_path, 80, 24, "b");
1457 defer c2.close(); 1433 defer c2.close();
1458 try attachNamed(c2.handle, 80, 24, "b");
1459 const f2 = (try awaitFrame(alloc, &srv, c2.handle, .snapshot, 400)) orelse 1434 const f2 = (try awaitFrame(alloc, &srv, c2.handle, .snapshot, 400)) orelse
1460 return error.NoSnapshotOnCreate; 1435 return error.NoSnapshotOnCreate;
1461 f2.deinit(alloc); 1436 f2.deinit(alloc);
1462 const si_b = srv.sessions.find("b") orelse return error.NotCreated; 1437 const si_b = srv.sessions.find("b") orelse return error.NotCreated;
1463 1438
1464 // ...and the very same 0x0 attach now joins it. 1439 // ...and the very same 0x0 attach now joins it.
1465 const c3 = try std.net.connectUnixSocket(sock_path); 1440 const c3 = try dial.dialAttachNamed(sock_path, 0, 0, "b");
1466 defer c3.close(); 1441 defer c3.close();
1467 try attachNamed(c3.handle, 0, 0, "b");
1468 const f3 = (try awaitFrame(alloc, &srv, c3.handle, .snapshot, 400)) orelse 1442 const f3 = (try awaitFrame(alloc, &srv, c3.handle, .snapshot, 400)) orelse
1469 return error.ZeroSizeJoinRefused; 1443 return error.ZeroSizeJoinRefused;
1470 f3.deinit(alloc); 1444 f3.deinit(alloc);
@@ -1499,9 +1473,8 @@ test "Server: a 1x1 attach joins but never creates" {
1499 // grid to 1x1, so a session created at 1x1 could never be resized by 1473 // grid to 1x1, so a session created at 1x1 could never be resized by
1500 // the client that caused it — a shell nobody can use, spawned by 1474 // the client that caused it — a shell nobody can use, spawned by
1501 // attaching with a name nobody had created yet. 1475 // attaching with a name nobody had created yet.
1502 const c1 = try std.net.connectUnixSocket(sock_path); 1476 const c1 = try dial.dialAttachNamed(sock_path, 1, 1, "tile");
1503 defer c1.close(); 1477 defer c1.close();
1504 try attachNamed(c1.handle, 1, 1, "tile");
1505 const f1 = (try awaitFrame(alloc, &srv, c1.handle, .exit_status, 400)) orelse 1478 const f1 = (try awaitFrame(alloc, &srv, c1.handle, .exit_status, 400)) orelse
1506 return error.NoRefusal; 1479 return error.NoRefusal;
1507 defer f1.deinit(alloc); 1480 defer f1.deinit(alloc);
@@ -1509,9 +1482,8 @@ test "Server: a 1x1 attach joins but never creates" {
1509 try std.testing.expect(srv.sessions.find("tile") == null); 1482 try std.testing.expect(srv.sessions.find("tile") == null);
1510 1483
1511 // Created at a real size by someone who has one... 1484 // Created at a real size by someone who has one...
1512 const c2 = try std.net.connectUnixSocket(sock_path); 1485 const c2 = try dial.dialAttachNamed(sock_path, 80, 24, "tile");
1513 defer c2.close(); 1486 defer c2.close();
1514 try attachNamed(c2.handle, 80, 24, "tile");
1515 const f2 = (try awaitFrame(alloc, &srv, c2.handle, .snapshot, 400)) orelse 1487 const f2 = (try awaitFrame(alloc, &srv, c2.handle, .snapshot, 400)) orelse
1516 return error.NoSnapshotOnCreate; 1488 return error.NoSnapshotOnCreate;
1517 f2.deinit(alloc); 1489 f2.deinit(alloc);
@@ -1521,9 +1493,8 @@ test "Server: a 1x1 attach joins but never creates" {
1521 // Refusing the creation must not cost the client its view: a terminal 1493 // Refusing the creation must not cost the client its view: a terminal
1522 // too small to spawn a session is still allowed to watch one, and may 1494 // too small to spawn a session is still allowed to watch one, and may
1523 // never be the reason one exists or the reason one resizes. 1495 // never be the reason one exists or the reason one resizes.
1524 const c3 = try std.net.connectUnixSocket(sock_path); 1496 const c3 = try dial.dialAttachNamed(sock_path, 1, 1, "tile");
1525 defer c3.close(); 1497 defer c3.close();
1526 try attachNamed(c3.handle, 1, 1, "tile");
1527 const f3 = (try awaitFrame(alloc, &srv, c3.handle, .snapshot, 400)) orelse 1498 const f3 = (try awaitFrame(alloc, &srv, c3.handle, .snapshot, 400)) orelse
1528 return error.TinyJoinRefused; 1499 return error.TinyJoinRefused;
1529 f3.deinit(alloc); 1500 f3.deinit(alloc);
@@ -1551,17 +1522,15 @@ test "Server: attaching seats a client in activity order, and typing or resizing
1551 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 1522 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
1552 defer srv.deinit(); 1523 defer srv.deinit();
1553 1524
1554 const ca = try std.net.connectUnixSocket(sock_path); 1525 const ca = try dial.dialAttachNamed(sock_path, 80, 24, "");
1555 defer ca.close(); 1526 defer ca.close();
1556 try attachNamed(ca.handle, 80, 24, "");
1557 var spun: usize = 0; 1527 var spun: usize = 0;
1558 while (spun < 200 and srv.clients[0] == null) : (spun += 1) try srv.pumpOnce(5); 1528 while (spun < 200 and srv.clients[0] == null) : (spun += 1) try srv.pumpOnce(5);
1559 1529
1560 // Seated one at a time so the slot indices below are the attach order: 1530 // Seated one at a time so the slot indices below are the attach order:
1561 // freeClientSlot hands out the lowest free slot. 1531 // freeClientSlot hands out the lowest free slot.
1562 const cb = try std.net.connectUnixSocket(sock_path); 1532 const cb = try dial.dialAttachNamed(sock_path, 80, 24, "");
1563 defer cb.close(); 1533 defer cb.close();
1564 try attachNamed(cb.handle, 80, 24, "");
1565 spun = 0; 1534 spun = 0;
1566 while (spun < 200 and srv.clients[1] == null) : (spun += 1) try srv.pumpOnce(5); 1535 while (spun < 200 and srv.clients[1] == null) : (spun += 1) try srv.pumpOnce(5);
1567 1536
@@ -1644,7 +1613,7 @@ test "Server: an observer that sends one byte does not stall the pump, and finis
1644 1613
1645 // Through the real listener, so the accept path seats it as the daemon 1614 // Through the real listener, so the accept path seats it as the daemon
1646 // would seat `mux d stats`. 1615 // would seat `mux d stats`.
1647 const obs = try std.net.connectUnixSocket(sock_path); 1616 const obs = try dial.dial(sock_path);
1648 defer obs.close(); 1617 defer obs.close();
1649 try srv.pumpOnce(20); // accept 1618 try srv.pumpOnce(20); // accept
1650 try std.testing.expect(srv.observers[0] != null); 1619 try std.testing.expect(srv.observers[0] != null);
@@ -1667,7 +1636,7 @@ test "Server: an observer that sends one byte does not stall the pump, and finis
1667 1636
1668 // A second observer is served while the first is still mid-frame: the 1637 // A second observer is served while the first is still mid-frame: the
1669 // daemon is answering everyone, which is the whole claim. 1638 // daemon is answering everyone, which is the whole claim.
1670 const obs2 = try std.net.connectUnixSocket(sock_path); 1639 const obs2 = try dial.dial(sock_path);
1671 defer obs2.close(); 1640 defer obs2.close();
1672 try proto.writeFrame(obs2.handle, .stats_req, ""); 1641 try proto.writeFrame(obs2.handle, .stats_req, "");
1673 const r2 = (try awaitFrame(alloc, &srv, obs2.handle, .stats_reply, 100)) orelse 1642 const r2 = (try awaitFrame(alloc, &srv, obs2.handle, .stats_reply, 100)) orelse
@@ -1697,7 +1666,7 @@ test "Server: stalled observers are dropped on the idle deadline, and the table
1697 // at accept with nothing said. 1666 // at accept with nothing said.
1698 var peers: [srv_mod.max_observers]std.net.Stream = undefined; 1667 var peers: [srv_mod.max_observers]std.net.Stream = undefined;
1699 for (&peers) |*p| { 1668 for (&peers) |*p| {
1700 p.* = try std.net.connectUnixSocket(sock_path); 1669 p.* = try dial.dial(sock_path);
1701 try proto.writeAllFd(p.handle, &[_]u8{@intFromEnum(proto.MsgType.stats_req)}); 1670 try proto.writeAllFd(p.handle, &[_]u8{@intFromEnum(proto.MsgType.stats_req)});
1702 try srv.pumpOnce(20); 1671 try srv.pumpOnce(20);
1703 } 1672 }
@@ -1717,7 +1686,7 @@ test "Server: stalled observers are dropped on the idle deadline, and the table
1717 var b: [1]u8 = undefined; 1686 var b: [1]u8 = undefined;
1718 try std.testing.expectEqual(@as(usize, 0), try std.posix.read(peers[0].handle, &b)); 1687 try std.testing.expectEqual(@as(usize, 0), try std.posix.read(peers[0].handle, &b));
1719 1688
1720 const fresh = try std.net.connectUnixSocket(sock_path); 1689 const fresh = try dial.dial(sock_path);
1721 defer fresh.close(); 1690 defer fresh.close();
1722 try proto.writeFrame(fresh.handle, .stats_req, ""); 1691 try proto.writeFrame(fresh.handle, .stats_req, "");
1723 const r = (try awaitFrame(alloc, &srv, fresh.handle, .stats_reply, 100)) orelse 1692 const r = (try awaitFrame(alloc, &srv, fresh.handle, .stats_reply, 100)) orelse
@@ -1737,7 +1706,7 @@ test "Server: bytes after an attach in the same write reach the promoted client"
1737 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" }); 1706 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" });
1738 defer srv.deinit(); 1707 defer srv.deinit();
1739 1708
1740 const c = try std.net.connectUnixSocket(sock_path); 1709 const c = try dial.dial(sock_path);
1741 defer c.close(); 1710 defer c.close();
1742 1711
1743 // attach + resize in ONE write: the resize lands in the observer's 1712 // attach + resize in ONE write: the resize lands in the observer's
@@ -1777,7 +1746,7 @@ test "Server: an observer that never reads its replies is dropped, not allowed t
1777 // reaper's — a nagging peer is never idle anyway. 1746 // reaper's — a nagging peer is never idle anyway.
1778 srv.observer_idle_ms = 60_000; 1747 srv.observer_idle_ms = 60_000;
1779 1748
1780 const peer = try std.net.connectUnixSocket(sock_path); 1749 const peer = try dial.dial(sock_path);
1781 defer peer.close(); 1750 defer peer.close();
1782 try srv.pumpOnce(20); 1751 try srv.pumpOnce(20);
1783 // Every reply is up to sessions_text_len bytes and the peer never reads 1752 // Every reply is up to sessions_text_len bytes and the peer never reads
@@ -1816,7 +1785,7 @@ test "Server: a burst of observer frames is answered across pumps, never all in
1816 defer srv.deinit(); 1785 defer srv.deinit();
1817 srv.observer_idle_ms = 60_000; 1786 srv.observer_idle_ms = 60_000;
1818 1787
1819 const peer = try std.net.connectUnixSocket(sock_path); 1788 const peer = try dial.dial(sock_path);
1820 defer peer.close(); 1789 defer peer.close();
1821 try srv.pumpOnce(20); 1790 try srv.pumpOnce(20);
1822 1791
@@ -1871,7 +1840,7 @@ test "Server: the two bounded deadlines read a monotonic clock, not the calendar
1871 // observer on a step forwards. 1840 // observer on a step forwards.
1872 const wall_now = std.time.milliTimestamp(); 1841 const wall_now = std.time.milliTimestamp();
1873 1842
1874 const obs = try std.net.connectUnixSocket(sock_path); 1843 const obs = try dial.dial(sock_path);
1875 defer obs.close(); 1844 defer obs.close();
1876 try srv.pumpOnce(20); 1845 try srv.pumpOnce(20);
1877 try std.testing.expect(srv.observers[0] != null); 1846 try std.testing.expect(srv.observers[0] != null);
@@ -1899,7 +1868,7 @@ test "Server: an observer that dribbles a huge frame is dropped at the cap, not
1899 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" }); 1868 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" });
1900 defer srv.deinit(); 1869 defer srv.deinit();
1901 1870
1902 const peer = try std.net.connectUnixSocket(sock_path); 1871 const peer = try dial.dial(sock_path);
1903 defer peer.close(); 1872 defer peer.close();
1904 try srv.pumpOnce(20); 1873 try srv.pumpOnce(20);
1905 try std.testing.expect(srv.observers[0] != null); 1874 try std.testing.expect(srv.observers[0] != null);
src/server/server_test_await.zig
Old New
@@ -3,6 +3,7 @@ const proto = @import("term").protocol;
3 const shellint = @import("shellint.zig"); 3 const shellint = @import("shellint.zig");
4 const TmpDir = @import("testtmp").TmpDir; 4 const TmpDir = @import("testtmp").TmpDir;
5 const h = @import("server_test_harness.zig"); 5 const h = @import("server_test_harness.zig");
6 const dial = h.dial;
6 const srv_mod = @import("server.zig"); 7 const srv_mod = @import("server.zig");
7 const Server = srv_mod.Server; 8 const Server = srv_mod.Server;
8 const attachNamed = h.attachNamed; 9 const attachNamed = h.attachNamed;
@@ -41,9 +42,8 @@ test "Server: OSC 133 marks reach attached clients as cmd_state pushes" {
41 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script }); 42 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script });
42 defer srv.deinit(); 43 defer srv.deinit();
43 44
44 const c = try std.net.connectUnixSocket(sock_path); 45 const c = try dial.dialAttach(sock_path, 80, 24);
45 defer c.close(); 46 defer c.close();
46 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
47 47
48 // Release the script into its C. The keystroke is echoed by the line 48 // Release the script into its C. The keystroke is echoed by the line
49 // discipline, so this pty chunk carries grid content as well as the 49 // discipline, so this pty chunk carries grid content as well as the
@@ -162,9 +162,8 @@ const IntegratedSession = struct {
162 try std.testing.expect(self.srv.shellint_dir != null); 162 try std.testing.expect(self.srv.shellint_dir != null);
163 try std.fs.cwd().access(self.srv.shellint_dir.?, .{}); 163 try std.fs.cwd().access(self.srv.shellint_dir.?, .{});
164 164
165 self.conn = try std.net.connectUnixSocket(self.sock_path); 165 self.conn = try dial.dialAttach(self.sock_path, 80, 24);
166 errdefer self.conn.close(); 166 errdefer self.conn.close();
167 try proto.writeFrame(self.conn.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
168 return self; 167 return self;
169 } 168 }
170 169
@@ -377,9 +376,8 @@ test "Server: an entry already at the daemon's pid name costs neither the marks
377 376
378 // ...and the session actually works. A daemon that starts and then 377 // ...and the session actually works. A daemon that starts and then
379 // cannot answer would satisfy every assertion above. 378 // cannot answer would satisfy every assertion above.
380 const c = try std.net.connectUnixSocket(sock_path); 379 const c = try dial.dialAttach(sock_path, 80, 24);
381 defer c.close(); 380 defer c.close();
382 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
383 try proto.writeFrame(c.handle, .status_req, ""); 381 try proto.writeFrame(c.handle, .status_req, "");
384 const f = (try awaitFrame(alloc, &srv, c.handle, .status_reply, 400)) orelse 382 const f = (try awaitFrame(alloc, &srv, c.handle, .status_reply, 400)) orelse
385 return error.NoStatusReply; 383 return error.NoStatusReply;
@@ -478,9 +476,8 @@ test "Server: an await is held open, answered by a mark, and re-answered immedia
478 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script }); 476 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script });
479 defer srv.deinit(); 477 defer srv.deinit();
480 478
481 const c = try std.net.connectUnixSocket(sock_path); 479 const c = try dial.dialAttach(sock_path, 80, 24);
482 defer c.close(); 480 defer c.close();
483 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
484 481
485 // since_seq is "what I already know about": only a return NEWER than this 482 // since_seq is "what I already know about": only a return NEWER than this
486 // may answer. Nothing has returned on this session at all. 483 // may answer. Nothing has returned on this session at all.
@@ -533,9 +530,8 @@ test "Server: an await is held open, answered by a mark, and re-answered immedia
533 // rather than learning nothing until the next transition. The EXIT CODE 530 // rather than learning nothing until the next transition. The EXIT CODE
534 // carries the news: an untouched tracker reports null, so 3 means this push 531 // carries the news: an untouched tracker reports null, so 3 means this push
535 // came from the command that ran. 532 // came from the command that ran.
536 const late = try std.net.connectUnixSocket(sock_path); 533 const late = try dial.dialAttach(sock_path, 80, 24);
537 defer late.close(); 534 defer late.close();
538 try proto.writeFrame(late.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
539 const f3 = (try awaitFrame(alloc, &srv, late.handle, .cmd_state, 200)) orelse 535 const f3 = (try awaitFrame(alloc, &srv, late.handle, .cmd_state, 200)) orelse
540 return error.NoCmdStateOnAttach; 536 return error.NoCmdStateOnAttach;
541 defer f3.deinit(alloc); 537 defer f3.deinit(alloc);
@@ -575,9 +571,8 @@ test "Server: a return is still answerable once the next command is running" {
575 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script }); 571 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script });
576 defer srv.deinit(); 572 defer srv.deinit();
577 573
578 const c = try std.net.connectUnixSocket(sock_path); 574 const c = try dial.dialAttach(sock_path, 80, 24);
579 defer c.close(); 575 defer c.close();
580 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
581 576
582 // Run the first command to completion, then start the second and wait 577 // Run the first command to completion, then start the second and wait
583 // until the daemon has actually seen it open. 578 // until the daemon has actually seen it open.
@@ -627,9 +622,8 @@ test "Server: an await with a settle floor is answered by output going quiet" {
627 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 622 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
628 defer srv.deinit(); 623 defer srv.deinit();
629 624
630 const c = try std.net.connectUnixSocket(sock_path); 625 const c = try dial.dialAttach(sock_path, 80, 24);
631 defer c.close(); 626 defer c.close();
632 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
633 try proto.writeFrame(c.handle, .await_req, &proto.encodeAwaitReq(.{ 627 try proto.writeFrame(c.handle, .await_req, &proto.encodeAwaitReq(.{
634 .since_seq = srv.sessions.table[0].?.tracker.seq, 628 .since_seq = srv.sessions.table[0].?.tracker.seq,
635 .settle_ms = 200, 629 .settle_ms = 200,
@@ -665,9 +659,8 @@ test "Server: an await with nothing to answer it ends at the bound the caller se
665 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 659 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
666 defer srv.deinit(); 660 defer srv.deinit();
667 661
668 const c = try std.net.connectUnixSocket(sock_path); 662 const c = try dial.dialAttach(sock_path, 80, 24);
669 defer c.close(); 663 defer c.close();
670 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
671 // Nothing is typed at this session, no settle floor is asked for and no 664 // Nothing is typed at this session, no settle floor is asked for and no
672 // mark will ever come: the timeout is the only thing left that can end 665 // mark will ever come: the timeout is the only thing left that can end
673 // this wait, which is the point. 666 // this wait, which is the point.
@@ -718,9 +711,8 @@ test "Server: a timed-out await carries no exit code, not the last command's" {
718 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script }); 711 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script });
719 defer srv.deinit(); 712 defer srv.deinit();
720 713
721 const c = try std.net.connectUnixSocket(sock_path); 714 const c = try dial.dialAttach(sock_path, 80, 24);
722 defer c.close(); 715 defer c.close();
723 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
724 716
725 // First, collect the return honestly, exactly as an agent would. 717 // First, collect the return honestly, exactly as an agent would.
726 try proto.writeFrame(c.handle, .await_req, &proto.encodeAwaitReq(.{ 718 try proto.writeFrame(c.handle, .await_req, &proto.encodeAwaitReq(.{
@@ -775,9 +767,8 @@ test "Server: without shell integration a foreground job's end is caught by the
775 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" }); 767 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" });
776 defer srv.deinit(); 768 defer srv.deinit();
777 769
778 const c = try std.net.connectUnixSocket(sock_path); 770 const c = try dial.dialAttach(sock_path, 80, 24);
779 defer c.close(); 771 defer c.close();
780 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
781 // sleep 2, not sleep 1: the edge has to SEE the pgid off the shell before 772 // sleep 2, not sleep 1: the edge has to SEE the pgid off the shell before
782 // "back on the shell" can mean anything, so the busy window must be wide 773 // "back on the shell" can mean anything, so the busy window must be wide
783 // enough to sample. A job too short to observe is one the settle floor is 774 // enough to sample. A job too short to observe is one the settle floor is
@@ -815,9 +806,8 @@ test "Server: re-attaching to another session drops the await it left behind" {
815 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 806 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
816 defer srv.deinit(); 807 defer srv.deinit();
817 808
818 const c = try std.net.connectUnixSocket(sock_path); 809 const c = try dial.dialAttachNamed(sock_path, 80, 24, "a");
819 defer c.close(); 810 defer c.close();
820 try attachNamed(c.handle, 80, 24, "a");
821 var spun: usize = 0; 811 var spun: usize = 0;
822 while (spun < 200 and srv.sessions.find("a") == null) : (spun += 1) try srv.pumpOnce(5); 812 while (spun < 200 and srv.sessions.find("a") == null) : (spun += 1) try srv.pumpOnce(5);
823 const si_a = srv.sessions.find("a") orelse return error.NoSessionA; 813 const si_a = srv.sessions.find("a") orelse return error.NoSessionA;
@@ -896,12 +886,10 @@ test "Server: cmd_state pushes stay inside their session" {
896 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script }); 886 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script });
897 defer srv.deinit(); 887 defer srv.deinit();
898 888
899 const a = try std.net.connectUnixSocket(sock_path); 889 const a = try dial.dialAttach(sock_path, 80, 24);
900 defer a.close(); 890 defer a.close();
901 try proto.writeFrame(a.handle, .attach, &proto.encodeAttach(80, 24, 0, 0)); 891 const b = try dial.dialAttachNamed(sock_path, 80, 24, "b");
902 const b = try std.net.connectUnixSocket(sock_path);
903 defer b.close(); 892 defer b.close();
904 try attachNamed(b.handle, 80, 24, "b");
905 const fa = (try awaitFrame(alloc, &srv, a.handle, .snapshot, 400)) orelse 893 const fa = (try awaitFrame(alloc, &srv, a.handle, .snapshot, 400)) orelse
906 return error.NoSnapshotA; 894 return error.NoSnapshotA;
907 fa.deinit(alloc); 895 fa.deinit(alloc);
@@ -956,12 +944,10 @@ test "Server: an await resolves against the awaiting client's session" {
956 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script }); 944 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script });
957 defer srv.deinit(); 945 defer srv.deinit();
958 946
959 const a = try std.net.connectUnixSocket(sock_path); 947 const a = try dial.dialAttach(sock_path, 80, 24);
960 defer a.close(); 948 defer a.close();
961 try proto.writeFrame(a.handle, .attach, &proto.encodeAttach(80, 24, 0, 0)); 949 const b = try dial.dialAttachNamed(sock_path, 80, 24, "b");
962 const b = try std.net.connectUnixSocket(sock_path);
963 defer b.close(); 950 defer b.close();
964 try attachNamed(b.handle, 80, 24, "b");
965 const fa = (try awaitFrame(alloc, &srv, a.handle, .snapshot, 400)) orelse 951 const fa = (try awaitFrame(alloc, &srv, a.handle, .snapshot, 400)) orelse
966 return error.NoSnapshotA; 952 return error.NoSnapshotA;
967 fa.deinit(alloc); 953 fa.deinit(alloc);
src/server/server_test_clipboard.zig
Old New
@@ -3,11 +3,11 @@ const Engine = @import("term").engine.Engine;
3 const proto = @import("term").protocol; 3 const proto = @import("term").protocol;
4 const TmpDir = @import("testtmp").TmpDir; 4 const TmpDir = @import("testtmp").TmpDir;
5 const h = @import("server_test_harness.zig"); 5 const h = @import("server_test_harness.zig");
6 const dial = h.dial;
6 const srv_mod = @import("server.zig"); 7 const srv_mod = @import("server.zig");
7 const Server = srv_mod.Server; 8 const Server = srv_mod.Server;
8 const Session = srv_mod.Session; 9 const Session = srv_mod.Session;
9 const selectionReplyStatus = srv_mod.selectionReplyStatus; 10 const selectionReplyStatus = srv_mod.selectionReplyStatus;
10 const attachNamed = h.attachNamed;
11 const awaitFrame = h.awaitFrame; 11 const awaitFrame = h.awaitFrame;
12 const awaitGridText = h.awaitGridText; 12 const awaitGridText = h.awaitGridText;
13 const connectedPair = h.connectedPair; 13 const connectedPair = h.connectedPair;
@@ -123,9 +123,10 @@ test "Server: selection extraction statuses map to wire replies" {
123 // nothing at all. 123 // nothing at all.
124 // --------------------------------------------------------------------------- 124 // ---------------------------------------------------------------------------
125 125
126 // The layering forbids `engine` importing `protocol`, so the cap is written in 126 // `engine` does not import `protocol` — the VT engine holds no opinion about
127 // two places and this is what stops them drifting in silence. server.zig is one 127 // the wire format, and that convention is worth keeping — so the cap is written
128 // of the few modules that legitimately imports both. 128 // in two places and this is what stops them drifting in silence. server.zig is
129 // one of the few modules that legitimately imports both.
129 test "the engine's default clipboard cap is the wire's" { 130 test "the engine's default clipboard cap is the wire's" {
130 try std.testing.expectEqual( 131 try std.testing.expectEqual(
131 proto.clipboard_base64_max, 132 proto.clipboard_base64_max,
@@ -147,15 +148,13 @@ test "Server: a clipboard event reaches this session's clients and no others" {
147 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 148 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
148 defer srv.deinit(); 149 defer srv.deinit();
149 150
150 const ca = try std.net.connectUnixSocket(sock_path); 151 const ca = try dial.dialAttachNamed(sock_path, 80, 24, "a");
151 defer ca.close(); 152 defer ca.close();
152 try attachNamed(ca.handle, 80, 24, "a");
153 (try awaitFrame(alloc, &srv, ca.handle, .snapshot, 400) orelse 153 (try awaitFrame(alloc, &srv, ca.handle, .snapshot, 400) orelse
154 return error.NoSnapshotA).deinit(alloc); 154 return error.NoSnapshotA).deinit(alloc);
155 155
156 const cb = try std.net.connectUnixSocket(sock_path); 156 const cb = try dial.dialAttachNamed(sock_path, 80, 24, "b");
157 defer cb.close(); 157 defer cb.close();
158 try attachNamed(cb.handle, 80, 24, "b");
159 (try awaitFrame(alloc, &srv, cb.handle, .snapshot, 400) orelse 158 (try awaitFrame(alloc, &srv, cb.handle, .snapshot, 400) orelse
160 return error.NoSnapshotB).deinit(alloc); 159 return error.NoSnapshotB).deinit(alloc);
161 160
@@ -235,8 +234,7 @@ fn clipboardIntoGap(
235 srv: *Server, 234 srv: *Server,
236 sock_path: []const u8, 235 sock_path: []const u8,
237 ) !GapWatermark { 236 ) !GapWatermark {
238 const a = try std.net.connectUnixSocket(sock_path); 237 const a = try dial.dialAttach(sock_path, 80, 24);
239 try proto.writeFrame(a.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
240 (try awaitFrame(alloc, srv, a.handle, .snapshot, 400) orelse 238 (try awaitFrame(alloc, srv, a.handle, .snapshot, 400) orelse
241 return error.NoSnapshotBeforeGap).deinit(alloc); 239 return error.NoSnapshotBeforeGap).deinit(alloc);
242 240
@@ -363,7 +361,7 @@ test "Server: a delta reattach is replayed the gap's last clipboard set, then it
363 // departed client really held. Yank in vim, the link blinks, you are back 361 // departed client really held. Yank in vim, the link blinks, you are back
364 // two seconds later — losing the yank to that is how a copy feature 362 // two seconds later — losing the yank to that is how a copy feature
365 // becomes one you stop believing. 363 // becomes one you stop believing.
366 const b = try std.net.connectUnixSocket(sock_path); 364 const b = try dial.dial(sock_path);
367 defer b.close(); 365 defer b.close();
368 try proto.writeFrame(b.handle, .attach, &proto.encodeAttach(80, 24, wm.seq, wm.epoch)); 366 try proto.writeFrame(b.handle, .attach, &proto.encodeAttach(80, 24, wm.seq, wm.epoch));
369 367
@@ -426,7 +424,7 @@ test "Server: a reattach quoting the seq an event was stamped at is not replayed
426 try std.testing.expectEqual(@as(usize, Session.kinds.len), recorded); 424 try std.testing.expectEqual(@as(usize, Session.kinds.len), recorded);
427 try std.testing.expect(lowest > wm.seq); 425 try std.testing.expect(lowest > wm.seq);
428 426
429 const b = try std.net.connectUnixSocket(sock_path); 427 const b = try dial.dial(sock_path);
430 defer b.close(); 428 defer b.close();
431 try proto.writeFrame(b.handle, .attach, &proto.encodeAttach(80, 24, at, wm.epoch)); 429 try proto.writeFrame(b.handle, .attach, &proto.encodeAttach(80, 24, at, wm.epoch));
432 430
@@ -459,9 +457,8 @@ test "Server: a clipboard event in the gap is not replayed to a snapshot attach"
459 // seq 0, epoch 0: what a fresh client sends, and what `canServe` refuses 457 // seq 0, epoch 0: what a fresh client sends, and what `canServe` refuses
460 // by construction. This client has never seen this session, so the 458 // by construction. This client has never seen this session, so the
461 // clipboard write it slept through is not addressed to it. 459 // clipboard write it slept through is not addressed to it.
462 const b = try std.net.connectUnixSocket(sock_path); 460 const b = try dial.dialAttach(sock_path, 80, 24);
463 defer b.close(); 461 defer b.close();
464 try proto.writeFrame(b.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
465 462
466 // `collectGapReplay` inspects EVERY frame: `awaitFrame` drops what it is not 463 // `collectGapReplay` inspects EVERY frame: `awaitFrame` drops what it is not
467 // looking for, so a replay queued ahead of the snapshot would be swallowed — 464 // looking for, so a replay queued ahead of the snapshot would be swallowed —
@@ -499,9 +496,8 @@ test "Server: a rebuild drops the pending clipboard it just put out of reach" {
499 496
500 // A joiner at a different size is the shortest route to a rebuild: it 497 // A joiner at a different size is the shortest route to a rebuild: it
501 // takes sendResync's size_changed arm straight into resyncSnapshot. 498 // takes sendResync's size_changed arm straight into resyncSnapshot.
502 const b = try std.net.connectUnixSocket(sock_path); 499 const b = try dial.dialAttach(sock_path, 100, 30);
503 defer b.close(); 500 defer b.close();
504 try proto.writeFrame(b.handle, .attach, &proto.encodeAttach(100, 30, 0, 0));
505 (try awaitFrame(alloc, &srv, b.handle, .snapshot, 400) orelse 501 (try awaitFrame(alloc, &srv, b.handle, .snapshot, 400) orelse
506 return error.NoSnapshotAfterResize).deinit(alloc); 502 return error.NoSnapshotAfterResize).deinit(alloc);
507 503
@@ -600,9 +596,8 @@ test "Server: a session that dies holding a pending event frees it" {
600 // because `reap` does more for a session that HAS clients — queue 596 // because `reap` does more for a session that HAS clients — queue
601 // `exit_status`, drain, drop, then free — so attaching puts the free after 597 // `exit_status`, drain, drop, then free — so attaching puts the free after
602 // that sequence rather than after two no-op loops. 598 // that sequence rather than after two no-op loops.
603 const c = try std.net.connectUnixSocket(sock_path); 599 const c = try dial.dialAttach(sock_path, 80, 24);
604 defer c.close(); 600 defer c.close();
605 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
606 (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400) orelse 601 (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400) orelse
607 return error.NoSnapshot).deinit(alloc); 602 return error.NoSnapshot).deinit(alloc);
608 603
src/server/server_test_deliver.zig
Old New
@@ -2,6 +2,7 @@ const std = @import("std");
2 const proto = @import("term").protocol; 2 const proto = @import("term").protocol;
3 const TmpDir = @import("testtmp").TmpDir; 3 const TmpDir = @import("testtmp").TmpDir;
4 const h = @import("server_test_harness.zig"); 4 const h = @import("server_test_harness.zig");
5 const dial = h.dial;
5 const srv_mod = @import("server.zig"); 6 const srv_mod = @import("server.zig");
6 const Server = srv_mod.Server; 7 const Server = srv_mod.Server;
7 const connectedPair = h.connectedPair; 8 const connectedPair = h.connectedPair;
@@ -264,9 +265,8 @@ test "Server: the shell's exit status reaches an attached client" {
264 defer th.join(); 265 defer th.join();
265 defer stop.store(true, .release); 266 defer stop.store(true, .release);
266 267
267 const c = try std.net.connectUnixSocket(sock_path); 268 const c = try dial.dialAttach(sock_path, 80, 24);
268 defer c.close(); 269 defer c.close();
269 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
270 270
271 // Settle first, so the shell is ready for input and the exit below is 271 // Settle first, so the shell is ready for input and the exit below is
272 // the next thing that happens. 272 // the next thing that happens.
src/server/server_test_harness.zig
Old New
@@ -5,6 +5,11 @@ const replica_mod = @import("term").replica;
5 const quic = @import("quic"); 5 const quic = @import("quic");
6 const quic_server = @import("quic_server.zig"); 6 const quic_server = @import("quic_server.zig");
7 const TmpDir = @import("testtmp").TmpDir; 7 const TmpDir = @import("testtmp").TmpDir;
8 /// Re-exported so the eight sibling test files spell the module name once,
9 /// here, and reach it as `h.dial` — the same connect-a-daemon primitive the
10 /// CLI client and `mux a` dial through, so these tests hold the socket the
11 /// way the product does rather than hand-rolling the pair of calls.
12 pub const dial = @import("dial");
8 const srv_mod = @import("server.zig"); 13 const srv_mod = @import("server.zig");
9 const Server = srv_mod.Server; 14 const Server = srv_mod.Server;
10 15
src/server/server_test_modes.zig
Old New
@@ -2,6 +2,7 @@ const std = @import("std");
2 const proto = @import("term").protocol; 2 const proto = @import("term").protocol;
3 const TmpDir = @import("testtmp").TmpDir; 3 const TmpDir = @import("testtmp").TmpDir;
4 const h = @import("server_test_harness.zig"); 4 const h = @import("server_test_harness.zig");
5 const dial = h.dial;
5 const srv_mod = @import("server.zig"); 6 const srv_mod = @import("server.zig");
6 const Server = srv_mod.Server; 7 const Server = srv_mod.Server;
7 const awaitFrame = h.awaitFrame; 8 const awaitFrame = h.awaitFrame;
@@ -118,9 +119,8 @@ test "Server: the pty's mode bits reach a client on attach, and again only when
118 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script }); 119 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script });
119 defer srv.deinit(); 120 defer srv.deinit();
120 121
121 const c = try std.net.connectUnixSocket(sock_path); 122 const c = try dial.dialAttach(sock_path, 80, 24);
122 defer c.close(); 123 defer c.close();
123 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
124 124
125 var modes: std.ArrayList(proto.PtyModeFlags) = .empty; 125 var modes: std.ArrayList(proto.PtyModeFlags) = .empty;
126 defer modes.deinit(alloc); 126 defer modes.deinit(alloc);
@@ -190,9 +190,8 @@ test "Server: a BEL from the session reaches its client as a bell term_event" {
190 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 190 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
191 defer srv.deinit(); 191 defer srv.deinit();
192 192
193 const c = try std.net.connectUnixSocket(sock_path); 193 const c = try dial.dialAttach(sock_path, 80, 24);
194 defer c.close(); 194 defer c.close();
195 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
196 (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400) orelse 195 (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400) orelse
197 return error.NoSnapshot).deinit(alloc); 196 return error.NoSnapshot).deinit(alloc);
198 197
@@ -243,9 +242,8 @@ test "Server: a burst of bells in one chunk is coalesced into one frame" {
243 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script }); 242 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script });
244 defer srv.deinit(); 243 defer srv.deinit();
245 244
246 const c = try std.net.connectUnixSocket(sock_path); 245 const c = try dial.dialAttach(sock_path, 80, 24);
247 defer c.close(); 246 defer c.close();
248 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
249 // Awaited, not assumed: this is what makes the ring below strictly later 247 // Awaited, not assumed: this is what makes the ring below strictly later
250 // than the attach, and so what removes the race described above. 248 // than the attach, and so what removes the race described above.
251 (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400) orelse 249 (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400) orelse
@@ -309,9 +307,8 @@ test "Server: a bell in a later chunk is its own frame, not folded into the firs
309 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script }); 307 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script });
310 defer srv.deinit(); 308 defer srv.deinit();
311 309
312 const c = try std.net.connectUnixSocket(sock_path); 310 const c = try dial.dialAttach(sock_path, 80, 24);
313 defer c.close(); 311 defer c.close();
314 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
315 (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400) orelse 312 (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400) orelse
316 return error.NoSnapshot).deinit(alloc); 313 return error.NoSnapshot).deinit(alloc);
317 314
@@ -361,9 +358,8 @@ test "Server: a session enabling bracketed paste tells its clients, and not agai
361 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script }); 358 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script });
362 defer srv.deinit(); 359 defer srv.deinit();
363 360
364 const c = try std.net.connectUnixSocket(sock_path); 361 const c = try dial.dialAttach(sock_path, 80, 24);
365 defer c.close(); 362 defer c.close();
366 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
367 363
368 // attach answers with the modes as they stand, which is before the shell 364 // attach answers with the modes as they stand, which is before the shell
369 // has run: keep pumping until one says the DECSET landed. Asserting on 365 // has run: keep pumping until one says the DECSET landed. Asserting on
@@ -429,9 +425,8 @@ test "Server: a client attaching to a session already in bracketed paste is told
429 // The first client is here to get the change SENT, not to observe it: 425 // The first client is here to get the change SENT, not to observe it:
430 // once term_modes_sent holds true, sampleTermModes will never fire again 426 // once term_modes_sent holds true, sampleTermModes will never fire again
431 // this session, so everything below can only be the resync's doing. 427 // this session, so everything below can only be the resync's doing.
432 const a = try std.net.connectUnixSocket(sock_path); 428 const a = try dial.dialAttach(sock_path, 80, 24);
433 defer a.close(); 429 defer a.close();
434 try proto.writeFrame(a.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
435 const enabled = while (try awaitFrame(alloc, &srv, a.handle, .term_modes, 400)) |f| { 430 const enabled = while (try awaitFrame(alloc, &srv, a.handle, .term_modes, 400)) |f| {
436 defer f.deinit(alloc); 431 defer f.deinit(alloc);
437 if ((try proto.decodeTermModes(f.payload)).bracketed_paste) break true; 432 if ((try proto.decodeTermModes(f.payload)).bracketed_paste) break true;
@@ -441,9 +436,8 @@ test "Server: a client attaching to a session already in bracketed paste is told
441 // Snapshot branch: a joiner holding nothing. It must be told what is 436 // Snapshot branch: a joiner holding nothing. It must be told what is
442 // true now, or it would paint a session whose application wants 437 // true now, or it would paint a session whose application wants
443 // bracketed paste while its host terminal has never heard of it. 438 // bracketed paste while its host terminal has never heard of it.
444 const b = try std.net.connectUnixSocket(sock_path); 439 const b = try dial.dialAttach(sock_path, 80, 24);
445 defer b.close(); 440 defer b.close();
446 try proto.writeFrame(b.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
447 var snap = try modesWithResync(alloc, &srv, b.handle); 441 var snap = try modesWithResync(alloc, &srv, b.handle);
448 try std.testing.expectEqual(proto.MsgType.snapshot, snap.content orelse 442 try std.testing.expectEqual(proto.MsgType.snapshot, snap.content orelse
449 return error.NoContentFrameOnJoin); 443 return error.NoContentFrameOnJoin);
@@ -488,9 +482,8 @@ test "Server: a joiner that resizes the grid is still told the session's modes"
488 // As in the resync-branch test above: the first client is here to latch 482 // As in the resync-branch test above: the first client is here to latch
489 // term_modes_sent, after which sampleTermModes can never fire again this 483 // term_modes_sent, after which sampleTermModes can never fire again this
490 // session and everything below is the resync's doing alone. 484 // session and everything below is the resync's doing alone.
491 const a = try std.net.connectUnixSocket(sock_path); 485 const a = try dial.dialAttach(sock_path, 80, 24);
492 defer a.close(); 486 defer a.close();
493 try proto.writeFrame(a.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
494 const enabled = while (try awaitFrame(alloc, &srv, a.handle, .term_modes, 400)) |f| { 487 const enabled = while (try awaitFrame(alloc, &srv, a.handle, .term_modes, 400)) |f| {
495 defer f.deinit(alloc); 488 defer f.deinit(alloc);
496 if ((try proto.decodeTermModes(f.payload)).bracketed_paste) break true; 489 if ((try proto.decodeTermModes(f.payload)).bracketed_paste) break true;
@@ -501,9 +494,8 @@ test "Server: a joiner that resizes the grid is still told the session's modes"
501 // joiner at a DIFFERENT size returns early through `resyncSnapshot`. Left 494 // joiner at a DIFFERENT size returns early through `resyncSnapshot`. Left
502 // bare, bracketed paste stops being mirrored after any window resize, and 495 // bare, bracketed paste stops being mirrored after any window resize, and
503 // nothing connects a shell echoing a literal `200~` to the resize. 496 // nothing connects a shell echoing a literal `200~` to the resize.
504 const b = try std.net.connectUnixSocket(sock_path); 497 const b = try dial.dialAttach(sock_path, 100, 30);
505 defer b.close(); 498 defer b.close();
506 try proto.writeFrame(b.handle, .attach, &proto.encodeAttach(100, 30, 0, 0));
507 499
508 // A's re-snapshot at B's size is the witness that this arm ran at all: 500 // A's re-snapshot at B's size is the witness that this arm ran at all:
509 // resyncSnapshot broadcasts, and the other two arms send to the joiner 501 // resyncSnapshot broadcasts, and the other two arms send to the joiner
@@ -568,9 +560,8 @@ test "Server: a window title reaches clients on change, and only on change" {
568 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script }); 560 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script });
569 defer srv.deinit(); 561 defer srv.deinit();
570 562
571 const c = try std.net.connectUnixSocket(sock_path); 563 const c = try dial.dialAttach(sock_path, 80, 24);
572 defer c.close(); 564 defer c.close();
573 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
574 565
575 // Queued behind the attach on the same socket, so the daemon has taken 566 // Queued behind the attach on the same socket, so the daemon has taken
576 // the attach before it can forward this — see the script's leading read. 567 // the attach before it can forward this — see the script's leading read.
@@ -642,9 +633,8 @@ test "Server: a session that never set a title has none sent for it" {
642 // Two attaches, because the two paths that could send an empty title are 633 // Two attaches, because the two paths that could send an empty title are
643 // different code: the SAMPLER and the RESYNC. An empty title on either makes 634 // different code: the SAMPLER and the RESYNC. An empty title on either makes
644 // the client wipe the title bar of a terminal whose session said nothing. 635 // the client wipe the title bar of a terminal whose session said nothing.
645 const a = try std.net.connectUnixSocket(sock_path); 636 const a = try dial.dialAttach(sock_path, 80, 24);
646 defer a.close(); 637 defer a.close();
647 try proto.writeFrame(a.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
648 638
649 var saw_content = false; 639 var saw_content = false;
650 var titles: usize = 0; 640 var titles: usize = 0;
@@ -658,8 +648,7 @@ test "Server: a session that never set a title has none sent for it" {
658 // The joiner goes in only once the shell's output has landed, so 648 // The joiner goes in only once the shell's output has landed, so
659 // its resync reads a session that has genuinely run. 649 // its resync reads a session that has genuinely run.
660 if (!joined) { 650 if (!joined) {
661 b = try std.net.connectUnixSocket(sock_path); 651 b = try dial.dialAttach(sock_path, 80, 24);
662 try proto.writeFrame(b.?.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
663 joined = true; 652 joined = true;
664 } 653 }
665 after += 1; 654 after += 1;
@@ -718,9 +707,8 @@ test "Server: a joiner that resizes the grid is still told the session's title"
718 // The first client latches title_sent, after which sampleTermTitle can 707 // The first client latches title_sent, after which sampleTermTitle can
719 // never fire again for this title and everything below is the resync's 708 // never fire again for this title and everything below is the resync's
720 // doing alone. 709 // doing alone.
721 const a = try std.net.connectUnixSocket(sock_path); 710 const a = try dial.dialAttach(sock_path, 80, 24);
722 defer a.close(); 711 defer a.close();
723 try proto.writeFrame(a.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
724 const latched = (try awaitFrame(alloc, &srv, a.handle, .term_title, 400)) orelse 712 const latched = (try awaitFrame(alloc, &srv, a.handle, .term_title, 400)) orelse
725 return error.NoTitleFrame; 713 return error.NoTitleFrame;
726 defer latched.deinit(alloc); 714 defer latched.deinit(alloc);
@@ -730,9 +718,8 @@ test "Server: a joiner that resizes the grid is still told the session's title"
730 // through resyncSnapshot, before the delta/snapshot split. Left bare it 718 // through resyncSnapshot, before the delta/snapshot split. Left bare it
731 // is a real regression and a quiet one — the title bar would silently 719 // is a real regression and a quiet one — the title bar would silently
732 // stop matching the session after any window resize. 720 // stop matching the session after any window resize.
733 const b = try std.net.connectUnixSocket(sock_path); 721 const b = try dial.dialAttach(sock_path, 100, 30);
734 defer b.close(); 722 defer b.close();
735 try proto.writeFrame(b.handle, .attach, &proto.encodeAttach(100, 30, 0, 0));
736 723
737 // A's re-snapshot at B's size is the witness that this arm ran at all: 724 // A's re-snapshot at B's size is the witness that this arm ran at all:
738 // resyncSnapshot broadcasts, and the other two arms send to the joiner 725 // resyncSnapshot broadcasts, and the other two arms send to the joiner
src/server/server_test_session.zig
Old New
@@ -6,6 +6,7 @@ const quic_server = @import("quic_server.zig");
6 const xdg = @import("xdg"); 6 const xdg = @import("xdg");
7 const TmpDir = @import("testtmp").TmpDir; 7 const TmpDir = @import("testtmp").TmpDir;
8 const h = @import("server_test_harness.zig"); 8 const h = @import("server_test_harness.zig");
9 const dial = h.dial;
9 const srv_mod = @import("server.zig"); 10 const srv_mod = @import("server.zig");
10 const SessionTable = @import("server_sessions.zig").SessionTable; 11 const SessionTable = @import("server_sessions.zig").SessionTable;
11 const Server = srv_mod.Server; 12 const Server = srv_mod.Server;
@@ -13,7 +14,6 @@ const boundUdpPort = srv_mod.boundUdpPort;
13 const max_sessions = srv_mod.max_sessions; 14 const max_sessions = srv_mod.max_sessions;
14 const shutdown_flag = &srv_mod.shutdown_flag; 15 const shutdown_flag = &srv_mod.shutdown_flag;
15 const applyFrame = h.applyFrame; 16 const applyFrame = h.applyFrame;
16 const attachNamed = h.attachNamed;
17 const awaitFrame = h.awaitFrame; 17 const awaitFrame = h.awaitFrame;
18 const connectedPair = h.connectedPair; 18 const connectedPair = h.connectedPair;
19 const firstStateFrame = h.firstStateFrame; 19 const firstStateFrame = h.firstStateFrame;
@@ -64,9 +64,8 @@ test "Server: a second daemon refuses a live socket instead of stealing it" {
64 // ...and it still reaches the daemon that was already there. The epoch 64 // ...and it still reaches the daemon that was already there. The epoch
65 // is what makes that precise: it names one daemon *instance*, so 65 // is what makes that precise: it names one daemon *instance*, so
66 // matching it rules out having been handed a replacement. 66 // matching it rules out having been handed a replacement.
67 const c = try std.net.connectUnixSocket(sock_path); 67 const c = try dial.dialAttach(sock_path, 80, 24);
68 defer c.close(); 68 defer c.close();
69 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
70 const first = try firstStateFrame(alloc, c.handle, 10_000); 69 const first = try firstStateFrame(alloc, c.handle, 10_000);
71 try std.testing.expect(first != null); 70 try std.testing.expect(first != null);
72 try std.testing.expectEqual(proto.MsgType.snapshot, first.?.type); 71 try std.testing.expectEqual(proto.MsgType.snapshot, first.?.type);
@@ -102,9 +101,8 @@ test "Server: a dead daemon's leftover socket file is cleared and rebound" {
102 defer stop.store(true, .release); 101 defer stop.store(true, .release);
103 102
104 // And the rebind is real, not just a file that reappeared: it serves. 103 // And the rebind is real, not just a file that reappeared: it serves.
105 const c = try std.net.connectUnixSocket(sock_path); 104 const c = try dial.dialAttach(sock_path, 80, 24);
106 defer c.close(); 105 defer c.close();
107 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
108 const first = try firstStateFrame(alloc, c.handle, 10_000); 106 const first = try firstStateFrame(alloc, c.handle, 10_000);
109 try std.testing.expect(first != null); 107 try std.testing.expect(first != null);
110 try std.testing.expectEqual(srv.sessions.table[0].?.epoch, first.?.epoch); 108 try std.testing.expectEqual(srv.sessions.table[0].?.epoch, first.?.epoch);
@@ -369,7 +367,7 @@ test "Server: stop_req from a bare connection requests shutdown; run returns 0"
369 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" }); 367 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" });
370 defer srv.deinit(); 368 defer srv.deinit();
371 369
372 const c = try std.net.connectUnixSocket(sock_path); 370 const c = try dial.dial(sock_path);
373 defer c.close(); 371 defer c.close();
374 // No attach first: the frame must be honored from the OBSERVER 372 // No attach first: the frame must be honored from the OBSERVER
375 // dispatch, which is where a bare `mux d stop` connection lives. 373 // dispatch, which is where a bare `mux d stop` connection lives.
@@ -404,9 +402,8 @@ test "Server: stop_req from an attached client is honored too" {
404 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" }); 402 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" });
405 defer srv.deinit(); 403 defer srv.deinit();
406 404
407 const c = try std.net.connectUnixSocket(sock_path); 405 const c = try dial.dialAttach(sock_path, 80, 24);
408 defer c.close(); 406 defer c.close();
409 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
410 // Both frames go out before any pump and the stop still lands in 407 // Both frames go out before any pump and the stop still lands in
411 // `handleFrame`: one observer read takes both into the buffer, the attach 408 // `handleFrame`: one observer read takes both into the buffer, the attach
412 // promotes the connection with that buffer, and the stop drains as the 409 // promotes the connection with that buffer, and the stop drains as the
@@ -450,7 +447,7 @@ test "Server: endpoint_req binds a listener lazily, answers the same port on bot
450 // keeps this half from depending on the test machine having a real 447 // keeps this half from depending on the test machine having a real
451 // ~/.config/mux/key. The observer arm is the load-bearing one: 448 // ~/.config/mux/key. The observer arm is the load-bearing one:
452 // `mux d endpoint` never attaches. 449 // `mux d endpoint` never attaches.
453 const obs = try std.net.connectUnixSocket(sock_path); 450 const obs = try dial.dial(sock_path);
454 defer obs.close(); 451 defer obs.close();
455 try proto.writeFrame(obs.handle, .endpoint_req, ""); 452 try proto.writeFrame(obs.handle, .endpoint_req, "");
456 const reply = (try awaitFrame(alloc, &srv, obs.handle, .endpoint_reply, 200)) orelse 453 const reply = (try awaitFrame(alloc, &srv, obs.handle, .endpoint_reply, 200)) orelse
@@ -469,9 +466,8 @@ test "Server: endpoint_req binds a listener lazily, answers the same port on bot
469 466
470 // The attached-client arm answers the same verb the same way, on a 467 // The attached-client arm answers the same verb the same way, on a
471 // separate connection that attaches first. 468 // separate connection that attaches first.
472 const cl = try std.net.connectUnixSocket(sock_path); 469 const cl = try dial.dialAttach(sock_path, 80, 24);
473 defer cl.close(); 470 defer cl.close();
474 try proto.writeFrame(cl.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
475 try proto.writeFrame(cl.handle, .endpoint_req, ""); 471 try proto.writeFrame(cl.handle, .endpoint_req, "");
476 const reply3 = (try awaitFrame(alloc, &srv, cl.handle, .endpoint_reply, 400)) orelse 472 const reply3 = (try awaitFrame(alloc, &srv, cl.handle, .endpoint_reply, 400)) orelse
477 return error.NoAttachedEndpointReply; 473 return error.NoAttachedEndpointReply;
@@ -539,9 +535,8 @@ test "Server: status_req is answered on an attached client and on a bare observe
539 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 535 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
540 defer srv.deinit(); 536 defer srv.deinit();
541 537
542 const c = try std.net.connectUnixSocket(sock_path); 538 const c = try dial.dialAttach(sock_path, 80, 24);
543 defer c.close(); 539 defer c.close();
544 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
545 try proto.writeFrame(c.handle, .status_req, ""); 540 try proto.writeFrame(c.handle, .status_req, "");
546 const f = (try awaitFrame(alloc, &srv, c.handle, .status_reply, 400)) orelse 541 const f = (try awaitFrame(alloc, &srv, c.handle, .status_reply, 400)) orelse
547 return error.NoAttachedStatusReply; 542 return error.NoAttachedStatusReply;
@@ -559,7 +554,7 @@ test "Server: status_req is answered on an attached client and on a bare observe
559 // `mux a status` never attaches, so the observer arm is the load-bearing 554 // `mux a status` never attaches, so the observer arm is the load-bearing
560 // one — same reasoning as endpoint_req's, and the same failure if it is 555 // one — same reasoning as endpoint_req's, and the same failure if it is
561 // missing: the frame falls into `else => {}` and the caller hangs. 556 // missing: the frame falls into `else => {}` and the caller hangs.
562 const obs = try std.net.connectUnixSocket(sock_path); 557 const obs = try dial.dial(sock_path);
563 defer obs.close(); 558 defer obs.close();
564 try proto.writeFrame(obs.handle, .status_req, ""); 559 try proto.writeFrame(obs.handle, .status_req, "");
565 const f2 = (try awaitFrame(alloc, &srv, obs.handle, .status_reply, 400)) orelse 560 const f2 = (try awaitFrame(alloc, &srv, obs.handle, .status_reply, 400)) orelse
@@ -611,12 +606,10 @@ test "Server: two named sessions hold two shells with independent content" {
611 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 606 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
612 defer srv.deinit(); 607 defer srv.deinit();
613 608
614 const ca = try std.net.connectUnixSocket(sock_path); 609 const ca = try dial.dialAttachNamed(sock_path, 80, 24, "a");
615 defer ca.close(); 610 defer ca.close();
616 try attachNamed(ca.handle, 80, 24, "a"); 611 const cb = try dial.dialAttachNamed(sock_path, 80, 24, "b");
617 const cb = try std.net.connectUnixSocket(sock_path);
618 defer cb.close(); 612 defer cb.close();
619 try attachNamed(cb.handle, 80, 24, "b");
620 613
621 try proto.writeFrame(ca.handle, .input, "MARKER-ALPHA\n"); 614 try proto.writeFrame(ca.handle, .input, "MARKER-ALPHA\n");
622 try proto.writeFrame(cb.handle, .input, "MARKER-BETA\n"); 615 try proto.writeFrame(cb.handle, .input, "MARKER-BETA\n");
@@ -710,12 +703,10 @@ test "Server: every session shell is told the socket it lives on and its own nam
710 ); 703 );
711 defer alloc.free(probe); 704 defer alloc.free(probe);
712 705
713 const c0 = try std.net.connectUnixSocket(sock_path); 706 const c0 = try dial.dialAttachNamed(sock_path, 80, 24, "");
714 defer c0.close(); 707 defer c0.close();
715 try attachNamed(c0.handle, 80, 24, ""); 708 const ca = try dial.dialAttachNamed(sock_path, 80, 24, "a");
716 const ca = try std.net.connectUnixSocket(sock_path);
717 defer ca.close(); 709 defer ca.close();
718 try attachNamed(ca.handle, 80, 24, "a");
719 710
720 try proto.writeFrame(c0.handle, .input, probe); 711 try proto.writeFrame(c0.handle, .input, probe);
721 try proto.writeFrame(ca.handle, .input, probe); 712 try proto.writeFrame(ca.handle, .input, probe);
@@ -751,9 +742,8 @@ test "Server: a bare 20-byte attach lands in the default session" {
751 // tail at all. An 742 // tail at all. An
752 // old client must land in the default session, not create a nameless 743 // old client must land in the default session, not create a nameless
753 // one beside it. 744 // one beside it.
754 const c = try std.net.connectUnixSocket(sock_path); 745 const c = try dial.dialAttach(sock_path, 80, 24);
755 defer c.close(); 746 defer c.close();
756 try proto.writeFrame(c.handle, .attach, &proto.encodeAttach(80, 24, 0, 0));
757 const f = (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400)) orelse 747 const f = (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400)) orelse
758 return error.NoSnapshotOnBareAttach; 748 return error.NoSnapshotOnBareAttach;
759 f.deinit(alloc); 749 f.deinit(alloc);
@@ -783,9 +773,8 @@ test "Server: an attach past max_sessions is refused with exit_status, sessions
783 for (1..max_sessions) |i| { 773 for (1..max_sessions) |i| {
784 var nb: [8]u8 = undefined; 774 var nb: [8]u8 = undefined;
785 const nm = try std.fmt.bufPrint(&nb, "s{d}", .{i}); 775 const nm = try std.fmt.bufPrint(&nb, "s{d}", .{i});
786 const c = try std.net.connectUnixSocket(sock_path); 776 const c = try dial.dialAttachNamed(sock_path, 80, 24, nm);
787 defer c.close(); 777 defer c.close();
788 try attachNamed(c.handle, 80, 24, nm);
789 const f = (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400)) orelse 778 const f = (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400)) orelse
790 return error.NoSnapshotFillingTable; 779 return error.NoSnapshotFillingTable;
791 f.deinit(alloc); 780 f.deinit(alloc);
@@ -797,9 +786,8 @@ test "Server: an attach past max_sessions is refused with exit_status, sessions
797 786
798 // One name past the table gets the same honest no a full client table 787 // One name past the table gets the same honest no a full client table
799 // gives. 788 // gives.
800 const extra = try std.net.connectUnixSocket(sock_path); 789 const extra = try dial.dialAttachNamed(sock_path, 80, 24, "extra");
801 defer extra.close(); 790 defer extra.close();
802 try attachNamed(extra.handle, 80, 24, "extra");
803 const f = (try awaitFrame(alloc, &srv, extra.handle, .exit_status, 400)) orelse 791 const f = (try awaitFrame(alloc, &srv, extra.handle, .exit_status, 400)) orelse
804 return error.NoRefusal; 792 return error.NoRefusal;
805 defer f.deinit(alloc); 793 defer f.deinit(alloc);
@@ -841,9 +829,8 @@ test "Server: a table of TERM-ignoring shells costs one grace, not one each" {
841 for (1..9) |i| { 829 for (1..9) |i| {
842 var nb: [8]u8 = undefined; 830 var nb: [8]u8 = undefined;
843 const nm = try std.fmt.bufPrint(&nb, "s{d}", .{i}); 831 const nm = try std.fmt.bufPrint(&nb, "s{d}", .{i});
844 const c = try std.net.connectUnixSocket(sock_path); 832 const c = try dial.dialAttachNamed(sock_path, 80, 24, nm);
845 defer c.close(); 833 defer c.close();
846 try attachNamed(c.handle, 80, 24, nm);
847 const f = (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400)) orelse 834 const f = (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400)) orelse
848 return error.NoSnapshotFillingTable; 835 return error.NoSnapshotFillingTable;
849 f.deinit(alloc); 836 f.deinit(alloc);
@@ -898,9 +885,8 @@ test "Server: an invalid name on the wire is refused, not created" {
898 885
899 // A raw-wire client can spell any bytes it likes; a name no tool could 886 // A raw-wire client can spell any bytes it likes; a name no tool could
900 // ever address must die here, not become a session. 887 // ever address must die here, not become a session.
901 const c = try std.net.connectUnixSocket(sock_path); 888 const c = try dial.dialAttachNamed(sock_path, 80, 24, "has space");
902 defer c.close(); 889 defer c.close();
903 try attachNamed(c.handle, 80, 24, "has space");
904 const f = (try awaitFrame(alloc, &srv, c.handle, .exit_status, 400)) orelse 890 const f = (try awaitFrame(alloc, &srv, c.handle, .exit_status, 400)) orelse
905 return error.NoRefusal; 891 return error.NoRefusal;
906 defer f.deinit(alloc); 892 defer f.deinit(alloc);
@@ -960,17 +946,15 @@ test "Server: one session's shell exiting drops only its clients; the daemon car
960 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script }); 946 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script });
961 defer srv.deinit(); 947 defer srv.deinit();
962 948
963 const ca = try std.net.connectUnixSocket(sock_path); 949 const ca = try dial.dialAttachNamed(sock_path, 80, 24, "a");
964 defer ca.close(); 950 defer ca.close();
965 try attachNamed(ca.handle, 80, 24, "a");
966 const fa = (try awaitFrame(alloc, &srv, ca.handle, .snapshot, 400)) orelse 951 const fa = (try awaitFrame(alloc, &srv, ca.handle, .snapshot, 400)) orelse
967 return error.NoSnapshotA; 952 return error.NoSnapshotA;
968 fa.deinit(alloc); 953 fa.deinit(alloc);
969 const si_a = srv.sessions.find("a") orelse return error.SessionAMissing; 954 const si_a = srv.sessions.find("a") orelse return error.SessionAMissing;
970 955
971 const cb = try std.net.connectUnixSocket(sock_path); 956 const cb = try dial.dialAttachNamed(sock_path, 80, 24, "b");
972 defer cb.close(); 957 defer cb.close();
973 try attachNamed(cb.handle, 80, 24, "b");
974 958
975 // b's liveness established BEFORE a dies, so its survival below is a 959 // b's liveness established BEFORE a dies, so its survival below is a
976 // comparison and not a hope. 960 // comparison and not a hope.
@@ -1062,7 +1046,7 @@ fn awaitFrameThreaded(
1062 /// stays buffered, so a shared socket lets a later question be answered by an 1046 /// stays buffered, so a shared socket lets a later question be answered by an
1063 /// earlier one's frame — generated before the very event being waited on. 1047 /// earlier one's frame — generated before the very event being waited on.
1064 fn waitStats(alloc: std.mem.Allocator, sock_path: []const u8, want: []const u8) !void { 1048 fn waitStats(alloc: std.mem.Allocator, sock_path: []const u8, want: []const u8) !void {
1065 const obs = try std.net.connectUnixSocket(sock_path); 1049 const obs = try dial.dial(sock_path);
1066 defer obs.close(); 1050 defer obs.close();
1067 var tries: usize = 0; 1051 var tries: usize = 0;
1068 while (tries < 40) : (tries += 1) { 1052 while (tries < 40) : (tries += 1) {
@@ -1083,9 +1067,8 @@ fn probeEmptiedDaemon(alloc: std.mem.Allocator, sock_path: []const u8) !void {
1083 // only one, so the table empties behind it. 7 rather than 0 so the 1067 // only one, so the table empties behind it. 7 rather than 0 so the
1084 // caller's exit-code pin can tell the two contracts apart: under the 1068 // caller's exit-code pin can tell the two contracts apart: under the
1085 // retired one this shell's code WAS the daemon's. 1069 // retired one this shell's code WAS the daemon's.
1086 const c1 = try std.net.connectUnixSocket(sock_path); 1070 const c1 = try dial.dialAttachNamed(sock_path, 80, 24, proto.default_session);
1087 defer c1.close(); 1071 defer c1.close();
1088 try attachNamed(c1.handle, 80, 24, proto.default_session);
1089 const snap = (try awaitFrameThreaded(alloc, c1.handle, .snapshot, 4000)) orelse 1072 const snap = (try awaitFrameThreaded(alloc, c1.handle, .snapshot, 4000)) orelse
1090 return error.NoFirstSnapshot; 1073 return error.NoFirstSnapshot;
1091 snap.deinit(alloc); 1074 snap.deinit(alloc);
@@ -1096,9 +1079,8 @@ fn probeEmptiedDaemon(alloc: std.mem.Allocator, sock_path: []const u8) !void {
1096 // Still serving with nothing to serve: a birth on the emptied daemon 1079 // Still serving with nothing to serve: a birth on the emptied daemon
1097 // takes the default name back, which is the attach `mux --sock PATH` 1080 // takes the default name back, which is the attach `mux --sock PATH`
1098 // sends. 1081 // sends.
1099 const c2 = try std.net.connectUnixSocket(sock_path); 1082 const c2 = try dial.dialAttachNamed(sock_path, 80, 24, proto.default_session);
1100 defer c2.close(); 1083 defer c2.close();
1101 try attachNamed(c2.handle, 80, 24, proto.default_session);
1102 const reborn = (try awaitFrameThreaded(alloc, c2.handle, .snapshot, 4000)) orelse 1084 const reborn = (try awaitFrameThreaded(alloc, c2.handle, .snapshot, 4000)) orelse
1103 return error.EmptyDaemonRefusedABirth; 1085 return error.EmptyDaemonRefusedABirth;
1104 reborn.deinit(alloc); 1086 reborn.deinit(alloc);
@@ -1162,9 +1144,8 @@ test "Server: a dead name re-attaches as a fresh session with a new epoch" {
1162 // First instance of "a": hold on to what its attach answered — the 1144 // First instance of "a": hold on to what its attach answered — the
1163 // epoch its snapshots were stamped with, and the newest seq counted 1145 // epoch its snapshots were stamped with, and the newest seq counted
1164 // under it. That pair is exactly what a surviving client would quote. 1146 // under it. That pair is exactly what a surviving client would quote.
1165 const c1 = try std.net.connectUnixSocket(sock_path); 1147 const c1 = try dial.dialAttachNamed(sock_path, 80, 24, "a");
1166 defer c1.close(); 1148 defer c1.close();
1167 try attachNamed(c1.handle, 80, 24, "a");
1168 const f1 = (try awaitFrame(alloc, &srv, c1.handle, .snapshot, 400)) orelse 1149 const f1 = (try awaitFrame(alloc, &srv, c1.handle, .snapshot, 400)) orelse
1169 return error.NoFirstSnapshot; 1150 return error.NoFirstSnapshot;
1170 defer f1.deinit(alloc); 1151 defer f1.deinit(alloc);
@@ -1182,7 +1163,7 @@ test "Server: a dead name re-attaches as a fresh session with a new epoch" {
1182 // Re-attach the dead name quoting the DEAD instance's seq and epoch — the 1163 // Re-attach the dead name quoting the DEAD instance's seq and epoch — the
1183 // reconnect a client that missed the death sends. A fresh session cannot 1164 // reconnect a client that missed the death sends. A fresh session cannot
1184 // delta-serve seqs counted by a shell it never was. 1165 // delta-serve seqs counted by a shell it never was.
1185 const c2 = try std.net.connectUnixSocket(sock_path); 1166 const c2 = try dial.dial(sock_path);
1186 defer c2.close(); 1167 defer c2.close();
1187 var abuf: [proto.attach_max_len]u8 = undefined; 1168 var abuf: [proto.attach_max_len]u8 = undefined;
1188 try proto.writeFrame( 1169 try proto.writeFrame(
@@ -1229,12 +1210,10 @@ test "Server: dump names a session; an unknown name answers in words" {
1229 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 1210 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
1230 defer srv.deinit(); 1211 defer srv.deinit();
1231 1212
1232 const ca = try std.net.connectUnixSocket(sock_path); 1213 const ca = try dial.dialAttachNamed(sock_path, 80, 24, "a");
1233 defer ca.close(); 1214 defer ca.close();
1234 try attachNamed(ca.handle, 80, 24, "a"); 1215 const cb = try dial.dialAttachNamed(sock_path, 80, 24, "b");
1235 const cb = try std.net.connectUnixSocket(sock_path);
1236 defer cb.close(); 1216 defer cb.close();
1237 try attachNamed(cb.handle, 80, 24, "b");
1238 1217
1239 try proto.writeFrame(ca.handle, .input, "MARKER-ALPHA\n"); 1218 try proto.writeFrame(ca.handle, .input, "MARKER-ALPHA\n");
1240 try proto.writeFrame(cb.handle, .input, "MARKER-BETA\n"); 1219 try proto.writeFrame(cb.handle, .input, "MARKER-BETA\n");
@@ -1297,12 +1276,10 @@ test "Server: an observer's status_req names a session by tail" {
1297 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script }); 1276 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script });
1298 defer srv.deinit(); 1277 defer srv.deinit();
1299 1278
1300 const a = try std.net.connectUnixSocket(sock_path); 1279 const a = try dial.dialAttachNamed(sock_path, 80, 24, "a");
1301 defer a.close(); 1280 defer a.close();
1302 try attachNamed(a.handle, 80, 24, "a"); 1281 const b = try dial.dialAttachNamed(sock_path, 80, 24, "b");
1303 const b = try std.net.connectUnixSocket(sock_path);
1304 defer b.close(); 1282 defer b.close();
1305 try attachNamed(b.handle, 80, 24, "b");
1306 const fa = (try awaitFrame(alloc, &srv, a.handle, .snapshot, 400)) orelse 1283 const fa = (try awaitFrame(alloc, &srv, a.handle, .snapshot, 400)) orelse
1307 return error.NoSnapshotA; 1284 return error.NoSnapshotA;
1308 fa.deinit(alloc); 1285 fa.deinit(alloc);
@@ -1319,7 +1296,7 @@ test "Server: an observer's status_req names a session by tail" {
1319 1296
1320 // A bare connection — an observer, never attached — asks about b by 1297 // A bare connection — an observer, never attached — asks about b by
1321 // name alone. 1298 // name alone.
1322 const obs = try std.net.connectUnixSocket(sock_path); 1299 const obs = try dial.dial(sock_path);
1323 defer obs.close(); 1300 defer obs.close();
1324 try proto.writeFrame(obs.handle, .status_req, "b"); 1301 try proto.writeFrame(obs.handle, .status_req, "b");
1325 const reply = (try awaitFrame(alloc, &srv, obs.handle, .status_reply, 400)) orelse 1302 const reply = (try awaitFrame(alloc, &srv, obs.handle, .status_reply, 400)) orelse
@@ -1366,12 +1343,10 @@ test "Server: stats names every live session" {
1366 1343
1367 // The default session (bare attach) plus one named session: two live 1344 // The default session (bare attach) plus one named session: two live
1368 // sessions total, both of which must show up by name. 1345 // sessions total, both of which must show up by name.
1369 const ca = try std.net.connectUnixSocket(sock_path); 1346 const ca = try dial.dialAttach(sock_path, 80, 24);
1370 defer ca.close(); 1347 defer ca.close();
1371 try proto.writeFrame(ca.handle, .attach, &proto.encodeAttach(80, 24, 0, 0)); 1348 const cb = try dial.dialAttachNamed(sock_path, 80, 24, "b");
1372 const cb = try std.net.connectUnixSocket(sock_path);
1373 defer cb.close(); 1349 defer cb.close();
1374 try attachNamed(cb.handle, 80, 24, "b");
1375 const fa = (try awaitFrame(alloc, &srv, ca.handle, .snapshot, 400)) orelse 1350 const fa = (try awaitFrame(alloc, &srv, ca.handle, .snapshot, 400)) orelse
1376 return error.NoSnapshotA; 1351 return error.NoSnapshotA;
1377 fa.deinit(alloc); 1352 fa.deinit(alloc);
@@ -1397,12 +1372,10 @@ test "Server: sessions_req answers every live name, whoever asks" {
1397 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 1372 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
1398 defer srv.deinit(); 1373 defer srv.deinit();
1399 1374
1400 const ca = try std.net.connectUnixSocket(sock_path); 1375 const ca = try dial.dialAttach(sock_path, 80, 24);
1401 defer ca.close(); 1376 defer ca.close();
1402 try proto.writeFrame(ca.handle, .attach, &proto.encodeAttach(80, 24, 0, 0)); 1377 const cb = try dial.dialAttachNamed(sock_path, 80, 24, "work");
1403 const cb = try std.net.connectUnixSocket(sock_path);
1404 defer cb.close(); 1378 defer cb.close();
1405 try attachNamed(cb.handle, 80, 24, "work");
1406 const fa = (try awaitFrame(alloc, &srv, ca.handle, .snapshot, 400)) orelse 1379 const fa = (try awaitFrame(alloc, &srv, ca.handle, .snapshot, 400)) orelse
1407 return error.NoSnapshotA; 1380 return error.NoSnapshotA;
1408 fa.deinit(alloc); 1381 fa.deinit(alloc);
@@ -1430,12 +1403,10 @@ test "Server: an attached client's mismatched status tail is ignored" {
1430 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" }); 1403 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/cat" });
1431 defer srv.deinit(); 1404 defer srv.deinit();
1432 1405
1433 const ca = try std.net.connectUnixSocket(sock_path); 1406 const ca = try dial.dialAttachNamed(sock_path, 80, 24, "a");
1434 defer ca.close(); 1407 defer ca.close();
1435 try attachNamed(ca.handle, 80, 24, "a"); 1408 const cb = try dial.dialAttachNamed(sock_path, 80, 24, "b");
1436 const cb = try std.net.connectUnixSocket(sock_path);
1437 defer cb.close(); 1409 defer cb.close();
1438 try attachNamed(cb.handle, 80, 24, "b");
1439 const fa = (try awaitFrame(alloc, &srv, ca.handle, .snapshot, 400)) orelse 1410 const fa = (try awaitFrame(alloc, &srv, ca.handle, .snapshot, 400)) orelse
1440 return error.NoSnapshotA; 1411 return error.NoSnapshotA;
1441 fa.deinit(alloc); 1412 fa.deinit(alloc);
@@ -1473,9 +1444,8 @@ test "Server: a session-less slot's status_req resolves the tail like an observe
1473 defer srv.deinit(); 1444 defer srv.deinit();
1474 1445
1475 // Stand up session "b" for real, over an ordinary attach. 1446 // Stand up session "b" for real, over an ordinary attach.
1476 const cb = try std.net.connectUnixSocket(sock_path); 1447 const cb = try dial.dialAttachNamed(sock_path, 80, 24, "b");
1477 defer cb.close(); 1448 defer cb.close();
1478 try attachNamed(cb.handle, 80, 24, "b");
1479 const fb = (try awaitFrame(alloc, &srv, cb.handle, .snapshot, 400)) orelse 1449 const fb = (try awaitFrame(alloc, &srv, cb.handle, .snapshot, 400)) orelse
1480 return error.NoSnapshotB; 1450 return error.NoSnapshotB;
1481 fb.deinit(alloc); 1451 fb.deinit(alloc);
@@ -1559,22 +1529,19 @@ test "Server: end_req with another client attached is refused with the count; fo
1559 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" }); 1529 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" });
1560 defer srv.deinit(); 1530 defer srv.deinit();
1561 1531
1562 const a1 = try std.net.connectUnixSocket(sock_path); 1532 const a1 = try dial.dialAttachNamed(sock_path, 80, 24, "a");
1563 defer a1.close(); 1533 defer a1.close();
1564 try attachNamed(a1.handle, 80, 24, "a");
1565 (try awaitFrame(alloc, &srv, a1.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc); 1534 (try awaitFrame(alloc, &srv, a1.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc);
1566 const a2 = try std.net.connectUnixSocket(sock_path); 1535 const a2 = try dial.dialAttachNamed(sock_path, 80, 24, "a");
1567 defer a2.close(); 1536 defer a2.close();
1568 try attachNamed(a2.handle, 80, 24, "a");
1569 (try awaitFrame(alloc, &srv, a2.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc); 1537 (try awaitFrame(alloc, &srv, a2.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc);
1570 const b1 = try std.net.connectUnixSocket(sock_path); 1538 const b1 = try dial.dialAttachNamed(sock_path, 80, 24, "b");
1571 defer b1.close(); 1539 defer b1.close();
1572 try attachNamed(b1.handle, 80, 24, "b");
1573 (try awaitFrame(alloc, &srv, b1.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc); 1540 (try awaitFrame(alloc, &srv, b1.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc);
1574 const pid_a = shellPidOf(&srv, "a"); 1541 const pid_a = shellPidOf(&srv, "a");
1575 1542
1576 // an observer asks: every attached client is "other" 1543 // an observer asks: every attached client is "other"
1577 const obs = try std.net.connectUnixSocket(sock_path); 1544 const obs = try dial.dial(sock_path);
1578 defer obs.close(); 1545 defer obs.close();
1579 var rq: [proto.end_req_max_len]u8 = undefined; 1546 var rq: [proto.end_req_max_len]u8 = undefined;
1580 try proto.writeFrame(obs.handle, .end_req, proto.encodeEndReq(&rq, false, "a")); 1547 try proto.writeFrame(obs.handle, .end_req, proto.encodeEndReq(&rq, false, "a"));
@@ -1619,9 +1586,8 @@ test "Server: end_req alone on a session ends it at once, and an unknown name is
1619 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" }); 1586 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" });
1620 defer srv.deinit(); 1587 defer srv.deinit();
1621 1588
1622 const c = try std.net.connectUnixSocket(sock_path); 1589 const c = try dial.dialAttachNamed(sock_path, 80, 24, "solo");
1623 defer c.close(); 1590 defer c.close();
1624 try attachNamed(c.handle, 80, 24, "solo");
1625 (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc); 1591 (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc);
1626 1592
1627 const pid = shellPidOf(&srv, "solo"); 1593 const pid = shellPidOf(&srv, "solo");
@@ -1637,7 +1603,7 @@ test "Server: end_req alone on a session ends it at once, and an unknown name is
1637 } 1603 }
1638 try std.testing.expect(!alive(pid)); 1604 try std.testing.expect(!alive(pid));
1639 1605
1640 const obs = try std.net.connectUnixSocket(sock_path); 1606 const obs = try dial.dial(sock_path);
1641 defer obs.close(); 1607 defer obs.close();
1642 try proto.writeFrame(obs.handle, .end_req, proto.encodeEndReq(&rq, true, "nosuch")); 1608 try proto.writeFrame(obs.handle, .end_req, proto.encodeEndReq(&rq, true, "nosuch"));
1643 const r2 = (try awaitFrame(alloc, &srv, obs.handle, .end_reply, 200)) orelse return error.NoEndReply; 1609 const r2 = (try awaitFrame(alloc, &srv, obs.handle, .end_reply, 200)) orelse return error.NoEndReply;
@@ -1658,16 +1624,14 @@ test "Server: an observer's sessions_req is answered with every live name" {
1658 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" }); 1624 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" });
1659 defer srv.deinit(); 1625 defer srv.deinit();
1660 1626
1661 const cx = try std.net.connectUnixSocket(sock_path); 1627 const cx = try dial.dialAttachNamed(sock_path, 80, 24, "x");
1662 defer cx.close(); 1628 defer cx.close();
1663 try attachNamed(cx.handle, 80, 24, "x");
1664 (try awaitFrame(alloc, &srv, cx.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc); 1629 (try awaitFrame(alloc, &srv, cx.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc);
1665 const cy = try std.net.connectUnixSocket(sock_path); 1630 const cy = try dial.dialAttachNamed(sock_path, 80, 24, "y");
1666 defer cy.close(); 1631 defer cy.close();
1667 try attachNamed(cy.handle, 80, 24, "y");
1668 (try awaitFrame(alloc, &srv, cy.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc); 1632 (try awaitFrame(alloc, &srv, cy.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc);
1669 1633
1670 const obs = try std.net.connectUnixSocket(sock_path); 1634 const obs = try dial.dial(sock_path);
1671 defer obs.close(); 1635 defer obs.close();
1672 try proto.writeFrame(obs.handle, .sessions_req, ""); 1636 try proto.writeFrame(obs.handle, .sessions_req, "");
1673 const r = (try awaitFrame(alloc, &srv, obs.handle, .sessions_reply, 200)) orelse return error.NoSessionsReply; 1637 const r = (try awaitFrame(alloc, &srv, obs.handle, .sessions_reply, 200)) orelse return error.NoSessionsReply;
@@ -1709,9 +1673,8 @@ test "Server: an accepted end kills a shell that ignores TERM and HUP" {
1709 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" }); 1673 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = "/bin/sh" });
1710 defer srv.deinit(); 1674 defer srv.deinit();
1711 1675
1712 const c = try std.net.connectUnixSocket(sock_path); 1676 const c = try dial.dialAttachNamed(sock_path, 80, 24, "stubborn");
1713 defer c.close(); 1677 defer c.close();
1714 try attachNamed(c.handle, 80, 24, "stubborn");
1715 (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc); 1678 (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc);
1716 1679
1717 // The shell ignores both signals `requestExit` has, and never reads stdin 1680 // The shell ignores both signals `requestExit` has, and never reads stdin
@@ -1749,9 +1712,8 @@ test "Server: a keystroke into an ending session does not cost that client its e
1749 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script }); 1712 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script });
1750 defer srv.deinit(); 1713 defer srv.deinit();
1751 1714
1752 const c = try std.net.connectUnixSocket(sock_path); 1715 const c = try dial.dialAttachNamed(sock_path, 80, 24, "typing");
1753 defer c.close(); 1716 defer c.close();
1754 try attachNamed(c.handle, 80, 24, "typing");
1755 (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc); 1717 (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc);
1756 1718
1757 var rq: [proto.end_req_max_len]u8 = undefined; 1719 var rq: [proto.end_req_max_len]u8 = undefined;
@@ -1781,9 +1743,8 @@ test "Server: a repeated end_req does not push the SIGKILL deadline out" {
1781 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script }); 1743 var srv = try Server.init(alloc, .{ .sock_path = sock_path, .shell = script });
1782 defer srv.deinit(); 1744 defer srv.deinit();
1783 1745
1784 const c = try std.net.connectUnixSocket(sock_path); 1746 const c = try dial.dialAttachNamed(sock_path, 80, 24, "nag");
1785 defer c.close(); 1747 defer c.close();
1786 try attachNamed(c.handle, 80, 24, "nag");
1787 (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc); 1748 (try awaitFrame(alloc, &srv, c.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc);
1788 1749
1789 const pid = shellPidOf(&srv, "nag"); 1750 const pid = shellPidOf(&srv, "nag");
@@ -1795,7 +1756,7 @@ test "Server: a repeated end_req does not push the SIGKILL deadline out" {
1795 1756
1796 // An observer nagging faster than the grace: if each accept restarted the 1757 // An observer nagging faster than the grace: if each accept restarted the
1797 // clock, the shell would outlive every deadline it was ever given. 1758 // clock, the shell would outlive every deadline it was ever given.
1798 const obs = try std.net.connectUnixSocket(sock_path); 1759 const obs = try dial.dial(sock_path);
1799 defer obs.close(); 1760 defer obs.close();
1800 var waited: u32 = 0; 1761 var waited: u32 = 0;
1801 while (alive(pid) and waited < 3000) : (waited += 80) { 1762 while (alive(pid) and waited < 3000) : (waited += 80) {
src/server/server_test_upgrade.zig
Old New
@@ -3,6 +3,7 @@ const proto = @import("term").protocol;
3 const upgrade = @import("upgrade.zig"); 3 const upgrade = @import("upgrade.zig");
4 const TmpDir = @import("testtmp").TmpDir; 4 const TmpDir = @import("testtmp").TmpDir;
5 const h = @import("server_test_harness.zig"); 5 const h = @import("server_test_harness.zig");
6 const dial = h.dial;
6 const srv_mod = @import("server.zig"); 7 const srv_mod = @import("server.zig");
7 const SessionTable = @import("server_sessions.zig").SessionTable; 8 const SessionTable = @import("server_sessions.zig").SessionTable;
8 const Server = srv_mod.Server; 9 const Server = srv_mod.Server;
@@ -162,7 +163,7 @@ test "initFromManifest: an adopted session answers a status_req without having b
162 163
163 // The socket the OLD daemon bound still answers, over the inherited 164 // The socket the OLD daemon bound still answers, over the inherited
164 // listener fd: no second bind, no claim, no unlink in between. 165 // listener fd: no second bind, no claim, no unlink in between.
165 const obs = try std.net.connectUnixSocket(sock_path); 166 const obs = try dial.dial(sock_path);
166 defer obs.close(); 167 defer obs.close();
167 try proto.writeFrame(obs.handle, .status_req, ""); 168 try proto.writeFrame(obs.handle, .status_req, "");
168 const reply = (try awaitFrame(alloc, &srv2, obs.handle, .status_reply, 400)) orelse 169 const reply = (try awaitFrame(alloc, &srv2, obs.handle, .status_reply, 400)) orelse
@@ -405,7 +406,7 @@ test "Server: an upgrade asked for during a session's hangup is refused, not att
405 406
406 // The shell outlives the hangup by the whole grace, so the exec's 407 // The shell outlives the hangup by the whole grace, so the exec's
407 // clearCloexec and the manifest's pty_fd would both see -1. 408 // clearCloexec and the manifest's pty_fd would both see -1.
408 const ender = try std.net.connectUnixSocket(sock_path); 409 const ender = try dial.dial(sock_path);
409 defer ender.close(); 410 defer ender.close();
410 var rq: [proto.end_req_max_len]u8 = undefined; 411 var rq: [proto.end_req_max_len]u8 = undefined;
411 try proto.writeFrame(ender.handle, .end_req, proto.encodeEndReq(&rq, true, "")); 412 try proto.writeFrame(ender.handle, .end_req, proto.encodeEndReq(&rq, true, ""));
@@ -431,7 +432,7 @@ test "Server: an upgrade asked for during a session's hangup is refused, not att
431 const cand = try std.fmt.allocPrint(alloc, "{s}/fakemux.sh", .{tmp.path()}); 432 const cand = try std.fmt.allocPrint(alloc, "{s}/fakemux.sh", .{tmp.path()});
432 defer alloc.free(cand); 433 defer alloc.free(cand);
433 434
434 const obs = try std.net.connectUnixSocket(sock_path); 435 const obs = try dial.dial(sock_path);
435 defer obs.close(); 436 defer obs.close();
436 var ureq: [512]u8 = undefined; 437 var ureq: [512]u8 = undefined;
437 try proto.writeFrame(obs.handle, .upgrade_req, try proto.encodeUpgradeReq(&ureq, .{ 438 try proto.writeFrame(obs.handle, .upgrade_req, try proto.encodeUpgradeReq(&ureq, .{
@@ -532,7 +533,7 @@ test "Server: an EMPTY daemon upgrades — the manifest names no session and the
532 533
533 // Serving, on the listener fd the old image bound: an empty daemon that 534 // Serving, on the listener fd the old image bound: an empty daemon that
534 // came through an exec is still the box a client can be born on. 535 // came through an exec is still the box a client can be born on.
535 const obs = try std.net.connectUnixSocket(sock_path); 536 const obs = try dial.dial(sock_path);
536 defer obs.close(); 537 defer obs.close();
537 try proto.writeFrame(obs.handle, .stats_req, ""); 538 try proto.writeFrame(obs.handle, .stats_req, "");
538 const reply = (try awaitFrame(alloc, &srv2, obs.handle, .stats_reply, 400)) orelse 539 const reply = (try awaitFrame(alloc, &srv2, obs.handle, .stats_reply, 400)) orelse
@@ -557,9 +558,8 @@ test "Server: an accepted end cancels a pending upgrade — the exec never sees
557 // Plural: the end takes ONE session and the upgrade would have carried 558 // Plural: the end takes ONE session and the upgrade would have carried
558 // the others, so a guard that only looked at the ending slot's own 559 // the others, so a guard that only looked at the ending slot's own
559 // clients would still pass here. 560 // clients would still pass here.
560 const ca = try std.net.connectUnixSocket(sock_path); 561 const ca = try dial.dialAttachNamed(sock_path, 80, 24, "a");
561 defer ca.close(); 562 defer ca.close();
562 try h.attachNamed(ca.handle, 80, 24, "a");
563 (try awaitFrame(alloc, &srv, ca.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc); 563 (try awaitFrame(alloc, &srv, ca.handle, .snapshot, 400) orelse return error.NoState).deinit(alloc);
564 564
565 try tmp.dir.writeFile(.{ 565 try tmp.dir.writeFile(.{
@@ -580,10 +580,10 @@ test "Server: an accepted end cancels a pending upgrade — the exec never sees
580 // Two observers, seated in slot order BEFORE either speaks: the hazard 580 // Two observers, seated in slot order BEFORE either speaks: the hazard
581 // is one pump servicing an upgrade at a lower slot and an end at a 581 // is one pump servicing an upgrade at a lower slot and an end at a
582 // higher one, after which `run` execs on a master that is already -1. 582 // higher one, after which `run` execs on a master that is already -1.
583 const up = try std.net.connectUnixSocket(sock_path); 583 const up = try dial.dial(sock_path);
584 defer up.close(); 584 defer up.close();
585 try srv.pumpOnce(20); 585 try srv.pumpOnce(20);
586 const ender = try std.net.connectUnixSocket(sock_path); 586 const ender = try dial.dial(sock_path);
587 defer ender.close(); 587 defer ender.close();
588 try srv.pumpOnce(20); 588 try srv.pumpOnce(20);
589 589