Skip to content

Commit ede5dcf

Browse files
committed
make the build runner and test runner talk to each other
std.Build.addTest creates a CompileStep as before, however, this kind of step no longer actually runs the unit tests. Instead it only compiles it, and one must additionally create a RunStep from the CompileStep in order to actually run the tests. RunStep gains integration with the default test runner, which now supports the standard --listen=- argument in order to communicate over stdin and stdout. It also reports test statistics; how many passed, failed, and leaked, as well as directly associating the relevant stderr with the particular test name that failed. This separation of CompileStep and RunStep means that `CompileStep.Kind.test_exe` is no longer needed, and therefore has been removed in this commit. * build runner: show unit test statistics in build summary * added Step.writeManifest since many steps want to treat it as a warning and emit the same message if it fails. * RunStep: fixed error message that prints the failed command printing the original argv and not the adjusted argv in case an interpreter was used. * RunStep: fixed not passing the command line arguments to the interpreter. * move src/Server.zig to std.zig.Server so that the default test runner can use it. * the simpler test runner function which is used by work-in-progress backends now no longer prints to stderr, which is necessary in order for the build runner to not print the stderr as a warning message.
1 parent ef5f8bd commit ede5dcf

File tree

30 files changed

+780
-373
lines changed

30 files changed

+780
-373
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ set(ZIG_STAGE2_SOURCES
518518
"${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig"
519519
"${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig"
520520
"${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig"
521+
"${CMAKE_SOURCE_DIR}/lib/std/zig/Server.zig"
521522
"${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig"
522523
"${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig"
523524
"${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig"
@@ -623,7 +624,6 @@ set(ZIG_STAGE2_SOURCES
623624
"${CMAKE_SOURCE_DIR}/src/print_targets.zig"
624625
"${CMAKE_SOURCE_DIR}/src/print_zir.zig"
625626
"${CMAKE_SOURCE_DIR}/src/register_manager.zig"
626-
"${CMAKE_SOURCE_DIR}/src/Server.zig"
627627
"${CMAKE_SOURCE_DIR}/src/target.zig"
628628
"${CMAKE_SOURCE_DIR}/src/tracy.zig"
629629
"${CMAKE_SOURCE_DIR}/src/translate_c.zig"

lib/build_runner.zig

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,12 @@ fn runStepNames(
416416
}
417417
assert(run.memory_blocked_steps.items.len == 0);
418418

419+
var test_skip_count: usize = 0;
420+
var test_fail_count: usize = 0;
421+
var test_pass_count: usize = 0;
422+
var test_leak_count: usize = 0;
423+
var test_count: usize = 0;
424+
419425
var success_count: usize = 0;
420426
var skipped_count: usize = 0;
421427
var failure_count: usize = 0;
@@ -425,6 +431,12 @@ fn runStepNames(
425431
defer compile_error_steps.deinit(gpa);
426432

427433
for (step_stack.keys()) |s| {
434+
test_fail_count += s.test_results.fail_count;
435+
test_skip_count += s.test_results.skip_count;
436+
test_leak_count += s.test_results.leak_count;
437+
test_pass_count += s.test_results.passCount();
438+
test_count += s.test_results.test_count;
439+
428440
switch (s.state) {
429441
.precheck_unstarted => unreachable,
430442
.precheck_started => unreachable,
@@ -468,6 +480,11 @@ fn runStepNames(
468480
if (skipped_count > 0) stderr.writer().print("; {d} skipped", .{skipped_count}) catch {};
469481
if (failure_count > 0) stderr.writer().print("; {d} failed", .{failure_count}) catch {};
470482

483+
if (test_count > 0) stderr.writer().print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {};
484+
if (test_skip_count > 0) stderr.writer().print("; {d} skipped", .{test_skip_count}) catch {};
485+
if (test_fail_count > 0) stderr.writer().print("; {d} failed", .{test_fail_count}) catch {};
486+
if (test_leak_count > 0) stderr.writer().print("; {d} leaked", .{test_leak_count}) catch {};
487+
471488
if (run.enable_summary == null) {
472489
ttyconf.setColor(stderr, .Dim) catch {};
473490
stderr.writeAll(" (disable with -fno-summary)") catch {};
@@ -566,6 +583,13 @@ fn printTreeStep(
566583
try ttyconf.setColor(stderr, .Green);
567584
if (s.result_cached) {
568585
try stderr.writeAll(" cached");
586+
} else if (s.test_results.test_count > 0) {
587+
const pass_count = s.test_results.passCount();
588+
try stderr.writer().print(" {d} passed", .{pass_count});
589+
if (s.test_results.skip_count > 0) {
590+
try ttyconf.setColor(stderr, .Yellow);
591+
try stderr.writer().print(" {d} skipped", .{s.test_results.skip_count});
592+
}
569593
} else {
570594
try stderr.writeAll(" success");
571595
}
@@ -609,15 +633,46 @@ fn printTreeStep(
609633
},
610634

611635
.failure => {
612-
try ttyconf.setColor(stderr, .Red);
613636
if (s.result_error_bundle.errorMessageCount() > 0) {
637+
try ttyconf.setColor(stderr, .Red);
614638
try stderr.writer().print(" {d} errors\n", .{
615639
s.result_error_bundle.errorMessageCount(),
616640
});
641+
try ttyconf.setColor(stderr, .Reset);
642+
} else if (!s.test_results.isSuccess()) {
643+
try stderr.writer().print(" {d}/{d} passed", .{
644+
s.test_results.passCount(), s.test_results.test_count,
645+
});
646+
if (s.test_results.fail_count > 0) {
647+
try stderr.writeAll(", ");
648+
try ttyconf.setColor(stderr, .Red);
649+
try stderr.writer().print("{d} failed", .{
650+
s.test_results.fail_count,
651+
});
652+
try ttyconf.setColor(stderr, .Reset);
653+
}
654+
if (s.test_results.skip_count > 0) {
655+
try stderr.writeAll(", ");
656+
try ttyconf.setColor(stderr, .Yellow);
657+
try stderr.writer().print("{d} skipped", .{
658+
s.test_results.skip_count,
659+
});
660+
try ttyconf.setColor(stderr, .Reset);
661+
}
662+
if (s.test_results.leak_count > 0) {
663+
try stderr.writeAll(", ");
664+
try ttyconf.setColor(stderr, .Red);
665+
try stderr.writer().print("{d} leaked", .{
666+
s.test_results.leak_count,
667+
});
668+
try ttyconf.setColor(stderr, .Reset);
669+
}
670+
try stderr.writeAll("\n");
617671
} else {
672+
try ttyconf.setColor(stderr, .Red);
618673
try stderr.writeAll(" failure\n");
674+
try ttyconf.setColor(stderr, .Reset);
619675
}
620-
try ttyconf.setColor(stderr, .Reset);
621676
},
622677
}
623678

lib/std/Build.zig

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,6 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep {
531531

532532
pub const TestOptions = struct {
533533
name: []const u8 = "test",
534-
kind: CompileStep.Kind = .@"test",
535534
root_source_file: FileSource,
536535
target: CrossTarget = .{},
537536
optimize: std.builtin.Mode = .Debug,
@@ -542,7 +541,7 @@ pub const TestOptions = struct {
542541
pub fn addTest(b: *Build, options: TestOptions) *CompileStep {
543542
return CompileStep.create(b, .{
544543
.name = options.name,
545-
.kind = options.kind,
544+
.kind = .@"test",
546545
.root_source_file = options.root_source_file,
547546
.target = options.target,
548547
.optimize = options.optimize,
@@ -626,16 +625,15 @@ pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep {
626625
/// Creates a `RunStep` with an executable built with `addExecutable`.
627626
/// Add command line arguments with methods of `RunStep`.
628627
pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep {
629-
assert(exe.kind == .exe or exe.kind == .test_exe);
630-
631628
// It doesn't have to be native. We catch that if you actually try to run it.
632629
// Consider that this is declarative; the run step may not be run unless a user
633630
// option is supplied.
634631
const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.name}));
635632
run_step.addArtifactArg(exe);
636633

637-
if (exe.kind == .test_exe) {
638-
run_step.addArg(b.zig_exe);
634+
if (exe.kind == .@"test") {
635+
run_step.stdio = .zig_test;
636+
run_step.addArgs(&.{"--listen=-"});
639637
}
640638

641639
if (exe.vcpkg_bin_path) |path| {

lib/std/Build/CompileStep.zig

Lines changed: 7 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,6 @@ pub const Kind = enum {
289289
lib,
290290
obj,
291291
@"test",
292-
test_exe,
293292
};
294293

295294
pub const Linkage = enum { dynamic, static };
@@ -328,7 +327,7 @@ pub fn create(owner: *std.Build, options: Options) *CompileStep {
328327
.exe => "zig build-exe",
329328
.lib => "zig build-lib",
330329
.obj => "zig build-obj",
331-
.test_exe, .@"test" => "zig test",
330+
.@"test" => "zig test",
332331
},
333332
name_adjusted,
334333
@tagName(options.optimize),
@@ -410,7 +409,7 @@ fn computeOutFileNames(self: *CompileStep) void {
410409
.output_mode = switch (self.kind) {
411410
.lib => .Lib,
412411
.obj => .Obj,
413-
.exe, .@"test", .test_exe => .Exe,
412+
.exe, .@"test" => .Exe,
414413
},
415414
.link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
416415
.dynamic => .Dynamic,
@@ -621,7 +620,7 @@ pub fn producesPdbFile(self: *CompileStep) bool {
621620
if (!self.target.isWindows() and !self.target.isUefi()) return false;
622621
if (self.target.getObjectFormat() == .c) return false;
623622
if (self.strip == true) return false;
624-
return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;
623+
return self.isDynamicLibrary() or self.kind == .exe or self.kind == .@"test";
625624
}
626625

627626
pub fn linkLibC(self: *CompileStep) void {
@@ -850,19 +849,19 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
850849

851850
pub fn setNamePrefix(self: *CompileStep, text: []const u8) void {
852851
const b = self.step.owner;
853-
assert(self.kind == .@"test" or self.kind == .test_exe);
852+
assert(self.kind == .@"test");
854853
self.name_prefix = b.dupe(text);
855854
}
856855

857856
pub fn setFilter(self: *CompileStep, text: ?[]const u8) void {
858857
const b = self.step.owner;
859-
assert(self.kind == .@"test" or self.kind == .test_exe);
858+
assert(self.kind == .@"test");
860859
self.filter = if (text) |t| b.dupe(t) else null;
861860
}
862861

863862
pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void {
864863
const b = self.step.owner;
865-
assert(self.kind == .@"test" or self.kind == .test_exe);
864+
assert(self.kind == .@"test");
866865
self.test_runner = if (path) |p| b.dupePath(p) else null;
867866
}
868867

@@ -938,7 +937,7 @@ pub fn getOutputLibSource(self: *CompileStep) FileSource {
938937
/// Returns the generated header file.
939938
/// This function can only be called for libraries or object files which have `emit_h` set.
940939
pub fn getOutputHSource(self: *CompileStep) FileSource {
941-
assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");
940+
assert(self.kind != .exe and self.kind != .@"test");
942941
assert(self.emit_h);
943942
return .{ .generated = &self.output_h_path_source };
944943
}
@@ -1243,7 +1242,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
12431242
.exe => "build-exe",
12441243
.obj => "build-obj",
12451244
.@"test" => "test",
1246-
.test_exe => "test",
12471245
};
12481246
try zig_args.append(cmd);
12491247

@@ -1293,7 +1291,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
12931291

12941292
.other_step => |other| switch (other.kind) {
12951293
.exe => @panic("Cannot link with an executable build artifact"),
1296-
.test_exe => @panic("Cannot link with an executable build artifact"),
12971294
.@"test" => @panic("Cannot link with a test"),
12981295
.obj => {
12991296
try zig_args.append(other.getOutputSource().getPath(b));
@@ -1661,83 +1658,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
16611658
try zig_args.append("--test-cmd-bin");
16621659
}
16631660
}
1664-
} else {
1665-
const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
1666-
1667-
switch (b.host.getExternalExecutor(self.target_info, .{
1668-
.qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null,
1669-
.link_libc = transitive_deps.is_linking_libc,
1670-
})) {
1671-
.native => {},
1672-
.bad_dl, .bad_os_or_cpu => {
1673-
try zig_args.append("--test-no-exec");
1674-
},
1675-
.rosetta => if (b.enable_rosetta) {
1676-
try zig_args.append("--test-cmd-bin");
1677-
} else {
1678-
try zig_args.append("--test-no-exec");
1679-
},
1680-
.qemu => |bin_name| ok: {
1681-
if (b.enable_qemu) qemu: {
1682-
const glibc_dir_arg = if (need_cross_glibc)
1683-
b.glibc_runtimes_dir orelse break :qemu
1684-
else
1685-
null;
1686-
try zig_args.append("--test-cmd");
1687-
try zig_args.append(bin_name);
1688-
if (glibc_dir_arg) |dir| {
1689-
// TODO look into making this a call to `linuxTriple`. This
1690-
// needs the directory to be called "i686" rather than
1691-
// "x86" which is why we do it manually here.
1692-
const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
1693-
const cpu_arch = self.target.getCpuArch();
1694-
const os_tag = self.target.getOsTag();
1695-
const abi = self.target.getAbi();
1696-
const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
1697-
"i686"
1698-
else
1699-
@tagName(cpu_arch);
1700-
const full_dir = try std.fmt.allocPrint(b.allocator, fmt_str, .{
1701-
dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
1702-
});
1703-
1704-
try zig_args.append("--test-cmd");
1705-
try zig_args.append("-L");
1706-
try zig_args.append("--test-cmd");
1707-
try zig_args.append(full_dir);
1708-
}
1709-
try zig_args.append("--test-cmd-bin");
1710-
break :ok;
1711-
}
1712-
try zig_args.append("--test-no-exec");
1713-
},
1714-
.wine => |bin_name| if (b.enable_wine) {
1715-
try zig_args.append("--test-cmd");
1716-
try zig_args.append(bin_name);
1717-
try zig_args.append("--test-cmd-bin");
1718-
} else {
1719-
try zig_args.append("--test-no-exec");
1720-
},
1721-
.wasmtime => |bin_name| if (b.enable_wasmtime) {
1722-
try zig_args.append("--test-cmd");
1723-
try zig_args.append(bin_name);
1724-
try zig_args.append("--test-cmd");
1725-
try zig_args.append("--dir=.");
1726-
try zig_args.append("--test-cmd-bin");
1727-
} else {
1728-
try zig_args.append("--test-no-exec");
1729-
},
1730-
.darling => |bin_name| if (b.enable_darling) {
1731-
try zig_args.append("--test-cmd");
1732-
try zig_args.append(bin_name);
1733-
try zig_args.append("--test-cmd-bin");
1734-
} else {
1735-
try zig_args.append("--test-no-exec");
1736-
},
1737-
}
17381661
}
1739-
} else if (self.kind == .test_exe) {
1740-
try zig_args.append("--test-no-exec");
17411662
}
17421663

17431664
try self.appendModuleArgs(&zig_args);

lib/std/Build/InstallArtifactStep.zig

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,11 @@ pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep {
3232
.artifact = artifact,
3333
.dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
3434
.obj => @panic("Cannot install a .obj build artifact."),
35-
.@"test" => @panic("Cannot install a .test build artifact, use .test_exe instead."),
36-
.exe, .test_exe => InstallDir{ .bin = {} },
35+
.exe, .@"test" => InstallDir{ .bin = {} },
3736
.lib => InstallDir{ .lib = {} },
3837
},
3938
.pdb_dir = if (artifact.producesPdbFile()) blk: {
40-
if (artifact.kind == .exe or artifact.kind == .test_exe) {
39+
if (artifact.kind == .exe or artifact.kind == .@"test") {
4140
break :blk InstallDir{ .bin = {} };
4241
} else {
4342
break :blk InstallDir{ .lib = {} };

0 commit comments

Comments
 (0)