| ... | ... | @@ -89,6 +89,8 @@ has_side_effects: bool, |
| 89 | 89 | /// If this is a Zig unit test binary, this tracks the indexes of the unit |
| 90 | 90 | /// tests that are also fuzz tests. |
| 91 | 91 | fuzz_tests: std.ArrayListUnmanaged(u32), |
| 92 | cached_test_metadata: ?CachedTestMetadata = null, |
| 93 | |
| 92 | 94 | /// Populated during the fuzz phase if this run step corresponds to a unit test |
| 93 | 95 | /// executable that contains fuzz tests. |
| 94 | 96 | rebuilt_executable: ?[]const u8, |
| ... | ... | @@ -754,7 +756,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 754 | 756 | b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path }); |
| 755 | 757 | } |
| 756 | 758 | |
| 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 | 760 | if (!has_side_effects) try step.writeManifestAndWatch(&man); |
| 759 | 761 | return; |
| 760 | 762 | }; |
| ... | ... | @@ -784,7 +786,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 784 | 786 | b.fmt("{s}{s}", .{ placeholder.output.prefix, output_path }); |
| 785 | 787 | } |
| 786 | 788 | |
| 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); |
| 788 | 790 | |
| 789 | 791 | const dep_file_dir = std.fs.cwd(); |
| 790 | 792 | const dep_file_basename = dep_output_file.generated_file.getPath(); |
| ... | ... | @@ -843,6 +845,38 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 843 | 845 | ); |
| 844 | 846 | } |
| 845 | 847 | |
| 848 | pub 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 | |
| 846 | 880 | fn populateGeneratedPaths( |
| 847 | 881 | arena: std.mem.Allocator, |
| 848 | 882 | output_placeholders: []const IndexedOutput, |
| ... | ... | @@ -921,6 +955,7 @@ fn runCommand( |
| 921 | 955 | has_side_effects: bool, |
| 922 | 956 | output_dir_path: []const u8, |
| 923 | 957 | prog_node: std.Progress.Node, |
| 958 | fuzz_unit_test_index: ?u32, |
| 924 | 959 | ) !void { |
| 925 | 960 | const step = &run.step; |
| 926 | 961 | const b = step.owner; |
| ... | ... | @@ -939,7 +974,7 @@ fn runCommand( |
| 939 | 974 | var interp_argv = std.ArrayList([]const u8).init(b.allocator); |
| 940 | 975 | defer interp_argv.deinit(); |
| 941 | 976 | |
| 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 | 978 | // InvalidExe: cpu arch mismatch |
| 944 | 979 | // FileNotFound: can happen with a wrong dynamic linker path |
| 945 | 980 | if (err == error.InvalidExe or err == error.FileNotFound) interpret: { |
| ... | ... | @@ -1075,7 +1110,7 @@ fn runCommand( |
| 1075 | 1110 | |
| 1076 | 1111 | try Step.handleVerbose2(step.owner, cwd, run.env_map, interp_argv.items); |
| 1077 | 1112 | |
| 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 | 1114 | if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped; |
| 1080 | 1115 | |
| 1081 | 1116 | return step.fail("unable to spawn interpreter {s}: {s}", .{ |
| ... | ... | @@ -1090,6 +1125,15 @@ fn runCommand( |
| 1090 | 1125 | step.result_duration_ns = result.elapsed_ns; |
| 1091 | 1126 | step.result_peak_rss = result.peak_rss; |
| 1092 | 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 | } |
| 1093 | 1137 | |
| 1094 | 1138 | // Capture stdout and stderr to GeneratedFile objects. |
| 1095 | 1139 | const Stream = struct { |
| ... | ... | @@ -1126,8 +1170,6 @@ fn runCommand( |
| 1126 | 1170 | } |
| 1127 | 1171 | } |
| 1128 | 1172 | |
| 1129 | | const final_argv = if (interp_argv.items.len == 0) argv else interp_argv.items; |
| 1130 | | |
| 1131 | 1173 | switch (run.stdio) { |
| 1132 | 1174 | .check => |checks| for (checks.items) |check| switch (check) { |
| 1133 | 1175 | .expect_stderr_exact => |expected_bytes| { |
| ... | ... | @@ -1253,10 +1295,16 @@ fn spawnChildAndCollect( |
| 1253 | 1295 | argv: []const []const u8, |
| 1254 | 1296 | has_side_effects: bool, |
| 1255 | 1297 | prog_node: std.Progress.Node, |
| 1298 | fuzz_unit_test_index: ?u32, |
| 1256 | 1299 | ) !ChildProcResult { |
| 1257 | 1300 | const b = run.step.owner; |
| 1258 | 1301 | const arena = b.allocator; |
| 1259 | 1302 | |
| 1303 | if (fuzz_unit_test_index != null) { |
| 1304 | assert(!has_side_effects); |
| 1305 | assert(run.stdio == .zig_test); |
| 1306 | } |
| 1307 | |
| 1260 | 1308 | var child = std.process.Child.init(argv, arena); |
| 1261 | 1309 | if (run.cwd) |lazy_cwd| { |
| 1262 | 1310 | child.cwd = lazy_cwd.getPath2(b, &run.step); |
| ... | ... | @@ -1306,7 +1354,7 @@ fn spawnChildAndCollect( |
| 1306 | 1354 | var timer = try std.time.Timer.start(); |
| 1307 | 1355 | |
| 1308 | 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 | 1358 | else |
| 1311 | 1359 | evalGeneric(run, &child); |
| 1312 | 1360 | |
| ... | ... | @@ -1332,6 +1380,7 @@ fn evalZigTest( |
| 1332 | 1380 | run: *Run, |
| 1333 | 1381 | child: *std.process.Child, |
| 1334 | 1382 | prog_node: std.Progress.Node, |
| 1383 | fuzz_unit_test_index: ?u32, |
| 1335 | 1384 | ) !StdIoResult { |
| 1336 | 1385 | const gpa = run.step.owner.allocator; |
| 1337 | 1386 | const arena = run.step.owner.allocator; |
| ... | ... | @@ -1342,7 +1391,12 @@ fn evalZigTest( |
| 1342 | 1391 | }); |
| 1343 | 1392 | defer poller.deinit(); |
| 1344 | 1393 | |
| 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 | } |
| 1346 | 1400 | |
| 1347 | 1401 | const Header = std.zig.Server.Message.Header; |
| 1348 | 1402 | |
| ... | ... | @@ -1360,8 +1414,6 @@ fn evalZigTest( |
| 1360 | 1414 | var sub_prog_node: ?std.Progress.Node = null; |
| 1361 | 1415 | defer if (sub_prog_node) |n| n.end(); |
| 1362 | 1416 | |
| 1363 | | run.fuzz_tests.clearRetainingCapacity(); |
| 1364 | | |
| 1365 | 1417 | poll: while (true) { |
| 1366 | 1418 | while (stdout.readableLength() < @sizeOf(Header)) { |
| 1367 | 1419 | if (!(try poller.poll())) break :poll; |
| ... | ... | @@ -1382,6 +1434,7 @@ fn evalZigTest( |
| 1382 | 1434 | } |
| 1383 | 1435 | }, |
| 1384 | 1436 | .test_metadata => { |
| 1437 | assert(fuzz_unit_test_index == null); |
| 1385 | 1438 | const TmHdr = std.zig.Server.Message.TestMetadata; |
| 1386 | 1439 | const tm_hdr = @as(*align(1) const TmHdr, @ptrCast(body)); |
| 1387 | 1440 | test_count = tm_hdr.tests_len; |
| ... | ... | @@ -1410,6 +1463,7 @@ fn evalZigTest( |
| 1410 | 1463 | try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node); |
| 1411 | 1464 | }, |
| 1412 | 1465 | .test_results => { |
| 1466 | assert(fuzz_unit_test_index == null); |
| 1413 | 1467 | const md = metadata.?; |
| 1414 | 1468 | |
| 1415 | 1469 | const TrHdr = std.zig.Server.Message.TestResults; |
| ... | ... | @@ -1479,7 +1533,23 @@ const TestMetadata = struct { |
| 1479 | 1533 | next_index: u32, |
| 1480 | 1534 | prog_node: std.Progress.Node, |
| 1481 | 1535 | |
| 1536 | fn toCachedTestMetadata(tm: TestMetadata) CachedTestMetadata { |
| 1537 | return .{ |
| 1538 | .names = tm.names, |
| 1539 | .string_bytes = tm.string_bytes, |
| 1540 | }; |
| 1541 | } |
| 1542 | |
| 1482 | 1543 | fn testName(tm: TestMetadata, index: u32) []const u8 { |
| 1544 | return tm.toCachedTestMetadata().testName(index); |
| 1545 | } |
| 1546 | }; |
| 1547 | |
| 1548 | pub const CachedTestMetadata = struct { |
| 1549 | names: []const u32, |
| 1550 | string_bytes: []const u8, |
| 1551 | |
| 1552 | pub fn testName(tm: CachedTestMetadata, index: u32) []const u8 { |
| 1483 | 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 | 1565 | if (sub_prog_node.*) |n| n.end(); |
| 1496 | 1566 | sub_prog_node.* = metadata.prog_node.start(name, 0); |
| 1497 | 1567 | |
| 1498 | | try sendRunTestMessage(in, i); |
| 1568 | try sendRunTestMessage(in, .run_test, i); |
| 1499 | 1569 | return; |
| 1500 | 1570 | } else { |
| 1501 | 1571 | try sendMessage(in, .exit); |
| ... | ... | @@ -1510,9 +1580,9 @@ fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void { |
| 1510 | 1580 | try file.writeAll(std.mem.asBytes(&header)); |
| 1511 | 1581 | } |
| 1512 | 1582 | |
| 1513 | | fn sendRunTestMessage(file: std.fs.File, index: u32) !void { |
| 1583 | fn sendRunTestMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag, index: u32) !void { |
| 1514 | 1584 | const header: std.zig.Client.Message.Header = .{ |
| 1515 | | .tag = .run_test, |
| 1585 | .tag = tag, |
| 1516 | 1586 | .bytes_len = 4, |
| 1517 | 1587 | }; |
| 1518 | 1588 | const full_msg = std.mem.asBytes(&header) ++ std.mem.asBytes(&index); |