maybe a prototype, maybe it will go in production
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102//! L3 isolation proof: two concurrent scenario-space runs (good fixture,//! distinct ports) must both pass, must use distinct work dirs, and must//! tear those dirs down on exit.//!//! Env (set by `zig build scenario-isolation`): SELF_SCENARIO (the//! scenario-space executable), SELF_FAKE_ZDS, SELF_FAKE_LINJI,//! SELF_FIXTURE (the known-good fixture).
const std = @import("std");
var app_threaded_io: std.Io.Threaded = undefined;pub const std_options_debug_threaded_io: ?*std.Io.Threaded = &app_threaded_io;
const Spawned = struct { child: std.process.Child, port: u16, work: []const u8,};
fn envOrPanic(name: [*:0]const u8) []const u8 { return std.mem.span(std.c.getenv(name) orelse { std.debug.print("self-isolation: {s} not set\n", .{name}); std.process.exit(2); });}
pub fn main(init: std.process.Init) !void { _ = init; var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const allocator = arena.allocator(); app_threaded_io = std.Io.Threaded.init(allocator, .{ .async_limit = .limited(4), .concurrent_limit = .limited(4), }); const io = app_threaded_io.io();
const scenario = envOrPanic("SELF_SCENARIO"); const fake_zds = envOrPanic("SELF_FAKE_ZDS"); const fake_linji = envOrPanic("SELF_FAKE_LINJI"); const fixture = envOrPanic("SELF_FIXTURE");
// Ports deliberately outside the default set (2591/2592/8791); each // run also gets its own pid-suffixed work dir, so the two overlap // only if the isolation contract is broken. var runs: [2]Spawned = undefined; for (&runs, 0..) |*run, i| { const port: u16 = if (i == 0) 2611 else 2612; var env = std.process.Environ.Map.init(allocator); try env.put("LINJI_ZDS_BIN", fake_zds); try env.put("LINJI_BIN", fake_linji); try env.put("LINJI_FAKE_FIXTURE", fixture); try env.put("LINJI_ZDS_PORT", try std.fmt.allocPrint(allocator, "{d}", .{port})); const child = try std.process.spawn(io, .{ .argv = &.{scenario}, .environ_map = &env, .stdout = .ignore, .stderr = .ignore, }); run.* = .{ .child = child, .port = port, .work = try std.fmt.allocPrint(allocator, "/tmp/linji-scenario-space-{d}", .{child.id.?}), }; }
// Both were started before either was waited on: the overlap window // is the point of the test. var all_pass = true; for (&runs) |*run| { const term = try run.child.wait(io); switch (term) { .exited => |code| { if (code == 0) { std.debug.print("ok run on port {d} (work {s})\n", .{ run.port, run.work }); } else { std.debug.print("FAIL run on port {d}: exit {d}\n", .{ run.port, code }); all_pass = false; } }, else => { std.debug.print("FAIL run on port {d}: abnormal term\n", .{run.port}); all_pass = false; }, } // Teardown contract: the work dir (and everything in it, // including the fake-CLI fixture cursor) is gone after exit. if (dirExists(io, run.work)) { std.debug.print("FAIL run on port {d}: work dir not cleaned up: {s}\n", .{ run.port, run.work }); all_pass = false; } }
if (!all_pass) return error.IsolationFailed; std.debug.print("== scenario isolation: PASS (concurrent runs, distinct ports, teardown) ==\n", .{});}
fn dirExists(io: std.Io, path: []const u8) bool { std.Io.Dir.cwd().access(io, path, .{}) catch return false; return true;}