const std = @import("std"); const thespian = @import("thespian"); const cbor = @import("cbor"); const framing = @import("framing"); const protocol = @import("protocol"); const endpoint = @import("endpoint"); const build_options = @import("build_options"); const Allocator = std.mem.Allocator; const result = thespian.result; const unexpected = thespian.unexpected; const pid_ref = thespian.pid_ref; const Receiver = thespian.Receiver; const message = thespian.message; const extract = thespian.extract; /// Top-level test actor. Registers as "test_receiver", spawns the endpoint, /// sends ["hello"] to the child's echo actor, and verifies the reply. const TestActor = struct { allocator: Allocator, ep: thespian.pid, receiver: Receiver(*@This()), const Args = struct { allocator: Allocator }; fn start(args: Args) result { return init(args) catch |e| return thespian.exit_error(e, @errorReturnTrace()); } fn init(args: Args) !void { // Register self so the endpoint can deliver the echo reply here. thespian.env.get().proc_set("test_receiver", thespian.self_pid().ref()); const argv = try args.allocator.dupe(u8, message.fmt(.{build_options.remote_child_endpoint_path}).buf); const ep = try thespian.spawn_link( args.allocator, endpoint.Args{ .allocator = args.allocator, .argv = argv, }, endpoint.start, "endpoint", ); // Send ["hello"] to the child's "echo" actor through the endpoint. try ep.send(.{ "send", @as(u64, 1), "echo", .{"hello"} }); const self = try args.allocator.create(@This()); self.* = .{ .allocator = args.allocator, .ep = ep, .receiver = .init(receive_fn, deinit, self), }; errdefer self.deinit(); thespian.receive(&self.receiver); } fn deinit(self: *@This()) void { self.ep.deinit(); self.allocator.destroy(self); } fn receive_fn(self: *@This(), from: pid_ref, m: message) result { return self.receive(from, m) catch |e| return thespian.exit_error(e, @errorReturnTrace()); } fn receive(_: *@This(), _: pid_ref, m: message) !void { // The endpoint delivers the echo reply as the raw payload: ["hello"] if (try m.match(.{"hello"})) { return thespian.exit("success"); } else { return unexpected(m); } } }; test "remote: endpoint delivers message cross-process and receives reply" { const allocator = std.testing.allocator; var ctx = try thespian.context.init(allocator); defer ctx.deinit(); var success = false; var exit_handler = thespian.make_exit_handler(&success, struct { fn handle(ok: *bool, status: []const u8) void { ok.* = std.mem.eql(u8, status, "success"); } }.handle); _ = try ctx.spawn_link( TestActor.Args{ .allocator = allocator }, TestActor.start, "test_actor", &exit_handler, null, ); ctx.run(); if (!success) return error.TestFailed; }