Skip to content

Commit 41cb02b

Browse files
authored
Tests for random seeding (#162)
1 parent ad593be commit 41cb02b

File tree

3 files changed

+151
-21
lines changed

3 files changed

+151
-21
lines changed

test/CMakeLists.txt

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
find_package(Threads REQUIRED)
22
enable_testing()
33

4+
# Usage: add_test(NAME src1.c src2.c ...
5+
# [EXTRA_LIBS lib1 lib2]
6+
# [EXTRA_INCLUDES dir1 dir2]
7+
# [ENV "VAR=val;VAR2=val2"])
48
function(add_test TEST_NAME)
5-
message("Adding test: ${TEST_NAME} -> ${ARGN}")
6-
add_executable(${TEST_NAME} ${ARGN})
7-
target_include_directories(${TEST_NAME} PRIVATE ${INCLUDE_DIRS})
8-
target_link_libraries(${TEST_NAME} ${CMOCKA_LIBRARY} ${SDL_LIBRARY})
9+
cmake_parse_arguments(ARG "" "" "EXTRA_LIBS;EXTRA_INCLUDES;ENV" ${ARGN})
10+
message("Adding test: ${TEST_NAME} -> ${ARG_UNPARSED_ARGUMENTS}")
11+
add_executable(${TEST_NAME} ${ARG_UNPARSED_ARGUMENTS})
12+
target_include_directories(${TEST_NAME} PRIVATE ${INCLUDE_DIRS} ${ARG_EXTRA_INCLUDES})
13+
target_link_libraries(${TEST_NAME} ${CMOCKA_LIBRARY} ${SDL_LIBRARY} ${ARG_EXTRA_LIBS})
914
_add_test(${TEST_NAME} ${TEST_NAME})
15+
if(ARG_ENV)
16+
set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT "${ARG_ENV}")
17+
endif()
1018
endfunction()
1119

1220
add_test(test_util test_util.c ../src/util.c)
@@ -17,10 +25,15 @@ add_test(test_input test_input.c ../src/input.c ../src/keyboard.c)
1725
add_test(test_position test_position.c ../src/position.c)
1826
add_test(test_collisions test_collisions.c ../src/collisions.c)
1927

