diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 97b8bce..27242e9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,12 +1,20 @@ find_package(Threads REQUIRED) enable_testing() +# Usage: add_test(NAME src1.c src2.c ... +# [EXTRA_LIBS lib1 lib2] +# [EXTRA_INCLUDES dir1 dir2] +# [ENV "VAR=val;VAR2=val2"]) function(add_test TEST_NAME) - message("Adding test: ${TEST_NAME} -> ${ARGN}") - add_executable(${TEST_NAME} ${ARGN}) - target_include_directories(${TEST_NAME} PRIVATE ${INCLUDE_DIRS}) - target_link_libraries(${TEST_NAME} ${CMOCKA_LIBRARY} ${SDL_LIBRARY}) + cmake_parse_arguments(ARG "" "" "EXTRA_LIBS;EXTRA_INCLUDES;ENV" ${ARGN}) + message("Adding test: ${TEST_NAME} -> ${ARG_UNPARSED_ARGUMENTS}") + add_executable(${TEST_NAME} ${ARG_UNPARSED_ARGUMENTS}) + target_include_directories(${TEST_NAME} PRIVATE ${INCLUDE_DIRS} ${ARG_EXTRA_INCLUDES}) + target_link_libraries(${TEST_NAME} ${CMOCKA_LIBRARY} ${SDL_LIBRARY} ${ARG_EXTRA_LIBS}) _add_test(${TEST_NAME} ${TEST_NAME}) + if(ARG_ENV) + set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT "${ARG_ENV}") + endif() endfunction() 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) add_test(test_position test_position.c ../src/position.c) add_test(test_collisions test_collisions.c ../src/collisions.c) +add_test(test_random test_random.c ../src/random.c ../src/util.c + EXTRA_INCLUDES ${CMAKE_SOURCE_DIR}/lib/bh_random/src + EXTRA_LIBS bh_random +) + # roommatrix needs a real SDL renderer (lightmap texture), so it uses the # offscreen video driver. Extra libs: SDL_image + SDL_ttf (pulled in by # texture.c) and physfs (pulled in by io_util.c / physfsrwops.c). -add_executable(test_roommatrix +add_test(test_roommatrix test_roommatrix.c ../src/roommatrix.c ../src/texture.c @@ -29,16 +42,6 @@ add_executable(test_roommatrix ../src/position.c ../src/io_util.c ../src/physfsrwops.c -) -target_include_directories(test_roommatrix PRIVATE ${INCLUDE_DIRS}) -target_link_libraries(test_roommatrix - ${CMOCKA_LIBRARY} - ${SDL_LIBRARY} - ${SDL_IMAGE_LIBRARY} - ${SDL_TTF_LIBRARY} - ${PHYSFS_LIBRARY} -) -_add_test(test_roommatrix test_roommatrix) -set_tests_properties(test_roommatrix PROPERTIES - ENVIRONMENT "SDL_VIDEODRIVER=offscreen;SDL_RENDER_DRIVER=software" -) + EXTRA_LIBS ${SDL_IMAGE_LIBRARY} ${SDL_TTF_LIBRARY} ${PHYSFS_LIBRARY} + ENV "SDL_VIDEODRIVER=offscreen;SDL_RENDER_DRIVER=software" +) \ No newline at end of file diff --git a/test/test_random.c b/test/test_random.c new file mode 100644 index 0000000..bfab5bf --- /dev/null +++ b/test/test_random.c @@ -0,0 +1,128 @@ +/* + * BreakHack - A dungeone crawler RPG + * Copyright (C) 2026 Linus Probert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "cmocka_include.h" +#include "../src/random.h" + +/* set_random_seed / get_random_seed round-trip */ +static void +test_seed_roundtrip(void **state) +{ + (void)state; + + set_random_seed(42); + assert_int_equal(get_random_seed(), 42); + + set_random_seed(0xDEADBEEF); + assert_int_equal(get_random_seed(), 0xDEADBEEF); +} + +/* Same seed must produce identical map seeds for all 20 levels */ +static void +test_map_seeds_are_deterministic(void **state) +{ + (void)state; + + unsigned int first[20], second[20]; + + set_random_seed(12345); + for (int i = 0; i < 20; ++i) + first[i] = get_random_map_seed(i + 1); + + set_random_seed(12345); + for (int i = 0; i < 20; ++i) + second[i] = get_random_map_seed(i + 1); + + for (int i = 0; i < 20; ++i) + assert_int_equal(first[i], second[i]); +} + +/* Two different seeds must not produce the same map seed sequence */ +static void +test_different_seeds_differ(void **state) +{ + (void)state; + + unsigned int a[20], b[20]; + + set_random_seed(11111); + for (int i = 0; i < 20; ++i) + a[i] = get_random_map_seed(i + 1); + + set_random_seed(22222); + for (int i = 0; i < 20; ++i) + b[i] = get_random_map_seed(i + 1); + + /* At least one level must differ — all matching would be a collision */ + int any_differ = 0; + for (int i = 0; i < 20; ++i) { + if (a[i] != b[i]) { + any_differ = 1; + break; + } + } + assert_true(any_differ); +} + +/* get_random(max) must always return a value in [0, max] */ +static void +test_get_random_bounds(void **state) +{ + (void)state; + + set_random_seed(99999); + + for (unsigned int max = 0; max <= 100; ++max) { + for (int trial = 0; trial < 50; ++trial) { + unsigned int r = get_random(max); + assert_true(r <= max); + } + } +} + +/* + * Seed 0 is the "uninitialised" sentinel in init_seed(). + * set_random_seed(0) works fine, but get_random_seed() will subsequently + * re-init with time(NULL) because init_seed() sees seed==0 as unset. + * Verify that the code doesn't crash and that get_random_seed returns + * something non-zero after that lazy re-init. + */ +static void +test_seed_zero_reinit(void **state) +{ + (void)state; + + set_random_seed(0); + /* get_random_seed() triggers init_seed() which replaces 0 with time(NULL) */ + assert_int_not_equal(get_random_seed(), 0); + /* map seeds are now populated from the time-based seed — must not crash */ + (void)get_random_map_seed(1); + (void)get_random_map_seed(20); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_seed_roundtrip), cmocka_unit_test(test_map_seeds_are_deterministic), + cmocka_unit_test(test_different_seeds_differ), cmocka_unit_test(test_get_random_bounds), + cmocka_unit_test(test_seed_zero_reinit), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/test/test_roommatrix.c b/test/test_roommatrix.c index ce63a53..a0c00eb 100644 --- a/test/test_roommatrix.c +++ b/test/test_roommatrix.c @@ -1,6 +1,6 @@ /* * BreakHack - A dungeone crawler RPG - * Copyright (C) 2025 Linus Probert + * Copyright (C) 2026 Linus Probert * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - #include #include "cmocka_include.h" #include "../src/roommatrix.h" @@ -141,4 +140,4 @@ main(void) }; return cmocka_run_group_tests(tests, NULL, NULL); -} \ No newline at end of file +}