| author | |
| committer | |
| log | 8082323dfd65c20328991d9a0d6740b779b26670 |
| tree | e689d430ba34ad788499fa85dff7ad4552dcc77e |
| parent | 5f6f38ff3160c75c03ce0477904051e6d8af0c80 |
| parent | a1b123bccbc2ae50442fcc1544b0da0595401038 |
| signature |
Custom test runner7 files changed, 100 insertions(+), 6 deletions(-)
lib/std/build.zig+12| ... | ... | @@ -1509,6 +1509,7 @@ pub const LibExeObjStep = struct { |
| 1509 | 1509 | name_prefix: []const u8, |
| 1510 | 1510 | filter: ?[]const u8, |
| 1511 | 1511 | test_evented_io: bool = false, |
| 1512 | test_runner: ?[]const u8, | |
| 1512 | 1513 | code_model: std.builtin.CodeModel = .default, |
| 1513 | 1514 | wasi_exec_model: ?std.builtin.WasiExecModel = null, |
| 1514 | 1515 | /// Symbols to be exported when compiling to wasm |
| ... | ... | @@ -1771,6 +1772,7 @@ pub const LibExeObjStep = struct { |
| 1771 | 1772 | .exec_cmd_args = null, |
| 1772 | 1773 | .name_prefix = "", |
| 1773 | 1774 | .filter = null, |
| 1775 | .test_runner = null, | |
| 1774 | 1776 | .disable_stack_probing = false, |
| 1775 | 1777 | .disable_sanitize_c = false, |
| 1776 | 1778 | .sanitize_thread = false, |
| ... | ... | @@ -2204,6 +2206,11 @@ pub const LibExeObjStep = struct { |
| 2204 | 2206 | self.filter = if (text) |t| self.builder.dupe(t) else null; |
| 2205 | 2207 | } |
| 2206 | 2208 | |
| 2209 | pub fn setTestRunner(self: *LibExeObjStep, path: ?[]const u8) void { | |
| 2210 | assert(self.kind == .@"test" or self.kind == .test_exe); | |
| 2211 | self.test_runner = if (path) |p| self.builder.dupePath(p) else null; | |
| 2212 | } | |
| 2213 | ||
| 2207 | 2214 | /// Handy when you have many C/C++ source files and want them all to have the same flags. |
| 2208 | 2215 | pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: []const []const u8) void { |
| 2209 | 2216 | const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable; |
| ... | ... | @@ -2669,6 +2676,11 @@ pub const LibExeObjStep = struct { |
| 2669 | 2676 | try zig_args.append(self.name_prefix); |
| 2670 | 2677 | } |
| 2671 | 2678 | |
| 2679 | if (self.test_runner) |test_runner| { | |
| 2680 | try zig_args.append("--test-runner"); | |
| 2681 | try zig_args.append(builder.pathFromRoot(test_runner)); | |
| 2682 | } | |
| 2683 | ||
| 2672 | 2684 | for (builder.debug_log_scopes) |log_scope| { |
| 2673 | 2685 | try zig_args.append("--debug-log"); |
| 2674 | 2686 | try zig_args.append(log_scope); |
src/Compilation.zig+10-6| ... | ... | @@ -994,6 +994,7 @@ pub const InitOptions = struct { |
| 994 | 994 | reference_trace: ?u32 = null, |
| 995 | 995 | test_filter: ?[]const u8 = null, |
| 996 | 996 | test_name_prefix: ?[]const u8 = null, |
| 997 | test_runner_path: ?[]const u8 = null, | |
| 997 | 998 | subsystem: ?std.Target.SubSystem = null, |
| 998 | 999 | /// WASI-only. Type of WASI execution model ("command" or "reactor"). |
| 999 | 1000 | wasi_exec_model: ?std.builtin.WasiExecModel = null, |
| ... | ... | @@ -1581,12 +1582,15 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1581 | 1582 | errdefer std_pkg.destroy(gpa); |
| 1582 | 1583 | |
| 1583 | 1584 | const root_pkg = if (options.is_test) root_pkg: { |
| 1584 | const test_pkg = try Package.createWithDir( | |
| 1585 | gpa, | |
| 1586 | options.zig_lib_directory, | |
| 1587 | null, | |
| 1588 | "test_runner.zig", | |
| 1589 | ); | |
| 1585 | const test_pkg = if (options.test_runner_path) |test_runner| | |
| 1586 | try Package.create(gpa, null, test_runner) | |
| 1587 | else | |
| 1588 | try Package.createWithDir( | |
| 1589 | gpa, | |
| 1590 | options.zig_lib_directory, | |
| 1591 | null, | |
| 1592 | "test_runner.zig", | |
| 1593 | ); | |
| 1590 | 1594 | errdefer test_pkg.destroy(gpa); |
| 1591 | 1595 | |
| 1592 | 1596 | break :root_pkg test_pkg; |
src/main.zig+5| ... | ... | @@ -503,6 +503,7 @@ const usage_build_generic = |
| 503 | 503 | \\ --test-cmd-bin Appends test binary path to test cmd args |
| 504 | 504 | \\ --test-evented-io Runs the test in evented I/O mode |
| 505 | 505 | \\ --test-no-exec Compiles test binary without running it |
| 506 | \\ --test-runner [path] Specify a custom test runner | |
| 506 | 507 | \\ |
| 507 | 508 | \\Debug Options (Zig Compiler Development): |
| 508 | 509 | \\ -ftime-report Print timing diagnostics |
| ... | ... | @@ -726,6 +727,7 @@ fn buildOutputType( |
| 726 | 727 | var runtime_args_start: ?usize = null; |
| 727 | 728 | var test_filter: ?[]const u8 = null; |
| 728 | 729 | var test_name_prefix: ?[]const u8 = null; |
| 730 | var test_runner_path: ?[]const u8 = null; | |
| 729 | 731 | var override_local_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LOCAL_CACHE_DIR"); |
| 730 | 732 | var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR"); |
| 731 | 733 | var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR"); |
| ... | ... | @@ -1043,6 +1045,8 @@ fn buildOutputType( |
| 1043 | 1045 | test_filter = args_iter.nextOrFatal(); |
| 1044 | 1046 | } else if (mem.eql(u8, arg, "--test-name-prefix")) { |
| 1045 | 1047 | test_name_prefix = args_iter.nextOrFatal(); |
| 1048 | } else if (mem.eql(u8, arg, "--test-runner")) { | |
| 1049 | test_runner_path = args_iter.nextOrFatal(); | |
| 1046 | 1050 | } else if (mem.eql(u8, arg, "--test-cmd")) { |
| 1047 | 1051 | try test_exec_args.append(args_iter.nextOrFatal()); |
| 1048 | 1052 | } else if (mem.eql(u8, arg, "--cache-dir")) { |
| ... | ... | @@ -2943,6 +2947,7 @@ fn buildOutputType( |
| 2943 | 2947 | .test_evented_io = test_evented_io, |
| 2944 | 2948 | .test_filter = test_filter, |
| 2945 | 2949 | .test_name_prefix = test_name_prefix, |
| 2950 | .test_runner_path = test_runner_path, | |
| 2946 | 2951 | .disable_lld_caching = !have_enable_cache, |
| 2947 | 2952 | .subsystem = subsystem, |
| 2948 | 2953 | .wasi_exec_model = wasi_exec_model, |
test/standalone.zig+1| ... | ... | @@ -15,6 +15,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 15 | 15 | cases.add("test/standalone/main_return_error/error_u8_non_zero.zig"); |
| 16 | 16 | cases.add("test/standalone/noreturn_call/inline.zig"); |
| 17 | 17 | cases.add("test/standalone/noreturn_call/as_arg.zig"); |
| 18 | cases.addBuildFile("test/standalone/test_runner_path/build.zig", .{ .requires_stage2 = true }); | |
| 18 | 19 | cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{}); |
| 19 | 20 | cases.addBuildFile("test/standalone/shared_library/build.zig", .{}); |
| 20 | 21 | cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{}); |
test/standalone/test_runner_path/build.zig created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 2 | ||
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const test_exe = b.addTestExe("test", "test.zig"); | |
| 5 | test_exe.test_runner = "test_runner.zig"; | |
| 6 | ||
| 7 | const test_run = test_exe.run(); | |
| 8 | ||
| 9 | const test_step = b.step("test", "Test the program"); | |
| 10 | test_step.dependOn(&test_run.step); | |
| 11 | } |
test/standalone/test_runner_path/test.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | test "test runner path pass" {} | |
| 2 | ||
| 3 | test "test runner path fail" { | |
| 4 | return error.Fail; | |
| 5 | } | |
| 6 | ||
| 7 | test "test runner path skip" { | |
| 8 | return error.SkipZigTest; | |
| 9 | } |
test/standalone/test_runner_path/test_runner.zig created+52| ... | ... | @@ -0,0 +1,52 @@ |
| 1 | const std = @import("std"); | |
| 2 | const io = std.io; | |
| 3 | const builtin = @import("builtin"); | |
| 4 | ||
| 5 | pub const io_mode: io.Mode = builtin.test_io_mode; | |
| 6 | ||
| 7 | pub fn main() void { | |
| 8 | const test_fn_list = builtin.test_functions; | |
| 9 | var ok_count: usize = 0; | |
| 10 | var skip_count: usize = 0; | |
| 11 | var fail_count: usize = 0; | |
| 12 | ||
| 13 | var async_frame_buffer: []align(std.Target.stack_align) u8 = undefined; | |
| 14 | // TODO this is on the next line (using `undefined` above) because otherwise zig incorrectly | |
| 15 | // ignores the alignment of the slice. | |
| 16 | async_frame_buffer = &[_]u8{}; | |
| 17 | ||
| 18 | for (test_fn_list) |test_fn| { | |
| 19 | const result = if (test_fn.async_frame_size) |size| switch (io_mode) { | |
| 20 | .evented => blk: { | |
| 21 | if (async_frame_buffer.len < size) { | |
| 22 | std.heap.page_allocator.free(async_frame_buffer); | |
| 23 | async_frame_buffer = std.heap.page_allocator.alignedAlloc(u8, std.Target.stack_align, size) catch @panic("out of memory"); | |
| 24 | } | |
| 25 | const casted_fn = @ptrCast(fn () callconv(.Async) anyerror!void, test_fn.func); | |
| 26 | break :blk await @asyncCall(async_frame_buffer, {}, casted_fn, .{}); | |
| 27 | }, | |
| 28 | .blocking => { | |
| 29 | skip_count += 1; | |
| 30 | continue; | |
| 31 | }, | |
| 32 | } else test_fn.func(); | |
| 33 | if (result) |_| { | |
| 34 | ok_count += 1; | |
| 35 | } else |err| switch (err) { | |
| 36 | error.SkipZigTest => { | |
| 37 | skip_count += 1; | |
| 38 | }, | |
| 39 | else => { | |
| 40 | fail_count += 1; | |
| 41 | }, | |
| 42 | } | |
| 43 | } | |
| 44 | if (ok_count == test_fn_list.len) { | |
| 45 | std.debug.print("All {d} tests passed.\n", .{ok_count}); | |
| 46 | } else { | |
| 47 | std.debug.print("{d} passed; {d} skipped; {d} failed.\n", .{ ok_count, skip_count, fail_count }); | |
| 48 | } | |
| 49 | if (ok_count != 1 or skip_count != 1 or fail_count != 1) { | |
| 50 | std.process.exit(1); | |
| 51 | } | |
| 52 | } |