28+
add_test(test_random test_random.c ../src/random.c ../src/util.c
29+
EXTRA_INCLUDES ${CMAKE_SOURCE_DIR}/lib/bh_random/src
30+
EXTRA_LIBS bh_random
31+
)
32+
2033
# roommatrix needs a real SDL renderer (lightmap texture), so it uses the
2134
# offscreen video driver. Extra libs: SDL_image + SDL_ttf (pulled in by
2235
# texture.c) and physfs (pulled in by io_util.c / physfsrwops.c).
23-
add_executable(test_roommatrix
36+
add_test(test_roommatrix
2437
test_roommatrix.c
2538
../src/roommatrix.c
2639
../src/texture.c
@@ -29,16 +42,6 @@ add_executable(test_roommatrix
2942
../src/position.c
3043
../src/io_util.c
3144
../src/physfsrwops.c
32-
)
33-
target_include_directories(test_roommatrix PRIVATE ${INCLUDE_DIRS})
34-
target_link_libraries(test_roommatrix
35-
${CMOCKA_LIBRARY}
36-
${SDL_LIBRARY}
37-
${SDL_IMAGE_LIBRARY}
38-
${SDL_TTF_LIBRARY}
39-
${PHYSFS_LIBRARY}
40-
)
41-
_add_test(test_roommatrix test_roommatrix)
42-
set_tests_properties(test_roommatrix PROPERTIES
43-
ENVIRONMENT "SDL_VIDEODRIVER=offscreen;SDL_RENDER_DRIVER=software"
44-
)
45+
EXTRA_LIBS ${SDL_IMAGE_LIBRARY} ${SDL_TTF_LIBRARY} ${PHYSFS_LIBRARY}
46+
ENV "SDL_VIDEODRIVER=offscreen;SDL_RENDER_DRIVER=software"
47+
)

test/test_random.c

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* BreakHack - A dungeone crawler RPG
3+
* Copyright (C) 2026 Linus Probert <linus.probert@gmail.com>
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
#include "cmocka_include.h"
20+
#include "../src/random.h"
21+
22+
/* set_random_seed / get_random_seed round-trip */
23+
static void
24+
test_seed_roundtrip(void **state)
25+
{
26+
(void)state;
27+
28+
set_random_seed(42);
29+
assert_int_equal(get_random_seed(), 42);
30+
31+
set_random_seed(0xDEADBEEF);
32+
assert_int_equal(get_random_seed(), 0xDEADBEEF);
33+
}
34+
35+
/* Same seed must produce identical map seeds for all 20 levels */
36+
static void
37+
test_map_seeds_are_deterministic(void **state)
38+
{
39+
(void)state;
40+
41+
unsigned int first[20], second[20];
42+
43+
set_random_seed(12345);
44+
for (int i = 0; i < 20; ++i)
45+
first[i] = get_random_map_seed(i + 1);
46+
47+
set_random_seed(12345);
48+
for (int i = 0; i < 20; ++i)
49+
second[i] = get_random_map_seed(i + 1);
50+
51+
for (int i = 0; i < 20; ++i)
52+
assert_int_equal(first[i], second[i]);
53+
}
54+
55+
/* Two different seeds must not produce the same map seed sequence */
56+
static void
57+
test_different_seeds_differ(void **state)
58+
{
59+
(void)state;
60+
61+
unsigned int a[20], b[20];
62+
63+
set_random_seed(11111);
64+
for (int i = 0; i < 20; ++i)
65+
a[i] = get_random_map_seed(i + 1);
66+
67+
set_random_seed(22222);
68+
for (int i = 0; i < 20; ++i)
69+
b[i] = get_random_map_seed(i + 1);
70+
71+
/* At least one level must differ — all matching would be a collision */
72+
int any_differ = 0;
73+
for (int i = 0; i < 20; ++i) {
74+
if (a[i] != b[i]) {
75+
any_differ = 1;
76+
break;
77+
}
78+
}
79+
assert_true(any_differ);
80+
}
81+
82+
/* get_random(max) must always return a value in [0, max] */
83+
static void
84+
test_get_random_bounds(void **state)
85+
{
86+
(void)state;
87+
88+
set_random_seed(99999);
89+
90+
for (unsigned int max = 0; max <= 100; ++max) {
91+
for (int trial = 0; trial < 50; ++trial) {
92+
unsigned int r = get_random(max);
93+
assert_true(r <= max);
94+
}
95+
}
96+
}
97+
98+
/*
99+
* Seed 0 is the "uninitialised" sentinel in init_seed().
100+
* set_random_seed(0) works fine, but get_random_seed() will subsequently
101+
* re-init with time(NULL) because init_seed() sees seed==0 as unset.
102+
* Verify that the code doesn't crash and that get_random_seed returns
103+
* something non-zero after that lazy re-init.
104+
*/
105+
static void
106+
test_seed_zero_reinit(void **state)
107+
{
108+
(void)state;
109+
110+
set_random_seed(0);
111+
/* get_random_seed() triggers init_seed() which replaces 0 with time(NULL) */
112+
assert_int_not_equal(get_random_seed(), 0);
113+
/* map seeds are now populated from the time-based seed — must not crash */
114+
(void)get_random_map_seed(1);
115+
(void)get_random_map_seed(20);
116+
}
117+
118+
int
119+
main(void)
120+
{
121+
const struct CMUnitTest tests[] = {
122+
cmocka_unit_test(test_seed_roundtrip), cmocka_unit_test(test_map_seeds_are_deterministic),
123+
cmocka_unit_test(test_different_seeds_differ), cmocka_unit_test(test_get_random_bounds),
124+
cmocka_unit_test(test_seed_zero_reinit),
125+
};
126+
127+
return cmocka_run_group_tests(tests, NULL, NULL);
128+
}

test/test_roommatrix.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* BreakHack - A dungeone crawler RPG
3-
* Copyright (C) 2025 Linus Probert <linus.probert@gmail.com>
3+
* Copyright (C) 2026 Linus Probert <linus.probert@gmail.com>
44
*
55
* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -15,7 +15,6 @@
1515
* You should have received a copy of the GNU General Public License
1616
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
*/
18-
1918
#include <SDL3/SDL.h>
2019
#include "cmocka_include.h"
2120
#include "../src/roommatrix.h"
@@ -141,4 +140,4 @@ main(void)
141140
};
142141

143142
return cmocka_run_group_tests(tests, NULL, NULL);
144-
}
143+
}

0 commit comments

Comments
 (0)