Commit 33c7984183

Andrew Kelley <andrew@ziglang.org>
2024-07-23 01:03:11
add std.testing.random_seed
closes #17609
1 parent 6f3e993
lib/compiler/build_runner.zig
@@ -97,7 +97,6 @@ pub fn main() !void {
     var max_rss: u64 = 0;
     var skip_oom_steps = false;
     var color: Color = .auto;
-    var seed: u32 = 0;
     var prominent_compile_errors = false;
     var help_menu = false;
     var steps_menu = false;
@@ -188,7 +187,7 @@ pub fn main() !void {
             } else if (mem.eql(u8, arg, "--seed")) {
                 const next_arg = nextArg(args, &arg_idx) orelse
                     fatalWithHint("expected u32 after '{s}'", .{arg});
-                seed = std.fmt.parseUnsigned(u32, next_arg, 0) catch |err| {
+                graph.random_seed = std.fmt.parseUnsigned(u32, next_arg, 0) catch |err| {
                     fatal("unable to parse seed '{s}' as unsigned 32-bit integer: {s}\n", .{
                         next_arg, @errorName(err),
                     });
@@ -371,7 +370,7 @@ pub fn main() !void {
     }
 
     const gpa = arena;
-    prepare(gpa, arena, builder, targets.items, &run, seed) catch |err| switch (err) {
+    prepare(gpa, arena, builder, targets.items, &run, graph.random_seed) catch |err| switch (err) {
         error.UncleanExit => process.exit(1),
         else => return err,
     };
lib/compiler/test_runner.zig
@@ -24,6 +24,9 @@ pub fn main() void {
     for (args[1..]) |arg| {
         if (std.mem.eql(u8, arg, "--listen=-")) {
             listen = true;
+        } else if (std.mem.startsWith(u8, arg, "--seed=")) {
+            std.testing.random_seed = std.fmt.parseUnsigned(u32, arg["--seed=".len..], 0) catch
+                @panic("unable to parse --seed command line argument");
         } else {
             @panic("unrecognized command line argument");
         }
lib/std/Build/Step/Run.zig
@@ -185,8 +185,13 @@ pub fn setName(run: *Run, name: []const u8) void {
 }
 
 pub fn enableTestRunnerMode(run: *Run) void {
+    const b = run.step.owner;
+    const arena = b.allocator;
     run.stdio = .zig_test;
-    run.addArgs(&.{"--listen=-"});
+    run.addArgs(&.{
+        std.fmt.allocPrint(arena, "--seed=0x{x}", .{b.graph.random_seed}) catch @panic("OOM"),
+        "--listen=-",
+    });
 }
 
 pub fn addArtifactArg(run: *Run, artifact: *Step.Compile) void {
lib/std/heap/arena_allocator.zig
@@ -250,7 +250,7 @@ test "reset with preheating" {
     var arena_allocator = ArenaAllocator.init(std.testing.allocator);
     defer arena_allocator.deinit();
     // provides some variance in the allocated data
-    var rng_src = std.Random.DefaultPrng.init(19930913);
+    var rng_src = std.Random.DefaultPrng.init(std.testing.random_seed);
     const random = rng_src.random();
     var rounds: usize = 25;
     while (rounds > 0) {
lib/std/math/big/rational.zig
@@ -594,7 +594,7 @@ test "toFloat" {
 test "set/to Float round-trip" {
     var a = try Rational.init(testing.allocator);
     defer a.deinit();
-    var prng = std.Random.DefaultPrng.init(0x5EED);
+    var prng = std.Random.DefaultPrng.init(std.testing.random_seed);
     const random = prng.random();
     var i: usize = 0;
     while (i < 512) : (i += 1) {
lib/std/os/linux/IoUring.zig
@@ -3995,7 +3995,7 @@ test "ring mapped buffers recv" {
     defer fds.close();
 
     // for random user_data in sqe/cqe
-    var Rnd = std.Random.DefaultPrng.init(0);
+    var Rnd = std.Random.DefaultPrng.init(std.testing.random_seed);
     var rnd = Rnd.random();
 
     var round: usize = 4; // repeat send/recv cycle round times
@@ -4081,7 +4081,7 @@ test "ring mapped buffers multishot recv" {
     defer fds.close();
 
     // for random user_data in sqe/cqe
-    var Rnd = std.Random.DefaultPrng.init(0);
+    var Rnd = std.Random.DefaultPrng.init(std.testing.random_seed);
     var rnd = Rnd.random();
 
     var round: usize = 4; // repeat send/recv cycle round times
lib/std/Random/ziggurat.zig
@@ -126,7 +126,9 @@ pub fn norm_zero_case(random: Random, u: f64) f64 {
     }
 }
 
-test "normal dist sanity" {
+test "normal dist smoke test" {
+    // Hardcode 0 as the seed because it's possible a seed exists that fails
+    // this test.
     var prng = Random.DefaultPrng.init(0);
     const random = prng.random();
 
lib/std/Build.zig
@@ -121,6 +121,7 @@ pub const Graph = struct {
     /// Information about the native target. Computed before build() is invoked.
     host: ResolvedTarget,
     incremental: ?bool = null,
+    random_seed: u32 = 0,
 };
 
 const AvailableDeps = []const struct { []const u8, []const u8 };
lib/std/hash_map.zig
@@ -1935,7 +1935,7 @@ test "put and remove loop in random order" {
     while (i < size) : (i += 1) {
         try keys.append(i);
     }
-    var prng = std.Random.DefaultPrng.init(0);
+    var prng = std.Random.DefaultPrng.init(std.testing.random_seed);
     const random = prng.random();
 
     while (i < iterations) : (i += 1) {
@@ -1967,7 +1967,7 @@ test "remove one million elements in random order" {
         keys.append(i) catch unreachable;
     }
 
-    var prng = std.Random.DefaultPrng.init(0);
+    var prng = std.Random.DefaultPrng.init(std.testing.random_seed);
     const random = prng.random();
     random.shuffle(u32, keys.items);
 
lib/std/priority_dequeue.zig
@@ -866,7 +866,7 @@ test "shrinkAndFree" {
 }
 
 test "fuzz testing min" {
-    var prng = std.Random.DefaultPrng.init(0x12345678);
+    var prng = std.Random.DefaultPrng.init(std.testing.random_seed);
     const random = prng.random();
 
     const test_case_count = 100;
@@ -895,7 +895,7 @@ fn fuzzTestMin(rng: std.Random, comptime queue_size: usize) !void {
 }
 
 test "fuzz testing max" {
-    var prng = std.Random.DefaultPrng.init(0x87654321);
+    var prng = std.Random.DefaultPrng.init(std.testing.random_seed);
     const random = prng.random();
 
     const test_case_count = 100;
@@ -924,7 +924,7 @@ fn fuzzTestMax(rng: std.Random, queue_size: usize) !void {
 }
 
 test "fuzz testing min and max" {
-    var prng = std.Random.DefaultPrng.init(0x87654321);
+    var prng = std.Random.DefaultPrng.init(std.testing.random_seed);
     const random = prng.random();
 
     const test_case_count = 100;
lib/std/sort.zig
@@ -223,7 +223,7 @@ test "stable sort" {
 }
 
 test "stable sort fuzz testing" {
-    var prng = std.Random.DefaultPrng.init(0x12345678);
+    var prng = std.Random.DefaultPrng.init(std.testing.random_seed);
     const random = prng.random();
     const test_case_count = 10;
 
@@ -408,7 +408,7 @@ test "sort with context in the middle of a slice" {
 }
 
 test "sort fuzz testing" {
-    var prng = std.Random.DefaultPrng.init(0x12345678);
+    var prng = std.Random.DefaultPrng.init(std.testing.random_seed);
     const random = prng.random();
     const test_case_count = 10;
 
lib/std/testing.zig
@@ -1,8 +1,12 @@
 const std = @import("std.zig");
 const builtin = @import("builtin");
-
+const assert = std.debug.assert;
 const math = std.math;
 
+/// Provides deterministic randomness in unit tests.
+/// Initialized on startup. Read-only after that.
+pub var random_seed: u32 = 0;
+
 pub const FailingAllocator = @import("testing/failing_allocator.zig").FailingAllocator;
 
 /// This should only be used in temporary test programs.
src/main.zig
@@ -4282,7 +4282,10 @@ fn runOrTest(
     defer argv.deinit();
 
     if (test_exec_args.len == 0) {
-        try argv.append(exe_path);
+        try argv.appendSlice(&.{
+            exe_path,
+            try std.fmt.allocPrint(arena, "--seed=0x{x}", .{std.crypto.random.int(u32)}),
+        });
     } else {
         for (test_exec_args) |arg| {
             try argv.append(arg orelse exe_path);