| ... | ... | @@ -1,18 +1,26 @@ |
| 1 | 1 | //! Default test runner for unit tests. |
| 2 | const builtin = @import("builtin"); |
| 2 | 3 | const std = @import("std"); |
| 3 | 4 | const io = std.io; |
| 4 | | const builtin = @import("builtin"); |
| 5 | const testing = std.testing; |
| 5 | 6 | |
| 6 | 7 | pub const std_options = .{ |
| 7 | 8 | .logFn = log, |
| 8 | 9 | }; |
| 9 | 10 | |
| 10 | 11 | var log_err_count: usize = 0; |
| 11 | | var cmdline_buffer: [4096]u8 = undefined; |
| 12 | | var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer); |
| 12 | var fba_buffer: [8192]u8 = undefined; |
| 13 | var fba = std.heap.FixedBufferAllocator.init(&fba_buffer); |
| 14 | |
| 15 | const crippled = switch (builtin.zig_backend) { |
| 16 | .stage2_riscv64 => true, |
| 17 | else => false, |
| 18 | }; |
| 13 | 19 | |
| 14 | 20 | pub fn main() void { |
| 15 | | if (builtin.zig_backend == .stage2_riscv64) { |
| 21 | @disableInstrumentation(); |
| 22 | |
| 23 | if (crippled) { |
| 16 | 24 | return mainSimple() catch @panic("test failure\n"); |
| 17 | 25 | } |
| 18 | 26 | |
| ... | ... | @@ -25,13 +33,15 @@ pub fn main() void { |
| 25 | 33 | if (std.mem.eql(u8, arg, "--listen=-")) { |
| 26 | 34 | listen = true; |
| 27 | 35 | } else if (std.mem.startsWith(u8, arg, "--seed=")) { |
| 28 | | std.testing.random_seed = std.fmt.parseUnsigned(u32, arg["--seed=".len..], 0) catch |
| 36 | testing.random_seed = std.fmt.parseUnsigned(u32, arg["--seed=".len..], 0) catch |
| 29 | 37 | @panic("unable to parse --seed command line argument"); |
| 30 | 38 | } else { |
| 31 | 39 | @panic("unrecognized command line argument"); |
| 32 | 40 | } |
| 33 | 41 | } |
| 34 | 42 | |
| 43 | fba.reset(); |
| 44 | |
| 35 | 45 | if (listen) { |
| 36 | 46 | return mainServer() catch @panic("internal test runner failure"); |
| 37 | 47 | } else { |
| ... | ... | @@ -40,6 +50,7 @@ pub fn main() void { |
| 40 | 50 | } |
| 41 | 51 | |
| 42 | 52 | fn mainServer() !void { |
| 53 | @disableInstrumentation(); |
| 43 | 54 | var server = try std.zig.Server.init(.{ |
| 44 | 55 | .gpa = fba.allocator(), |
| 45 | 56 | .in = std.io.getStdIn(), |
| ... | ... | @@ -55,24 +66,24 @@ fn mainServer() !void { |
| 55 | 66 | return std.process.exit(0); |
| 56 | 67 | }, |
| 57 | 68 | .query_test_metadata => { |
| 58 | | std.testing.allocator_instance = .{}; |
| 59 | | defer if (std.testing.allocator_instance.deinit() == .leak) { |
| 69 | testing.allocator_instance = .{}; |
| 70 | defer if (testing.allocator_instance.deinit() == .leak) { |
| 60 | 71 | @panic("internal test runner memory leak"); |
| 61 | 72 | }; |
| 62 | 73 | |
| 63 | 74 | var string_bytes: std.ArrayListUnmanaged(u8) = .{}; |
| 64 | | defer string_bytes.deinit(std.testing.allocator); |
| 65 | | try string_bytes.append(std.testing.allocator, 0); // Reserve 0 for null. |
| 75 | defer string_bytes.deinit(testing.allocator); |
| 76 | try string_bytes.append(testing.allocator, 0); // Reserve 0 for null. |
| 66 | 77 | |
| 67 | 78 | const test_fns = builtin.test_functions; |
| 68 | | const names = try std.testing.allocator.alloc(u32, test_fns.len); |
| 69 | | defer std.testing.allocator.free(names); |
| 70 | | const expected_panic_msgs = try std.testing.allocator.alloc(u32, test_fns.len); |
| 71 | | defer std.testing.allocator.free(expected_panic_msgs); |
| 79 | const names = try testing.allocator.alloc(u32, test_fns.len); |
| 80 | defer testing.allocator.free(names); |
| 81 | const expected_panic_msgs = try testing.allocator.alloc(u32, test_fns.len); |
| 82 | defer testing.allocator.free(expected_panic_msgs); |
| 72 | 83 | |
| 73 | 84 | for (test_fns, names, expected_panic_msgs) |test_fn, *name, *expected_panic_msg| { |
| 74 | 85 | name.* = @as(u32, @intCast(string_bytes.items.len)); |
| 75 | | try string_bytes.ensureUnusedCapacity(std.testing.allocator, test_fn.name.len + 1); |
| 86 | try string_bytes.ensureUnusedCapacity(testing.allocator, test_fn.name.len + 1); |
| 76 | 87 | string_bytes.appendSliceAssumeCapacity(test_fn.name); |
| 77 | 88 | string_bytes.appendAssumeCapacity(0); |
| 78 | 89 | expected_panic_msg.* = 0; |
| ... | ... | @@ -86,13 +97,13 @@ fn mainServer() !void { |
| 86 | 97 | }, |
| 87 | 98 | |
| 88 | 99 | .run_test => { |
| 89 | | std.testing.allocator_instance = .{}; |
| 100 | testing.allocator_instance = .{}; |
| 90 | 101 | log_err_count = 0; |
| 91 | 102 | const index = try server.receiveBody_u32(); |
| 92 | 103 | const test_fn = builtin.test_functions[index]; |
| 93 | 104 | var fail = false; |
| 94 | 105 | var skip = false; |
| 95 | | var leak = false; |
| 106 | is_fuzz_test = false; |
| 96 | 107 | test_fn.func() catch |err| switch (err) { |
| 97 | 108 | error.SkipZigTest => skip = true, |
| 98 | 109 | else => { |
| ... | ... | @@ -102,13 +113,14 @@ fn mainServer() !void { |
| 102 | 113 | } |
| 103 | 114 | }, |
| 104 | 115 | }; |
| 105 | | leak = std.testing.allocator_instance.deinit() == .leak; |
| 116 | const leak = testing.allocator_instance.deinit() == .leak; |
| 106 | 117 | try server.serveTestResults(.{ |
| 107 | 118 | .index = index, |
| 108 | 119 | .flags = .{ |
| 109 | 120 | .fail = fail, |
| 110 | 121 | .skip = skip, |
| 111 | 122 | .leak = leak, |
| 123 | .fuzz = is_fuzz_test, |
| 112 | 124 | .log_err_count = std.math.lossyCast( |
| 113 | 125 | @TypeOf(@as(std.zig.Server.Message.TestResults.Flags, undefined).log_err_count), |
| 114 | 126 | log_err_count, |
| ... | ... | @@ -118,7 +130,7 @@ fn mainServer() !void { |
| 118 | 130 | }, |
| 119 | 131 | |
| 120 | 132 | else => { |
| 121 | | std.debug.print("unsupported message: {x}", .{@intFromEnum(hdr.tag)}); |
| 133 | std.debug.print("unsupported message: {x}\n", .{@intFromEnum(hdr.tag)}); |
| 122 | 134 | std.process.exit(1); |
| 123 | 135 | }, |
| 124 | 136 | } |
| ... | ... | @@ -126,6 +138,7 @@ fn mainServer() !void { |
| 126 | 138 | } |
| 127 | 139 | |
| 128 | 140 | fn mainTerminal() void { |
| 141 | @disableInstrumentation(); |
| 129 | 142 | const test_fn_list = builtin.test_functions; |
| 130 | 143 | var ok_count: usize = 0; |
| 131 | 144 | var skip_count: usize = 0; |
| ... | ... | @@ -143,18 +156,19 @@ fn mainTerminal() void { |
| 143 | 156 | |
| 144 | 157 | var leaks: usize = 0; |
| 145 | 158 | for (test_fn_list, 0..) |test_fn, i| { |
| 146 | | std.testing.allocator_instance = .{}; |
| 159 | testing.allocator_instance = .{}; |
| 147 | 160 | defer { |
| 148 | | if (std.testing.allocator_instance.deinit() == .leak) { |
| 161 | if (testing.allocator_instance.deinit() == .leak) { |
| 149 | 162 | leaks += 1; |
| 150 | 163 | } |
| 151 | 164 | } |
| 152 | | std.testing.log_level = .warn; |
| 165 | testing.log_level = .warn; |
| 153 | 166 | |
| 154 | 167 | const test_node = root_node.start(test_fn.name, 0); |
| 155 | 168 | if (!have_tty) { |
| 156 | 169 | std.debug.print("{d}/{d} {s}...", .{ i + 1, test_fn_list.len, test_fn.name }); |
| 157 | 170 | } |
| 171 | // Track in a global variable so that `fuzzInput` can see it. |
| 158 | 172 | if (test_fn.func()) |_| { |
| 159 | 173 | ok_count += 1; |
| 160 | 174 | test_node.end(); |
| ... | ... | @@ -208,10 +222,11 @@ pub fn log( |
| 208 | 222 | comptime format: []const u8, |
| 209 | 223 | args: anytype, |
| 210 | 224 | ) void { |
| 225 | @disableInstrumentation(); |
| 211 | 226 | if (@intFromEnum(message_level) <= @intFromEnum(std.log.Level.err)) { |
| 212 | 227 | log_err_count +|= 1; |
| 213 | 228 | } |
| 214 | | if (@intFromEnum(message_level) <= @intFromEnum(std.testing.log_level)) { |
| 229 | if (@intFromEnum(message_level) <= @intFromEnum(testing.log_level)) { |
| 215 | 230 | std.debug.print( |
| 216 | 231 | "[" ++ @tagName(scope) ++ "] (" ++ @tagName(message_level) ++ "): " ++ format ++ "\n", |
| 217 | 232 | args, |
| ... | ... | @@ -222,6 +237,7 @@ pub fn log( |
| 222 | 237 | /// Simpler main(), exercising fewer language features, so that |
| 223 | 238 | /// work-in-progress backends can handle it. |
| 224 | 239 | pub fn mainSimple() anyerror!void { |
| 240 | @disableInstrumentation(); |
| 225 | 241 | // is the backend capable of printing to stderr? |
| 226 | 242 | const enable_print = switch (builtin.zig_backend) { |
| 227 | 243 | else => false, |
| ... | ... | @@ -266,3 +282,34 @@ pub fn mainSimple() anyerror!void { |
| 266 | 282 | } |
| 267 | 283 | if (failed != 0) std.process.exit(1); |
| 268 | 284 | } |
| 285 | |
| 286 | const FuzzerSlice = extern struct { |
| 287 | ptr: [*]const u8, |
| 288 | len: usize, |
| 289 | |
| 290 | inline fn toSlice(s: FuzzerSlice) []const u8 { |
| 291 | return s.ptr[0..s.len]; |
| 292 | } |
| 293 | }; |
| 294 | |
| 295 | var is_fuzz_test: bool = undefined; |
| 296 | |
| 297 | extern fn fuzzer_next() FuzzerSlice; |
| 298 | |
| 299 | pub fn fuzzInput(options: testing.FuzzInputOptions) []const u8 { |
| 300 | @disableInstrumentation(); |
| 301 | if (crippled) { |
| 302 | return ""; |
| 303 | } else if (builtin.fuzz) { |
| 304 | return fuzzer_next().toSlice(); |
| 305 | } else { |
| 306 | is_fuzz_test = true; |
| 307 | if (options.corpus.len == 0) { |
| 308 | return ""; |
| 309 | } else { |
| 310 | var prng = std.Random.DefaultPrng.init(testing.random_seed); |
| 311 | const random = prng.random(); |
| 312 | return options.corpus[random.uintLessThan(usize, options.corpus.len)]; |
| 313 | } |
| 314 | } |
| 315 | } |