Commit 50056a5b3a

Matthew Lugg <mlugg@mlugg.co.uk>
2025-10-15 00:01:13
compiler: rename `--test-timeout-ms` to `--test-timeout`
The unit can now be specified in the argument.
1 parent db8330a
ci/aarch64-linux-debug.sh
@@ -51,7 +51,7 @@ stage3-debug/bin/zig build test docs \
   --search-prefix "$PREFIX" \
   --zig-lib-dir "$PWD/../lib" \
   -Denable-superhtml \
-  --test-timeout-ms 60_000
+  --test-timeout 60s
 
 stage3-debug/bin/zig build \
   --prefix stage4-debug \
ci/aarch64-linux-release.sh
@@ -51,7 +51,7 @@ stage3-release/bin/zig build test docs \
   --search-prefix "$PREFIX" \
   --zig-lib-dir "$PWD/../lib" \
   -Denable-superhtml \
-  --test-timeout-ms 60_000
+  --test-timeout 60s
 
 # Ensure that stage3 and stage4 are byte-for-byte identical.
 stage3-release/bin/zig build \
ci/aarch64-macos-debug.sh
@@ -47,4 +47,4 @@ stage3-debug/bin/zig build test docs \
   -Dstatic-llvm \
   -Dskip-non-native \
   --search-prefix "$PREFIX" \
-  --test-timeout-ms 60_000
+  --test-timeout 60s
ci/aarch64-macos-release.sh
@@ -47,7 +47,7 @@ stage3-release/bin/zig build test docs \
   -Dstatic-llvm \
   -Dskip-non-native \
   --search-prefix "$PREFIX" \
-  --test-timeout-ms 60_000
+  --test-timeout 60s
 
 # Ensure that stage3 and stage4 are byte-for-byte identical.
 stage3-release/bin/zig build \
ci/aarch64-windows.ps1
@@ -59,7 +59,7 @@ Write-Output "Main test suite..."
   -Dstatic-llvm `
   -Dskip-non-native `
   -Denable-symlinks-windows `
-  --test-timeout-ms 60_000
+  --test-timeout 60s
 CheckLastExitCode
 
 # Ensure that stage3 and stage4 are byte-for-byte identical.
ci/loongarch64-linux-debug.sh
@@ -51,7 +51,7 @@ stage3-debug/bin/zig build test docs \
   -Dtarget=native-native-musl \
   --search-prefix "$PREFIX" \
   --zig-lib-dir "$PWD/../lib" \
-  --test-timeout-ms 120_000
+  --test-timeout 2m
 
 stage3-debug/bin/zig build \
   --prefix stage4-debug \
ci/loongarch64-linux-release.sh
@@ -51,7 +51,7 @@ stage3-release/bin/zig build test docs \
   -Dtarget=native-native-musl \
   --search-prefix "$PREFIX" \
   --zig-lib-dir "$PWD/../lib" \
-  --test-timeout-ms 120_000
+  --test-timeout 2m
 
 # Ensure that stage3 and stage4 are byte-for-byte identical.
 stage3-release/bin/zig build \
ci/riscv64-linux-debug.sh
@@ -52,4 +52,4 @@ stage3-debug/bin/zig build test-cases test-modules test-unit test-c-abi test-sta
   -Dtarget=native-native-musl \
   --search-prefix "$PREFIX" \
   --zig-lib-dir "$PWD/../lib" \
-  --test-timeout-ms 120_000
+  --test-timeout 2m
ci/riscv64-linux-release.sh
@@ -52,4 +52,4 @@ stage3-release/bin/zig build test-cases test-modules test-unit test-c-abi test-s
   -Dtarget=native-native-musl \
   --search-prefix "$PREFIX" \
   --zig-lib-dir "$PWD/../lib" \
-  --test-timeout-ms 120_000
+  --test-timeout 2m
ci/x86_64-freebsd-debug.sh
@@ -52,7 +52,7 @@ stage3-debug/bin/zig build test docs \
   -Dskip-macos \
   --search-prefix "$PREFIX" \
   --zig-lib-dir "$PWD/../lib" \
-  --test-timeout-ms 60_000
+  --test-timeout 60s
 
 stage3-debug/bin/zig build \
   --prefix stage4-debug \
ci/x86_64-freebsd-release.sh
@@ -52,7 +52,7 @@ stage3-release/bin/zig build test docs \
   -Dskip-macos \
   --search-prefix "$PREFIX" \
   --zig-lib-dir "$PWD/../lib" \
-  --test-timeout-ms 60_000
+  --test-timeout 60s
 
 # Ensure that stage3 and stage4 are byte-for-byte identical.
 stage3-release/bin/zig build \
ci/x86_64-linux-debug-llvm.sh
@@ -62,4 +62,4 @@ stage3-debug/bin/zig build test docs \
   --search-prefix "$PREFIX" \
   --zig-lib-dir "$PWD/../lib" \
   -Denable-superhtml \
-  --test-timeout-ms 240_000
+  --test-timeout 4m
ci/x86_64-linux-debug.sh
@@ -62,4 +62,4 @@ stage3-debug/bin/zig build test docs \
   --search-prefix "$PREFIX" \
   --zig-lib-dir "$PWD/../lib" \
   -Denable-superhtml \
-  --test-timeout-ms 240_000
+  --test-timeout 4m
ci/x86_64-linux-release.sh
@@ -64,7 +64,7 @@ stage3-release/bin/zig build test docs \
   --search-prefix "$PREFIX" \
   --zig-lib-dir "$PWD/../lib" \
   -Denable-superhtml \
-  --test-timeout-ms 240_000
+  --test-timeout 4m
 
 # Ensure that stage3 and stage4 are byte-for-byte identical.
 stage3-release/bin/zig build \
ci/x86_64-windows-debug.ps1
@@ -60,7 +60,7 @@ Write-Output "Main test suite..."
   -Dskip-non-native `
   -Dskip-release `
   -Denable-symlinks-windows `
