From 5dfd303fd15750f2128f6f45b8d3ef157a2090d8 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 15 Jun 2024 13:26:40 -0400 Subject: [PATCH 1/5] Setup default `context.random_generator` for tests --- core/testing/runner.odin | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/testing/runner.odin b/core/testing/runner.odin index a0f9eee31..01464e1aa 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -9,6 +9,7 @@ import "core:encoding/ansi" import "core:fmt" import "core:io" @require import pkg_log "core:log" +import "core:math/rand" import "core:mem" import "core:os" import "core:slice" @@ -108,6 +109,9 @@ run_test_task :: proc(task: thread.Task) { free_all(context.temp_allocator) + random_generator_state := rand.create(data.t.seed) + context.random_generator = rand.default_random_generator(&random_generator_state) + data.it.p(&data.t) end_t(&data.t) From 1a52cf1f1c4becff3f107111038e50b0a0c74594 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 15 Jun 2024 13:37:04 -0400 Subject: [PATCH 2/5] Use test's random generator This removes the `create` calls when a test was only setting up a generator, and it replaces them with `reset` when run in a loop. --- tests/core/container/test_core_avl.odin | 3 -- tests/core/container/test_core_rbtree.odin | 3 -- tests/core/hash/test_core_hash.odin | 3 +- tests/core/slice/test_core_slice.odin | 6 ++-- tests/internal/test_map.odin | 39 ++++++++-------------- 5 files changed, 16 insertions(+), 38 deletions(-) diff --git a/tests/core/container/test_core_avl.odin b/tests/core/container/test_core_avl.odin index 556f371af..0e2d0d94a 100644 --- a/tests/core/container/test_core_avl.odin +++ b/tests/core/container/test_core_avl.odin @@ -20,9 +20,6 @@ test_avl :: proc(t: ^testing.T) { iter := avl.iterator(&tree, avl.Direction.Forward) testing.expect(t, avl.iterator_get(&iter) == nil, "empty/iterator: first node should be nil") - r := rand.create(t.seed) - context.random_generator = rand.default_random_generator(&r) - // Test insertion. NR_INSERTS :: 32 + 1 // Ensure at least 1 collision. inserted_map := make(map[int]^avl.Node(int)) diff --git a/tests/core/container/test_core_rbtree.odin b/tests/core/container/test_core_rbtree.odin index 425a9b440..b686ef6dd 100644 --- a/tests/core/container/test_core_rbtree.odin +++ b/tests/core/container/test_core_rbtree.odin @@ -14,9 +14,6 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) { defer mem.tracking_allocator_destroy(&track) context.allocator = mem.tracking_allocator(&track) - r := rand.create(t.seed) - context.random_generator = rand.default_random_generator(&r) - log.infof("Testing Red-Black Tree($Key=%v,$Value=%v) using random seed %v.", type_info_of(Key), type_info_of(Value), t.seed) tree: rb.Tree(Key, Value) rb.init(&tree) diff --git a/tests/core/hash/test_core_hash.odin b/tests/core/hash/test_core_hash.odin index 0255717a2..c3f0bee91 100644 --- a/tests/core/hash/test_core_hash.odin +++ b/tests/core/hash/test_core_hash.odin @@ -53,8 +53,7 @@ test_xxhash_zero_streamed_random_updates :: proc(t: ^testing.T) { testing.expect(t, xxh3_128_err == nil, "Problem initializing XXH3_128 state") // XXH3_128_update - random_seed := rand.create(t.seed) - context.random_generator = rand.default_random_generator(&random_seed) + rand.reset(t.seed) for len(b) > 0 { update_size := min(len(b), rand.int_max(8192)) if update_size > 4096 { diff --git a/tests/core/slice/test_core_slice.odin b/tests/core/slice/test_core_slice.odin index 432636664..5825c49b6 100644 --- a/tests/core/slice/test_core_slice.odin +++ b/tests/core/slice/test_core_slice.odin @@ -11,8 +11,7 @@ test_sort_with_indices :: proc(t: ^testing.T) { test_sizes :: []int{7, 13, 347, 1031, 10111, 100003} for test_size in test_sizes { - r := rand.create(t.seed) - context.random_generator = rand.default_random_generator(&r) + rand.reset(t.seed) vals := make([]u64, test_size) r_idx := make([]int, test_size) // Reverse index @@ -63,8 +62,7 @@ test_sort_by_indices :: proc(t: ^testing.T) { test_sizes :: []int{7, 13, 347, 1031, 10111, 100003} for test_size in test_sizes { - r := rand.create(t.seed) - context.random_generator = rand.default_random_generator(&r) + rand.reset(t.seed) vals := make([]u64, test_size) r_idx := make([]int, test_size) // Reverse index diff --git a/tests/internal/test_map.odin b/tests/internal/test_map.odin index ab7e52f33..9bd5d34ea 100644 --- a/tests/internal/test_map.odin +++ b/tests/internal/test_map.odin @@ -16,8 +16,7 @@ map_insert_random_key_value :: proc(t: ^testing.T) { defer delete(m) unique_keys := 0 - r := rand.create(t.seed + seed_incr) - context.random_generator = rand.default_random_generator(&r) + rand.reset(t.seed + seed_incr) for _ in 0.. Date: Sat, 15 Jun 2024 13:50:00 -0400 Subject: [PATCH 3/5] Add tests for `core:math/rand` --- tests/core/math/rand/test_core_math_rand.odin | 35 +++++++++++++++++++ tests/core/normal.odin | 1 + 2 files changed, 36 insertions(+) create mode 100644 tests/core/math/rand/test_core_math_rand.odin diff --git a/tests/core/math/rand/test_core_math_rand.odin b/tests/core/math/rand/test_core_math_rand.odin new file mode 100644 index 000000000..392d3d241 --- /dev/null +++ b/tests/core/math/rand/test_core_math_rand.odin @@ -0,0 +1,35 @@ +package test_core_math_rand + +import "core:math/rand" +import "core:testing" + +@test +test_default_rand_determinism :: proc(t: ^testing.T) { + rand.reset(13) + first_value := rand.int127() + rand.reset(13) + second_value := rand.int127() + + testing.expect(t, first_value == second_value, "Context default random number generator is non-deterministic.") +} + +@test +test_default_rand_determinism_user_set :: proc(t: ^testing.T) { + rng_state_1 := rand.create(13) + rng_state_2 := rand.create(13) + + rng_1 := rand.default_random_generator(&rng_state_1) + rng_2 := rand.default_random_generator(&rng_state_2) + + first_value, second_value: i128 + { + context.random_generator = rng_1 + first_value = rand.int127() + } + { + context.random_generator = rng_2 + second_value = rand.int127() + } + + testing.expect(t, first_value == second_value, "User-set default random number generator is non-deterministic.") +} diff --git a/tests/core/normal.odin b/tests/core/normal.odin index 7620d7d6e..c8bb59269 100644 --- a/tests/core/normal.odin +++ b/tests/core/normal.odin @@ -24,6 +24,7 @@ download_assets :: proc() { @(require) import "math/big" @(require) import "math/linalg/glsl" @(require) import "math/noise" +@(require) import "math/rand" @(require) import "mem" @(require) import "net" @(require) import "odin" From 8b31cddaba18cc646d471b35249a3f2771e20651 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 15 Jun 2024 13:50:21 -0400 Subject: [PATCH 4/5] Keep `-vet` happy --- tests/core/slice/test_core_slice.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/slice/test_core_slice.odin b/tests/core/slice/test_core_slice.odin index 5825c49b6..1f8f539c7 100644 --- a/tests/core/slice/test_core_slice.odin +++ b/tests/core/slice/test_core_slice.odin @@ -201,7 +201,7 @@ test_permutation_iterator :: proc(t: ^testing.T) { permutations_counted: int for slice.permute(&iter) { n := 0 - for item, index in s { + for item in s { n *= 10 n += item } From d4803583ffced81e08d3e68f947fa3c230f26923 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 18 Jun 2024 23:21:04 -0400 Subject: [PATCH 5/5] Work around Windows test failure I am uncertain why this works, but it does. Previously, `rtti_test` was failing due to non-zero data appearing in the `l_buggy` `Buggy_Struct`. The issue was caused by calling `runtime.default_random_generator` with a pointer to the state, somehow. The pointer could be on the stack or in the heap; it did not matter. I found two workarounds. - One is to move the RNG setup behind the call to `free_all`. - The other is to construct the random generator manually. Despite my digging and testing, I could find no reason as to why this works or what the fundamental issue was to begin with. If anyone comes upon this in the future with direct access to a Windows machine, I recommend stepping through the program with a debugger to investigate more deeply into why this happens. --- core/testing/runner.odin | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/core/testing/runner.odin b/core/testing/runner.odin index 01464e1aa..3510856c7 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -107,10 +107,14 @@ run_test_task :: proc(task: thread.Task) { options = Default_Test_Logger_Opts, } - free_all(context.temp_allocator) + random_generator_state: runtime.Default_Random_State + context.random_generator = { + procedure = runtime.default_random_generator_proc, + data = &random_generator_state, + } + rand.reset(data.t.seed) - random_generator_state := rand.create(data.t.seed) - context.random_generator = rand.default_random_generator(&random_generator_state) + free_all(context.temp_allocator) data.it.p(&data.t)