authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-07-23 22:59:25-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-07-25 18:52:21-07:00
logbce3b1efb0879ba2f0da4d215c3190f3e8a4345b
tree74b75d45e7b284b78614995313c8fc6febf867fb
parent711ed56ce361fd9051fcf6039de48022b8dbc2d1

build runner sends a start_fuzzing message to test runner


4 files changed, 166 insertions(+), 31 deletions(-)

lib/compiler/build_runner.zig+10-6
...@@ -401,7 +401,7 @@ pub fn main() !void {...@@ -401,7 +401,7 @@ pub fn main() !void {
401 else => return err,401 else => return err,
402 };402 };
403 if (fuzz) {403 if (fuzz) {
404 Fuzz.start(&run.thread_pool, run.step_stack.keys(), main_progress_node);404 Fuzz.start(&run.thread_pool, run.step_stack.keys(), run.ttyconf, main_progress_node);
405 }405 }
406406
407 if (!watch) return cleanExit();407 if (!watch) return cleanExit();
...@@ -1072,7 +1072,7 @@ fn workerMakeOneStep(...@@ -1072,7 +1072,7 @@ fn workerMakeOneStep(
1072 std.debug.lockStdErr();1072 std.debug.lockStdErr();
1073 defer std.debug.unlockStdErr();1073 defer std.debug.unlockStdErr();
10741074
1075 printErrorMessages(b, s, run) catch {};1075 printErrorMessages(b, s, run.ttyconf, run.stderr, run.prominent_compile_errors) catch {};
1076 }1076 }
10771077
1078 handle_result: {1078 handle_result: {
...@@ -1125,10 +1125,14 @@ fn workerMakeOneStep(...@@ -1125,10 +1125,14 @@ fn workerMakeOneStep(
1125 }1125 }
1126}1126}
11271127
1128fn printErrorMessages(b: *std.Build, failing_step: *Step, run: *const Run) !void {1128pub fn printErrorMessages(
1129 b: *std.Build,
1130 failing_step: *Step,
1131 ttyconf: std.io.tty.Config,
1132 stderr: File,
1133 prominent_compile_errors: bool,
1134) !void {
1129 const gpa = b.allocator;1135 const gpa = b.allocator;
1130 const stderr = run.stderr;
1131 const ttyconf = run.ttyconf;
11321136
1133 // Provide context for where these error messages are coming from by1137 // Provide context for where these error messages are coming from by
1134 // printing the corresponding Step subtree.1138 // printing the corresponding Step subtree.
...@@ -1166,7 +1170,7 @@ fn printErrorMessages(b: *std.Build, failing_step: *Step, run: *const Run) !void...@@ -1166,7 +1170,7 @@ fn printErrorMessages(b: *std.Build, failing_step: *Step, run: *const Run) !void
1166 }1170 }
1167 }1171 }
11681172
1169 if (!run.prominent_compile_errors and failing_step.result_error_bundle.errorMessageCount() > 0)1173 if (!prominent_compile_errors and failing_step.result_error_bundle.errorMessageCount() > 0)
1170 try failing_step.result_error_bundle.renderToWriter(renderOptions(ttyconf), stderr.writer());1174 try failing_step.result_error_bundle.renderToWriter(renderOptions(ttyconf), stderr.writer());
11711175
1172 for (failing_step.result_error_msgs.items) |msg| {1176 for (failing_step.result_error_msgs.items) |msg| {
lib/std/Build/Fuzz.zig+70-12
...@@ -3,9 +3,15 @@ const Fuzz = @This();...@@ -3,9 +3,15 @@ const Fuzz = @This();
3const Step = std.Build.Step;3const Step = std.Build.Step;
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const fatal = std.process.fatal;5const fatal = std.process.fatal;
6const build_runner = @import("root");
67
7pub fn start(thread_pool: *std.Thread.Pool, all_steps: []const *Step, prog_node: std.Progress.Node) void {8pub fn start(
8 {9 thread_pool: *std.Thread.Pool,
10 all_steps: []const *Step,
11 ttyconf: std.io.tty.Config,
12 prog_node: std.Progress.Node,
13) void {
14 const count = block: {
9 const rebuild_node = prog_node.start("Rebuilding Unit Tests", 0);15 const rebuild_node = prog_node.start("Rebuilding Unit Tests", 0);
10 defer rebuild_node.end();16 defer rebuild_node.end();
11 var count: usize = 0;17 var count: usize = 0;
...@@ -14,13 +20,14 @@ pub fn start(thread_pool: *std.Thread.Pool, all_steps: []const *Step, prog_node:...@@ -14,13 +20,14 @@ pub fn start(thread_pool: *std.Thread.Pool, all_steps: []const *Step, prog_node:
14 for (all_steps) |step| {20 for (all_steps) |step| {
15 const run = step.cast(Step.Run) orelse continue;21 const run = step.cast(Step.Run) orelse continue;
16 if (run.fuzz_tests.items.len > 0 and run.producer != null) {22 if (run.fuzz_tests.items.len > 0 and run.producer != null) {
17 thread_pool.spawnWg(&wait_group, rebuildTestsWorkerRun, .{ run, prog_node });23 thread_pool.spawnWg(&wait_group, rebuildTestsWorkerRun, .{ run, ttyconf, rebuild_node });
18 count += 1;24 count += 1;
19 }25 }
20 }26 }
21 if (count == 0) fatal("no fuzz tests found", .{});27 if (count == 0) fatal("no fuzz tests found", .{});
22 rebuild_node.setEstimatedTotalItems(count);28 rebuild_node.setEstimatedTotalItems(count);
23 }29 break :block count;
30 };
2431
25 // Detect failure.32 // Detect failure.
26 for (all_steps) |step| {33 for (all_steps) |step| {
...@@ -29,18 +36,69 @@ pub fn start(thread_pool: *std.Thread.Pool, all_steps: []const *Step, prog_node:...@@ -29,18 +36,69 @@ pub fn start(thread_pool: *std.Thread.Pool, all_steps: []const *Step, prog_node:
29 fatal("one or more unit tests failed to be rebuilt in fuzz mode", .{});36 fatal("one or more unit tests failed to be rebuilt in fuzz mode", .{});
30 }37 }
3138
32 @panic("TODO do something with the rebuilt unit tests");39 {
40 const rebuild_node = prog_node.start("Fuzzing", count);
41 defer rebuild_node.end();
42 var wait_group: std.Thread.WaitGroup = .{};
43 defer wait_group.wait();
44
45 for (all_steps) |step| {
46 const run = step.cast(Step.Run) orelse continue;
47 for (run.fuzz_tests.items) |unit_test_index| {
48 assert(run.rebuilt_executable != null);
49 thread_pool.spawnWg(&wait_group, fuzzWorkerRun, .{ run, unit_test_index, ttyconf, prog_node });
50 }
51 }
52 }
53
54 fatal("all fuzz workers crashed", .{});
33}55}
3456
35fn rebuildTestsWorkerRun(run: *Step.Run, parent_prog_node: std.Progress.Node) void {57fn rebuildTestsWorkerRun(run: *Step.Run, ttyconf: std.io.tty.Config, parent_prog_node: std.Progress.Node) void {
36 const compile_step = run.producer.?;58 const compile_step = run.producer.?;
37 const prog_node = parent_prog_node.start(compile_step.step.name, 0);59 const prog_node = parent_prog_node.start(compile_step.step.name, 0);
38 defer prog_node.end();60 defer prog_node.end();
39 const rebuilt_bin_path = compile_step.rebuildInFuzzMode(prog_node) catch |err| {61 if (compile_step.rebuildInFuzzMode(prog_node)) |rebuilt_bin_path| {
40 std.debug.print("failed to rebuild {s} in fuzz mode: {s}", .{62 run.rebuilt_executable = rebuilt_bin_path;
41 compile_step.step.name, @errorName(err),63 } else |err| switch (err) {
42 });64 error.MakeFailed => {
43 return;65 const b = run.step.owner;
66 const stderr = std.io.getStdErr();
67 std.debug.lockStdErr();
68 defer std.debug.unlockStdErr();
69 build_runner.printErrorMessages(b, &compile_step.step, ttyconf, stderr, false) catch {};
70 },
71 else => {
72 std.debug.print("step '{s}': failed to rebuild in fuzz mode: {s}\n", .{
73 compile_step.step.name, @errorName(err),
74 });
75 },
76 }
77}
78
79fn fuzzWorkerRun(
80 run: *Step.Run,
81 unit_test_index: u32,
82 ttyconf: std.io.tty.Config,
83 parent_prog_node: std.Progress.Node,
84) void {
85 const test_name = run.cached_test_metadata.?.testName(unit_test_index);
86
87 const prog_node = parent_prog_node.start(test_name, 0);
88 defer prog_node.end();
89
90 run.rerunInFuzzMode(unit_test_index, prog_node) catch |err| switch (err) {
91 error.MakeFailed => {
92 const b = run.step.owner;
93 const stderr = std.io.getStdErr();
94 std.debug.lockStdErr();
95 defer std.debug.unlockStdErr();
96 build_runner.printErrorMessages(b, &run.step, ttyconf, stderr, false) catch {};
97 },
98 else => {
99 std.debug.print("step '{s}': failed to rebuild '{s}' in fuzz mode: {s}\n", .{
100 run.step.name, test_name, @errorName(err),
101 });
102 },
44 };103 };
45 run.rebuilt_executable = rebuilt_bin_path;
46}104}
lib/std/Build/Step/Run.zig+83-13
...@@ -89,6 +89,8 @@ has_side_effects: bool,...@@ -89,6 +89,8 @@ has_side_effects: bool,
89/// If this is a Zig unit test binary, this tracks the indexes of the unit89/// If this is a Zig unit test binary, this tracks the indexes of the unit
90/// tests that are also fuzz tests.90/// tests that are also fuzz tests.
91fuzz_tests: std.ArrayListUnmanaged(u32),91fuzz_tests: std.ArrayListUnmanaged(u32),
92cached_test_metadata: ?CachedTestMetadata = null,
93
92/// Populated during the fuzz phase if this run step corresponds to a unit test94/// Populated during the fuzz phase if this run step corresponds to a unit test
93/// executable that contains fuzz tests.95/// executable that contains fuzz tests.
94rebuilt_executable: ?[]const u8,96rebuilt_executable: ?[]const u8,
...@@ -754,7 +756,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -754,7 +756,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
754 b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path });756 b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path });
755 }757 }
756758
757 try runCommand(run, argv_list.items, has_side_effects, output_dir_path, prog_node);759 try runCommand(run, argv_list.items, has_side_effects, output_dir_path, prog_node, null);
758 if (!has_side_effects) try step.writeManifestAndWatch(&man);760 if (!has_side_effects) try step.writeManifestAndWatch(&man);
759 return;761 return;
760 };762 };
...@@ -784,7 +786,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -784,7 +786,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
784 b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path });786 b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path });
785 }787 }
786788
787 try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node);789 try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node, null);
788790
789 const dep_file_dir = std.fs.cwd();791 const dep_file_dir = std.fs.cwd();
790 const dep_file_basename = dep_output_file.generated_file.getPath();792 const dep_file_basename = dep_output_file.generated_file.getPath();
...@@ -843,6 +845,38 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -843,6 +845,38 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
843 );845 );
844}846}
845847
848pub fn rerunInFuzzMode(run: *Run, unit_test_index: u32, prog_node: std.Progress.Node) !void {
849 const step = &run.step;
850 const b = step.owner;
851 const arena = b.allocator;
852 var argv_list: std.ArrayListUnmanaged([]const u8) = .{};
853 for (run.argv.items) |arg| {
854 switch (arg) {
855 .bytes => |bytes| {
856 try argv_list.append(arena, bytes);
857 },
858 .lazy_path => |file| {
859 const file_path = file.lazy_path.getPath2(b, step);
860 try argv_list.append(arena, b.fmt("{s}{s}", .{ file.prefix, file_path }));
861 },
862 .directory_source => |file| {
863 const file_path = file.lazy_path.getPath2(b, step);
864 try argv_list.append(arena, b.fmt("{s}{s}", .{ file.prefix, file_path }));
865 },
866 .artifact => |pa| {
867 const artifact = pa.artifact;
868 const file_path = artifact.installed_path orelse artifact.generated_bin.?.path.?;
869 try argv_list.append(arena, b.fmt("{s}{s}", .{ pa.prefix, file_path }));
870 },
871 .output_file, .output_directory => unreachable,
872 }
873 }
874 const has_side_effects = false;
875 const rand_int = std.crypto.random.int(u64);
876 const tmp_dir_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
877 try runCommand(run, argv_list.items, has_side_effects, tmp_dir_path, prog_node, unit_test_index);
878}
879
846fn populateGeneratedPaths(880fn populateGeneratedPaths(
847 arena: std.mem.Allocator,881 arena: std.mem.Allocator,
848 output_placeholders: []const IndexedOutput,882 output_placeholders: []const IndexedOutput,
...@@ -921,6 +955,7 @@ fn runCommand(...@@ -921,6 +955,7 @@ fn runCommand(
921 has_side_effects: bool,955 has_side_effects: bool,
922 output_dir_path: []const u8,956 output_dir_path: []const u8,
923 prog_node: std.Progress.Node,957 prog_node: std.Progress.Node,
958 fuzz_unit_test_index: ?u32,
924) !void {959) !void {
925 const step = &run.step;960 const step = &run.step;
926 const b = step.owner;961 const b = step.owner;
...@@ -939,7 +974,7 @@ fn runCommand(...@@ -939,7 +974,7 @@ fn runCommand(
939 var interp_argv = std.ArrayList([]const u8).init(b.allocator);974 var interp_argv = std.ArrayList([]const u8).init(b.allocator);
940 defer interp_argv.deinit();975 defer interp_argv.deinit();
941976
942 const result = spawnChildAndCollect(run, argv, has_side_effects, prog_node) catch |err| term: {977 const result = spawnChildAndCollect(run, argv, has_side_effects, prog_node, fuzz_unit_test_index) catch |err| term: {
943 // InvalidExe: cpu arch mismatch978 // InvalidExe: cpu arch mismatch
944 // FileNotFound: can happen with a wrong dynamic linker path979 // FileNotFound: can happen with a wrong dynamic linker path
945 if (err == error.InvalidExe or err == error.FileNotFound) interpret: {980 if (err == error.InvalidExe or err == error.FileNotFound) interpret: {
...@@ -1075,7 +1110,7 @@ fn runCommand(...@@ -1075,7 +1110,7 @@ fn runCommand(
10751110
1076 try Step.handleVerbose2(step.owner, cwd, run.env_map, interp_argv.items);1111 try Step.handleVerbose2(step.owner, cwd, run.env_map, interp_argv.items);
10771112
1078 break :term spawnChildAndCollect(run, interp_argv.items, has_side_effects, prog_node) catch |e| {1113 break :term spawnChildAndCollect(run, interp_argv.items, has_side_effects, prog_node, fuzz_unit_test_index) catch |e| {
1079 if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped;1114 if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped;
10801115
1081 return step.fail("unable to spawn interpreter {s}: {s}", .{1116 return step.fail("unable to spawn interpreter {s}: {s}", .{
...@@ -1090,6 +1125,15 @@ fn runCommand(...@@ -1090,6 +1125,15 @@ fn runCommand(
1090 step.result_duration_ns = result.elapsed_ns;1125 step.result_duration_ns = result.elapsed_ns;
1091 step.result_peak_rss = result.peak_rss;1126 step.result_peak_rss = result.peak_rss;
1092 step.test_results = result.stdio.test_results;1127 step.test_results = result.stdio.test_results;
1128 if (result.stdio.test_metadata) |tm|
1129 run.cached_test_metadata = tm.toCachedTestMetadata();
1130
1131 const final_argv = if (interp_argv.items.len == 0) argv else interp_argv.items;
1132
1133 if (fuzz_unit_test_index != null) {
1134 try step.handleChildProcessTerm(result.term, cwd, final_argv);
1135 return;
1136 }
10931137
1094 // Capture stdout and stderr to GeneratedFile objects.1138 // Capture stdout and stderr to GeneratedFile objects.
1095 const Stream = struct {1139 const Stream = struct {
...@@ -1126,8 +1170,6 @@ fn runCommand(...@@ -1126,8 +1170,6 @@ fn runCommand(
1126 }1170 }
1127 }1171 }
11281172
1129 const final_argv = if (interp_argv.items.len == 0) argv else interp_argv.items;
1130
1131 switch (run.stdio) {1173 switch (run.stdio) {
1132 .check => |checks| for (checks.items) |check| switch (check) {1174 .check => |checks| for (checks.items) |check| switch (check) {
1133 .expect_stderr_exact => |expected_bytes| {1175 .expect_stderr_exact => |expected_bytes| {
...@@ -1253,10 +1295,16 @@ fn spawnChildAndCollect(...@@ -1253,10 +1295,16 @@ fn spawnChildAndCollect(
1253 argv: []const []const u8,1295 argv: []const []const u8,
1254 has_side_effects: bool,1296 has_side_effects: bool,
1255 prog_node: std.Progress.Node,1297 prog_node: std.Progress.Node,
1298 fuzz_unit_test_index: ?u32,
1256) !ChildProcResult {1299) !ChildProcResult {
1257 const b = run.step.owner;1300 const b = run.step.owner;
1258 const arena = b.allocator;1301 const arena = b.allocator;
12591302
1303 if (fuzz_unit_test_index != null) {
1304 assert(!has_side_effects);
1305 assert(run.stdio == .zig_test);
1306 }
1307
1260 var child = std.process.Child.init(argv, arena);1308 var child = std.process.Child.init(argv, arena);
1261 if (run.cwd) |lazy_cwd| {1309 if (run.cwd) |lazy_cwd| {
1262 child.cwd = lazy_cwd.getPath2(b, &run.step);1310 child.cwd = lazy_cwd.getPath2(b, &run.step);
...@@ -1306,7 +1354,7 @@ fn spawnChildAndCollect(...@@ -1306,7 +1354,7 @@ fn spawnChildAndCollect(
1306 var timer = try std.time.Timer.start();1354 var timer = try std.time.Timer.start();
13071355
1308 const result = if (run.stdio == .zig_test)1356 const result = if (run.stdio == .zig_test)
1309 evalZigTest(run, &child, prog_node)1357 evalZigTest(run, &child, prog_node, fuzz_unit_test_index)
1310 else1358 else
1311 evalGeneric(run, &child);1359 evalGeneric(run, &child);
13121360
...@@ -1332,6 +1380,7 @@ fn evalZigTest(...@@ -1332,6 +1380,7 @@ fn evalZigTest(
1332 run: *Run,1380 run: *Run,
1333 child: *std.process.Child,1381 child: *std.process.Child,
1334 prog_node: std.Progress.Node,1382 prog_node: std.Progress.Node,
1383 fuzz_unit_test_index: ?u32,
1335) !StdIoResult {1384) !StdIoResult {
1336 const gpa = run.step.owner.allocator;1385 const gpa = run.step.owner.allocator;
1337 const arena = run.step.owner.allocator;1386 const arena = run.step.owner.allocator;
...@@ -1342,7 +1391,12 @@ fn evalZigTest(...@@ -1342,7 +1391,12 @@ fn evalZigTest(
1342 });1391 });
1343 defer poller.deinit();1392 defer poller.deinit();
13441393
1345 try sendMessage(child.stdin.?, .query_test_metadata);1394 if (fuzz_unit_test_index) |index| {
1395 try sendRunTestMessage(child.stdin.?, .start_fuzzing, index);
1396 } else {
1397 run.fuzz_tests.clearRetainingCapacity();
1398 try sendMessage(child.stdin.?, .query_test_metadata);
1399 }
13461400
1347 const Header = std.zig.Server.Message.Header;1401 const Header = std.zig.Server.Message.Header;
13481402
...@@ -1360,8 +1414,6 @@ fn evalZigTest(...@@ -1360,8 +1414,6 @@ fn evalZigTest(
1360 var sub_prog_node: ?std.Progress.Node = null;1414 var sub_prog_node: ?std.Progress.Node = null;
1361 defer if (sub_prog_node) |n| n.end();1415 defer if (sub_prog_node) |n| n.end();
13621416
1363 run.fuzz_tests.clearRetainingCapacity();
1364
1365 poll: while (true) {1417 poll: while (true) {
1366 while (stdout.readableLength() < @sizeOf(Header)) {1418 while (stdout.readableLength() < @sizeOf(Header)) {
1367 if (!(try poller.poll())) break :poll;1419 if (!(try poller.poll())) break :poll;
...@@ -1382,6 +1434,7 @@ fn evalZigTest(...@@ -1382,6 +1434,7 @@ fn evalZigTest(
1382 }1434 }
1383 },1435 },
1384 .test_metadata => {1436 .test_metadata => {
1437 assert(fuzz_unit_test_index == null);
1385 const TmHdr = std.zig.Server.Message.TestMetadata;1438 const TmHdr = std.zig.Server.Message.TestMetadata;
1386 const tm_hdr = @as(*align(1) const TmHdr, @ptrCast(body));1439 const tm_hdr = @as(*align(1) const TmHdr, @ptrCast(body));
1387 test_count = tm_hdr.tests_len;1440 test_count = tm_hdr.tests_len;
...@@ -1410,6 +1463,7 @@ fn evalZigTest(...@@ -1410,6 +1463,7 @@ fn evalZigTest(
1410 try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node);1463 try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node);
1411 },1464 },
1412 .test_results => {1465 .test_results => {
1466 assert(fuzz_unit_test_index == null);
1413 const md = metadata.?;1467 const md = metadata.?;
14141468
1415 const TrHdr = std.zig.Server.Message.TestResults;1469 const TrHdr = std.zig.Server.Message.TestResults;
...@@ -1479,7 +1533,23 @@ const TestMetadata = struct {...@@ -1479,7 +1533,23 @@ const TestMetadata = struct {
1479 next_index: u32,1533 next_index: u32,
1480 prog_node: std.Progress.Node,1534 prog_node: std.Progress.Node,
14811535
1536 fn toCachedTestMetadata(tm: TestMetadata) CachedTestMetadata {
1537 return .{
1538 .names = tm.names,
1539 .string_bytes = tm.string_bytes,
1540 };
1541 }
1542
1482 fn testName(tm: TestMetadata, index: u32) []const u8 {1543 fn testName(tm: TestMetadata, index: u32) []const u8 {
1544 return tm.toCachedTestMetadata().testName(index);
1545 }
1546};
1547
1548pub const CachedTestMetadata = struct {
1549 names: []const u32,
1550 string_bytes: []const u8,
1551
1552 pub fn testName(tm: CachedTestMetadata, index: u32) []const u8 {
1483 return std.mem.sliceTo(tm.string_bytes[tm.names[index]..], 0);1553 return std.mem.sliceTo(tm.string_bytes[tm.names[index]..], 0);
1484 }1554 }
1485};1555};
...@@ -1495,7 +1565,7 @@ fn requestNextTest(in: fs.File, metadata: *TestMetadata, sub_prog_node: *?std.Pr...@@ -1495,7 +1565,7 @@ fn requestNextTest(in: fs.File, metadata: *TestMetadata, sub_prog_node: *?std.Pr
1495 if (sub_prog_node.*) |n| n.end();1565 if (sub_prog_node.*) |n| n.end();
1496 sub_prog_node.* = metadata.prog_node.start(name, 0);1566 sub_prog_node.* = metadata.prog_node.start(name, 0);
14971567
1498 try sendRunTestMessage(in, i);1568 try sendRunTestMessage(in, .run_test, i);
1499 return;1569 return;
1500 } else {1570 } else {
1501 try sendMessage(in, .exit);1571 try sendMessage(in, .exit);
...@@ -1510,9 +1580,9 @@ fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void {...@@ -1510,9 +1580,9 @@ fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void {
1510 try file.writeAll(std.mem.asBytes(&header));1580 try file.writeAll(std.mem.asBytes(&header));
1511}1581}
15121582
1513fn sendRunTestMessage(file: std.fs.File, index: u32) !void {1583fn sendRunTestMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag, index: u32) !void {
1514 const header: std.zig.Client.Message.Header = .{1584 const header: std.zig.Client.Message.Header = .{
1515 .tag = .run_test,1585 .tag = tag,
1516 .bytes_len = 4,1586 .bytes_len = 4,
1517 };1587 };
1518 const full_msg = std.mem.asBytes(&header) ++ std.mem.asBytes(&index);1588 const full_msg = std.mem.asBytes(&header) ++ std.mem.asBytes(&index);
lib/std/zig/Client.zig+3
...@@ -33,6 +33,9 @@ pub const Message = struct {...@@ -33,6 +33,9 @@ pub const Message = struct {
33 /// Ask the test runner to run a particular test.33 /// Ask the test runner to run a particular test.
34 /// The message body is a u32 test index.34 /// The message body is a u32 test index.
35 run_test,35 run_test,
36 /// Ask the test runner to start fuzzing a particular test.
37 /// The message body is a u32 test index.
38 start_fuzzing,
3639
37 _,40 _,
38 };41 };