Add headless scenario tests

Run a compiled System 1/2/3 scenarios with SDL's dummy video and audio
drivers, and report failures through the text hook and exit status.
This commit is contained in:
kichikuou
2026-08-08 08:16:38 +09:00
parent 6f5933adaf
commit 64e99b9235
21 changed files with 164 additions and 6 deletions
+6 -5
View File
@@ -17,11 +17,12 @@ jobs:
- name: Install Deps
run: |
sudo apt update
sudo apt install libsdl2-dev libsdl2-ttf-dev librtmidi-dev nlohmann-json3-dev
sudo apt install libsdl2-dev libsdl2-ttf-dev librtmidi-dev nlohmann-json3-dev ninja-build
- name: Build
run: |
mkdir -p out/${{ matrix.build-type }}
cd out/${{ matrix.build-type }}
cmake -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} ../../
make -j4
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build-type }}
cmake --build build
- name: Test
run: ctest --output-on-failure --test-dir build
+3
View File
@@ -51,6 +51,9 @@ jobs:
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_COMPILE_WARNING_AS_ERROR=YES
cmake --build build
- name: Test
run: ctest --output-on-failure --test-dir build
- name: Package
run: |
cp build/system3.exe COPYING.txt dist/
+5
View File
@@ -7,6 +7,7 @@ endif ()
project(System3 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
include(CTest)
# Generates a static library from pkg_check_modules() result
function(add_static_library name pkg)
@@ -250,3 +251,7 @@ endif()
target_compile_definitions(system3 PRIVATE RESOURCE_PATH="${RESOURCE_PATH}")
target_include_directories(system3 PRIVATE src src/sys)
if (BUILD_TESTING AND NOT ANDROID AND NOT EMSCRIPTEN AND NOT NINTENDO_SWITCH)
add_subdirectory(test)
endif()
+4 -1
View File
@@ -96,6 +96,7 @@ const struct CRCTable {
uint32 crc32_a;
uint32 crc32_b;
} crc_table[] = {
{GameId::SYSTEM1_GENERIC, "system1_generic", 1, "System 1", JAPANESE},
{GameId::BUNKASAI, "bunkasai", 1, "あぶない文化祭前夜", JAPANESE, CRC32_BUNKASAI},
{GameId::CRESCENT, "crescent", 1, "クレセントムーンがぁる", JAPANESE, CRC32_CRESCENT},
{GameId::DPS, "dps", 1, "D.P.S - Dream Program System", JAPANESE, CRC32_DPS},
@@ -122,6 +123,7 @@ const struct CRCTable {
{GameId::GAKUEN, "gakuen", 1, "学園戦記", JAPANESE, CRC32_GAKUEN},
{GameId::GAKUEN, "gakuen_eng", 1, "Gakuen Senki", ENGLISH, CRC32_GAKUEN_ENG},
{GameId::SYSTEM2_GENERIC, "system2_generic", 2, "System 2", JAPANESE},
{GameId::AYUMI_FD, "ayumi_fd", 2, "あゆみちゃん物語", JAPANESE, CRC32_AYUMI_FD},
{GameId::AYUMI_HINT, "ayumi_hint", 2, "あゆみちゃん物語 ヒントディスク", JAPANESE, CRC32_AYUMI_HINT},
{GameId::AYUMI_PROTO, "ayumi_proto", 2, "あゆみちゃん物語 PROTO", JAPANESE, CRC32_AYUMI_PROTO},
@@ -138,6 +140,7 @@ const struct CRCTable {
{GameId::SDPS_KAIZOKU, "sdps_kaizoku", 2, "Super D.P.S - うれしたのし海賊稼業", JAPANESE, CRC32_SDPS, CRC32_SDPS_KAIZOKU},
{GameId::YAKATA2, "yakata2", 2, "ALICEの館II", JAPANESE, CRC32_YAKATA2},
{GameId::SYSTEM3_GENERIC, "system3_generic", 3, "System 3", JAPANESE},
{GameId::AMBIVALENZ_FD, "ambivalenz_fd", 3, "AmbivalenZ −二律背反−", JAPANESE, CRC32_AMBIVALENZ_FD},
{GameId::AMBIVALENZ_CD, "ambivalenz_cd", 3, "AmbivalenZ −二律背反−", JAPANESE, CRC32_AMBIVALENZ_CD},
{GameId::DPS_ALL, "dps_all", 3, "D.P.S. 全部", JAPANESE, CRC32_DPSALL},
@@ -172,7 +175,7 @@ const struct CRCTable {
const CRCTable* lookup(uint32 crc32_a, uint32 crc32_b)
{
for (const CRCTable* t = crc_table; t->id; t++) {
if (crc32_a == t->crc32_a && (!t->crc32_b || crc32_b == t->crc32_b))
if (t->crc32_a && crc32_a == t->crc32_a && (!t->crc32_b || crc32_b == t->crc32_b))
return t;
}
return NULL;
+3
View File
@@ -15,6 +15,7 @@ struct GameId {
UNKNOWN,
// System 1
SYSTEM1_GENERIC,
BUNKASAI,
CRESCENT,
DPS,
@@ -40,6 +41,7 @@ struct GameId {
GAKUEN,
// System 2
SYSTEM2_GENERIC,
AYUMI_FD,
AYUMI_HINT,
AYUMI_PROTO,
@@ -57,6 +59,7 @@ struct GameId {
YAKATA2,
// System 3
SYSTEM3_GENERIC,
AMBIVALENZ_FD,
AMBIVALENZ_CD,
DPS_ALL,
+25
View File
@@ -0,0 +1,25 @@
function(add_scenario_test system)
set(TEST_DIR ${CMAKE_CURRENT_BINARY_DIR}/scenario/${system})
file(MAKE_DIRECTORY ${TEST_DIR})
configure_file(scenario/${system}/ADISK.DAT ${TEST_DIR}/ADISK.DAT COPYONLY)
add_test(
NAME scenario_${system}
COMMAND $<TARGET_FILE:system3>
-gamedir ${TEST_DIR}
-game ${system}_generic
-encoding utf8
-fontfile ${PROJECT_SOURCE_DIR}/resources/fonts/MTLc3m.ttf
-texthook print
)
set_tests_properties(scenario_${system} PROPERTIES
ENVIRONMENT "SDL_VIDEODRIVER=dummy;SDL_AUDIODRIVER=dummy"
FAIL_REGULAR_EXPRESSION "\\[FAIL\\]"
PASS_REGULAR_EXPRESSION "(^|[^0-9])0 tests failed"
TIMEOUT 5
)
endfunction()
add_scenario_test(system1)
add_scenario_test(system2)
add_scenario_test(system3)
+16
View File
@@ -0,0 +1,16 @@
# Scenario tests
The scenario tests run compiled System 1, 2, and 3 scenarios through the
normal engine with SDL's dummy video and audio drivers. CTest checks both the
process exit status and the `0 tests failed` summary printed through the text
hook.
`ADISK.DAT` is checked in so that building and running the tests does not
require [sys3c](https://github.com/kichikuou/sys3c). After installing sys3c
and changing the scenario sources, regenerate it with:
```sh
for system in system1 system2 system3; do
(cd test/scenario/$system && sys3c)
done
```
+31
View File
@@ -0,0 +1,31 @@
; Addition and saturating overflow.
{3 + 4 \ 7: !tests_failed : tests_failed + 1! '[FAIL] addition' R}
{50000 + 50000 \ 65535: !tests_failed : tests_failed + 1! '[FAIL] addition overflow' R}
; Subtraction saturates at zero.
{10 - 3 \ 7: !tests_failed : tests_failed + 1! '[FAIL] subtraction' R}
{3 - 10 \ 0: !tests_failed : tests_failed + 1! '[FAIL] subtraction underflow' R}
; Multiplication and precedence.
{3 * 4 \ 12: !tests_failed : tests_failed + 1! '[FAIL] multiplication' R}
{1000 * 1000 \ 65535: !tests_failed : tests_failed + 1! '[FAIL] multiplication overflow' R}
{2 + 3 * 4 \ 14: !tests_failed : tests_failed + 1! '[FAIL] precedence' R}
{(2 + 3) * 4 \ 20: !tests_failed : tests_failed + 1! '[FAIL] parentheses' R}
; Comparisons.
{3 < 4 \ 1: !tests_failed : tests_failed + 1! '[FAIL] less than' R}
{4 < 3 \ 0: !tests_failed : tests_failed + 1! '[FAIL] false less than' R}
{4 > 3 \ 1: !tests_failed : tests_failed + 1! '[FAIL] greater than' R}
{3 \ 3: !tests_failed : tests_failed + 1! '[FAIL] equality' R}
{3 = 4: !tests_failed : tests_failed + 1! '[FAIL] inequality' R}
; Variables use the same expression evaluator.
!actual : 40!
!expected : 42!
!actual : actual + 2!
{actual \ expected: !tests_failed : tests_failed + 1! '[FAIL] variables' R}
%0:
*default:
EOF
+7
View File
@@ -0,0 +1,7 @@
{10 / 3 \ 3: !tests_failed : tests_failed + 1! '[FAIL] division' R}
{10 / 0 \ 0: !tests_failed : tests_failed + 1! '[FAIL] division by zero' R}
%0:
*default:
EOF
Binary file not shown.
+4
View File
@@ -0,0 +1,4 @@
game = system1_generic
hed = test.hed
encoding = utf8
unicode = true
+14
View File
@@ -0,0 +1,14 @@
!RND : 0!
!tests_failed : 0!
Y 240, 1:
%#arithmetic.adv:
; System 1 cannot return the failure count as its process exit status.
; Print the success marker only when every assertion passed.
{tests_failed = 0: '0 tests failed' R}
Y 253, 0:
*default:
EOF
+2
View File
@@ -0,0 +1,2 @@
test.adv
../common/arithmetic.adv
Binary file not shown.
+4
View File
@@ -0,0 +1,4 @@
game = system2_generic
hed = test.hed
encoding = utf8
unicode = true
+14
View File
@@ -0,0 +1,14 @@
!RND : 0!
!tests_failed : 0!
Y 240, 1:
%#arithmetic.adv:
%#division.adv:
H 0, tests_failed: ' tests failed' R
!RND : tests_failed!
Y 255, 0:
*default:
EOF
+3
View File
@@ -0,0 +1,3 @@
test.adv
../common/arithmetic.adv
../common/division.adv
Binary file not shown.
+4
View File
@@ -0,0 +1,4 @@
game = system3_generic
hed = test.hed
encoding = utf8
unicode = true
+16
View File
@@ -0,0 +1,16 @@
!RND : 0!
!tests_failed : 0!
; System3-sdl2 extension: draw ASCII as half-width characters so the
; text-hook output is easy for CTest and humans to read.
Y 240, 1:
%#arithmetic.adv:
%#division.adv:
H 0, tests_failed: ' tests failed' R
!RND : tests_failed!
Y 255, 0:
*default:
EOF
+3
View File
@@ -0,0 +1,3 @@
test.adv
../common/arithmetic.adv
../common/division.adv