Skip to content

Commit 9b3b7aa

Browse files
rpkakandrewrk
authored andcommitted
Integrate libc-test cases into the build system
zig build test-libc -Dlibc-test-path=/path/to/libc-test
1 parent 4fb0898 commit 9b3b7aa

File tree

4 files changed

+457
-0
lines changed

4 files changed

+457
-0
lines changed

build.zig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,12 @@ pub fn build(b: *std.Build) !void {
632632
const test_incremental_step = b.step("test-incremental", "Run the incremental compilation test cases");
633633
try tests.addIncrementalTests(b, test_incremental_step);
634634
test_step.dependOn(test_incremental_step);
635+
636+
if (tests.addLibcTests(b, .{
637+
.optimize_modes = optimization_modes,
638+
.test_filters = test_filters,
639+
.test_target_filters = test_target_filters,
640+
})) |test_libc_step| test_step.dependOn(test_libc_step);
635641
}
636642

637643
fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {

test/libc.zig

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
pub fn addCases(cases: *tests.LibcContext) void {
2+
cases.addLibcTestCase("api/main.c", true, .{});
3+
4+
cases.addLibcTestCase("functional/argv.c", true, .{});
5+
cases.addLibcTestCase("functional/basename.c", true, .{});
6+
cases.addLibcTestCase("functional/clocale_mbfuncs.c", true, .{});
7+
cases.addLibcTestCase("functional/clock_gettime.c", true, .{});
8+
cases.addLibcTestCase("functional/crypt.c", true, .{});
9+
cases.addLibcTestCase("functional/dirname.c", true, .{});
10+
cases.addLibcTestCase("functional/env.c", true, .{});
11+
cases.addLibcTestCase("functional/fcntl.c", false, .{});
12+
cases.addLibcTestCase("functional/fdopen.c", false, .{});
13+
cases.addLibcTestCase("functional/fnmatch.c", true, .{});
14+
cases.addLibcTestCase("functional/fscanf.c", false, .{});
15+
cases.addLibcTestCase("functional/fwscanf.c", false, .{});
16+
cases.addLibcTestCase("functional/iconv_open.c", true, .{});
17+
cases.addLibcTestCase("functional/inet_pton.c", false, .{});
18+
// "functional/ipc_msg.c": Probably a bug in qemu
19+
// "functional/ipc_sem.c": Probably a bug in qemu
20+
// "functional/ipc_shm.c": Probably a bug in qemu
21+
cases.addLibcTestCase("functional/mbc.c", true, .{});
22+
cases.addLibcTestCase("functional/memstream.c", true, .{});
23+
// "functional/mntent.c": https://www.openwall.com/lists/musl/2024/10/22/1
24+
cases.addLibcTestCase("functional/popen.c", false, .{});
25+
cases.addLibcTestCase("functional/pthread_cancel-points.c", false, .{});
26+
cases.addLibcTestCase("functional/pthread_cancel.c", false, .{});
27+
cases.addLibcTestCase("functional/pthread_cond.c", false, .{});
28+
cases.addLibcTestCase("functional/pthread_mutex.c", false, .{});
29+
// "functional/pthread_mutex_pi.c": Probably a bug in qemu (big/little endian FUTEX_LOCK_PI)
30+
// "functional/pthread_robust.c": https://gitlab.com/qemu-project/qemu/-/issues/2424
31+
cases.addLibcTestCase("functional/pthread_tsd.c", false, .{});
32+
cases.addLibcTestCase("functional/qsort.c", true, .{});
33+
cases.addLibcTestCase("functional/random.c", true, .{});
34+
cases.addLibcTestCase("functional/search_hsearch.c", false, .{}); // The test suite of wasi-libc runs this test case
35+
cases.addLibcTestCase("functional/search_insque.c", true, .{});
36+
cases.addLibcTestCase("functional/search_lsearch.c", true, .{});
37+
cases.addLibcTestCase("functional/search_tsearch.c", true, .{});
38+
cases.addLibcTestCase("functional/sem_init.c", false, .{});
39+
cases.addLibcTestCase("functional/sem_open.c", false, .{});
40+
cases.addLibcTestCase("functional/setjmp.c", false, .{});
41+
cases.addLibcTestCase("functional/snprintf.c", true, .{});
42+
cases.addLibcTestCase("functional/socket.c", false, .{});
43+
cases.addLibcTestCase("functional/spawn.c", false, .{});
44+
cases.addLibcTestCase("functional/sscanf.c", true, .{});
45+
cases.addLibcTestCase("functional/sscanf_long.c", false, .{});
46+
cases.addLibcTestCase("functional/stat.c", false, .{});
47+
cases.addLibcTestCase("functional/strftime.c", true, .{});
48+
cases.addLibcTestCase("functional/string.c", true, .{});
49+
cases.addLibcTestCase("functional/string_memcpy.c", true, .{});
50+
cases.addLibcTestCase("functional/string_memmem.c", true, .{});
51+
cases.addLibcTestCase("functional/string_memset.c", true, .{});
52+
cases.addLibcTestCase("functional/string_strchr.c", true, .{});
53+
cases.addLibcTestCase("functional/string_strcspn.c", true, .{});
54+
cases.addLibcTestCase("functional/string_strstr.c", true, .{});
55+
cases.addLibcTestCase("functional/strtod.c", true, .{});
56+
cases.addLibcTestCase("functional/strtod_long.c", true, .{});
57+
cases.addLibcTestCase("functional/strtod_simple.c", true, .{});
58+
cases.addLibcTestCase("functional/strtof.c", true, .{});
59+
cases.addLibcTestCase("functional/strtol.c", true, .{});
60+
cases.addLibcTestCase("functional/strtold.c", true, .{});
61+
cases.addLibcTestCase("functional/swprintf.c", true, .{});
62+
cases.addLibcTestCase("functional/tgmath.c", true, .{});
63+
cases.addLibcTestCase("functional/time.c", false, .{});
64+
cases.addLibcTestCase("functional/tls_align.c", true, .{ .additional_src_file = "functional/tls_align_dso.c" });
65+
cases.addLibcTestCase("functional/tls_init.c", false, .{});
66+
cases.addLibcTestCase("functional/tls_local_exec.c", false, .{});
67+
cases.addLibcTestCase("functional/udiv.c", true, .{});
68+
cases.addLibcTestCase("functional/ungetc.c", false, .{});
69+
cases.addLibcTestCase("functional/utime.c", false, .{});
70+
cases.addLibcTestCase("functional/vfork.c", false, .{});
71+
cases.addLibcTestCase("functional/wcsstr.c", true, .{});
72+
cases.addLibcTestCase("functional/wcstol.c", true, .{});
73+
74+
cases.addLibcTestCase("regression/daemon-failure.c", false, .{});
75+
cases.addLibcTestCase("regression/dn_expand-empty.c", false, .{});
76+
cases.addLibcTestCase("regression/dn_expand-ptr-0.c", false, .{});
77+
cases.addLibcTestCase("regression/execle-env.c", false, .{});
78+
cases.addLibcTestCase("regression/fflush-exit.c", false, .{});
79+
cases.addLibcTestCase("regression/fgets-eof.c", true, .{});
80+
cases.addLibcTestCase("regression/fgetwc-buffering.c", false, .{});
81+
cases.addLibcTestCase("regression/flockfile-list.c", false, .{});
82+
cases.addLibcTestCase("regression/fpclassify-invalid-ld80.c", true, .{});
83+
cases.addLibcTestCase("regression/ftello-unflushed-append.c", false, .{});
84+
cases.addLibcTestCase("regression/getpwnam_r-crash.c", false, .{});
85+
cases.addLibcTestCase("regression/getpwnam_r-errno.c", false, .{});
86+
cases.addLibcTestCase("regression/iconv-roundtrips.c", true, .{});
87+
cases.addLibcTestCase("regression/inet_ntop-v4mapped.c", true, .{});
88+
cases.addLibcTestCase("regression/inet_pton-empty-last-field.c", true, .{});
89+
cases.addLibcTestCase("regression/iswspace-null.c", true, .{});
90+
cases.addLibcTestCase("regression/lrand48-signextend.c", true, .{});
91+
cases.addLibcTestCase("regression/lseek-large.c", false, .{});
92+
cases.addLibcTestCase("regression/malloc-0.c", true, .{});
93+
// "regression/malloc-brk-fail.c": QEMU OOM
94+
cases.addLibcTestCase("regression/malloc-oom.c", false, .{}); // wasi-libc: requires t_memfill
95+
cases.addLibcTestCase("regression/mbsrtowcs-overflow.c", true, .{});
96+
cases.addLibcTestCase("regression/memmem-oob-read.c", true, .{});
97+
cases.addLibcTestCase("regression/memmem-oob.c", true, .{});
98+
cases.addLibcTestCase("regression/mkdtemp-failure.c", false, .{});
99+
cases.addLibcTestCase("regression/mkstemp-failure.c", false, .{});
100+
cases.addLibcTestCase("regression/printf-1e9-oob.c", true, .{});
101+
cases.addLibcTestCase("regression/printf-fmt-g-round.c", true, .{});
102+
cases.addLibcTestCase("regression/printf-fmt-g-zeros.c", true, .{});
103+
cases.addLibcTestCase("regression/printf-fmt-n.c", true, .{});
104+
// "regression/pthread-robust-detach.c": https://gitlab.com/qemu-project/qemu/-/issues/2424
105+
cases.addLibcTestCase("regression/pthread_atfork-errno-clobber.c", false, .{});
106+
cases.addLibcTestCase("regression/pthread_cancel-sem_wait.c", false, .{});
107+
cases.addLibcTestCase("regression/pthread_cond-smasher.c", false, .{});
108+
cases.addLibcTestCase("regression/pthread_cond_wait-cancel_ignored.c", false, .{});
109+
cases.addLibcTestCase("regression/pthread_condattr_setclock.c", false, .{});
110+
// "regression/pthread_create-oom.c": QEMU OOM
111+
cases.addLibcTestCase("regression/pthread_exit-cancel.c", false, .{});
112+
cases.addLibcTestCase("regression/pthread_exit-dtor.c", false, .{});
113+
cases.addLibcTestCase("regression/pthread_once-deadlock.c", false, .{});
114+
cases.addLibcTestCase("regression/pthread_rwlock-ebusy.c", false, .{});
115+
cases.addLibcTestCase("regression/putenv-doublefree.c", true, .{});
116+
cases.addLibcTestCase("regression/raise-race.c", false, .{});
117+
cases.addLibcTestCase("regression/regex-backref-0.c", true, .{});
118+
cases.addLibcTestCase("regression/regex-bracket-icase.c", true, .{});
119+
cases.addLibcTestCase("regression/regex-ere-backref.c", true, .{});
120+
cases.addLibcTestCase("regression/regex-escaped-high-byte.c", true, .{});
121+
cases.addLibcTestCase("regression/regex-negated-range.c", true, .{});
122+
cases.addLibcTestCase("regression/regexec-nosub.c", true, .{});
123+
cases.addLibcTestCase("regression/rewind-clear-error.c", false, .{});
124+
cases.addLibcTestCase("regression/rlimit-open-files.c", false, .{});
125+
cases.addLibcTestCase("regression/scanf-bytes-consumed.c", true, .{});
126+
cases.addLibcTestCase("regression/scanf-match-literal-eof.c", true, .{});
127+
cases.addLibcTestCase("regression/scanf-nullbyte-char.c", true, .{});
128+
cases.addLibcTestCase("regression/sem_close-unmap.c", false, .{});
129+
// "regression/setenv-oom.c": QEMU OOM
130+
cases.addLibcTestCase("regression/setvbuf-unget.c", true, .{});
131+
cases.addLibcTestCase("regression/sigaltstack.c", false, .{});
132+
cases.addLibcTestCase("regression/sigprocmask-internal.c", false, .{});
133+
cases.addLibcTestCase("regression/sigreturn.c", true, .{});
134+
cases.addLibcTestCase("regression/sscanf-eof.c", true, .{});
135+
cases.addLibcTestCase("regression/strverscmp.c", true, .{});
136+
cases.addLibcTestCase("regression/syscall-sign-extend.c", false, .{});
137+
cases.addLibcTestCase("regression/uselocale-0.c", true, .{});
138+
cases.addLibcTestCase("regression/wcsncpy-read-overflow.c", true, .{});
139+
cases.addLibcTestCase("regression/wcsstr-false-negative.c", true, .{});
140+
}
141+
142+
const std = @import("std");
143+
const tests = @import("tests.zig");

test/src/Libc.zig

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
b: *std.Build,
2+
options: Options,
3+
root_step: *std.Build.Step,
4+
5+
libc_test_src_path: std.Build.LazyPath,
6+
7+
test_cases: std.ArrayList(TestCase) = .empty,
8+
9+
pub const Options = struct {
10+
optimize_modes: []const std.builtin.OptimizeMode,
11+
test_filters: []const []const u8,
12+
test_target_filters: []const []const u8,
13+
};
14+
15+
const TestCase = struct {
16+
name: []const u8,
17+
src_file: std.Build.LazyPath,
18+
additional_src_file: ?std.Build.LazyPath,
19+
supports_wasi_libc: bool,
20+
};
21+
22+
pub const LibcTestCaseOption = struct {
23+
additional_src_file: ?[]const u8 = null,
24+
};
25+
26+
pub fn addLibcTestCase(
27+
libc: *Libc,
28+
path: []const u8,
29+
supports_wasi_libc: bool,
30+
options: LibcTestCaseOption,
31+
) void {
32+
const name = libc.b.dupe(path[0 .. path.len - std.fs.path.extension(path).len]);
33+
std.mem.replaceScalar(u8, name, '/', '.');
34+
libc.test_cases.append(libc.b.allocator, .{
35+
.name = name,
36+
.src_file = libc.libc_test_src_path.path(libc.b, path),
37+
.additional_src_file = if (options.additional_src_file) |additional_src_file| libc.libc_test_src_path.path(libc.b, additional_src_file) else null,
38+
.supports_wasi_libc = supports_wasi_libc,
39+
}) catch @panic("OOM");
40+
}
41+
42+
pub fn addTarget(libc: *const Libc, target: std.Build.ResolvedTarget) void {
43+
if (libc.options.test_target_filters.len > 0) {
44+
const triple_txt = target.query.zigTriple(libc.b.allocator) catch @panic("OOM");
45+
for (libc.options.test_target_filters) |filter| {
46+
if (std.mem.indexOf(u8, triple_txt, filter)) |_| break;
47+
} else return;
48+
}
49+
50+
const common = libc.libc_test_src_path.path(libc.b, "common");
51+
52+
for (libc.options.optimize_modes) |optimize| {
53+
const libtest_mod = libc.b.createModule(.{
54+
.target = target,
55+
.optimize = optimize,
56+
.link_libc = true,
57+
});
58+
59+
var libtest_c_source_files: []const []const u8 = &.{ "print.c", "rand.c", "setrlim.c", "memfill.c", "vmfill.c", "fdfill.c", "utf8.c" };
60+
libtest_mod.addCSourceFiles(.{
61+
.root = common,
62+
.files = libtest_c_source_files[0..if (target.result.isMuslLibC()) 7 else 2],
63+
.flags = &.{"-fno-builtin"},
64+
});
65+
66+
const libtest = libc.b.addLibrary(.{
67+
.name = "test",
68+
.root_module = libtest_mod,
69+
});
70+
71+
for (libc.test_cases.items) |*test_case| {
72+
if (target.result.isWasiLibC() and !test_case.supports_wasi_libc)
73+
continue;
74+
75+
const annotated_case_name = libc.b.fmt("run libc-test {s} ({t})", .{ test_case.name, optimize });
76+
for (libc.options.test_filters) |test_filter| {
77+
if (std.mem.indexOf(u8, annotated_case_name, test_filter)) |_| break;
78+
} else if (libc.options.test_filters.len > 0) continue;
79+
80+
const mod = libc.b.createModule(.{
81+
.target = target,
82+
.optimize = optimize,
83+
.link_libc = true,
84+
});
85+
mod.addIncludePath(common);
86+
if (target.result.isWasiLibC())
87+
mod.addCMacro("_WASI_EMULATED_SIGNAL", "");
88+
mod.addCSourceFile(.{
89+
.file = test_case.src_file,
90+
.flags = &.{"-fno-builtin"},
91+
});
92+
if (test_case.additional_src_file) |additional_src_file| {
93+
mod.addCSourceFile(.{
94+
.file = additional_src_file,
95+
.flags = &.{"-fno-builtin"},
96+
});
97+
}
98+
mod.linkLibrary(libtest);
99+
100+
const exe = libc.b.addExecutable(.{
101+
.name = test_case.name,
102+
.root_module = mod,
103+
});
104+
105+
const run = libc.b.addRunArtifact(exe);
106+
run.setName(annotated_case_name);
107+
run.skip_foreign_checks = true;
108+
run.expectStdErrEqual("");
109+
run.expectStdOutEqual("");
110+
run.expectExitCode(0);
111+
112+
libc.root_step.dependOn(&run.step);
113+
}
114+
}
115+
}
116+
117+
const Libc = @This();
118+
const std = @import("std");

0 commit comments

Comments
 (0)