From 5464392e116b590cc9c2b662e967bc544b7d0a64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Tue, 14 Apr 2026 14:08:32 +0200 Subject: [PATCH 1/4] std.os: add APIs to determine whether Zig std requires libc for a target This is distinct from the question of whether the target formally considers libc to be the only stable syscall interface; for example, FreeBSD and NetBSD have stable syscalls, but we don't yet have syscall layers for them in std.os. --- lib/std/os.zig | 41 ++++++++++++++++++++++++++++++++++++++ src/Compilation/Config.zig | 11 +--------- test/src/StackTrace.zig | 5 +---- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 4f6643c3af33381ec38b6490600e4b64fe879109..76364027fd865e568c130f328b7f5fe5a59db6ea 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1,4 +1,5 @@ const builtin = @import("builtin"); +const std = @import("std.zig"); const native_os = builtin.os.tag; pub const linux = @import("os/linux.zig"); @@ -8,6 +9,46 @@ pub const wasi = @import("os/wasi.zig"); pub const emscripten = @import("os/emscripten.zig"); pub const windows = @import("os/windows.zig"); +/// Returns whether the Zig standard library requires libc in order to interface +/// with the operating system on the given target. +pub fn targetRequiresLibC(target: *const std.Target) bool { + if (target.requiresLibC()) return true; + return switch (target.os.tag) { + .linux => switch (target.cpu.arch) { + // https://codeberg.org/ziglang/zig/issues/30940 + .alpha, + // https://codeberg.org/ziglang/zig/issues/30942 + .csky, + // https://codeberg.org/ziglang/zig/issues/30943 + .hppa, + .hppa64, + // https://codeberg.org/ziglang/zig/issues/30944 + .microblaze, + .microblazeel, + // https://codeberg.org/ziglang/zig/issues/30946 + .sh, + .sheb, + // https://codeberg.org/ziglang/zig/issues/30945 + .sparc, + // https://codeberg.org/ziglang/zig/issues/30947 + .xtensa, + .xtensaeb, + => true, + else => false, + }, + .freebsd => true, // https://codeberg.org/ziglang/zig/issues/30981 + .netbsd => true, // https://codeberg.org/ziglang/zig/issues/30980 + .openbsd => true, // https://codeberg.org/ziglang/zig/issues/30982 + else => false, + }; +} + +/// Returns whether the Zig standard library requires libc in order to interface +/// with the operating system on the current target. +pub fn requiresLibC() bool { + return targetRequiresLibC(&builtin.target); +} + test { _ = linux; if (native_os == .uefi) _ = uefi; diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig index 3687db7b8e47e5fd23bfed1fdc4fa98d9e072845..b7f6df90969ae192b70b75668c41fb60c5d73724 100644 --- a/src/Compilation/Config.zig +++ b/src/Compilation/Config.zig @@ -234,19 +234,10 @@ pub fn resolve(options: Options) ResolveError!Config { break :b true; } if (options.link_libc) |x| break :b x; - switch (target.os.tag) { - // These targets don't require libc, but we don't yet have a syscall layer for them, - // so we default to linking libc for now. - .freebsd, - .netbsd, - .openbsd, - => break :b true, - else => {}, - } if (options.ensure_libc_on_non_freestanding and target.os.tag != .freestanding) break :b true; - break :b target.requiresLibC(); + break :b std.os.targetRequiresLibC(target); }; const link_mode = b: { diff --git a/test/src/StackTrace.zig b/test/src/StackTrace.zig index ae323c44979c0fb2d4a49120532347b453b0dc9f..9beb8904607bbf550c3080fe4e34a9cd617b28c0 100644 --- a/test/src/StackTrace.zig +++ b/test/src/StackTrace.zig @@ -58,10 +58,7 @@ fn addCaseTarget( .fuchsia => false, else => true, }; - const both_libc = switch (target.result.os.tag) { - .freebsd, .netbsd, .openbsd => false, - else => !target.result.requiresLibC(), - }; + const both_libc = !std.os.targetRequiresLibC(&target.result); // See `std.debug.StackIterator.fp_usability` logic. const fp_usability: enum { useless, unsafe, safe, ideal } = switch (target.result.cpu.arch) { -- 2.54.0 From 3e1e625814165d9d9282f3cd89bc117cf1b6d61c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Tue, 14 Apr 2026 14:42:37 +0200 Subject: [PATCH 2/4] test: handle -Dskip-libc correctly for module tests For a test target with link_libc = null, we need to skip it if we would implicitly link libc because the target requires it or because we don't yet have a syscall layer for it. --- test/tests.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tests.zig b/test/tests.zig index fea928652d76472e08735494be33a6a8ad4e77b5..9887b0f024894db00f014ccbf7de389dc1b31520 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -2531,7 +2531,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { } else continue; } - if (options.skip_libc and test_target.link_libc == true) + if (options.skip_libc and (test_target.link_libc == true or std.os.targetRequiresLibC(target))) continue; // We can't provide MSVC libc when cross-compiling. -- 2.54.0 From 7ea8f842bc65cdaf046483b2bfe7948ebad16516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Fri, 27 Mar 2026 11:14:02 +0100 Subject: [PATCH 3/4] libzigc: move all unit tests from lib/c/ to test/c/ Before: * test-zigc: run libzigc unit tests (part of test-modules) * test-libc: run libc-test cases Now: * test-libc: run libc API unit tests (part of test-modules) * test-libc-nsz: run libc-test cases libc API unit tests (previously referred to as libzigc unit tests) now run for all supported targets, even those we don't provide libzigc for. The idea is that this will help us catch bad assumptions in the unit tests, as well as bugs in other libcs. I considered this setup: * test-c: run libc API unit tests (part of test-modules) * test-libc-nsz: run libc-test cases * test-libc: both of the above However, I do not like it because it gives a false sense of security; the full module and C ABI test suites are still liable to catch libzigc bugs that test-c and test-libc-nsz might not. So contributors should just run the test steps outlined in https://codeberg.org/ziglang/zig/issues/30978. Co-authored-by: rpkak --- build.zig | 60 ++++++++++----------- lib/c.zig | 3 +- lib/c/inttypes.zig | 10 ---- lib/c/math.zig | 100 ---------------------------------- lib/c/search.zig | 28 +--------- lib/c/stdlib.zig | 100 ---------------------------------- lib/c/stdlib/drand48.zig | 57 -------------------- lib/c/string.zig | 7 --- lib/c/strings.zig | 38 ------------- lib/c/unistd.zig | 26 --------- lib/std/c.zig | 59 ++++++++++++++++++--- test/c.zig | 12 +++++ test/c/inttypes.zig | 15 ++++++ test/c/math.zig | 101 +++++++++++++++++++++++++++++++++++ test/c/search.zig | 38 +++++++++++++ test/c/stdlib.zig | 109 ++++++++++++++++++++++++++++++++++++++ test/c/stdlib/drand48.zig | 62 ++++++++++++++++++++++ test/c/string.zig | 12 +++++ test/c/strings.zig | 57 ++++++++++++++++++++ test/c/unistd.zig | 31 +++++++++++ test/tests.zig | 63 ++++++++-------------- 21 files changed, 544 insertions(+), 444 deletions(-) create mode 100644 test/c.zig create mode 100644 test/c/inttypes.zig create mode 100644 test/c/math.zig create mode 100644 test/c/search.zig create mode 100644 test/c/stdlib.zig create mode 100644 test/c/stdlib/drand48.zig create mode 100644 test/c/string.zig create mode 100644 test/c/strings.zig create mode 100644 test/c/unistd.zig diff --git a/build.zig b/build.zig index 5cb50620df3b73959a573608a5965c2887ae6f40..e4f7bb91f0162f473959a8ac7eb63a64648720f4 100644 --- a/build.zig +++ b/build.zig @@ -522,33 +522,6 @@ pub fn build(b: *std.Build) !void { .max_rss = 4_000_000_000, })); - test_modules_step.dependOn(tests.addModuleTests(b, .{ - .test_filters = test_filters, - .test_target_filters = test_target_filters, - .test_extra_targets = test_extra_targets, - .root_src = "lib/c.zig", - .name = "zigc", - .desc = "Run the zig libc implementation unit tests", - .optimize_modes = optimization_modes, - .include_paths = &.{}, - .sanitize_thread = sanitize_thread, - .skip_single_threaded = true, - .skip_non_native = skip_non_native, - .test_only = test_only, - .skip_spirv = skip_spirv, - .skip_wasm = skip_wasm, - .skip_freebsd = skip_freebsd, - .skip_netbsd = skip_netbsd, - .skip_openbsd = skip_openbsd, - .skip_windows = skip_windows, - .skip_darwin = skip_darwin, - .skip_linux = skip_linux, - .skip_llvm = skip_llvm, - .skip_libc = true, - .no_builtin = true, - .max_rss = 4_000_000_000, - })); - test_modules_step.dependOn(tests.addModuleTests(b, .{ .test_filters = test_filters, .test_target_filters = test_target_filters, @@ -562,7 +535,7 @@ pub fn build(b: *std.Build) !void { .skip_single_threaded = skip_single_threaded, .skip_non_native = skip_non_native, .test_only = test_only, - .skip_spirv = skip_spirv, + .skip_spirv = true, .skip_wasm = skip_wasm, .skip_freebsd = skip_freebsd, .skip_netbsd = skip_netbsd, @@ -575,6 +548,33 @@ pub fn build(b: *std.Build) !void { .max_rss = 9_300_000_000, })); + test_modules_step.dependOn(tests.addModuleTests(b, .{ + .test_filters = test_filters, + .test_target_filters = test_target_filters, + .test_extra_targets = test_extra_targets, + .root_src = "test/c.zig", + .name = "libc", + .desc = "Run the libc API tests", + .optimize_modes = optimization_modes, + .include_paths = &.{}, + .sanitize_thread = sanitize_thread, + .skip_single_threaded = true, + .skip_non_native = skip_non_native, + .test_only = test_only, + .skip_spirv = true, + .skip_wasm = skip_wasm, + .skip_freebsd = skip_freebsd, + .skip_netbsd = skip_netbsd, + .skip_openbsd = skip_openbsd, + .skip_windows = skip_windows, + .skip_darwin = skip_darwin, + .skip_linux = skip_linux, + .skip_llvm = skip_llvm, + .skip_libc = skip_libc, + .no_builtin = true, + .max_rss = 4_000_000_000, + })); + const unit_tests_step = b.step("test-unit", "Run the compiler source unit tests"); test_step.dependOn(unit_tests_step); @@ -662,13 +662,13 @@ pub fn build(b: *std.Build) !void { try tests.addIncrementalTests(b, test_incremental_step, test_filters); if (!skip_test_incremental) test_step.dependOn(test_incremental_step); - if (tests.addLibcTests(b, .{ + if (tests.addLibcTestNszTests(b, .{ .optimize_modes = optimization_modes, .test_filters = test_filters, .test_target_filters = test_target_filters, .skip_wasm = skip_wasm, .max_rss = 3_500_000_000, - })) |test_libc_step| test_step.dependOn(test_libc_step); + })) |test_libc_nsz_step| test_step.dependOn(test_libc_nsz_step); } fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { diff --git a/lib/c.zig b/lib/c.zig index c79e7a129fc6240652138506b31a357c57967ad2..4a73995e2d70d099958d7b52b9403775c188bbb8 100644 --- a/lib/c.zig +++ b/lib/c.zig @@ -2,8 +2,7 @@ //! bundled libcs. //! //! mingw-w64 libc is not fully statically linked, so some symbols don't need -//! to be exported. However, a future enhancement could be eliminating Zig's -//! dependency on msvcrt dll even when linking libc and targeting Windows. +//! to be exported. const builtin = @import("builtin"); const std = @import("std"); diff --git a/lib/c/inttypes.zig b/lib/c/inttypes.zig index 7bdcae873eb657d5a6acf1dd7b099d907576df40..d8807154d3409359af1b8ab2e6e32d4eabee3a18 100644 --- a/lib/c/inttypes.zig +++ b/lib/c/inttypes.zig @@ -24,13 +24,3 @@ fn imaxdiv(a: intmax_t, b: intmax_t) callconv(.c) imaxdiv_t { .rem = @rem(a, b), }; } - -test imaxabs { - const val: intmax_t = -10; - try std.testing.expectEqual(10, imaxabs(val)); -} - -test imaxdiv { - const expected: imaxdiv_t = .{ .quot = 9, .rem = 0 }; - try std.testing.expectEqual(expected, imaxdiv(9, 1)); -} diff --git a/lib/c/math.zig b/lib/c/math.zig index 5eff669a9755975cc7423a203c15ecc6d5a199c6..20f0b70b5f769831b0e3400620302d9d4e919314 100644 --- a/lib/c/math.zig +++ b/lib/c/math.zig @@ -2,10 +2,6 @@ const builtin = @import("builtin"); const std = @import("std"); const math = std.math; -const expect = std.testing.expect; -const expectEqual = std.testing.expectEqual; -const expectApproxEqAbs = std.testing.expectApproxEqAbs; -const expectApproxEqRel = std.testing.expectApproxEqRel; const symbol = @import("../c.zig").symbol; @@ -304,59 +300,6 @@ fn modfl(x: c_longdouble, iptr: *c_longdouble) callconv(.c) c_longdouble { }; } -fn testModf(comptime T: type) !void { - // Choose the appropriate `modf` impl to test based on type - const f = switch (T) { - f32 => modff, - f64 => modf, - c_longdouble => modfl, - else => @compileError("modf not implemented for " ++ @typeName(T)), - }; - - var int: T = undefined; - const iptr = ∫ - const eps_val: comptime_float = @max(1e-6, math.floatEps(T)); - - const normal_frac = f(@as(T, 1234.567), iptr); - // Account for precision error - const expected = 1234.567 - @as(T, 1234); - try expectApproxEqAbs(expected, normal_frac, eps_val); - try expectApproxEqRel(@as(T, 1234.0), iptr.*, eps_val); - - // When `x` is a NaN, NaN is returned and `*iptr` is set to NaN - const nan_frac = f(math.nan(T), iptr); - try expect(math.isNan(nan_frac)); - try expect(math.isNan(iptr.*)); - - // When `x` is positive infinity, +0 is returned and `*iptr` is set to - // positive infinity - const pos_zero_frac = f(math.inf(T), iptr); - try expect(math.isPositiveZero(pos_zero_frac)); - try expect(math.isPositiveInf(iptr.*)); - - // When `x` is negative infinity, -0 is returned and `*iptr` is set to - // negative infinity - const neg_zero_frac = f(-math.inf(T), iptr); - try expect(math.isNegativeZero(neg_zero_frac)); - try expect(math.isNegativeInf(iptr.*)); - - // Return -0 when `x` is a negative integer - const nz_frac = f(@as(T, -1000.0), iptr); - try expect(math.isNegativeZero(nz_frac)); - try expectEqual(@as(T, -1000.0), iptr.*); - - // Return +0 when `x` is a positive integer - const pz_frac = f(@as(T, 1000.0), iptr); - try expect(math.isPositiveZero(pz_frac)); - try expectEqual(@as(T, 1000.0), iptr.*); -} - -test "modf" { - try testModf(f32); - try testModf(f64); - try testModf(c_longdouble); -} - fn nan(_: [*:0]const c_char) callconv(.c) f64 { return math.nan(f64); } @@ -421,49 +364,6 @@ fn rintf(x: f32) callconv(.c) f32 { return y; } -fn testRint(comptime T: type) !void { - const f = switch (T) { - f32 => rintf, - f64 => rint, - else => @compileError("rint not implemented for" ++ @typeName(T)), - }; - - // Positive numbers round correctly - try expectEqual(@as(T, 42.0), f(42.2)); - try expectEqual(@as(T, 42.0), f(41.8)); - - // Negative numbers round correctly - try expectEqual(@as(T, -6.0), f(-5.9)); - try expectEqual(@as(T, -6.0), f(-6.1)); - - // No rounding needed test - try expectEqual(@as(T, 5.0), f(5.0)); - try expectEqual(@as(T, -10.0), f(-10.0)); - try expectEqual(@as(T, 0.0), f(0.0)); - - // Very large numbers return unchanged - const large: T = 9007199254740992.0; // 2^53 - try expectEqual(large, f(large)); - try expectEqual(-large, f(-large)); - - // Small positive numbers round to zero - const pos_result = f(0.3); - try expect(math.isPositiveZero(pos_result)); - - // Small negative numbers round to negative zero - const neg_result = f(-0.3); - try expect(math.isNegativeZero(neg_result)); - - // Exact half rounds to nearest even (banker's rounding) - try expectEqual(@as(T, 2.0), f(2.5)); - try expectEqual(@as(T, 4.0), f(3.5)); -} - -test "rint" { - try testRint(f32); - try testRint(f64); -} - fn tanh(x: f64) callconv(.c) f64 { return math.tanh(x); } diff --git a/lib/c/search.zig b/lib/c/search.zig index 271aec873e9d2554f303dd2ba896c80b6f108cb9..12a9018d63b5849a04014682d561e0bec93ee783 100644 --- a/lib/c/search.zig +++ b/lib/c/search.zig @@ -9,6 +9,7 @@ comptime { } } +/// Not defined in `std.c` because C headers don't either. const Node = extern struct { next: ?*Node, prev: ?*Node, @@ -38,30 +39,3 @@ fn remque(element: *anyopaque) callconv(.c) void { if (e.next) |next| next.prev = e.prev; if (e.prev) |prev| prev.next = e.next; } - -test "insque and remque" { - var first = Node{ .next = null, .prev = null }; - var second = Node{ .next = null, .prev = null }; - var third = Node{ .next = null, .prev = null }; - - insque(&first, null); - try std.testing.expectEqual(@as(?*Node, null), first.next); - try std.testing.expectEqual(@as(?*Node, null), first.prev); - - insque(&second, &first); - try std.testing.expectEqual(@as(?*Node, &second), first.next); - try std.testing.expectEqual(@as(?*Node, &first), second.prev); - - insque(&third, &first); - try std.testing.expectEqual(@as(?*Node, &third), first.next); - try std.testing.expectEqual(@as(?*Node, &second), third.next); - try std.testing.expectEqual(@as(?*Node, &first), third.prev); - try std.testing.expectEqual(@as(?*Node, &third), second.prev); - - remque(&third); - try std.testing.expectEqual(@as(?*Node, &second), first.next); - try std.testing.expectEqual(@as(?*Node, &first), second.prev); - - remque(&second); - try std.testing.expectEqual(@as(?*Node, null), first.next); -} diff --git a/lib/c/stdlib.zig b/lib/c/stdlib.zig index ccc8c08a143959353951939df3efb6593e02e999..eed91193c3a26a363dd5dcca10e769c411abf7a1 100644 --- a/lib/c/stdlib.zig +++ b/lib/c/stdlib.zig @@ -294,103 +294,3 @@ fn bsearch(key: *const anyopaque, base: *const anyopaque, n: usize, size: usize, } return null; } - -test abs { - const val: c_int = -10; - try std.testing.expectEqual(10, abs(val)); -} - -test labs { - const val: c_long = -10; - try std.testing.expectEqual(10, labs(val)); -} - -test llabs { - const val: c_longlong = -10; - try std.testing.expectEqual(10, llabs(val)); -} - -test div { - const expected: div_t = .{ .quot = 5, .rem = 5 }; - try std.testing.expectEqual(expected, div(55, 10)); -} - -test ldiv { - const expected: ldiv_t = .{ .quot = -6, .rem = 2 }; - try std.testing.expectEqual(expected, ldiv(38, -6)); -} - -test lldiv { - const expected: lldiv_t = .{ .quot = 1, .rem = 2 }; - try std.testing.expectEqual(expected, lldiv(5, 3)); -} - -test atoi { - try std.testing.expectEqual(0, atoi(@ptrCast("stop42true"))); - try std.testing.expectEqual(42, atoi(@ptrCast("42true"))); - try std.testing.expectEqual(-1, atoi(@ptrCast("-01"))); - try std.testing.expectEqual(1, atoi(@ptrCast("+001"))); - try std.testing.expectEqual(100, atoi(@ptrCast(" 100"))); - try std.testing.expectEqual(500, atoi(@ptrCast("000000000000500"))); - try std.testing.expectEqual(1111, atoi(@ptrCast("0000000000001111_0000"))); - try std.testing.expectEqual(0, atoi(@ptrCast("0xAA"))); - try std.testing.expectEqual(700, atoi(@ptrCast("700B"))); - try std.testing.expectEqual(32453, atoi(@ptrCast("+32453more"))); - try std.testing.expectEqual(std.math.maxInt(c_int), atoi(@ptrCast(std.fmt.comptimePrint("{d}", .{std.math.maxInt(c_int)})))); - try std.testing.expectEqual(std.math.minInt(c_int), atoi(@ptrCast(std.fmt.comptimePrint("{d}", .{std.math.minInt(c_int)})))); -} - -test atol { - try std.testing.expectEqual(0, atol(@ptrCast("stop42true"))); - try std.testing.expectEqual(42, atol(@ptrCast("42true"))); - try std.testing.expectEqual(-1, atol(@ptrCast("-01"))); - try std.testing.expectEqual(1, atol(@ptrCast("+001"))); - try std.testing.expectEqual(100, atol(@ptrCast(" 100"))); - try std.testing.expectEqual(500, atol(@ptrCast("000000000000500"))); - try std.testing.expectEqual(1111, atol(@ptrCast("0000000000001111_0000"))); - try std.testing.expectEqual(0, atol(@ptrCast("0xAA"))); - try std.testing.expectEqual(700, atol(@ptrCast("700B"))); - try std.testing.expectEqual(32453, atol(@ptrCast("+32453more"))); - try std.testing.expectEqual(std.math.maxInt(c_long), atol(@ptrCast(std.fmt.comptimePrint("{d}", .{std.math.maxInt(c_long)})))); - try std.testing.expectEqual(std.math.minInt(c_long), atol(@ptrCast(std.fmt.comptimePrint("{d}", .{std.math.minInt(c_long)})))); -} - -test atoll { - try std.testing.expectEqual(0, atoll(@ptrCast("stop42true"))); - try std.testing.expectEqual(42, atoll(@ptrCast("42true"))); - try std.testing.expectEqual(-1, atoll(@ptrCast("-01"))); - try std.testing.expectEqual(1, atoll(@ptrCast("+001"))); - try std.testing.expectEqual(100, atoll(@ptrCast(" 100"))); - try std.testing.expectEqual(500, atoll(@ptrCast("000000000000500"))); - try std.testing.expectEqual(1111, atoll(@ptrCast("0000000000001111_0000"))); - try std.testing.expectEqual(0, atoll(@ptrCast("0xAA"))); - try std.testing.expectEqual(700, atoll(@ptrCast("700B"))); - try std.testing.expectEqual(32453, atoll(@ptrCast(" +32453more"))); - try std.testing.expectEqual(std.math.maxInt(c_longlong), atoll(@ptrCast(std.fmt.comptimePrint("{d}", .{std.math.maxInt(c_longlong)})))); - try std.testing.expectEqual(std.math.minInt(c_longlong), atoll(@ptrCast(std.fmt.comptimePrint("{d}", .{std.math.minInt(c_longlong)})))); -} - -// FIXME: We cannot test strtol, strtoll, strtoul, etc.. here as it must modify errno and libc is not linked in tests - -test bsearch { - const Comparison = struct { - pub fn compare(a: *const anyopaque, b: *const anyopaque) callconv(.c) c_int { - const a_u16: *const u16 = @ptrCast(@alignCast(a)); - const b_u16: *const u16 = @ptrCast(@alignCast(b)); - - return switch (std.math.order(a_u16.*, b_u16.*)) { - .gt => 1, - .eq => 0, - .lt => -1, - }; - } - }; - - const items: []const u16 = &.{ 0, 5, 7, 9, 10, 200, 512, 768 }; - - try std.testing.expectEqual(@as(?*anyopaque, null), bsearch(&@as(u16, 2000), items.ptr, items.len, @sizeOf(u16), Comparison.compare)); - - for (items) |*value| { - try std.testing.expectEqual(@as(*const anyopaque, value), bsearch(value, items.ptr, items.len, @sizeOf(u16), Comparison.compare)); - } -} diff --git a/lib/c/stdlib/drand48.zig b/lib/c/stdlib/drand48.zig index 266cc7af4a64fa1afe6ea50fe5367c9633fc9e55..692230d12196cfa75be727a3fa61c117aea2e967 100644 --- a/lib/c/stdlib/drand48.zig +++ b/lib/c/stdlib/drand48.zig @@ -90,60 +90,3 @@ fn srand48(seedval: c_long) callconv(.c) void { const xi = (@as(u32, @truncate(@as(c_ulong, @bitCast(seedval)))) << 16) | 0x330E; lcg = .init(xi, default_multiplier, default_addend); } - -test erand48 { - var xsubi: [3]c_ushort = .{ 37174, 64810, 11603 }; - - try std.testing.expectApproxEqAbs(0.8965, erand48(&xsubi), 0.0005); - try std.testing.expectEqualSlices(c_ushort, &.{ 22537, 47966, 58735 }, &xsubi); - - try std.testing.expectApproxEqAbs(0.3375, erand48(&xsubi), 0.0005); - try std.testing.expectEqualSlices(c_ushort, &.{ 37344, 32911, 22119 }, &xsubi); - - try std.testing.expectApproxEqAbs(0.6475, erand48(&xsubi), 0.0005); - try std.testing.expectEqualSlices(c_ushort, &.{ 23659, 29872, 42445 }, &xsubi); - - try std.testing.expectApproxEqAbs(0.5005, erand48(&xsubi), 0.0005); - try std.testing.expectEqualSlices(c_ushort, &.{ 31642, 7875, 32802 }, &xsubi); - - try std.testing.expectApproxEqAbs(0.5065, erand48(&xsubi), 0.0005); - try std.testing.expectEqualSlices(c_ushort, &.{ 64669, 14399, 33170 }, &xsubi); -} - -test jrand48 { - var xsubi: [3]c_ushort = .{ 25175, 11052, 45015 }; - - try std.testing.expectEqual(1699503220, jrand48(&xsubi)); - try std.testing.expectEqualSlices(c_ushort, &.{ 2326, 23668, 25932 }, &xsubi); - - try std.testing.expectEqual(-992276007, jrand48(&xsubi)); - try std.testing.expectEqualSlices(c_ushort, &.{ 41577, 4569, 50395 }, &xsubi); - - try std.testing.expectEqual(-19535776, jrand48(&xsubi)); - try std.testing.expectEqualSlices(c_ushort, &.{ 31936, 59488, 65237 }, &xsubi); - - try std.testing.expectEqual(79438377, jrand48(&xsubi)); - try std.testing.expectEqualSlices(c_ushort, &.{ 40395, 8745, 1212 }, &xsubi); - - try std.testing.expectEqual(-1258917728, jrand48(&xsubi)); - try std.testing.expectEqualSlices(c_ushort, &.{ 37242, 28832, 46326 }, &xsubi); -} - -test nrand48 { - var xsubi: [3]c_ushort = .{ 546, 33817, 23389 }; - - try std.testing.expectEqual(914920692, nrand48(&xsubi)); - try std.testing.expectEqualSlices(c_ushort, &.{ 29829, 10728, 27921 }, &xsubi); - - try std.testing.expectEqual(754104482, nrand48(&xsubi)); - try std.testing.expectEqualSlices(c_ushort, &.{ 6828, 28997, 23013 }, &xsubi); - - try std.testing.expectEqual(609453945, nrand48(&xsubi)); - try std.testing.expectEqualSlices(c_ushort, &.{ 58183, 3826, 18599 }, &xsubi); - - try std.testing.expectEqual(1878644360, nrand48(&xsubi)); - try std.testing.expectEqualSlices(c_ushort, &.{ 36678, 44304, 57331 }, &xsubi); - - try std.testing.expectEqual(2114923686, nrand48(&xsubi)); - try std.testing.expectEqualSlices(c_ushort, &.{ 58585, 22861, 64542 }, &xsubi); -} diff --git a/lib/c/string.zig b/lib/c/string.zig index 722ef0ba77d948251bb0121d9d31f23989c6c7f6..da301a19eb86bfca1511f2b1284da1cb73bd0d3c 100644 --- a/lib/c/string.zig +++ b/lib/c/string.zig @@ -290,10 +290,3 @@ fn mempcpy(noalias dst: *anyopaque, noalias src: *const anyopaque, len: usize) c @memcpy(dst_bytes[0..len], src_bytes[0..len]); return dst_bytes + len; } - -test strncmp { - try std.testing.expect(strncmp(@ptrCast("a"), @ptrCast("b"), 1) < 0); - try std.testing.expect(strncmp(@ptrCast("a"), @ptrCast("c"), 1) < 0); - try std.testing.expect(strncmp(@ptrCast("b"), @ptrCast("a"), 1) > 0); - try std.testing.expect(strncmp(@ptrCast("\xff"), @ptrCast("\x02"), 1) > 0); -} diff --git a/lib/c/strings.zig b/lib/c/strings.zig index 8a052795ae40768d47ececbf527865650e5ab046..111da9ad0f04eff48c890c6b33b5481c733a8aae 100644 --- a/lib/c/strings.zig +++ b/lib/c/strings.zig @@ -81,41 +81,3 @@ fn __strncasecmp_l(a: [*:0]const c_char, b: [*:0]const c_char, n: usize, locale: _ = locale; return strncasecmp(a, b, n); } - -test bzero { - var array: [10]u8 = [_]u8{ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }; - var a = std.mem.zeroes([array.len]u8); - a[9] = '0'; - bzero(&array[0], 9); - try std.testing.expect(std.mem.eql(u8, &array, &a)); -} - -test firstBitSet { - try std.testing.expectEqual(0, firstBitSet(usize, 0)); - - for (0..@bitSizeOf(usize)) |i| { - const bit = @as(usize, 1) << @intCast(i); - - try std.testing.expectEqual(i + 1, firstBitSet(usize, bit)); - } -} - -test strcasecmp { - try std.testing.expect(strcasecmp(@ptrCast("a"), @ptrCast("b")) < 0); - try std.testing.expect(strcasecmp(@ptrCast("b"), @ptrCast("a")) > 0); - try std.testing.expect(strcasecmp(@ptrCast("A"), @ptrCast("b")) < 0); - try std.testing.expect(strcasecmp(@ptrCast("b"), @ptrCast("A")) > 0); - try std.testing.expect(strcasecmp(@ptrCast("A"), @ptrCast("A")) == 0); - try std.testing.expect(strcasecmp(@ptrCast("B"), @ptrCast("b")) == 0); - try std.testing.expect(strcasecmp(@ptrCast("bb"), @ptrCast("AA")) > 0); -} - -test strncasecmp { - try std.testing.expect(strncasecmp(@ptrCast("a"), @ptrCast("b"), 1) < 0); - try std.testing.expect(strncasecmp(@ptrCast("b"), @ptrCast("a"), 1) > 0); - try std.testing.expect(strncasecmp(@ptrCast("A"), @ptrCast("b"), 1) < 0); - try std.testing.expect(strncasecmp(@ptrCast("b"), @ptrCast("A"), 1) > 0); - try std.testing.expect(strncasecmp(@ptrCast("A"), @ptrCast("A"), 1) == 0); - try std.testing.expect(strncasecmp(@ptrCast("B"), @ptrCast("b"), 1) == 0); - try std.testing.expect(strncasecmp(@ptrCast("bb"), @ptrCast("AA"), 2) > 0); -} diff --git a/lib/c/unistd.zig b/lib/c/unistd.zig index 676f943f36b24e393713fad3f13e042cfe29f087..f802fdb92e25a35a07dd640b1f82b3f141ae3a65 100644 --- a/lib/c/unistd.zig +++ b/lib/c/unistd.zig @@ -206,32 +206,6 @@ fn swab(noalias src_ptr: *const anyopaque, noalias dest_ptr: *anyopaque, n: isiz } } -test swab { - var a: [4]u8 = undefined; - @memset(a[0..], '\x00'); - swab("abcd", &a, 4); - try std.testing.expectEqualSlices(u8, "badc", &a); - - // Partial copy - @memset(a[0..], '\x00'); - swab("abcd", &a, 2); - try std.testing.expectEqualSlices(u8, "ba\x00\x00", &a); - - // n < 1 - @memset(a[0..], '\x00'); - swab("abcd", &a, 0); - try std.testing.expectEqualSlices(u8, "\x00" ** 4, &a); - swab("abcd", &a, -1); - try std.testing.expectEqualSlices(u8, "\x00" ** 4, &a); - - // Odd n - @memset(a[0..], '\x00'); - swab("abcd", &a, 1); - try std.testing.expectEqualSlices(u8, "\x00" ** 4, &a); - swab("abcd", &a, 3); - try std.testing.expectEqualSlices(u8, "ba\x00\x00", &a); -} - fn close(fd: std.c.fd_t) callconv(.c) c_int { const signed: isize = @bitCast(linux.close(fd)); if (signed < 0) { diff --git a/lib/std/c.zig b/lib/std/c.zig index bb028dbdaa4fa4b84212d81efb7ffab0c4b863de..11b58b21628ce8ac6daa00d2af88f6e92fb55004 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -11102,13 +11102,60 @@ pub const ioctl = switch (native_os) { else => private.ioctl, }; +pub extern "c" fn bzero(s: *anyopaque, n: usize) void; + +pub extern "c" fn swab(noalias from: *const anyopaque, noalias to: *anyopaque, n: isize) void; + +pub extern "c" fn strncmp(a: [*:0]const c_char, b: [*:0]const c_char, max: usize) c_int; +pub extern "c" fn strcasecmp(a: [*:0]const c_char, b: [*:0]const c_char) c_int; +pub extern "c" fn strncasecmp(a: [*:0]const c_char, b: [*:0]const c_char, max: usize) c_int; + +pub extern "c" fn ffs(i: c_int) c_int; +pub extern "c" fn ffsl(i: c_long) c_long; +pub extern "c" fn ffsll(i: c_longlong) c_longlong; + +pub extern "c" fn erand48(xsubi: *[3]c_ushort) f64; +pub extern "c" fn jrand48(xsubi: *[3]c_ushort) c_long; +pub extern "c" fn nrand48(xsubi: *[3]c_ushort) c_long; + +pub extern "c" fn insque(element: *anyopaque, pred: ?*anyopaque) void; +pub extern "c" fn remque(element: *anyopaque) void; + +pub extern "c" fn imaxabs(a: intmax_t) intmax_t; +pub extern "c" fn imaxdiv(a: intmax_t, b: intmax_t) imaxdiv_t; + +pub extern "c" fn abs(a: c_int) c_int; +pub extern "c" fn labs(a: c_long) c_long; +pub extern "c" fn llabs(a: c_longlong) c_longlong; + +pub extern "c" fn div(a: c_int, b: c_int) div_t; +pub extern "c" fn ldiv(a: c_long, b: c_long) ldiv_t; +pub extern "c" fn lldiv(a: c_longlong, b: c_longlong) lldiv_t; + +pub extern "c" fn atoi(str: [*:0]const c_char) c_int; +pub extern "c" fn atol(str: [*:0]const c_char) c_long; +pub extern "c" fn atoll(str: [*:0]const c_char) c_longlong; + +pub extern "c" fn bsearch( + key: *const anyopaque, + base: *const anyopaque, + n: usize, + size: usize, + compare: *const fn (a: *const anyopaque, b: *const anyopaque) callconv(.c) c_int, +) ?*anyopaque; + // Math -pub extern "c" fn atan(x: f64) callconv(.c) f64; -pub extern "c" fn copysign(x: f64, y: f64) callconv(.c) f64; -pub extern "c" fn fdim(x: f64, y: f64) callconv(.c) f64; -pub extern "c" fn frexp(x: f64, e: *c_int) callconv(.c) f64; -pub extern "c" fn hypot(x: f64, y: f64) callconv(.c) f64; -pub extern "c" fn modf(x: f64, iptr: *f64) callconv(.c) f64; +pub extern "c" fn atan(x: f64) f64; +pub extern "c" fn copysign(x: f64, y: f64) f64; +pub extern "c" fn fdim(x: f64, y: f64) f64; +pub extern "c" fn frexp(x: f64, e: *c_int) f64; +pub extern "c" fn hypot(x: f64, y: f64) f64; +pub extern "c" fn modff(x: f32, iptr: *f32) f32; +pub extern "c" fn modf(x: f64, iptr: *f64) f64; +pub extern "c" fn modfl(x: c_longdouble, iptr: *c_longdouble) c_longdouble; +pub extern "c" fn rintf(x: f32) f32; +pub extern "c" fn rint(x: f64) f64; +pub extern "c" fn rintl(x: c_longdouble) c_longdouble; // OS-specific bits. These are protected from being used on the wrong OS by // comptime assertions inside each OS-specific file. diff --git a/test/c.zig b/test/c.zig new file mode 100644 index 0000000000000000000000000000000000000000..797f16f5cb25b1df8db00429696a08662586daf2 --- /dev/null +++ b/test/c.zig @@ -0,0 +1,12 @@ +const builtin = @import("builtin"); +const std = @import("std"); + +test { + _ = @import("c/inttypes.zig"); + _ = @import("c/math.zig"); + _ = @import("c/search.zig"); + _ = @import("c/stdlib.zig"); + _ = @import("c/string.zig"); + _ = @import("c/strings.zig"); + _ = @import("c/unistd.zig"); +} diff --git a/test/c/inttypes.zig b/test/c/inttypes.zig new file mode 100644 index 0000000000000000000000000000000000000000..9b9cbfe966f4f5d61396e2b461997517a21c40fc --- /dev/null +++ b/test/c/inttypes.zig @@ -0,0 +1,15 @@ +const builtin = @import("builtin"); +const std = @import("std"); + +const c = std.c; +const testing = std.testing; + +test "imaxabs" { + const val: c.intmax_t = -10; + try testing.expectEqual(10, c.imaxabs(val)); +} + +test "imaxdiv" { + const expected: c.imaxdiv_t = .{ .quot = 9, .rem = 0 }; + try testing.expectEqual(expected, c.imaxdiv(9, 1)); +} diff --git a/test/c/math.zig b/test/c/math.zig new file mode 100644 index 0000000000000000000000000000000000000000..cf372dc0be5551537edca6e1f7e384ea2b29961a --- /dev/null +++ b/test/c/math.zig @@ -0,0 +1,101 @@ +const builtin = @import("builtin"); +const std = @import("std"); + +const c = std.c; +const math = std.math; +const testing = std.testing; + +fn testModf(comptime T: type) !void { + const f = switch (T) { + f32 => c.modff, + f64 => c.modf, + c_longdouble => c.modfl, + else => unreachable, + }; + + var int: T = undefined; + const iptr = ∫ + const eps_val: comptime_float = @max(1e-6, math.floatEps(T)); + + const normal_frac = f(@as(T, 1234.567), iptr); + // Account for precision error + const expected = 1234.567 - @as(T, 1234); + try testing.expectApproxEqAbs(expected, normal_frac, eps_val); + try testing.expectApproxEqRel(@as(T, 1234.0), iptr.*, eps_val); + + // When `x` is a NaN, NaN is returned and `*iptr` is set to NaN + const nan_frac = f(math.nan(T), iptr); + try testing.expect(math.isNan(nan_frac)); + try testing.expect(math.isNan(iptr.*)); + + // When `x` is positive infinity, +0 is returned and `*iptr` is set to + // positive infinity + const pos_zero_frac = f(math.inf(T), iptr); + try testing.expect(math.isPositiveZero(pos_zero_frac)); + try testing.expect(math.isPositiveInf(iptr.*)); + + // When `x` is negative infinity, -0 is returned and `*iptr` is set to + // negative infinity + const neg_zero_frac = f(-math.inf(T), iptr); + try testing.expect(math.isNegativeZero(neg_zero_frac)); + try testing.expect(math.isNegativeInf(iptr.*)); + + // Return -0 when `x` is a negative integer + const nz_frac = f(@as(T, -1000.0), iptr); + try testing.expect(math.isNegativeZero(nz_frac)); + try testing.expectEqual(@as(T, -1000.0), iptr.*); + + // Return +0 when `x` is a positive integer + const pz_frac = f(@as(T, 1000.0), iptr); + try testing.expect(math.isPositiveZero(pz_frac)); + try testing.expectEqual(@as(T, 1000.0), iptr.*); +} + +test "modf" { + try testModf(f32); + try testModf(f64); + try testModf(c_longdouble); +} + +fn testRint(comptime T: type) !void { + const f = switch (T) { + f32 => c.rintf, + f64 => c.rint, + else => @compileError("rint not implemented for" ++ @typeName(T)), + }; + + // Positive numbers round correctly + try testing.expectEqual(@as(T, 42.0), f(42.2)); + try testing.expectEqual(@as(T, 42.0), f(41.8)); + + // Negative numbers round correctly + try testing.expectEqual(@as(T, -6.0), f(-5.9)); + try testing.expectEqual(@as(T, -6.0), f(-6.1)); + + // No rounding needed test + try testing.expectEqual(@as(T, 5.0), f(5.0)); + try testing.expectEqual(@as(T, -10.0), f(-10.0)); + try testing.expectEqual(@as(T, 0.0), f(0.0)); + + // Very large numbers return unchanged + const large: T = 9007199254740992.0; // 2^53 + try testing.expectEqual(large, f(large)); + try testing.expectEqual(-large, f(-large)); + + // Small positive numbers round to zero + const pos_result = f(0.3); + try testing.expect(math.isPositiveZero(pos_result)); + + // Small negative numbers round to negative zero + const neg_result = f(-0.3); + try testing.expect(math.isNegativeZero(neg_result)); + + // Exact half rounds to nearest even (banker's rounding) + try testing.expectEqual(@as(T, 2.0), f(2.5)); + try testing.expectEqual(@as(T, 4.0), f(3.5)); +} + +test "rint" { + try testRint(f32); + try testRint(f64); +} diff --git a/test/c/search.zig b/test/c/search.zig new file mode 100644 index 0000000000000000000000000000000000000000..aa79c343680afe726926b1ccdd0ae649d3f068e7 --- /dev/null +++ b/test/c/search.zig @@ -0,0 +1,38 @@ +const builtin = @import("builtin"); +const std = @import("std"); + +const c = std.c; +const testing = std.testing; + +/// Not defined in `std.c` because C headers don't either. +const Node = extern struct { + next: ?*Node, + prev: ?*Node, +}; + +test "insque and remque" { + var first: Node = .{ .next = null, .prev = null }; + var second: Node = .{ .next = null, .prev = null }; + var third: Node = .{ .next = null, .prev = null }; + + c.insque(&first, null); + try testing.expectEqual(@as(?*Node, null), first.next); + try testing.expectEqual(@as(?*Node, null), first.prev); + + c.insque(&second, &first); + try testing.expectEqual(@as(?*Node, &second), first.next); + try testing.expectEqual(@as(?*Node, &first), second.prev); + + c.insque(&third, &first); + try testing.expectEqual(@as(?*Node, &third), first.next); + try testing.expectEqual(@as(?*Node, &second), third.next); + try testing.expectEqual(@as(?*Node, &first), third.prev); + try testing.expectEqual(@as(?*Node, &third), second.prev); + + c.remque(&third); + try testing.expectEqual(@as(?*Node, &second), first.next); + try testing.expectEqual(@as(?*Node, &first), second.prev); + + c.remque(&second); + try testing.expectEqual(@as(?*Node, null), first.next); +} diff --git a/test/c/stdlib.zig b/test/c/stdlib.zig new file mode 100644 index 0000000000000000000000000000000000000000..1491c9fa4a0aa03b51de3d0d35ca44d7ad703a4f --- /dev/null +++ b/test/c/stdlib.zig @@ -0,0 +1,109 @@ +const builtin = @import("builtin"); +const std = @import("std"); + +const c = std.c; +const fmt = std.fmt; +const math = std.math; +const testing = std.testing; + +test "abs" { + const val: c_int = -10; + try testing.expectEqual(10, c.abs(val)); +} + +test "labs" { + const val: c_long = -10; + try testing.expectEqual(10, c.labs(val)); +} + +test "llabs" { + const val: c_longlong = -10; + try testing.expectEqual(10, c.llabs(val)); +} + +test "div" { + const expected: c.div_t = .{ .quot = 5, .rem = 5 }; + try testing.expectEqual(expected, c.div(55, 10)); +} + +test "ldiv" { + const expected: c.ldiv_t = .{ .quot = -6, .rem = 2 }; + try testing.expectEqual(expected, c.ldiv(38, -6)); +} + +test "lldiv" { + const expected: c.lldiv_t = .{ .quot = 1, .rem = 2 }; + try testing.expectEqual(expected, c.lldiv(5, 3)); +} + +test "atoi" { + try testing.expectEqual(0, c.atoi(@ptrCast("stop42true"))); + try testing.expectEqual(42, c.atoi(@ptrCast("42true"))); + try testing.expectEqual(-1, c.atoi(@ptrCast("-01"))); + try testing.expectEqual(1, c.atoi(@ptrCast("+001"))); + try testing.expectEqual(100, c.atoi(@ptrCast(" 100"))); + try testing.expectEqual(500, c.atoi(@ptrCast("000000000000500"))); + try testing.expectEqual(1111, c.atoi(@ptrCast("0000000000001111_0000"))); + try testing.expectEqual(0, c.atoi(@ptrCast("0xAA"))); + try testing.expectEqual(700, c.atoi(@ptrCast("700B"))); + try testing.expectEqual(32453, c.atoi(@ptrCast("+32453more"))); + try testing.expectEqual(math.maxInt(c_int), c.atoi(@ptrCast(fmt.comptimePrint("{d}", .{math.maxInt(c_int)})))); + try testing.expectEqual(math.minInt(c_int), c.atoi(@ptrCast(fmt.comptimePrint("{d}", .{math.minInt(c_int)})))); +} + +test "atol" { + try testing.expectEqual(0, c.atol(@ptrCast("stop42true"))); + try testing.expectEqual(42, c.atol(@ptrCast("42true"))); + try testing.expectEqual(-1, c.atol(@ptrCast("-01"))); + try testing.expectEqual(1, c.atol(@ptrCast("+001"))); + try testing.expectEqual(100, c.atol(@ptrCast(" 100"))); + try testing.expectEqual(500, c.atol(@ptrCast("000000000000500"))); + try testing.expectEqual(1111, c.atol(@ptrCast("0000000000001111_0000"))); + try testing.expectEqual(0, c.atol(@ptrCast("0xAA"))); + try testing.expectEqual(700, c.atol(@ptrCast("700B"))); + try testing.expectEqual(32453, c.atol(@ptrCast("+32453more"))); + try testing.expectEqual(math.maxInt(c_long), c.atol(@ptrCast(fmt.comptimePrint("{d}", .{math.maxInt(c_long)})))); + try testing.expectEqual(math.minInt(c_long), c.atol(@ptrCast(fmt.comptimePrint("{d}", .{math.minInt(c_long)})))); +} + +test "atoll" { + try testing.expectEqual(0, c.atoll(@ptrCast("stop42true"))); + try testing.expectEqual(42, c.atoll(@ptrCast("42true"))); + try testing.expectEqual(-1, c.atoll(@ptrCast("-01"))); + try testing.expectEqual(1, c.atoll(@ptrCast("+001"))); + try testing.expectEqual(100, c.atoll(@ptrCast(" 100"))); + try testing.expectEqual(500, c.atoll(@ptrCast("000000000000500"))); + try testing.expectEqual(1111, c.atoll(@ptrCast("0000000000001111_0000"))); + try testing.expectEqual(0, c.atoll(@ptrCast("0xAA"))); + try testing.expectEqual(700, c.atoll(@ptrCast("700B"))); + try testing.expectEqual(32453, c.atoll(@ptrCast(" +32453more"))); + try testing.expectEqual(math.maxInt(c_longlong), c.atoll(@ptrCast(fmt.comptimePrint("{d}", .{math.maxInt(c_longlong)})))); + try testing.expectEqual(math.minInt(c_longlong), c.atoll(@ptrCast(fmt.comptimePrint("{d}", .{math.minInt(c_longlong)})))); +} + +test "bsearch" { + const Comparison = struct { + pub fn compare(a: *const anyopaque, b: *const anyopaque) callconv(.c) c_int { + const a_u16: *const u16 = @ptrCast(@alignCast(a)); + const b_u16: *const u16 = @ptrCast(@alignCast(b)); + + return switch (math.order(a_u16.*, b_u16.*)) { + .gt => 1, + .eq => 0, + .lt => -1, + }; + } + }; + + const items: []const u16 = &.{ 0, 5, 7, 9, 10, 200, 512, 768 }; + + try testing.expectEqual(@as(?*anyopaque, null), c.bsearch(&@as(u16, 2000), items.ptr, items.len, @sizeOf(u16), Comparison.compare)); + + for (items) |*value| { + try testing.expectEqual(@as(*const anyopaque, value), c.bsearch(value, items.ptr, items.len, @sizeOf(u16), Comparison.compare)); + } +} + +test { + _ = @import("stdlib/drand48.zig"); +} diff --git a/test/c/stdlib/drand48.zig b/test/c/stdlib/drand48.zig new file mode 100644 index 0000000000000000000000000000000000000000..6552730b5293b4bc350e0f2bd39cc0b634994c31 --- /dev/null +++ b/test/c/stdlib/drand48.zig @@ -0,0 +1,62 @@ +const builtin = @import("builtin"); +const std = @import("std"); + +const c = std.c; +const testing = std.testing; + +test "erand48" { + var xsubi: [3]c_ushort = .{ 37174, 64810, 11603 }; + + try testing.expectApproxEqAbs(0.8965, c.erand48(&xsubi), 0.0005); + try testing.expectEqualSlices(c_ushort, &.{ 22537, 47966, 58735 }, &xsubi); + + try testing.expectApproxEqAbs(0.3375, c.erand48(&xsubi), 0.0005); + try testing.expectEqualSlices(c_ushort, &.{ 37344, 32911, 22119 }, &xsubi); + + try testing.expectApproxEqAbs(0.6475, c.erand48(&xsubi), 0.0005); + try testing.expectEqualSlices(c_ushort, &.{ 23659, 29872, 42445 }, &xsubi); + + try testing.expectApproxEqAbs(0.5005, c.erand48(&xsubi), 0.0005); + try testing.expectEqualSlices(c_ushort, &.{ 31642, 7875, 32802 }, &xsubi); + + try testing.expectApproxEqAbs(0.5065, c.erand48(&xsubi), 0.0005); + try testing.expectEqualSlices(c_ushort, &.{ 64669, 14399, 33170 }, &xsubi); +} + +test "jrand48" { + var xsubi: [3]c_ushort = .{ 25175, 11052, 45015 }; + + try testing.expectEqual(1699503220, c.jrand48(&xsubi)); + try testing.expectEqualSlices(c_ushort, &.{ 2326, 23668, 25932 }, &xsubi); + + try testing.expectEqual(-992276007, c.jrand48(&xsubi)); + try testing.expectEqualSlices(c_ushort, &.{ 41577, 4569, 50395 }, &xsubi); + + try testing.expectEqual(-19535776, c.jrand48(&xsubi)); + try testing.expectEqualSlices(c_ushort, &.{ 31936, 59488, 65237 }, &xsubi); + + try testing.expectEqual(79438377, c.jrand48(&xsubi)); + try testing.expectEqualSlices(c_ushort, &.{ 40395, 8745, 1212 }, &xsubi); + + try testing.expectEqual(-1258917728, c.jrand48(&xsubi)); + try testing.expectEqualSlices(c_ushort, &.{ 37242, 28832, 46326 }, &xsubi); +} + +test "nrand48" { + var xsubi: [3]c_ushort = .{ 546, 33817, 23389 }; + + try testing.expectEqual(914920692, c.nrand48(&xsubi)); + try testing.expectEqualSlices(c_ushort, &.{ 29829, 10728, 27921 }, &xsubi); + + try testing.expectEqual(754104482, c.nrand48(&xsubi)); + try testing.expectEqualSlices(c_ushort, &.{ 6828, 28997, 23013 }, &xsubi); + + try testing.expectEqual(609453945, c.nrand48(&xsubi)); + try testing.expectEqualSlices(c_ushort, &.{ 58183, 3826, 18599 }, &xsubi); + + try testing.expectEqual(1878644360, c.nrand48(&xsubi)); + try testing.expectEqualSlices(c_ushort, &.{ 36678, 44304, 57331 }, &xsubi); + + try testing.expectEqual(2114923686, c.nrand48(&xsubi)); + try testing.expectEqualSlices(c_ushort, &.{ 58585, 22861, 64542 }, &xsubi); +} diff --git a/test/c/string.zig b/test/c/string.zig new file mode 100644 index 0000000000000000000000000000000000000000..255c147eae2a0a8ba4df4eaeade89bd2cd0b7081 --- /dev/null +++ b/test/c/string.zig @@ -0,0 +1,12 @@ +const builtin = @import("builtin"); +const std = @import("std"); + +const c = std.c; +const testing = std.testing; + +test "strncmp" { + try testing.expect(c.strncmp(@ptrCast("a"), @ptrCast("b"), 1) < 0); + try testing.expect(c.strncmp(@ptrCast("a"), @ptrCast("c"), 1) < 0); + try testing.expect(c.strncmp(@ptrCast("b"), @ptrCast("a"), 1) > 0); + try testing.expect(c.strncmp(@ptrCast("\xff"), @ptrCast("\x02"), 1) > 0); +} diff --git a/test/c/strings.zig b/test/c/strings.zig new file mode 100644 index 0000000000000000000000000000000000000000..08bad240affe251b07eee825c41412947e1a04e0 --- /dev/null +++ b/test/c/strings.zig @@ -0,0 +1,57 @@ +const builtin = @import("builtin"); +const std = @import("std"); + +const c = std.c; +const mem = std.mem; +const testing = std.testing; + +test "bzero" { + var array: [10]u8 = [_]u8{ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }; + var a = mem.zeroes([array.len]u8); + a[9] = '0'; + c.bzero(&array[0], 9); + try testing.expect(mem.eql(u8, &array, &a)); +} + +fn testFfs(comptime T: type) !void { + const ffs = switch (T) { + c_int => c.ffs, + c_long => c.ffsl, + c_longlong => c.ffsll, + else => unreachable, + }; + + try testing.expectEqual(0, ffs(0)); + + for (0..@bitSizeOf(T)) |i| { + const bit = @as(T, 1) << @intCast(i); + + try testing.expectEqual(@as(T, @intCast(i + 1)), ffs(bit)); + } +} + +test "ffs" { + try testFfs(c_int); + try testFfs(c_long); + try testFfs(c_longlong); +} + +test "strcasecmp" { + try testing.expect(c.strcasecmp(@ptrCast("a"), @ptrCast("b")) < 0); + try testing.expect(c.strcasecmp(@ptrCast("b"), @ptrCast("a")) > 0); + try testing.expect(c.strcasecmp(@ptrCast("A"), @ptrCast("b")) < 0); + try testing.expect(c.strcasecmp(@ptrCast("b"), @ptrCast("A")) > 0); + try testing.expect(c.strcasecmp(@ptrCast("A"), @ptrCast("A")) == 0); + try testing.expect(c.strcasecmp(@ptrCast("B"), @ptrCast("b")) == 0); + try testing.expect(c.strcasecmp(@ptrCast("bb"), @ptrCast("AA")) > 0); +} + +test "strncasecmp" { + try testing.expect(c.strncasecmp(@ptrCast("a"), @ptrCast("b"), 1) < 0); + try testing.expect(c.strncasecmp(@ptrCast("b"), @ptrCast("a"), 1) > 0); + try testing.expect(c.strncasecmp(@ptrCast("A"), @ptrCast("b"), 1) < 0); + try testing.expect(c.strncasecmp(@ptrCast("b"), @ptrCast("A"), 1) > 0); + try testing.expect(c.strncasecmp(@ptrCast("A"), @ptrCast("A"), 1) == 0); + try testing.expect(c.strncasecmp(@ptrCast("B"), @ptrCast("b"), 1) == 0); + try testing.expect(c.strncasecmp(@ptrCast("bb"), @ptrCast("AA"), 2) > 0); +} diff --git a/test/c/unistd.zig b/test/c/unistd.zig new file mode 100644 index 0000000000000000000000000000000000000000..3c99bd2f483dfea1f62c4f23554425e121d89d2e --- /dev/null +++ b/test/c/unistd.zig @@ -0,0 +1,31 @@ +const builtin = @import("builtin"); +const std = @import("std"); + +const c = std.c; +const testing = std.testing; + +test "swab" { + var a: [4]u8 = undefined; + @memset(a[0..], '\x00'); + c.swab("abcd", &a, 4); + try testing.expectEqualSlices(u8, "badc", &a); + + // Partial copy + @memset(a[0..], '\x00'); + c.swab("abcd", &a, 2); + try testing.expectEqualSlices(u8, "ba\x00\x00", &a); + + // n < 1 + @memset(a[0..], '\x00'); + c.swab("abcd", &a, 0); + try testing.expectEqualSlices(u8, "\x00" ** 4, &a); + c.swab("abcd", &a, -1); + try testing.expectEqualSlices(u8, "\x00" ** 4, &a); + + // Odd n + @memset(a[0..], '\x00'); + c.swab("abcd", &a, 1); + try testing.expectEqualSlices(u8, "\x00" ** 4, &a); + c.swab("abcd", &a, 3); + try testing.expectEqualSlices(u8, "ba\x00\x00", &a); +} diff --git a/test/tests.zig b/test/tests.zig index 9887b0f024894db00f014ccbf7de389dc1b31520..f8187b1618022c24c9f4747bba8a43150101df8a 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -18,7 +18,7 @@ pub const DebuggerContext = @import("src/Debugger.zig"); pub const LlvmIrContext = @import("src/LlvmIr.zig"); pub const LibcContext = @import("src/Libc.zig"); -const TestTarget = struct { +const ModuleTestTarget = struct { linkage: ?std.builtin.LinkMode = null, target: std.Target.Query = .{}, optimize_mode: std.builtin.OptimizeMode = .Debug, @@ -36,33 +36,14 @@ const TestTarget = struct { // invocation. This could be because of a slow backend, requiring a newer LLVM version, being // too niche, etc. extra_target: bool = false, - - pub fn supportsModule( - self: *const TestTarget, - target: *const std.Build.ResolvedTarget, - name: []const u8, - ) bool { - if (mem.eql(u8, name, "zigc")) { - if (target.result.isMuslLibC()) return self.linkage == .static or (self.linkage == null and !target.query.isNative()); - if (target.result.isMinGW()) return true; - if (target.result.isWasiLibC()) return true; - return false; - } - if (mem.eql(u8, name, "std")) { - if (target.result.cpu.arch.isSpirV()) return false; - return true; - } - - return true; - } }; -const test_targets = blk: { +const module_test_targets = blk: { // getBaselineCpuFeatures calls populateDependencies which has a O(N ^ 2) algorithm // (where N is roughly 160, which technically makes it O(1), but it adds up to a // lot of branches) @setEvalBranchQuota(80_000); - break :blk [_]TestTarget{ + break :blk [_]ModuleTestTarget{ // Native Targets .{}, // 0 index must be all defaults @@ -2475,24 +2456,21 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { const step = b.step(b.fmt("test-{s}", .{options.name}), options.desc); if (options.test_only) |test_only| { - const test_target: TestTarget = switch (test_only) { - .default => test_targets[0], + const test_target: ModuleTestTarget = switch (test_only) { + .default => module_test_targets[0], .fuzz => |optimize| .{ .optimize_mode = optimize, .use_llvm = true, }, }; const resolved_target = b.resolveTargetQuery(test_target.target); - - if (test_target.supportsModule(&resolved_target, options.name)) { - const triple_txt = resolved_target.query.zigTriple(b.allocator) catch @panic("OOM"); - addOneModuleTest(b, step, test_target, &resolved_target, triple_txt, options); - } + const triple_txt = resolved_target.query.zigTriple(b.allocator) catch @panic("OOM"); + addOneModuleTest(b, step, test_target, &resolved_target, triple_txt, options); return step; } - for_targets: for (test_targets) |test_target| { + for_targets: for (module_test_targets) |test_target| { if (test_target.skip_modules.len > 0) { for (test_target.skip_modules) |skip_mod| { if (std.mem.eql(u8, options.name, skip_mod)) continue :for_targets; @@ -2501,8 +2479,6 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { const resolved_target = b.resolveTargetQuery(test_target.target); - if (!test_target.supportsModule(&resolved_target, options.name)) continue; - if (!options.test_extra_targets and test_target.extra_target) continue; if (options.skip_non_native and !test_target.target.isNative()) @@ -2510,6 +2486,13 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { const target = &resolved_target.result; + if (std.mem.eql(u8, options.name, "libc")) { + // The libc API tests obviously need to link libc. So for test + // target entries where we wouldn't link libc by default, skip the + // libc API tests. + if (test_target.link_libc == null and !std.os.targetRequiresLibC(target)) continue; + } + if (options.skip_spirv and target.cpu.arch.isSpirV()) continue; if (options.skip_wasm and target.cpu.arch.isWasm()) continue; @@ -2544,8 +2527,6 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { if (!would_use_llvm and target.cpu.arch == .aarch64) { // TODO get std tests passing for the aarch64 self-hosted backend. if (mem.eql(u8, options.name, "std")) continue; - // TODO get zigc tests passing for the aarch64 self-hosted backend. - if (mem.eql(u8, options.name, "zigc")) continue; } const want_this_mode = for (options.optimize_modes) |m| { @@ -2561,7 +2542,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { fn addOneModuleTest( b: *std.Build, step: *Step, - test_target: TestTarget, + test_target: ModuleTestTarget, resolved_target: *const std.Build.ResolvedTarget, triple_txt: []const u8, options: ModuleTestOptions, @@ -2598,11 +2579,11 @@ fn addOneModuleTest( }); these_tests.linkage = test_target.linkage; // https://codeberg.org/ziglang/zig/issues/31701 - if (!(mem.eql(u8, options.name, "compiler-rt") or mem.eql(u8, options.name, "zigc"))) { + if (!(mem.eql(u8, options.name, "compiler-rt") or mem.eql(u8, options.name, "libc"))) { if (options.no_builtin) these_tests.root_module.no_builtin = true; } // https://codeberg.org/ziglang/zig/issues/31702 - if (mem.eql(u8, options.name, "compiler-rt") or mem.eql(u8, options.name, "zigc")) { + if (mem.eql(u8, options.name, "compiler-rt") or mem.eql(u8, options.name, "libc")) { these_tests.root_module.stack_protector = false; } if (options.build_options) |build_options| { @@ -2992,7 +2973,7 @@ pub fn addLlvmIrTests(b: *std.Build, options: LlvmIrContext.Options) ?*Step { return step; } -const libc_targets: []const std.Target.Query = &.{ +const libc_test_nsz_targets: []const std.Target.Query = &.{ .{ .cpu_arch = .arm, .os_tag = .linux, @@ -3155,8 +3136,8 @@ const libc_targets: []const std.Target.Query = &.{ }, }; -pub fn addLibcTests(b: *std.Build, options: LibcContext.Options) ?*Step { - const step = b.step("test-libc", "Run libc-test test cases"); +pub fn addLibcTestNszTests(b: *std.Build, options: LibcContext.Options) ?*Step { + const step = b.step("test-libc-nsz", "Run external libc-test test cases"); const opt_libc_test_path = b.option(std.Build.LazyPath, "libc-test-path", "path to libc-test source directory"); if (opt_libc_test_path) |libc_test_path| { var context: LibcContext = .{ @@ -3168,7 +3149,7 @@ pub fn addLibcTests(b: *std.Build, options: LibcContext.Options) ?*Step { libc.addCases(&context); - for (libc_targets) |target_query| { + for (libc_test_nsz_targets) |target_query| { const target = b.resolveTargetQuery(target_query); context.addTarget(target); } -- 2.54.0 From 6612e7cdb26a761392f129525872bd00758095c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 16 Apr 2026 21:16:25 +0200 Subject: [PATCH 4/4] libzigc: skip tests that are failing under various targets/conditions --- test/c/inttypes.zig | 3 +++ test/c/math.zig | 3 +++ test/c/search.zig | 2 ++ test/c/stdlib.zig | 16 ++++++++++++++++ test/c/stdlib/drand48.zig | 8 ++++++++ test/c/strings.zig | 11 +++++++++++ test/c/unistd.zig | 4 ++++ 7 files changed, 47 insertions(+) diff --git a/test/c/inttypes.zig b/test/c/inttypes.zig index 9b9cbfe966f4f5d61396e2b461997517a21c40fc..ff3cdd7cd78f6e6a1c1e5cbaeb715cadf7c2060f 100644 --- a/test/c/inttypes.zig +++ b/test/c/inttypes.zig @@ -10,6 +10,9 @@ test "imaxabs" { } test "imaxdiv" { + if (builtin.target.cpu.arch.isPowerPC32()) return error.SkipZigTest; // TODO + if (builtin.target.cpu.arch == .s390x) return error.SkipZigTest; // TODO + const expected: c.imaxdiv_t = .{ .quot = 9, .rem = 0 }; try testing.expectEqual(expected, c.imaxdiv(9, 1)); } diff --git a/test/c/math.zig b/test/c/math.zig index cf372dc0be5551537edca6e1f7e384ea2b29961a..fe17e23abea158df1e19c5ca4fd502d848f87ba7 100644 --- a/test/c/math.zig +++ b/test/c/math.zig @@ -54,6 +54,9 @@ fn testModf(comptime T: type) !void { test "modf" { try testModf(f32); try testModf(f64); + + if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO + try testModf(c_longdouble); } diff --git a/test/c/search.zig b/test/c/search.zig index aa79c343680afe726926b1ccdd0ae649d3f068e7..392325b842db117079fc7a82a98fe371b8562986 100644 --- a/test/c/search.zig +++ b/test/c/search.zig @@ -11,6 +11,8 @@ const Node = extern struct { }; test "insque and remque" { + if (builtin.target.os.tag == .windows) return; // no insque/remque + var first: Node = .{ .next = null, .prev = null }; var second: Node = .{ .next = null, .prev = null }; var third: Node = .{ .next = null, .prev = null }; diff --git a/test/c/stdlib.zig b/test/c/stdlib.zig index 1491c9fa4a0aa03b51de3d0d35ca44d7ad703a4f..11ac5de2bc27462b6b24f26ccf01cd522adc74a5 100644 --- a/test/c/stdlib.zig +++ b/test/c/stdlib.zig @@ -7,11 +7,15 @@ const math = std.math; const testing = std.testing; test "abs" { + if (builtin.target.cpu.arch.isMIPS64()) return error.SkipZigTest; // TODO + const val: c_int = -10; try testing.expectEqual(10, c.abs(val)); } test "labs" { + if (builtin.target.cpu.arch.isMIPS64() and @sizeOf(usize) == 4) return error.SkipZigTest; // TODO + const val: c_long = -10; try testing.expectEqual(10, c.labs(val)); } @@ -22,16 +26,28 @@ test "llabs" { } test "div" { + if (builtin.target.cpu.arch.isLoongArch()) return error.SkipZigTest; // TODO + if (builtin.target.cpu.arch.isMIPS64()) return error.SkipZigTest; // TODO + if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO + if (builtin.target.cpu.arch == .s390x) return error.SkipZigTest; // TODO + const expected: c.div_t = .{ .quot = 5, .rem = 5 }; try testing.expectEqual(expected, c.div(55, 10)); } test "ldiv" { + if (builtin.target.cpu.arch.isMIPS64() and @sizeOf(usize) == 4) return error.SkipZigTest; // TODO + if (builtin.target.cpu.arch.isPowerPC32()) return error.SkipZigTest; // TODO + if (builtin.target.cpu.arch == .s390x) return error.SkipZigTest; // TODO + const expected: c.ldiv_t = .{ .quot = -6, .rem = 2 }; try testing.expectEqual(expected, c.ldiv(38, -6)); } test "lldiv" { + if (builtin.target.cpu.arch.isPowerPC32()) return error.SkipZigTest; // TODO + if (builtin.target.cpu.arch == .s390x) return error.SkipZigTest; // TODO + const expected: c.lldiv_t = .{ .quot = 1, .rem = 2 }; try testing.expectEqual(expected, c.lldiv(5, 3)); } diff --git a/test/c/stdlib/drand48.zig b/test/c/stdlib/drand48.zig index 6552730b5293b4bc350e0f2bd39cc0b634994c31..61bc08c6b78dedd09308c162a94e308d65662aa6 100644 --- a/test/c/stdlib/drand48.zig +++ b/test/c/stdlib/drand48.zig @@ -5,6 +5,8 @@ const c = std.c; const testing = std.testing; test "erand48" { + if (builtin.target.os.tag == .windows) return; // no erand48 + var xsubi: [3]c_ushort = .{ 37174, 64810, 11603 }; try testing.expectApproxEqAbs(0.8965, c.erand48(&xsubi), 0.0005); @@ -24,6 +26,10 @@ test "erand48" { } test "jrand48" { + if (builtin.target.os.tag == .windows) return; // no jrand48 + + if (builtin.target.os.tag == .openbsd) return error.SkipZigTest; // TODO + var xsubi: [3]c_ushort = .{ 25175, 11052, 45015 }; try testing.expectEqual(1699503220, c.jrand48(&xsubi)); @@ -43,6 +49,8 @@ test "jrand48" { } test "nrand48" { + if (builtin.target.os.tag == .windows) return; // no nrand48 + var xsubi: [3]c_ushort = .{ 546, 33817, 23389 }; try testing.expectEqual(914920692, c.nrand48(&xsubi)); diff --git a/test/c/strings.zig b/test/c/strings.zig index 08bad240affe251b07eee825c41412947e1a04e0..3675b0396d17e3978d1a539d578c23a37dd81a0f 100644 --- a/test/c/strings.zig +++ b/test/c/strings.zig @@ -6,6 +6,8 @@ const mem = std.mem; const testing = std.testing; test "bzero" { + if (builtin.target.os.tag == .windows) return; // no bzero + var array: [10]u8 = [_]u8{ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }; var a = mem.zeroes([array.len]u8); a[9] = '0'; @@ -31,8 +33,17 @@ fn testFfs(comptime T: type) !void { } test "ffs" { + if (builtin.target.os.tag == .openbsd) return; // no ffsl/ffsll + if (builtin.target.os.tag == .windows) return; // no ffs + try testFfs(c_int); + + if (builtin.target.os.tag == .netbsd) return; // no ffsl/ffsll until 11 + try testFfs(c_long); + + if (@sizeOf(usize) == 4) return error.SkipZigTest; // TODO + try testFfs(c_longlong); } diff --git a/test/c/unistd.zig b/test/c/unistd.zig index 3c99bd2f483dfea1f62c4f23554425e121d89d2e..42090e1cc2a0d1c92b52b520eeb72ef6bcd7a0a8 100644 --- a/test/c/unistd.zig +++ b/test/c/unistd.zig @@ -5,6 +5,10 @@ const c = std.c; const testing = std.testing; test "swab" { + if (builtin.target.cpu.arch.isMIPS64() and @sizeOf(usize) == 4) return error.SkipZigTest; // TODO + if (builtin.target.cpu.arch == .x86_64 and @sizeOf(usize) == 4) return error.SkipZigTest; // TODO + if (builtin.target.os.tag == .netbsd) return error.SkipZigTest; // TODO + var a: [4]u8 = undefined; @memset(a[0..], '\x00'); c.swab("abcd", &a, 4); -- 2.54.0