summaryrefslogtreecommitdiff
path: root/src/session_test.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-20 00:58:22 -0300
committerGabriel Schneider <[email protected]>2026-09-20 00:58:22 -0300
commit65209217b5b68f56bc0bd5bc6c4dce33911ded59 (patch)
treebe3848ef46b7a2ce1d0ad1a768b7a97d428481c4 /src/session_test.zig
parent3e9f8805f293f622bb885cf849b5ce47dc062ad1 (diff)
downloadcloud9-65209217b5b68f56bc0bd5bc6c4dce33911ded59.tar.gz
cloud9-65209217b5b68f56bc0bd5bc6c4dce33911ded59.zip
Add the file-server engine: cloud9.fs.Server(Backend, Options)
The asynchronous 9P file-server engine from the Pardes editor moves into the library: fid table, walks, directory cursors, a job/slot model where backend replies arrive later by tag (status again = parked), Tflush cancellation and orphaned fids on hangup. Allocation-free, no OS calls, no std.Io; comptime Options (fid, slot, park data, name and user capacities) replace the editor's constants. Backend contract types (Req, Reply/ReplyWith, Op, Status, Attr, E, error strings) live here. 29 engine tests plus the client tests that sat beside it in Pardes. Co-Authored-By: Claude Fable 5.1 <[email protected]>
Diffstat (limited to 'src/session_test.zig')
-rw-r--r--src/session_test.zig103
1 files changed, 103 insertions, 0 deletions
diff --git a/src/session_test.zig b/src/session_test.zig
index 8600d03..f31ff31 100644
--- a/src/session_test.zig
+++ b/src/session_test.zig
@@ -220,3 +220,106 @@ test "server reserves cancellation capacity and rejects duplicate pending tags"
try testing.expectError(error.Protocol, server.receive());
try testing.expect(server.dead);
}
+
+test "client: an msize below the floor, and one the server tried to raise" {
+ var in: [512]u8 = undefined;
+ var out: [512]u8 = undefined;
+ var buf: [64]u8 = undefined;
+
+ var c = c9.Client.init(.{ .in = &in, .out = &out });
+ try testing.expectError(error.BadRequest, c.submit(.{ .version = .{ .msize = c9.Server.msize_min - 1 } }));
+ _ = try c.submit(.{ .version = .{} });
+ try testing.expectEqual(@as(u32, 512), c.asked);
+
+ _ = c.push(try c9.encode(.{ .rversion = .{ .msize = 1024, .version = "9P2000" } }, c9.notag, &buf));
+ try testing.expect(c.take() == null);
+ try testing.expect(c.dead);
+
+ var c2 = c9.Client.init(.{ .in = &in, .out = &out });
+ _ = try c2.submit(.{ .version = .{} });
+ _ = c2.push(try c9.encode(.{ .rversion = .{ .msize = 512, .version = "unknown" } }, c9.notag, &buf));
+ const done = c2.take() orelse return error.NoReply;
+ try testing.expectEqualStrings("unknown", done.result.version.version);
+ try testing.expect(!c2.dead);
+ try testing.expectEqual(@as(u32, 0), c2.msize);
+ try testing.expectError(error.Handshake, c2.submit(.{ .stat = .{ .fid = 0 } }));
+
+ var c3 = c9.Client.init(.{ .in = &in, .out = &out });
+ _ = try c3.submit(.{ .version = .{} });
+ _ = c3.push(try c9.encode(.{ .rversion = .{ .msize = 512, .version = "9P2000.u" } }, c9.notag, &buf));
+ try testing.expect(c3.take() == null);
+ try testing.expect(c3.dead);
+}
+
+test "client: what is not an answer to one of our requests ends the connection" {
+ var buf: [64]u8 = undefined;
+ const Case = struct {
+ fn armed(in: []u8, out: []u8, scratch: []u8) !c9.Client {
+ var c = c9.Client.init(.{ .in = in, .out = out });
+ _ = try c.submit(.{ .version = .{} });
+ c.wrote(c.output().len);
+ _ = c.push(try c9.encode(.{ .rversion = .{ .msize = 512, .version = "9P2000" } }, c9.notag, scratch));
+ _ = c.take() orelse return error.NoReply;
+ _ = try c.submit(.{ .stat = .{ .fid = 0 } });
+ c.wrote(c.output().len);
+ return c;
+ }
+ };
+ var in: [512]u8 = undefined;
+ var out: [512]u8 = undefined;
+
+ {
+ var c = try Case.armed(&in, &out, &buf);
+ _ = c.push(try c9.encode(.{ .tstat = .{ .fid = 0 } }, 0, &buf));
+ try testing.expect(c.take() == null);
+ try testing.expect(c.dead);
+ }
+ {
+ var c = try Case.armed(&in, &out, &buf);
+ _ = c.push(try c9.encode(.rclunk, 3, &buf));
+ try testing.expect(c.take() == null);
+ try testing.expect(c.dead);
+ }
+ {
+ var c = try Case.armed(&in, &out, &buf);
+ _ = c.push(try c9.encode(.rclunk, 900, &buf));
+ try testing.expect(c.take() == null);
+ try testing.expect(c.dead);
+ }
+ {
+ var c = try Case.armed(&in, &out, &buf);
+ _ = c.push(try c9.encode(.rclunk, 0, &buf));
+ try testing.expect(c.take() == null);
+ try testing.expect(c.dead);
+ }
+ {
+ var c = try Case.armed(&in, &out, &buf);
+ _ = c.push(try c9.encode(.rwstat, 0, &buf));
+ try testing.expect(c.take() == null);
+ try testing.expect(c.dead);
+ }
+ {
+ var c = try Case.armed(&in, &out, &buf);
+ _ = c.push(&.{ 3, 0, 0, 0 });
+ try testing.expect(c.take() == null);
+ try testing.expect(c.dead);
+ }
+ {
+ var c = try Case.armed(&in, &out, &buf);
+ _ = c.push(&.{ 0, 4, 0, 0 });
+ try testing.expect(c.take() == null);
+ try testing.expect(c.dead);
+ }
+ {
+ var c = try Case.armed(&in, &out, &buf);
+ _ = c.push(&.{ 8, 0, 0, 0, @intFromEnum(c9.Type.rstat), 0, 0, 0 });
+ try testing.expect(c.take() == null);
+ try testing.expect(c.dead);
+ }
+}
+
+test "client: session memory is bounded independently of input" {
+ try testing.expect(@sizeOf(c9.Client) <= 512);
+ try testing.expect(2 * 8192 + @sizeOf(c9.Client) <= 17 * 1024);
+ try testing.expect(2 * c9.Server.msize_min + @sizeOf(c9.Client) <= 700);
+}