Merge pull request 'libzigc: move all unit tests from lib/c/ to test/c/' (#31923) from alexrp/zig:libc-test-refactor into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31923
Reviewed-by: Andrew Kelley <andrew@ziglang.org>
This commit is contained in:
Alex Rønne Petersen
2026-04-17 22:31:35 +02:00
24 changed files with 635 additions and 459 deletions
+30 -30
View File
@@ -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 {
+1 -2
View File
@@ -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");
-10
View File
@@ -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));
}
-100
View File
@@ -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 = &int;
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);
}
+1 -27
View File
@@ -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);
}
-100
View File
@@ -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));
}
}
-57
View File
@@ -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);
}
-7
View File
@@ -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);
}
-38
View File
@@ -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);
}
-26
View File
@@ -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) {
+53 -6
View File
@@ -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.
+41
View File
@@ -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;
+1 -10
View File
@@ -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: {
+12
View File
@@ -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");
}
+18
View File
@@ -0,0 +1,18 @@
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" {
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));
}
+104
View File
@@ -0,0 +1,104 @@
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 = &int;
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);
if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO
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);
}
+40
View File
@@ -0,0 +1,40 @@
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" {
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 };
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);
}
+125
View File
@@ -0,0 +1,125 @@
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" {
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));
}
test "llabs" {
const val: c_longlong = -10;
try testing.expectEqual(10, c.llabs(val));
}
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));
}
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");
}
+70
View File
@@ -0,0 +1,70 @@
const builtin = @import("builtin");
const std = @import("std");
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);
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" {
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));
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" {
if (builtin.target.os.tag == .windows) return; // no 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);
}
+12
View File
@@ -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);
}
+68
View File
@@ -0,0 +1,68 @@
const builtin = @import("builtin");
const std = @import("std");
const c = std.c;
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';
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" {
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);
}
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);
}
+35
View File
@@ -0,0 +1,35 @@
const builtin = @import("builtin");
const std = @import("std");
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);
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);
}
+1 -4
View File
@@ -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) {
+23 -42
View File
@@ -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;
@@ -2531,7 +2514,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.
@@ -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);
}