-  --test-timeout-ms 240_000
+  --test-timeout 4m
 CheckLastExitCode
 
 Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..."
ci/x86_64-windows-release.ps1
@@ -59,7 +59,7 @@ Write-Output "Main test suite..."
   -Dstatic-llvm `
   -Dskip-non-native `
   -Denable-symlinks-windows `
-  --test-timeout-ms 240_000
+  --test-timeout 4m
 CheckLastExitCode
 
 # Ensure that stage3 and stage4 are byte-for-byte identical.
lib/compiler/build_runner.zig
@@ -108,7 +108,7 @@ pub fn main() !void {
     var summary: ?Summary = null;
     var max_rss: u64 = 0;
     var skip_oom_steps = false;
-    var test_timeout_ms: ?u64 = null;
+    var test_timeout_ns: ?u64 = null;
     var color: Color = .auto;
     var help_menu = false;
     var steps_menu = false;
@@ -189,14 +189,41 @@ pub fn main() !void {
                 };
             } else if (mem.eql(u8, arg, "--skip-oom-steps")) {
                 skip_oom_steps = true;
-            } else if (mem.eql(u8, arg, "--test-timeout-ms")) {
-                const millis_str = nextArgOrFatal(args, &arg_idx);
-                test_timeout_ms = std.fmt.parseInt(u64, millis_str, 10) catch |err| {
-                    std.debug.print("invalid millisecond count: '{s}': {s}\n", .{
-                        millis_str, @errorName(err),
-                    });
-                    process.exit(1);
+            } else if (mem.eql(u8, arg, "--test-timeout")) {
+                const units: []const struct { []const u8, u64 } = &.{
+                    .{ "ns", 1 },
+                    .{ "nanosecond", 1 },
+                    .{ "us", std.time.ns_per_us },
+                    .{ "microsecond", std.time.ns_per_us },
+                    .{ "ms", std.time.ns_per_ms },
+                    .{ "millisecond", std.time.ns_per_ms },
+                    .{ "s", std.time.ns_per_s },
+                    .{ "second", std.time.ns_per_s },
+                    .{ "m", std.time.ns_per_min },
+                    .{ "minute", std.time.ns_per_min },
+                    .{ "h", std.time.ns_per_hour },
+                    .{ "hour", std.time.ns_per_hour },
                 };
+                const timeout_str = nextArgOrFatal(args, &arg_idx);
+                const num_end_idx = std.mem.findLastNone(u8, timeout_str, "abcdefghijklmnopqrstuvwxyz") orelse fatal(
+                    "invalid timeout '{s}': expected unit (ns, us, ms, s, m, h)",
+                    .{timeout_str},
+                );
+                const num_str = timeout_str[0 .. num_end_idx + 1];
+                const unit_str = timeout_str[num_end_idx + 1 ..];
+                const unit_factor: f64 = for (units) |unit_and_factor| {
+                    if (std.mem.eql(u8, unit_str, unit_and_factor[0])) {
+                        break @floatFromInt(unit_and_factor[1]);
+                    }
+                } else fatal(
+                    "invalid timeout '{s}': invalid unit '{s}' (expected ns, us, ms, s, m, h)",
+                    .{ timeout_str, unit_str },
+                );
+                const num_parsed = std.fmt.parseFloat(f64, num_str) catch |err| fatal(
+                    "invalid timeout '{s}': invalid number '{s}' ({t})",
+                    .{ timeout_str, num_str, err },
+                );
+                test_timeout_ns = std.math.lossyCast(u64, unit_factor * num_parsed);
             } else if (mem.eql(u8, arg, "--search-prefix")) {
                 const search_prefix = nextArgOrFatal(args, &arg_idx);
                 builder.addSearchPrefix(search_prefix);
@@ -480,10 +507,7 @@ pub fn main() !void {
         .max_rss_is_default = false,
         .max_rss_mutex = .{},
         .skip_oom_steps = skip_oom_steps,
-        .unit_test_timeout_ns = ns: {
-            const ms = test_timeout_ms orelse break :ns null;
-            break :ns std.math.mul(u64, ms, std.time.ns_per_ms) catch null;
-        },
+        .unit_test_timeout_ns = test_timeout_ns,
 
         .watch = watch,
         .web_server = undefined, // set after `prepare`
@@ -1584,7 +1608,8 @@ fn printUsage(b: *std.Build, w: *Writer) !void {
         \\  -j<N>                        Limit concurrent jobs (default is to use all CPU cores)
         \\  --maxrss <bytes>             Limit memory usage (default is to use available memory)
         \\  --skip-oom-steps             Instead of failing, skip steps that would exceed --maxrss
-        \\  --test-timeout-ms <ms>       Limit execution time of unit tests, terminating if exceeded
+        \\  --test-timeout <timeout>     Limit execution time of unit tests, terminating if exceeded.
+        \\                               The timeout must include a unit: ns, us, ms, s, m, h
         \\  --fetch[=mode]               Fetch dependency tree (optionally choose laziness) and exit
         \\    needed                     (Default) Lazy dependencies are fetched as needed
         \\    all                        Lazy dependencies are always fetched
lib/std/Build/Step/Run.zig
@@ -1814,7 +1814,7 @@ fn pollZigTest(
     // test. For instance, if the test runner leaves this much time between us requesting a test to
     // start and it acknowledging the test starting, we terminate the child and raise an error. This
     // *should* never happen, but could in theory be caused by some very unlucky IB in a test.
-    const response_timeout_ns = options.unit_test_timeout_ns orelse 60 * std.time.ns_per_s;
+    const response_timeout_ns = @max(options.unit_test_timeout_ns orelse 0, 60 * std.time.ns_per_s);
 
     const stdout = poller.reader(.stdout);
     const stderr = poller.reader(.stderr);