fix: ip_tcp_client_server.zig testcase
This commit is contained in:
parent
224342611c
commit
b16a47efae
3 changed files with 79 additions and 26 deletions
|
|
@ -52,17 +52,14 @@ struct client_connection {
|
|||
|
||||
auto receive(const buffer &m) {
|
||||
int written{};
|
||||
int err{};
|
||||
string_view buf{};
|
||||
|
||||
if (m("socket", "client_connection", "read_complete", extract(buf))) {
|
||||
check(buf == "ping");
|
||||
check(err == 0);
|
||||
socket.write("pong");
|
||||
} else if (m("socket", "client_connection", "write_complete",
|
||||
extract(written))) {
|
||||
check(written == 4);
|
||||
check(err == 0);
|
||||
// socket.close(); // let server close
|
||||
} else if (m("socket", "client_connection", "closed")) {
|
||||
auto ret = connector.send("client_connection", "done");
|
||||
|
|
@ -130,17 +127,14 @@ struct server_connection {
|
|||
|
||||
auto receive(const buffer &m) {
|
||||
int written{};
|
||||
int err{};
|
||||
string_view buf{};
|
||||
|
||||
if (m("socket", "server_connection", "write_complete", extract(written))) {
|
||||
check(written == 4);
|
||||
check(err == 0);
|
||||
socket.read();
|
||||
} else if (m("socket", "server_connection", "read_complete",
|
||||
extract(buf))) {
|
||||
check(buf == "pong");
|
||||
check(err == 0);
|
||||
socket.close();
|
||||
} else if (m("socket", "server_connection", "closed")) {
|
||||
auto ret = server.send("server_connection", "done");
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ const thespian = @import("thespian");
|
|||
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const exit = thespian.exit;
|
||||
const exit_normal = thespian.exit_normal;
|
||||
const exit_error = thespian.exit_error;
|
||||
const result = thespian.result;
|
||||
|
|
@ -23,10 +24,6 @@ const tcp_acceptor = thespian.tcp_acceptor;
|
|||
const tcp_connector = thespian.tcp_connector;
|
||||
const in6addr_loopback = thespian.in6addr_loopback;
|
||||
|
||||
pub const std_options = .{
|
||||
.log_level = .err,
|
||||
};
|
||||
|
||||
const ClientConnection = struct {
|
||||
allocator: Allocator,
|
||||
sock: socket,
|
||||
|
|
@ -36,7 +33,12 @@ const ClientConnection = struct {
|
|||
const Args = struct { allocator: Allocator, fd: i32, client_pid: pid };
|
||||
|
||||
fn start(args: Args) result {
|
||||
const sock = socket.init("client_connection", args.fd) catch |e| return exit_error(e, @errorReturnTrace());
|
||||
std.log.err("ClientConnection.start entered fd={d}", .{args.fd});
|
||||
const sock = socket.init("client_connection", args.fd) catch |e| {
|
||||
std.log.err("ClientConnection.start socket.init FAILED: {any}", .{e});
|
||||
return exit_error(e, @errorReturnTrace());
|
||||
};
|
||||
std.log.err("ClientConnection.start socket created", .{});
|
||||
var self = args.allocator.create(@This()) catch |e| return exit_error(e, @errorReturnTrace());
|
||||
self.* = .{
|
||||
.allocator = args.allocator,
|
||||
|
|
@ -45,13 +47,20 @@ const ClientConnection = struct {
|
|||
.receiver = .init(receive_safe, self),
|
||||
};
|
||||
errdefer self.deinit();
|
||||
self.sock.read() catch |e| return exit_error(e, @errorReturnTrace());
|
||||
self.sock.read() catch |e| {
|
||||
std.log.err("ClientConnection.start sock.read FAILED: {any}", .{e});
|
||||
return exit_error(e, @errorReturnTrace());
|
||||
};
|
||||
std.log.err("ClientConnection.start calling receive()", .{});
|
||||
thespian.receive(&self.receiver);
|
||||
std.log.err("ClientConnection.start returning", .{});
|
||||
}
|
||||
|
||||
fn deinit(self: *@This()) void {
|
||||
self.sock.deinit();
|
||||
self.client_pid.deinit();
|
||||
self.allocator.destroy(self);
|
||||
std.log.err("ClientConnection.deinit: client connection destroyed", .{});
|
||||
}
|
||||
|
||||
fn receive_safe(self: *@This(), from: pid_ref, m: message) result {
|
||||
|
|
@ -60,18 +69,25 @@ const ClientConnection = struct {
|
|||
}
|
||||
|
||||
fn receive(self: *@This(), _: pid_ref, m: message) !void {
|
||||
std.log.err("ClientConnection.receive msg={f}", .{m});
|
||||
var buf: []const u8 = "";
|
||||
var written: i64 = 0;
|
||||
if (try m.match(.{ "socket", "client_connection", "read_complete", extract(&buf) })) {
|
||||
if (std.mem.eql(u8, buf, "ping"))
|
||||
try self.sock.write("pong");
|
||||
std.log.err("ClientConnection.receive: read_complete buf={s}", .{buf});
|
||||
try std.testing.expectEqualSlices(u8, "ping", buf);
|
||||
try self.sock.write("pong");
|
||||
// try self.sock.read();
|
||||
} else if (try m.match(.{ "socket", "client_connection", "write_complete", extract(&written) })) {
|
||||
// just wait for server to close
|
||||
std.log.err("ClientConnection.receive: write_complete written={d}", .{written});
|
||||
try std.testing.expectEqual(4, written);
|
||||
// wait for server to close - must keep a read pending to detect it
|
||||
// try self.sock.read();
|
||||
} else if (try m.match(.{ "socket", "client_connection", "closed" })) {
|
||||
// connection done
|
||||
std.log.err("ClientConnection.receive: closed, sending done", .{});
|
||||
_ = self.client_pid.send(.{ "client_connection", "done" }) catch {};
|
||||
return exit_normal();
|
||||
return exit("success");
|
||||
} else {
|
||||
std.log.err("ClientConnection.receive: UNEXPECTED msg={f}", .{m});
|
||||
return unexpected(m);
|
||||
}
|
||||
}
|
||||
|
|
@ -90,6 +106,7 @@ const Client = struct {
|
|||
}
|
||||
|
||||
fn init(args: Args) !void {
|
||||
std.log.err("Client.init entered port={d}", .{args.port});
|
||||
const connector: tcp_connector = try .init("client");
|
||||
var self = try args.allocator.create(@This());
|
||||
self.* = .{
|
||||
|
|
@ -100,12 +117,15 @@ const Client = struct {
|
|||
};
|
||||
errdefer self.deinit();
|
||||
try self.connector.connect(in6addr_loopback, args.port);
|
||||
std.log.err("Client.init connect() called, calling receive()", .{});
|
||||
thespian.receive(&self.receiver);
|
||||
std.log.err("Client.init returning", .{});
|
||||
}
|
||||
|
||||
fn deinit(self: *@This()) void {
|
||||
self.server_pid.deinit();
|
||||
self.allocator.destroy(self);
|
||||
std.log.err("Client.deinit: client destroyed", .{});
|
||||
}
|
||||
|
||||
fn receive_safe(self: *@This(), from: pid_ref, m: message) result {
|
||||
|
|
@ -114,17 +134,22 @@ const Client = struct {
|
|||
}
|
||||
|
||||
fn receive(self: *@This(), _: pid_ref, m: message) !void {
|
||||
std.log.err("Client.receive msg={f}", .{m});
|
||||
var fd: i32 = 0;
|
||||
if (try m.match(.{ "connector", "client", "connected", extract(&fd) })) {
|
||||
std.log.err("Client.receive: connected fd={d}, spawning ClientConnection", .{fd});
|
||||
_ = try spawn_link(self.allocator, ClientConnection.Args{
|
||||
.allocator = self.allocator,
|
||||
.fd = fd,
|
||||
.client_pid = self_pid().clone(),
|
||||
}, ClientConnection.start, "client_connection");
|
||||
std.log.err("Client.receive: ClientConnection spawned", .{});
|
||||
} else if (try m.match(.{ "client_connection", "done" })) {
|
||||
std.log.err("Client.receive: client_connection done, exiting", .{});
|
||||
_ = try self.server_pid.send(.{ "client", "done" });
|
||||
return exit_normal();
|
||||
} else {
|
||||
std.log.err("Client.receive: UNEXPECTED msg={f}", .{m});
|
||||
return unexpected(m);
|
||||
}
|
||||
}
|
||||
|
|
@ -143,6 +168,7 @@ const ServerConnection = struct {
|
|||
}
|
||||
|
||||
fn init(args: Args) !void {
|
||||
std.log.err("ServerConnection.init entered fd={d}", .{args.fd});
|
||||
const sock = try socket.init("server_connection", args.fd);
|
||||
var self = try args.allocator.create(@This());
|
||||
self.* = .{
|
||||
|
|
@ -153,12 +179,16 @@ const ServerConnection = struct {
|
|||
};
|
||||
errdefer self.deinit();
|
||||
try self.sock.write("ping");
|
||||
std.log.err("ServerConnection.init write(ping) called, calling receive()", .{});
|
||||
thespian.receive(&self.receiver);
|
||||
std.log.err("ServerConnection.init returning", .{});
|
||||
}
|
||||
|
||||
fn deinit(self: *@This()) void {
|
||||
self.sock.deinit();
|
||||
self.server_pid.deinit();
|
||||
self.allocator.destroy(self);
|
||||
std.log.err("ServerConnection.deinit: server connection destroyed", .{});
|
||||
}
|
||||
|
||||
fn receive_safe(self: *@This(), from: pid_ref, m: message) result {
|
||||
|
|
@ -167,21 +197,26 @@ const ServerConnection = struct {
|
|||
}
|
||||
|
||||
fn receive(self: *@This(), _: pid_ref, m: message) !void {
|
||||
std.log.err("ServerConnection.receive msg={f}", .{m});
|
||||
var buf: []const u8 = "";
|
||||
var written: i64 = 0;
|
||||
|
||||
if (try m.match(.{ "socket", "server_connection", "write_complete", extract(&written) })) {
|
||||
std.log.err("ServerConnection.receive: write_complete written={d}", .{written});
|
||||
try std.testing.expectEqual(4, written);
|
||||
// ping sent, start reading
|
||||
try self.sock.read();
|
||||
} else if (try m.match(.{ "socket", "server_connection", "read_complete", extract(&buf) })) {
|
||||
std.log.err("ServerConnection.receive: read_complete buf={s}", .{buf});
|
||||
try std.testing.expectEqualSlices(u8, "pong", buf);
|
||||
// received pong, close socket
|
||||
if (std.mem.eql(u8, buf, "pong"))
|
||||
try self.sock.close();
|
||||
try self.sock.close();
|
||||
} else if (try m.match(.{ "socket", "server_connection", "closed" })) {
|
||||
// connection done, notify server
|
||||
_ = self.server_pid.send(.{ "server_connection", "done" }) catch {};
|
||||
return exit_normal();
|
||||
std.log.err("ServerConnection.receive: closed, sending done", .{});
|
||||
self.server_pid.send(.{ "server_connection", "done" }) catch |e| return exit_error(e, @errorReturnTrace());
|
||||
return exit("success");
|
||||
} else {
|
||||
std.log.err("ServerConnection.receive: UNEXPECTED msg={f}", .{m});
|
||||
return unexpected(m);
|
||||
}
|
||||
}
|
||||
|
|
@ -202,6 +237,7 @@ const Server = struct {
|
|||
}
|
||||
|
||||
fn init(args: Args) !void {
|
||||
std.log.err("Server.init entered", .{});
|
||||
const acceptor: tcp_acceptor = try .init("server");
|
||||
var self = try args.allocator.create(@This());
|
||||
self.* = .{
|
||||
|
|
@ -212,19 +248,23 @@ const Server = struct {
|
|||
errdefer self.deinit();
|
||||
|
||||
const port = try self.acceptor.listen(in6addr_loopback, 0);
|
||||
std.log.err("Server.init listening on port={d}", .{port});
|
||||
|
||||
_ = try spawn_link(args.allocator, Client.Args{
|
||||
.allocator = args.allocator,
|
||||
.server_pid = self_pid().clone(),
|
||||
.port = port,
|
||||
}, Client.start, "client");
|
||||
std.log.err("Server.init Client spawned, calling receive()", .{});
|
||||
|
||||
thespian.receive(&self.receiver);
|
||||
std.log.err("Server.init returning", .{});
|
||||
}
|
||||
|
||||
fn deinit(self: *@This()) void {
|
||||
self.acceptor.deinit();
|
||||
self.allocator.destroy(self);
|
||||
std.log.err("Server.deinit: server destroyed", .{});
|
||||
}
|
||||
|
||||
fn receive_safe(self: *@This(), from: pid_ref, m: message) result {
|
||||
|
|
@ -233,26 +273,34 @@ const Server = struct {
|
|||
}
|
||||
|
||||
fn receive(self: *@This(), _: pid_ref, m: message) !void {
|
||||
std.log.err("Server.receive msg={f}", .{m});
|
||||
var fd: i32 = 0;
|
||||
if (try m.match(.{ "acceptor", "server", "accept", extract(&fd) })) {
|
||||
std.log.err("Server.receive: accept fd={d}, spawning ServerConnection", .{fd});
|
||||
_ = try spawn_link(self.allocator, ServerConnection.Args{
|
||||
.allocator = self.allocator,
|
||||
.fd = fd,
|
||||
.server_pid = self_pid().clone(),
|
||||
}, ServerConnection.start, "server_connection");
|
||||
// just one connection for this test
|
||||
std.log.err("Server.receive: ServerConnection spawned, closing acceptor", .{});
|
||||
try self.acceptor.close();
|
||||
} else if (try m.match(.{ "acceptor", "server", "closed" })) {
|
||||
std.log.err("Server.receive: acceptor closed (flags: client={} conn={})", .{ self.client_done, self.server_conn_done });
|
||||
self.acceptor_closed = true;
|
||||
} else if (try m.match(.{ "client", "done" })) {
|
||||
std.log.err("Server.receive: client done (flags: acceptor={} conn={})", .{ self.acceptor_closed, self.server_conn_done });
|
||||
self.client_done = true;
|
||||
} else if (try m.match(.{ "server_connection", "done" })) {
|
||||
std.log.err("Server.receive: server_connection done (flags: acceptor={} client={})", .{ self.acceptor_closed, self.client_done });
|
||||
self.server_conn_done = true;
|
||||
} else {
|
||||
std.log.err("Server.receive: UNEXPECTED msg={f}", .{m});
|
||||
return unexpected(m);
|
||||
}
|
||||
if (self.acceptor_closed and self.client_done and self.server_conn_done)
|
||||
return exit_normal();
|
||||
if (self.acceptor_closed and self.client_done and self.server_conn_done) {
|
||||
std.log.err("Server.receive: all done, exiting with 'success'", .{});
|
||||
return exit("success");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -265,8 +313,9 @@ test "ip_tcp_client_server test" {
|
|||
|
||||
var exit_handler = thespian.make_exit_handler(&success, struct {
|
||||
fn handle(ok: *bool, status: []const u8) void {
|
||||
if (std.mem.eql(u8, status, "normal")) {
|
||||
if (std.mem.eql(u8, status, "success")) {
|
||||
ok.* = true;
|
||||
std.log.err("EXITED: {s}", .{status});
|
||||
} else {
|
||||
std.log.err("EXITED: {s}", .{status});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,3 +66,13 @@ test "spawn_exit" {
|
|||
test "timeout_test" {
|
||||
try testcase("timeout_test");
|
||||
}
|
||||
|
||||
test "unx_c_api" {
|
||||
try testcase("unx_c_api");
|
||||
}
|
||||
test "tcp_c_api" {
|
||||
try testcase("tcp_c_api");
|
||||
}
|
||||
test "socket_c_api" {
|
||||
try testcase("socket_c_api");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue