Commit 6f3767862d
Changed files (4)
lib
lib/compiler/test_runner.zig
@@ -1,18 +1,26 @@
//! Default test runner for unit tests.
+const builtin = @import("builtin");
const std = @import("std");
const io = std.io;
-const builtin = @import("builtin");
+const testing = std.testing;
pub const std_options = .{
.logFn = log,
};
var log_err_count: usize = 0;
-var cmdline_buffer: [4096]u8 = undefined;
-var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer);
+var fba_buffer: [8192]u8 = undefined;
+var fba = std.heap.FixedBufferAllocator.init(&fba_buffer);
+
+const crippled = switch (builtin.zig_backend) {
+ .stage2_riscv64 => true,
+ else => false,
+};
pub fn main() void {
- if (builtin.zig_backend == .stage2_riscv64) {
+ @disableInstrumentation();
+
+ if (crippled) {
return mainSimple() catch @panic("test failure\n");
}
@@ -25,13 +33,15 @@ pub fn main() void {
if (std.mem.eql(u8, arg, "--listen=-")) {
listen = true;
} else if (std.mem.startsWith(u8, arg, "--seed=")) {
- std.testing.random_seed = std.fmt.parseUnsigned(u32, arg["--seed=".len..], 0) catch
+ testing.random_seed = std.fmt.parseUnsigned(u32, arg["--seed=".len..], 0) catch
@panic("unable to parse --seed command line argument");
} else {
@panic("unrecognized command line argument");
}
}
+ fba.reset();
+
if (listen) {
return mainServer() catch @panic("internal test runner failure");
} else {
@@ -40,6 +50,7 @@ pub fn main() void {
}
fn mainServer() !void {
+ @disableInstrumentation();
var server = try std.zig.Server.init(.{
.gpa = fba.allocator(),
.in = std.io.getStdIn(),
@@ -55,24 +66,24 @@ fn mainServer() !void {
return std.process.exit(0);
},
.query_test_metadata => {
- std.testing.allocator_instance = .{};
- defer if (std.testing.allocator_instance.deinit() == .leak) {
+ testing.allocator_instance = .{};
+ defer if (testing.allocator_instance.deinit() == .leak) {
@panic("internal test runner memory leak");
};
var string_bytes: std.ArrayListUnmanaged(u8) = .{};
- defer string_bytes.deinit(std.testing.allocator);
- try string_bytes.append(std.testing.allocator, 0); // Reserve 0 for null.
+ defer string_bytes.deinit(testing.allocator);
+ try string_bytes.append(testing.allocator, 0); // Reserve 0 for null.
const test_fns = builtin.test_functions;
- const names = try std.testing.allocator.alloc(u32, test_fns.len);
- defer std.testing.allocator.free(names);
- const expected_panic_msgs = try std.testing.allocator.alloc(u32, test_fns.len);
- defer std.testing.allocator.free(expected_panic_msgs);
+ const names = try testing.allocator.alloc(u32, test_fns.len);
+ defer testing.allocator.free(names);
+ const expected_panic_msgs = try testing.allocator.alloc(u32, test_fns.len);
+ defer testing.allocator.free(expected_panic_msgs);
for (test_fns, names, expected_panic_msgs) |test_fn, *name, *expected_panic_msg| {
name.* = @as(u32, @intCast(string_bytes.items.len));
- try string_bytes.ensureUnusedCapacity(std.testing.allocator, test_fn.name.len + 1);
+ try string_bytes.ensureUnusedCapacity(testing.allocator, test_fn.name.len + 1);
string_bytes.appendSliceAssumeCapacity(test_fn.name);
string_bytes.appendAssumeCapacity(0);
expected_panic_msg.* = 0;
@@ -86,13 +97,13 @@ fn mainServer() !void {
},
.run_test => {
- std.testing.allocator_instance = .{};
+ testing.allocator_instance = .{};
log_err_count = 0;
const index = try server.receiveBody_u32();
const test_fn = builtin.test_functions[index];
var fail = false;
var skip = false;
- var leak = false;
+ is_fuzz_test = false;
test_fn.func() catch |err| switch (err) {
error.SkipZigTest => skip = true,
else => {
@@ -102,13 +113,14 @@ fn mainServer() !void {
}
},
};
- leak = std.testing.allocator_instance.deinit() == .leak;
+ const leak = testing.allocator_instance.deinit() == .leak;
try server.serveTestResults(.{
.index = index,
.flags = .{
.fail = fail,
.skip = skip,
.leak = leak,
+ .fuzz = is_fuzz_test,
.log_err_count = std.math.lossyCast(
@TypeOf(@as(std.zig.Server.Message.TestResults.Flags, undefined).log_err_count),
log_err_count,
@@ -118,7 +130,7 @@ fn mainServer() !void {
},
else => {
- std.debug.print("unsupported message: {x}", .{@intFromEnum(hdr.tag)});
+ std.debug.print("unsupported message: {x}\n", .{@intFromEnum(hdr.tag)});
std.process.exit(1);
},
}
@@ -126,6 +138,7 @@ fn mainServer() !void {
}
fn mainTerminal() void {
+ @disableInstrumentation();
const test_fn_list = builtin.test_functions;
var ok_count: usize = 0;
var skip_count: usize = 0;
@@ -143,18 +156,19 @@ fn mainTerminal() void {
var leaks: usize = 0;
for (test_fn_list, 0..) |test_fn, i| {
- std.testing.allocator_instance = .{};
+ testing.allocator_instance = .{};
defer {
- if (std.testing.allocator_instance.deinit() == .leak) {
+ if (testing.allocator_instance.deinit() == .leak) {
leaks += 1;
}
}
- std.testing.log_level = .warn;
+ testing.log_level = .warn;
const test_node = root_node.start(test_fn.name, 0);
if (!have_tty) {
std.debug.print("{d}/{d} {s}...", .{ i + 1, test_fn_list.len, test_fn.name });
}
+ // Track in a global variable so that `fuzzInput` can see it.
if (test_fn.func()) |_| {
ok_count += 1;
test_node.end();
@@ -208,10 +222,11 @@ pub fn log(
comptime format: []const u8,
args: anytype,
) void {
+ @disableInstrumentation();
if (@intFromEnum(message_level) <= @intFromEnum(std.log.Level.err)) {
log_err_count +|= 1;
}
- if (@intFromEnum(message_level) <= @intFromEnum(std.testing.log_level)) {
+ if (@intFromEnum(message_level) <= @intFromEnum(testing.log_level)) {
std.debug.print(
"[" ++ @tagName(scope) ++ "] (" ++ @tagName(message_level) ++ "): " ++ format ++ "\n",
args,
@@ -222,6 +237,7 @@ pub fn log(
/// Simpler main(), exercising fewer language features, so that
/// work-in-progress backends can handle it.
pub fn mainSimple() anyerror!void {
+ @disableInstrumentation();
// is the backend capable of printing to stderr?
const enable_print = switch (builtin.zig_backend) {
else => false,
@@ -266,3 +282,34 @@ pub fn mainSimple() anyerror!void {
}
if (failed != 0) std.process.exit(1);
}
+
+const FuzzerSlice = extern struct {
+ ptr: [*]const u8,
+ len: usize,
+
+ inline fn toSlice(s: FuzzerSlice) []const u8 {
+ return s.ptr[0..s.len];
+ }
+};
+
+var is_fuzz_test: bool = undefined;
+
+extern fn fuzzer_next() FuzzerSlice;
+
+pub fn fuzzInput(options: testing.FuzzInputOptions) []const u8 {
+ @disableInstrumentation();
+ if (crippled) {
+ return "";
+ } else if (builtin.fuzz) {
+ return fuzzer_next().toSlice();
+ } else {
+ is_fuzz_test = true;
+ if (options.corpus.len == 0) {
+ return "";
+ } else {
+ var prng = std.Random.DefaultPrng.init(testing.random_seed);
+ const random = prng.random();
+ return options.corpus[random.uintLessThan(usize, options.corpus.len)];
+ }
+ }
+}
lib/std/zig/Server.zig
@@ -53,7 +53,7 @@ pub const Message = struct {
/// - null-terminated string_bytes index
/// * expected_panic_msg: [tests_len]u32,
/// - null-terminated string_bytes index
- /// - 0 means does not expect pani
+ /// - 0 means does not expect panic
/// * string_bytes: [string_bytes_len]u8,
pub const TestMetadata = extern struct {
string_bytes_len: u32,
@@ -68,7 +68,8 @@ pub const Message = struct {
fail: bool,
skip: bool,
leak: bool,
- log_err_count: u29 = 0,
+ fuzz: bool,
+ log_err_count: u28 = 0,
};
};
lib/std/testing.zig
@@ -1137,32 +1137,10 @@ pub fn refAllDeclsRecursive(comptime T: type) void {
}
}
-const FuzzerSlice = extern struct {
- ptr: [*]const u8,
- len: usize,
-
- fn toSlice(s: FuzzerSlice) []const u8 {
- return s.ptr[0..s.len];
- }
-};
-
-extern fn fuzzer_next() FuzzerSlice;
-
pub const FuzzInputOptions = struct {
corpus: []const []const u8 = &.{},
};
-pub fn fuzzInput(options: FuzzInputOptions) []const u8 {
- @disableInstrumentation();
- if (builtin.fuzz) {
- return fuzzer_next().toSlice();
- } else {
- if (options.corpus.len == 0) {
- return "";
- } else {
- var prng = std.Random.DefaultPrng.init(std.testing.random_seed);
- const random = prng.random();
- return options.corpus[random.uintLessThan(usize, options.corpus.len)];
- }
- }
+pub inline fn fuzzInput(options: FuzzInputOptions) []const u8 {
+ return @import("root").fuzzInput(options);
}
lib/fuzzer.zig
@@ -1,13 +1,14 @@
const std = @import("std");
+const Allocator = std.mem.Allocator;
export threadlocal var __sancov_lowest_stack: usize = 0;
export fn __sanitizer_cov_8bit_counters_init(start: [*]u8, stop: [*]u8) void {
- std.debug.print("__sanitizer_cov_8bit_counters_init start={*}, stop={*}\n", .{ start, stop });
+ std.log.debug("__sanitizer_cov_8bit_counters_init start={*}, stop={*}", .{ start, stop });
}
export fn __sanitizer_cov_pcs_init(pcs_beg: [*]const usize, pcs_end: [*]const usize) void {
- std.debug.print("__sanitizer_cov_pcs_init pcs_beg={*}, pcs_end={*}\n", .{ pcs_beg, pcs_end });
+ std.log.debug("__sanitizer_cov_pcs_init pcs_beg={*}, pcs_end={*}", .{ pcs_beg, pcs_end });
}
export fn __sanitizer_cov_trace_const_cmp1(arg1: u8, arg2: u8) void {
@@ -47,16 +48,61 @@ export fn __sanitizer_cov_trace_switch(val: u64, cases_ptr: [*]u64) void {
const len = cases_ptr[0];
const val_size_in_bits = cases_ptr[1];
const cases = cases_ptr[2..][0..len];
- std.debug.print("0x{x}: switch on value {d} ({d} bits) with {d} cases\n", .{
+ std.log.debug("0x{x}: switch on value {d} ({d} bits) with {d} cases", .{
pc, val, val_size_in_bits, cases.len,
});
}
export fn __sanitizer_cov_trace_pc_indir(callee: usize) void {
const pc = @returnAddress();
- std.debug.print("0x{x}: indirect call to 0x{x}\n", .{ pc, callee });
+ std.log.debug("0x{x}: indirect call to 0x{x}", .{ pc, callee });
}
fn handleCmp(pc: usize, arg1: u64, arg2: u64) void {
- std.debug.print("0x{x}: comparison of {d} and {d}\n", .{ pc, arg1, arg2 });
+ std.log.debug("0x{x}: comparison of {d} and {d}", .{ pc, arg1, arg2 });
+}
+
+const Fuzzer = struct {
+ gpa: Allocator,
+ rng: std.Random.DefaultPrng,
+ input: std.ArrayListUnmanaged(u8),
+
+ const Slice = extern struct {
+ ptr: [*]const u8,
+ len: usize,
+
+ fn toSlice(s: Slice) []const u8 {
+ return s.ptr[0..s.len];
+ }
+
+ fn fromSlice(s: []const u8) Slice {
+ return .{
+ .ptr = s.ptr,
+ .len = s.len,
+ };
+ }
+ };
+
+ fn next(f: *Fuzzer) ![]const u8 {
+ const gpa = f.gpa;
+ const rng = fuzzer.rng.random();
+ const len = rng.uintLessThan(usize, 64);
+ try f.input.resize(gpa, len);
+ rng.bytes(f.input.items);
+ return f.input.items;
+ }
+};
+
+var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .{};
+
+var fuzzer: Fuzzer = .{
+ .gpa = general_purpose_allocator.allocator(),
+ .rng = std.Random.DefaultPrng.init(0),
+ .input = .{},
+};
+
+export fn fuzzer_next() Fuzzer.Slice {
+ return Fuzzer.Slice.fromSlice(fuzzer.next() catch |err| switch (err) {
+ error.OutOfMemory => @panic("out of memory"),
+ });
}