summaryrefslogtreecommitdiff
path: root/test/unit_profile.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-06 18:11:36 -0300
committerGabriel Schneider <[email protected]>2026-09-07 13:59:12 -0300
commit60367d8fe23f6af98ec28e3cf6c2094dfe332df0 (patch)
tree310fc734173cf771881f4691c71909135fadde97 /test/unit_profile.zig
parentfa82cac885cb4738fe36d1e49b4749b5a3e31a4a (diff)
downloadpardes-60367d8fe23f6af98ec28e3cf6c2094dfe332df0.tar.gz
pardes-60367d8fe23f6af98ec28e3cf6c2094dfe332df0.zip
Refactor panes and filesystem; replace FUSE with 9P
Consolidate pane, layout, memory and host code. Serve 9P by default over Unix sockets, with runtime mounts and optional TCP/QUIC transports. Remove FUSE and obsolete proof-of-concept examples. Fix highlighting and terminal-history performance, expand differential and stress-test infrastructure, sort navigation results while preserving the next occurrence, add syntax-colored Braille minimaps, remove SPC-k, and document 9P interaction as a repository skill.
Diffstat (limited to 'test/unit_profile.zig')
-rw-r--r--test/unit_profile.zig247
1 files changed, 247 insertions, 0 deletions
diff --git a/test/unit_profile.zig b/test/unit_profile.zig
new file mode 100644
index 00000000..59794df9
--- /dev/null
+++ b/test/unit_profile.zig
@@ -0,0 +1,247 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const Server = std.zig.Server.Message;
+const Client = std.zig.Client.Message;
+const Memory = struct { rss_after_bytes: ?usize = null, cumulative_peak_rss_bytes: ?usize = null };
+
+fn send(io: std.Io, file: std.Io.File, tag: Client.Tag, body: []const u8, deadline: std.Io.Timeout) !void {
+ var header: [8]u8 = undefined;
+ std.mem.writeInt(u32, header[0..4], @intFromEnum(tag), .little);
+ std.mem.writeInt(u32, header[4..8], @intCast(body.len), .little);
+ for ([_][]const u8{ &header, body }) |part| {
+ var offset: usize = 0;
+ while (offset < part.len) {
+ const n = try (try io.operateTimeout(.{ .file_write_streaming = .{
+ .file = file,
+ .data = &.{part[offset..]},
+ } }, deadline)).file_write_streaming;
+ if (n == 0) return error.EndOfStream;
+ offset += n;
+ }
+ }
+}
+
+fn readAll(io: std.Io, file: std.Io.File, bytes: []u8, deadline: std.Io.Timeout) !void {
+ var offset: usize = 0;
+ while (offset < bytes.len) {
+ const n = try (try io.operateTimeout(.{ .file_read_streaming = .{
+ .file = file,
+ .data = &.{bytes[offset..]},
+ } }, deadline)).file_read_streaming;
+ if (n == 0) return error.EndOfStream;
+ offset += n;
+ }
+}
+
+fn receive(io: std.Io, file: std.Io.File, gpa: std.mem.Allocator, deadline: std.Io.Timeout) !struct { tag: Server.Tag, body: []u8 } {
+ var header: [8]u8 = undefined;
+ try readAll(io, file, &header, deadline);
+ const tag: Server.Tag = @enumFromInt(std.mem.readInt(u32, header[0..4], .little));
+ const len = std.mem.readInt(u32, header[4..8], .little);
+ if (len > 16 * 1024 * 1024) return error.MessageTooLarge;
+ const bytes = try gpa.alloc(u8, len);
+ errdefer gpa.free(bytes);
+ try readAll(io, file, bytes, deadline);
+ return .{ .tag = tag, .body = bytes };
+}
+
+fn wait(io: std.Io, child: *std.process.Child, deadline: std.Io.Timeout) !std.process.Child.Term {
+ const Result = union(enum) { exited: std.process.Child.WaitError!std.process.Child.Term, timeout: std.Io.Cancelable!void };
+ var results: [2]Result = undefined;
+ var select = std.Io.Select(Result).init(io, &results);
+ defer select.cancelDiscard();
+ try select.concurrent(.exited, std.process.Child.wait, .{ child, io });
+ select.async(.timeout, std.Io.Timeout.sleep, .{ deadline, io });
+ return switch (try select.await()) {
+ .exited => |result| result,
+ .timeout => |result| {
+ try result;
+ return error.Timeout;
+ },
+ };
+}
+
+fn memory(io: std.Io, gpa: std.mem.Allocator, pid: std.process.Child.Id) !Memory {
+ if (comptime builtin.os.tag != .linux) return .{};
+ var path_buffer: [64]u8 = undefined;
+ const path = try std.fmt.bufPrint(&path_buffer, "/proc/{d}/status", .{pid});
+ const file = try std.Io.Dir.cwd().openFile(io, path, .{});
+ defer file.close(io);
+ var reader = file.readerStreaming(io, &.{});
+ const status = try reader.interface.allocRemaining(gpa, .limited(8192));
+ defer gpa.free(status);
+ var value: Memory = .{};
+ var lines = std.mem.splitScalar(u8, status, '\n');
+ while (lines.next()) |line| {
+ if (!std.mem.startsWith(u8, line, "VmRSS:") and !std.mem.startsWith(u8, line, "VmHWM:")) continue;
+ var words = std.mem.tokenizeAny(u8, line, " \t");
+ const kind = words.next().?;
+ const bytes = try std.fmt.parseInt(usize, words.next() orelse return error.InvalidMemoryReport, 10);
+ if (std.mem.eql(u8, kind, "VmRSS:")) value.rss_after_bytes = bytes * 1024 else value.cumulative_peak_rss_bytes = bytes * 1024;
+ }
+ return value;
+}
+
+fn queryTests(io: std.Io, child: *std.process.Child, gpa: std.mem.Allocator, deadline: std.Io.Timeout) ![]u8 {
+ const version = try receive(io, child.stdout.?, gpa, deadline);
+ defer gpa.free(version.body);
+ if (version.tag != .zig_version or !std.mem.eql(u8, version.body, builtin.zig_version_string))
+ return error.DifferentZigVersion;
+ try send(io, child.stdin.?, .query_test_metadata, "", deadline);
+ const metadata = try receive(io, child.stdout.?, gpa, deadline);
+ errdefer gpa.free(metadata.body);
+ if (metadata.tag != .test_metadata or metadata.body.len < 8) return error.InvalidMetadata;
+ const strings = std.mem.readInt(u32, metadata.body[0..4], .little);
+ const count = std.mem.readInt(u32, metadata.body[4..8], .little);
+ const table_bytes = try std.math.mul(usize, count, 8);
+ if (metadata.body.len != 8 + table_bytes + strings) return error.InvalidMetadata;
+ return metadata.body;
+}
+
+fn testName(metadata: []const u8, index: usize) ![]const u8 {
+ const count = std.mem.readInt(u32, metadata[4..8], .little);
+ if (index >= count) return error.InvalidMetadata;
+ const offset = std.mem.readInt(u32, metadata[8 + index * 4 ..][0..4], .little);
+ const strings = metadata[8 + @as(usize, count) * 8 ..];
+ if (offset >= strings.len) return error.InvalidMetadata;
+ const end = std.mem.indexOfScalar(u8, strings[offset..], 0) orelse return error.InvalidMetadata;
+ return strings[offset..][0..end];
+}
+
+fn checkFilter(io: std.Io, gpa: std.mem.Allocator, filter: []const u8, binaries: []const []const u8) !void {
+ var matches: usize = 0;
+ for (binaries) |binary| {
+ var child = try std.process.spawn(io, .{
+ .argv = &.{ binary, "--listen=-" },
+ .stdin = .pipe,
+ .stdout = .pipe,
+ .stderr = .inherit,
+ });
+ errdefer child.kill(io);
+ const deadline: std.Io.Timeout = (std.Io.Timeout{ .duration = .{ .clock = .awake, .raw = .fromSeconds(30) } }).toDeadline(io);
+ const metadata = try queryTests(io, &child, gpa, deadline);
+ defer gpa.free(metadata);
+ const count = std.mem.readInt(u32, metadata[4..8], .little);
+ for (0..count) |index| {
+ const name = try testName(metadata, index);
+ if (std.mem.indexOf(u8, name, ".test.") == null) if (std.mem.lastIndexOf(u8, name, ".test_")) |at| {
+ const suffix = name[at + ".test_".len ..];
+ const unnamed = for (suffix) |c| {
+ if (!std.ascii.isDigit(c)) break false;
+ } else true;
+ if (suffix.len > 0 and unnamed) continue;
+ };
+ if (std.mem.indexOf(u8, name, filter) != null) matches += 1;
+ }
+ try send(io, child.stdin.?, .exit, "", deadline);
+ const result = try wait(io, &child, deadline);
+ if (result != .exited or result.exited != 0) return error.TestRunnerFailed;
+ }
+ if (matches == 0) {
+ std.debug.print("test filter '{s}' matched no named tests in {d} test program(s)\n", .{ filter, binaries.len });
+ return error.NoMatchingTests;
+ }
+ std.debug.print("test filter '{s}': {d} named match(es) in {d} test program(s)\n", .{ filter, matches, binaries.len });
+}
+
+pub fn main(init: std.process.Init) !void {
+ const io = init.io;
+ const gpa = init.gpa;
+ const args = try init.minimal.args.toSlice(gpa);
+ defer gpa.free(args);
+ if (args.len > 1 and std.mem.eql(u8, args[1], "--check-filter")) {
+ if (args.len < 4) return error.ExpectedFilterAndTestBinaries;
+ return checkFilter(io, gpa, args[2], args[3..]);
+ }
+ if (args.len < 2) return error.ExpectedTestBinaryAndOptionalFilter;
+ var filter: ?[]const u8 = null;
+ var timeout_ms: u32 = 30_000;
+ for (args[2..]) |arg| {
+ if (std.mem.startsWith(u8, arg, "--timeout-ms=")) {
+ timeout_ms = try std.fmt.parseInt(u32, arg["--timeout-ms=".len..], 10);
+ if (timeout_ms == 0) return error.InvalidTimeout;
+ } else if (filter == null) {
+ filter = arg;
+ } else return error.ExpectedTestBinaryAndOptionalFilter;
+ }
+ const timeout: std.Io.Timeout = .{ .duration = .{ .clock = .awake, .raw = .fromMilliseconds(timeout_ms) } };
+ const process_begin = std.Io.Clock.Timestamp.now(io, .awake);
+ var child = try std.process.spawn(io, .{
+ .argv = &.{ args[1], "--listen=-" },
+ .stdin = .pipe,
+ .stdout = .pipe,
+ .stderr = .inherit,
+ .request_resource_usage_statistics = true,
+ });
+ errdefer child.kill(io);
+ var output_buffer: [4096]u8 = undefined;
+ var output = std.Io.File.stdout().writerStreaming(io, &output_buffer);
+ const startup_timeout: std.Io.Timeout = .{ .duration = .{ .clock = .awake, .raw = .fromMilliseconds(@max(timeout_ms, 1500)) } };
+ const startup_deadline = startup_timeout.toDeadline(io);
+ const metadata = try queryTests(io, &child, gpa, startup_deadline);
+ defer gpa.free(metadata);
+ const count = std.mem.readInt(u32, metadata[4..8], .little);
+ var passed: usize = 0;
+ var failed: usize = 0;
+ var skipped: usize = 0;
+ var request_to_result_ns: i128 = 0;
+ var observed_peak_rss: ?usize = null;
+ for (0..count) |i| {
+ const name = try testName(metadata, i);
+ if (filter) |needle| if (std.mem.indexOf(u8, name, needle) == null) continue;
+ var id: [4]u8 = undefined;
+ std.mem.writeInt(u32, &id, @intCast(i), .little);
+ const begin = std.Io.Clock.Timestamp.now(io, .awake);
+ const deadline: std.Io.Timeout = .{ .deadline = begin.addDuration(timeout.duration) };
+ errdefer std.debug.print("unit-profile: running {s}\n", .{name});
+ try send(io, child.stdin.?, .run_test, &id, deadline);
+ const started = try receive(io, child.stdout.?, gpa, deadline);
+ defer gpa.free(started.body);
+ if (started.tag != .test_started or started.body.len != 0) return error.UnexpectedReply;
+ const result = try receive(io, child.stdout.?, gpa, deadline);
+ defer gpa.free(result.body);
+ const elapsed = begin.untilNow(io).raw.nanoseconds;
+ if (result.tag != .test_results or result.body.len != @sizeOf(Server.TestResults)) return error.UnexpectedReply;
+ if (std.mem.readInt(u32, result.body[0..4], .little) != i) return error.UnexpectedTestIndex;
+ const bits = std.mem.readInt(u64, result.body[4..12], .little);
+ if (bits & 3 == 3) return error.InvalidTestStatus;
+ const flags: Server.TestResults.Flags = @bitCast(bits);
+ const bad = flags.status == .fail or flags.log_err_count != 0 or flags.leak_count != 0;
+ if (bad) failed += 1 else if (flags.status == .skip) skipped += 1 else passed += 1;
+ request_to_result_ns += elapsed;
+ const usage = memory(io, gpa, child.id.?) catch Memory{};
+ if (usage.cumulative_peak_rss_bytes) |peak| observed_peak_rss = @max(observed_peak_rss orelse 0, peak);
+ try std.json.Stringify.value(.{
+ .kind = "case",
+ .name = name,
+ .request_to_result_ns = elapsed,
+ .rss_after_bytes = usage.rss_after_bytes,
+ .cumulative_peak_rss_bytes = usage.cumulative_peak_rss_bytes,
+ .status = if (bad) Server.TestResults.Status.fail else flags.status,
+ .log_errors = flags.log_err_count,
+ .leaks = flags.leak_count,
+ }, .{}, &output.interface);
+ try output.interface.writeByte('\n');
+ try output.interface.flush();
+ }
+ const exit_deadline = timeout.toDeadline(io);
+ try send(io, child.stdin.?, .exit, "", exit_deadline);
+ const result = try wait(io, &child, exit_deadline);
+ const process_wall_ns = process_begin.untilNow(io).raw.nanoseconds;
+ if (child.resource_usage_statistics.getMaxRss()) |peak| observed_peak_rss = @max(observed_peak_rss orelse 0, peak);
+ try std.json.Stringify.value(.{
+ .kind = "summary",
+ .passed = passed,
+ .failed = failed,
+ .skipped = skipped,
+ .request_to_result_ns = request_to_result_ns,
+ .process_wall_ns = process_wall_ns,
+ .process_peak_rss_bytes = observed_peak_rss,
+ .exit = result,
+ }, .{}, &output.interface);
+ try output.interface.writeByte('\n');
+ try output.interface.flush();
+ if (result != .exited or result.exited != 0) return error.TestRunnerFailed;
+ if (passed + skipped + failed == 0) return error.NoMatchingTests;
+ if (failed != 0) return error.TestFailed;
+}