mirror of
https://github.com/libretro/virtualjaguar-libretro.git
synced 2026-11-07 04:36:00 -10:00
Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
fe80b36a2b
commit
06fa8a7193
7 files changed
+264
-70
No files matched your search
@@ -17,6 +17,9 @@
|
||||
# cheat_tempest_* (corpus + private ROM) the real database's Tempest 2000
|
||||
# "Infinite Lives" is found through the core's own title
|
||||
# identity and reaches RAM
|
||||
# <each of the above through the real binary>_threaded the same run with --emu-thread on (#967, label emu_thread, so the
|
||||
# TSan job runs it); the persist chain gets its own data dirs and fixture names, so the two
|
||||
# chains never share state
|
||||
|
||||
set(_fx "${CMAKE_CURRENT_LIST_DIR}/../tests/fixtures")
|
||||
get_filename_component(_fx "${_fx}" ABSOLUTE)
|
||||
@@ -45,41 +48,68 @@ add_test(NAME cheat_apply
|
||||
add_test(NAME cheat_apply_control
|
||||
COMMAND virtualjaguar --headless --frames 60 --expect-ram 1F0000=AB "${_yarc}")
|
||||
set_tests_properties(cheat_apply_control PROPERTIES PASS_REGULAR_EXPRESSION "${_no_cheat}")
|
||||
add_test(NAME cheat_apply_control_threaded
|
||||
COMMAND virtualjaguar --headless --emu-thread on --frames 60 --expect-ram 1F0000=AB "${_yarc}")
|
||||
set_tests_properties(cheat_apply_control_threaded PROPERTIES PASS_REGULAR_EXPRESSION "${_no_cheat}")
|
||||
|
||||
add_test(NAME cheat_state
|
||||
COMMAND virtualjaguar --headless --frames 240 --state-roundtrip
|
||||
--cheat 001F0000:AB --expect-ram 1F0000=AB "${VJ_ROMS}/jagniccc.j64")
|
||||
add_test(NAME cheat_state_threaded
|
||||
COMMAND virtualjaguar --headless --emu-thread on --frames 240 --state-roundtrip
|
||||
--cheat 001F0000:AB --expect-ram 1F0000=AB "${VJ_ROMS}/jagniccc.j64")
|
||||
|
||||
set(_pd "${CMAKE_BINARY_DIR}/cheat-persist-data")
|
||||
set(_pc "${CMAKE_BINARY_DIR}/cheat-persist-control")
|
||||
add_test(NAME cheat_persist_clean
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory "${_pd}" "${_pc}")
|
||||
# Run 1: yarc.cht (a fixture whose name matches the ROM's file name) supplies "Lives byte".
|
||||
add_test(NAME cheat_persist_save
|
||||
COMMAND virtualjaguar --headless --frames 60 --data-dir "${_pd}" --cheats-dir "${_fx}"
|
||||
--cheat-on "Lives byte" --expect-ram 1F0010=05 "${_yarc}")
|
||||
# Run 2: a new process, same data directory, NO cheat option: it must still be on.
|
||||
add_test(NAME cheat_persist_restart
|
||||
COMMAND virtualjaguar --headless --frames 60 --data-dir "${_pd}" --cheats-dir "${_fx}"
|
||||
--expect-ram 1F0010=05 "${_yarc}")
|
||||
# Control: the same, but a data directory nothing was saved to.
|
||||
add_test(NAME cheat_persist_control
|
||||
COMMAND virtualjaguar --headless --frames 60 --data-dir "${_pc}" --cheats-dir "${_fx}"
|
||||
--expect-ram 1F0010=05 "${_yarc}")
|
||||
set_tests_properties(cheat_persist_control PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "headless: FAIL ram 0x1F0010 is [0-9A-F]+, expected 05")
|
||||
add_test(NAME cheat_persist_cleanup
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory "${_pd}" "${_pc}")
|
||||
set_tests_properties(cheat_persist_clean PROPERTIES FIXTURES_SETUP cheat_persist_fresh)
|
||||
set_tests_properties(cheat_persist_save PROPERTIES
|
||||
FIXTURES_REQUIRED cheat_persist_fresh FIXTURES_SETUP cheat_persist_saved)
|
||||
set_tests_properties(cheat_persist_restart cheat_persist_control PROPERTIES
|
||||
FIXTURES_REQUIRED "cheat_persist_fresh;cheat_persist_saved")
|
||||
set_tests_properties(cheat_persist_cleanup PROPERTIES FIXTURES_CLEANUP "cheat_persist_fresh;cheat_persist_saved")
|
||||
# The save -> restart -> control chain, once inline and once threaded: <_th> is "" or "_threaded", and the twin has its
|
||||
# own data dirs and fixture names, so the two chains never share state.
|
||||
foreach(_th "" "_threaded")
|
||||
if(_th)
|
||||
set(_thargs --emu-thread on)
|
||||
else()
|
||||
set(_thargs)
|
||||
endif()
|
||||
set(_pd "${CMAKE_BINARY_DIR}/cheat-persist-data${_th}")
|
||||
set(_pc "${CMAKE_BINARY_DIR}/cheat-persist-control${_th}")
|
||||
add_test(NAME cheat_persist_clean${_th}
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory "${_pd}" "${_pc}")
|
||||
# Run 1: yarc.cht (a fixture whose name matches the ROM's file name) supplies "Lives byte".
|
||||
add_test(NAME cheat_persist_save${_th}
|
||||
COMMAND virtualjaguar --headless ${_thargs} --frames 60 --data-dir "${_pd}" --cheats-dir "${_fx}"
|
||||
--cheat-on "Lives byte" --expect-ram 1F0010=05 "${_yarc}")
|
||||
# Run 2: a new process, same data directory, NO cheat option: it must still be on.
|
||||
add_test(NAME cheat_persist_restart${_th}
|
||||
COMMAND virtualjaguar --headless ${_thargs} --frames 60 --data-dir "${_pd}" --cheats-dir "${_fx}"
|
||||
--expect-ram 1F0010=05 "${_yarc}")
|
||||
# Control: the same, but a data directory nothing was saved to.
|
||||
add_test(NAME cheat_persist_control${_th}
|
||||
COMMAND virtualjaguar --headless ${_thargs} --frames 60 --data-dir "${_pc}" --cheats-dir "${_fx}"
|
||||
--expect-ram 1F0010=05 "${_yarc}")
|
||||
set_tests_properties(cheat_persist_control${_th} PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "headless: FAIL ram 0x1F0010 is [0-9A-F]+, expected 05")
|
||||
add_test(NAME cheat_persist_cleanup${_th}
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory "${_pd}" "${_pc}")
|
||||
set_tests_properties(cheat_persist_clean${_th} PROPERTIES FIXTURES_SETUP cheat_persist_fresh${_th})
|
||||
set_tests_properties(cheat_persist_save${_th} PROPERTIES
|
||||
FIXTURES_REQUIRED cheat_persist_fresh${_th} FIXTURES_SETUP cheat_persist_saved${_th})
|
||||
set_tests_properties(cheat_persist_restart${_th} cheat_persist_control${_th} PROPERTIES
|
||||
FIXTURES_REQUIRED "cheat_persist_fresh${_th};cheat_persist_saved${_th}")
|
||||
set_tests_properties(cheat_persist_cleanup${_th} PROPERTIES
|
||||
FIXTURES_CLEANUP "cheat_persist_fresh${_th};cheat_persist_saved${_th}")
|
||||
endforeach()
|
||||
set_tests_properties(cheat_persist_clean_threaded cheat_persist_save_threaded cheat_persist_restart_threaded
|
||||
cheat_persist_control_threaded cheat_persist_cleanup_threaded PROPERTIES LABELS emu_thread)
|
||||
|
||||
set_tests_properties(cheats_unit cheat_apply cheat_state cheat_persist_save cheat_persist_restart PROPERTIES
|
||||
TIMEOUT 300
|
||||
FAIL_REGULAR_EXPRESSION "headless: FAIL")
|
||||
# The twins: also the threaded OK line (the run really was threaded), and no owner violation / TSan report. The controls
|
||||
# pass by failing for their stated reason, so they only guard against the threading errors.
|
||||
set_tests_properties(cheat_state_threaded cheat_persist_save_threaded cheat_persist_restart_threaded PROPERTIES
|
||||
TIMEOUT 300 LABELS emu_thread
|
||||
PASS_REGULAR_EXPRESSION "${_vj_threaded_ok}"
|
||||
FAIL_REGULAR_EXPRESSION "headless: FAIL|owner violation|ThreadSanitizer")
|
||||
set_tests_properties(cheat_apply_control_threaded cheat_persist_control_threaded PROPERTIES
|
||||
TIMEOUT 300 LABELS emu_thread
|
||||
FAIL_REGULAR_EXPRESSION "owner violation|ThreadSanitizer")
|
||||
|
||||
# ---- the real database, when it was downloaded -------------------------------------------
|
||||
if(VJ_CHEATS_DIR AND EXISTS "${VJ_CHEATS_DIR}/Tempest 2000.cht")
|
||||
@@ -115,6 +145,19 @@ if(VJ_CHEATS_DIR AND EXISTS "${VJ_CHEATS_DIR}/Tempest 2000.cht")
|
||||
set_tests_properties(cheat_tempest_control PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "headless: FAIL ram 0x007D3D is [0-9A-F]+, expected 05")
|
||||
set_tests_properties(cheat_tempest_lives PROPERTIES TIMEOUT 300 FAIL_REGULAR_EXPRESSION "headless: FAIL")
|
||||
add_test(NAME cheat_tempest_lives_threaded
|
||||
COMMAND virtualjaguar --headless --emu-thread on --frames 600 --cheats-dir "${VJ_CHEATS_DIR}"
|
||||
--cheat-on "Infinite Lives" --expect-ram 7D3D=05 "${_t2k}")
|
||||
add_test(NAME cheat_tempest_control_threaded
|
||||
COMMAND virtualjaguar --headless --emu-thread on --frames 600 --cheats-dir "${VJ_CHEATS_DIR}"
|
||||
--expect-ram 7D3D=05 "${_t2k}")
|
||||
set_tests_properties(cheat_tempest_control_threaded PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "headless: FAIL ram 0x007D3D is [0-9A-F]+, expected 05"
|
||||
FAIL_REGULAR_EXPRESSION "owner violation|ThreadSanitizer")
|
||||
set_tests_properties(cheat_tempest_lives_threaded PROPERTIES TIMEOUT 300
|
||||
PASS_REGULAR_EXPRESSION "${_vj_threaded_ok}"
|
||||
FAIL_REGULAR_EXPRESSION "headless: FAIL|owner violation|ThreadSanitizer")
|
||||
set_tests_properties(cheat_tempest_lives_threaded cheat_tempest_control_threaded PROPERTIES LABELS emu_thread)
|
||||
else()
|
||||
message(STATUS "VJCheatTests: no private Tempest 2000 ROM; the database end-to-end test is not registered")
|
||||
endif()
|
||||
@@ -138,6 +181,13 @@ if(Python3_Interpreter_FOUND AND _vj_bash2 AND EXISTS "${VJ_ROOT}/site/cheats/pa
|
||||
"$<TARGET_FILE:virtualjaguar>" "${VJ_ROOT}/site/cheats" "${_evil}" 5E2CDBC0
|
||||
"${CMAKE_BINARY_DIR}/cheat-community-test")
|
||||
set_tests_properties(cheat_community_install PROPERTIES TIMEOUT 300 FAIL_REGULAR_EXPRESSION "FAIL ")
|
||||
# Threaded: the script runs every app launch with --emu-thread on (its own work dir) and fails on an owner violation.
|
||||
add_test(NAME cheat_community_install_threaded
|
||||
COMMAND "${Python3_EXECUTABLE}" -I "${CMAKE_CURRENT_LIST_DIR}/../tests/cheat_community_smoke.py"
|
||||
"$<TARGET_FILE:virtualjaguar>" "${VJ_ROOT}/site/cheats" "${_evil}" 5E2CDBC0
|
||||
"${CMAKE_BINARY_DIR}/cheat-community-test_threaded" on)
|
||||
set_tests_properties(cheat_community_install_threaded PROPERTIES TIMEOUT 300 LABELS emu_thread
|
||||
FAIL_REGULAR_EXPRESSION "FAIL |owner violation|ThreadSanitizer")
|
||||
else()
|
||||
message(STATUS "VJCheatTests: no private Doom - Evil Unleashed ROM; cheat_community_install is not registered")
|
||||
endif()
|
||||
|
||||
@@ -25,11 +25,15 @@
|
||||
# gdb_inline_halt the same four steps with the emulation thread off (the headless loop syncs the halt after each frame)
|
||||
# gdb_inline_halt_hardcore inline: the client continues a boot halt, then a hardcore sign-in reloads the game (needs the halt mirror synced)
|
||||
# smoke_*_threaded the headless smoke tests with --emu-thread on: the inline hashes, and no owner violation
|
||||
# smoke_{cd,boot_cart_rom,bad_option,locked_option,disc_insert_cart_hidden}_threaded the rest of the smoke set, same expectations
|
||||
# controls_overlay_*_threaded / cheat_{state,apply_control,persist_*,tempest_*,community_install}_threaded (VJSmokeTests / VJCheatTests)
|
||||
# the controls overlay and cheat tests with --emu-thread on, each on its own data dir; netplay_pair_shared_threaded
|
||||
# smoke_disc_swap_threaded File > Disc on a loaded CD through EmuThread::call, read back from the pinned snapshot
|
||||
# smoke_disc_swap_bios_threaded the same in real-BIOS boot mode, where each insert re-stages the CD BIOS
|
||||
# smoke_disc_insert_bios_threaded the CD BIOS with no disc: Add disc image... inserts the first disc through call()
|
||||
# smoke_m3u_playlist_threaded an .m3u's discs registered inside EmuThread::call, read back from the pinned snapshot
|
||||
# smoke_busy_threaded a slow load call shows the busy state (log line headless)
|
||||
# smoke_quit_during_call_threaded SIGTERM while that slow call is in flight: the call finishes, exit 0, no owner violation
|
||||
# emu_thread_start_fallback a failed thread start runs inline (nothing to lift any more)
|
||||
# debug_on_threaded / gdb_enabled(_threaded) nothing is off while threaded; the silent gdb host attaches at load
|
||||
# emu_thread_default_unit build channel -> emulation-thread default; a stored choice always wins
|
||||
@@ -195,7 +199,7 @@ set_tests_properties(perf_trace_unit PROPERTIES TIMEOUT 60 LABELS emu_thread FAI
|
||||
|
||||
# The headless smoke tests (VJSmokeTests.cmake) again with --emu-thread on: the same hashes as inline, nothing on the
|
||||
# UI thread calling into the core, and the run really threaded (the OK line says so only then).
|
||||
set(_vj_threaded_ok "headless: OK \\([0-9]+ load\\(s\\), emulation thread\\)")
|
||||
# (_vj_threaded_ok, the OK line of a threaded run, is set in VJSmokeTests.cmake.)
|
||||
add_test(NAME smoke_yarc_threaded
|
||||
COMMAND virtualjaguar --headless --emu-thread on --frames ${VJ_SMOKE_FRAMES} ${_yarc_hash_args}
|
||||
"${VJ_ROMS}/yarc.j64")
|
||||
@@ -210,12 +214,35 @@ add_test(NAME smoke_boot_cd_bios_threaded
|
||||
add_test(NAME cheat_apply_threaded
|
||||
COMMAND virtualjaguar --headless --emu-thread on --frames 60 --cheat 001F0000:AB --expect-ram 1F0000=AB
|
||||
"${VJ_ROMS}/yarc.j64")
|
||||
add_test(NAME smoke_cd_threaded
|
||||
COMMAND virtualjaguar --headless --emu-thread on --frames 120 --allow-blank "${VJ_ROMS}/synth_jagcd.chd")
|
||||
add_test(NAME smoke_boot_cart_rom_threaded
|
||||
COMMAND virtualjaguar --headless --emu-thread on --frames 400 --boot-bios cart --expect-bios cart:builtin
|
||||
--expect-hash 3cc9c43e)
|
||||
set_tests_properties(smoke_yarc_threaded smoke_reload_threaded smoke_state_threaded smoke_boot_cd_bios_threaded
|
||||
cheat_apply_threaded PROPERTIES
|
||||
smoke_cd_threaded smoke_boot_cart_rom_threaded cheat_apply_threaded PROPERTIES
|
||||
TIMEOUT 300 LABELS emu_thread
|
||||
PASS_REGULAR_EXPRESSION "${_vj_threaded_ok}"
|
||||
FAIL_REGULAR_EXPRESSION "headless: FAIL|owner violation|ThreadSanitizer")
|
||||
|
||||
# The negative controls threaded: they pass by failing for the stated reason (the inline originals' PASS lines), and must
|
||||
# not trip an owner violation on the way.
|
||||
add_test(NAME smoke_bad_option_threaded
|
||||
COMMAND virtualjaguar --headless --emu-thread on --frames 5
|
||||
--option virtualjaguar_no_such_option=1 "${VJ_ROMS}/yarc.j64")
|
||||
set_tests_properties(smoke_bad_option_threaded PROPERTIES PASS_REGULAR_EXPRESSION "rejected by the core")
|
||||
add_test(NAME smoke_locked_option_threaded
|
||||
COMMAND virtualjaguar --headless --emu-thread on --frames 5
|
||||
--option virtualjaguar_alt_inputs=enabled "${VJ_ROMS}/yarc.j64")
|
||||
set_tests_properties(smoke_locked_option_threaded PROPERTIES PASS_REGULAR_EXPRESSION "locked by the app.*rejected by the core")
|
||||
add_test(NAME smoke_disc_insert_cart_hidden_threaded
|
||||
COMMAND virtualjaguar --headless --emu-thread on --frames 30 --allow-blank --boot-bios cart
|
||||
--disc-insert "${VJ_ROMS}/synth_jagcd.chd")
|
||||
set_tests_properties(smoke_disc_insert_cart_hidden_threaded PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "headless: FAIL --disc-insert: the disc menu is hidden")
|
||||
set_tests_properties(smoke_bad_option_threaded smoke_locked_option_threaded smoke_disc_insert_cart_hidden_threaded PROPERTIES
|
||||
TIMEOUT 120 LABELS emu_thread FAIL_REGULAR_EXPRESSION "owner violation|ThreadSanitizer")
|
||||
|
||||
# A thread that fails to start (VJ_TEST_EMU_START_FAIL takes the real failure branch): the run falls back inline. Nothing
|
||||
# is switched off while threaded any more, so there is nothing to give back (the old "are back on" line must be gone).
|
||||
add_test(NAME emu_thread_start_fallback
|
||||
@@ -468,3 +495,14 @@ add_test(NAME smoke_busy_threaded
|
||||
set_tests_properties(smoke_busy_threaded PROPERTIES TIMEOUT 120 LABELS emu_thread
|
||||
PASS_REGULAR_EXPRESSION "\\[emu\\] busy: a call has taken over 250 ms"
|
||||
FAIL_REGULAR_EXPRESSION "headless: FAIL|owner violation|ThreadSanitizer")
|
||||
|
||||
# A quit (SIGTERM) while that same slow call is in flight: the call completes, the frame loops are skipped, the app exits 0
|
||||
# and the thread stops. A script sends the signal once the busy line shows (so the UI thread is inside the call); not on Windows.
|
||||
if(Python3_Interpreter_FOUND AND NOT WIN32)
|
||||
add_test(NAME smoke_quit_during_call_threaded
|
||||
COMMAND ${Python3_EXECUTABLE} "${CMAKE_CURRENT_LIST_DIR}/../tests/quit_during_call.py"
|
||||
$<TARGET_FILE:virtualjaguar> "${VJ_ROMS}/yarc.j64")
|
||||
set_tests_properties(smoke_quit_during_call_threaded PROPERTIES TIMEOUT 120 LABELS emu_thread
|
||||
PASS_REGULAR_EXPRESSION "PASS: quit during a slow call"
|
||||
FAIL_REGULAR_EXPRESSION "FAIL |owner violation|ThreadSanitizer")
|
||||
endif()
|
||||
@@ -12,6 +12,7 @@
|
||||
# and the client are instances 2 and 3. The link must still come up, the shared
|
||||
# config.ini must be untouched, each copy gets its own save folder, and the
|
||||
# client hands the core its address through the environment (no vj_netlink.txt).
|
||||
# (netplay_pair_shared_threaded: both copies with --emu-thread on.)
|
||||
# netplay_no_partner control: with nobody to talk to both must FAIL, so the check can fail.
|
||||
# netplay_discovery a client must FIND the host by LAN discovery and join it. Skipped (77) when
|
||||
# no host is discovered: CI runners and many VMs do not deliver broadcast.
|
||||
@@ -58,9 +59,11 @@ add_test(NAME netplay_pair_voicemodem_threaded
|
||||
COMMAND vj_netplay_test pair $<TARGET_FILE:virtualjaguar> "${_yarc}" voicemodem on on)
|
||||
add_test(NAME netplay_pair_mixed COMMAND vj_netplay_test pair $<TARGET_FILE:virtualjaguar> "${_yarc}" jaglink on off)
|
||||
add_test(NAME netplay_no_partner_threaded COMMAND vj_netplay_test control $<TARGET_FILE:virtualjaguar> "${_yarc}" on)
|
||||
add_test(NAME netplay_pair_shared_threaded COMMAND vj_netplay_test pair-shared $<TARGET_FILE:virtualjaguar> "${_yarc}" on)
|
||||
set_tests_properties(netplay_pair_threaded netplay_pair_voicemodem_threaded netplay_pair_mixed netplay_no_partner_threaded
|
||||
netplay_pair_shared_threaded
|
||||
PROPERTIES TIMEOUT 300 RUN_SERIAL TRUE LABELS "emu_thread")
|
||||
set_tests_properties(netplay_pair_threaded netplay_pair_voicemodem_threaded netplay_pair_mixed
|
||||
set_tests_properties(netplay_pair_threaded netplay_pair_voicemodem_threaded netplay_pair_mixed netplay_pair_shared_threaded
|
||||
PROPERTIES FAIL_REGULAR_EXPRESSION "FAIL |owner violation|ThreadSanitizer")
|
||||
set_tests_properties(netplay_no_partner_threaded PROPERTIES FAIL_REGULAR_EXPRESSION "owner violation|ThreadSanitizer")
|
||||
|
||||
|
||||
@@ -51,6 +51,10 @@ set(VJ_ROMS "${VJ_ROOT}/test/roms")
|
||||
set(VJ_SMOKE_FRAMES 300 CACHE STRING "Frames per game in the smoke tests")
|
||||
set(VJ_SMOKE_YARC_HASH "f4c30ea3" CACHE STRING "Expected final-frame hash of yarc.j64 (hex); empty disables the check")
|
||||
|
||||
# The OK line of a headless run on the emulation thread (it says "emulation thread" only when the thread really ran):
|
||||
# what every _threaded twin requires (VJEmuThreadTests.cmake, VJCheatTests.cmake, the controls tests below).
|
||||
set(_vj_threaded_ok "headless: OK \\([0-9]+ load\\(s\\), emulation thread\\)")
|
||||
|
||||
if(VJ_SMOKE_YARC_HASH)
|
||||
set(_yarc_hash_args --expect-hash ${VJ_SMOKE_YARC_HASH})
|
||||
else()
|
||||
@@ -308,33 +312,51 @@ set_tests_properties(remap_unit input_e2e devices_unit devices_e2e bios_e2e PROP
|
||||
# controls_overlay_rebound a saved global rebinding (B = M, scancode 16) changes the text;
|
||||
# the per-game layer is covered by devices_unit.
|
||||
# ---------------------------------------------------------------------------
|
||||
set(VJ_CTL_DISMISSED "${CMAKE_BINARY_DIR}/ctl_dismissed")
|
||||
set(VJ_CTL_REBOUND "${CMAKE_BINARY_DIR}/ctl_rebound")
|
||||
set(VJ_CTL_DEFAULT "${CMAKE_BINARY_DIR}/ctl_default")
|
||||
file(MAKE_DIRECTORY "${VJ_CTL_DISMISSED}" "${VJ_CTL_REBOUND}" "${VJ_CTL_DEFAULT}")
|
||||
set(VJ_CTL_ON "${CMAKE_BINARY_DIR}/ctl_on")
|
||||
file(MAKE_DIRECTORY "${VJ_CTL_ON}")
|
||||
file(WRITE "${VJ_CTL_ON}/config.ini" "ui.controls.autoshow=1\n")
|
||||
file(WRITE "${VJ_CTL_DISMISSED}/config.ini" "ui.controls.autoshow=0\n")
|
||||
file(WRITE "${VJ_CTL_REBOUND}/config.ini" "input.kb.b=k16\nui.controls.autoshow=1\n")
|
||||
# Each test also runs as <name>_threaded (--emu-thread on, label emu_thread: the TSan job): the same expectations, and its
|
||||
# own data folder, since a headless run saves config.ini and the two must never share it.
|
||||
foreach(_th "" "_threaded")
|
||||
if(_th)
|
||||
set(_thargs --emu-thread on)
|
||||
else()
|
||||
set(_thargs)
|
||||
endif()
|
||||
set(VJ_CTL_DISMISSED "${CMAKE_BINARY_DIR}/ctl_dismissed${_th}")
|
||||
set(VJ_CTL_REBOUND "${CMAKE_BINARY_DIR}/ctl_rebound${_th}")
|
||||
set(VJ_CTL_DEFAULT "${CMAKE_BINARY_DIR}/ctl_default${_th}")
|
||||
set(VJ_CTL_ON "${CMAKE_BINARY_DIR}/ctl_on${_th}")
|
||||
file(MAKE_DIRECTORY "${VJ_CTL_DISMISSED}" "${VJ_CTL_REBOUND}" "${VJ_CTL_DEFAULT}" "${VJ_CTL_ON}")
|
||||
file(WRITE "${VJ_CTL_ON}/config.ini" "ui.controls.autoshow=1\n")
|
||||
file(WRITE "${VJ_CTL_DISMISSED}/config.ini" "ui.controls.autoshow=0\n")
|
||||
file(WRITE "${VJ_CTL_REBOUND}/config.ini" "input.kb.b=k16\nui.controls.autoshow=1\n")
|
||||
|
||||
add_test(NAME controls_overlay_default
|
||||
COMMAND virtualjaguar --headless --frames 5 --data-dir "${VJ_CTL_DEFAULT}" --expect-controls-overlay hidden "${VJ_ROMS}/yarc.j64")
|
||||
set_tests_properties(controls_overlay_default PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "\\[osd\\] Press F3 for the controls")
|
||||
add_test(NAME controls_overlay_default${_th}
|
||||
COMMAND virtualjaguar --headless ${_thargs} --frames 5 --data-dir "${VJ_CTL_DEFAULT}" --expect-controls-overlay hidden "${VJ_ROMS}/yarc.j64")
|
||||
set_tests_properties(controls_overlay_default${_th} PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "\\[osd\\] Press F3 for the controls")
|
||||
|
||||
add_test(NAME controls_overlay_keyboard
|
||||
COMMAND virtualjaguar --headless --frames 5 --data-dir "${VJ_CTL_ON}" --expect-controls-overlay shown --print-controls "${VJ_ROMS}/yarc.j64")
|
||||
set_tests_properties(controls_overlay_keyboard PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "Player 1: Standard Joypad - keyboard.*B: Z.*Keypad 9 \\(Pro: X\\): [^\n]*, A\n")
|
||||
add_test(NAME controls_overlay_keyboard${_th}
|
||||
COMMAND virtualjaguar --headless ${_thargs} --frames 5 --data-dir "${VJ_CTL_ON}" --expect-controls-overlay shown --print-controls "${VJ_ROMS}/yarc.j64")
|
||||
set_tests_properties(controls_overlay_keyboard${_th} PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "Player 1: Standard Joypad - keyboard.*B: Z.*Keypad 9 \\(Pro: X\\): [^\n]*, A\n")
|
||||
|
||||
add_test(NAME controls_overlay_dismissed
|
||||
COMMAND virtualjaguar --headless --frames 5 --data-dir "${VJ_CTL_DISMISSED}" --expect-controls-overlay hidden "${VJ_ROMS}/yarc.j64")
|
||||
add_test(NAME controls_overlay_dismissed${_th}
|
||||
COMMAND virtualjaguar --headless ${_thargs} --frames 5 --data-dir "${VJ_CTL_DISMISSED}" --expect-controls-overlay hidden "${VJ_ROMS}/yarc.j64")
|
||||
if(_th) # no text of its own to pass on: the run must say it was threaded
|
||||
set_tests_properties(controls_overlay_dismissed${_th} PROPERTIES PASS_REGULAR_EXPRESSION "${_vj_threaded_ok}")
|
||||
endif()
|
||||
|
||||
add_test(NAME controls_overlay_rebound
|
||||
COMMAND virtualjaguar --headless --frames 5 --data-dir "${VJ_CTL_REBOUND}" --expect-controls-overlay shown --print-controls "${VJ_ROMS}/yarc.j64")
|
||||
set_tests_properties(controls_overlay_rebound PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION " B: M\n")
|
||||
set_tests_properties(controls_overlay_default controls_overlay_keyboard controls_overlay_dismissed controls_overlay_rebound PROPERTIES
|
||||
TIMEOUT 120
|
||||
FAIL_REGULAR_EXPRESSION "headless: FAIL")
|
||||
add_test(NAME controls_overlay_rebound${_th}
|
||||
COMMAND virtualjaguar --headless ${_thargs} --frames 5 --data-dir "${VJ_CTL_REBOUND}" --expect-controls-overlay shown --print-controls "${VJ_ROMS}/yarc.j64")
|
||||
set_tests_properties(controls_overlay_rebound${_th} PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION " B: M\n")
|
||||
set_tests_properties(controls_overlay_default${_th} controls_overlay_keyboard${_th} controls_overlay_dismissed${_th}
|
||||
controls_overlay_rebound${_th} PROPERTIES TIMEOUT 120)
|
||||
if(_th)
|
||||
set_tests_properties(controls_overlay_default${_th} controls_overlay_keyboard${_th} controls_overlay_dismissed${_th}
|
||||
controls_overlay_rebound${_th} PROPERTIES LABELS emu_thread
|
||||
FAIL_REGULAR_EXPRESSION "headless: FAIL|owner violation|ThreadSanitizer")
|
||||
else()
|
||||
set_tests_properties(controls_overlay_default controls_overlay_keyboard controls_overlay_dismissed
|
||||
controls_overlay_rebound PROPERTIES FAIL_REGULAR_EXPRESSION "headless: FAIL")
|
||||
endif()
|
||||
endforeach()
|
||||
@@ -1,7 +1,9 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Community cheat store end to end (#1071): the real app, headless, real HTTP over loopback.
|
||||
|
||||
usage: cheat_community_smoke.py VIRTUALJAGUAR SITE_CHEATS_DIR ROM CRC32 WORKDIR
|
||||
usage: cheat_community_smoke.py VIRTUALJAGUAR SITE_CHEATS_DIR ROM CRC32 WORKDIR [on]
|
||||
The optional sixth argument "on" runs every app launch with --emu-thread on (the _threaded twin); an owner violation or a
|
||||
ThreadSanitizer report in any launch fails the run.
|
||||
|
||||
1. Serves SITE_CHEATS_DIR on 127.0.0.1 and runs `virtualjaguar --headless --cheats-community-install ROM` with
|
||||
cheats.community.url pointing at it: the pack must land in <data>/cheats/community/packs/<CRC>.cht with its sidecar,
|
||||
@@ -24,16 +26,21 @@ class Quiet(http.server.SimpleHTTPRequestHandler):
|
||||
pass
|
||||
|
||||
|
||||
def run(exe, data, rom, url):
|
||||
def run(exe, data, rom, url, threaded=False):
|
||||
data.mkdir(parents=True, exist_ok=True)
|
||||
(data / "config.ini").write_text("cheats.community.url=%s\nlibrary.art=1\n" % url)
|
||||
p = subprocess.run([exe, "--headless", "--frames", "30", "--allow-blank", "--data-dir", str(data),
|
||||
"--cheats-community-install", rom], capture_output=True, text=True, timeout=120)
|
||||
return p.returncode, p.stdout + p.stderr
|
||||
cmd = [exe, "--headless"] + (["--emu-thread", "on"] if threaded else []) + [
|
||||
"--frames", "30", "--allow-blank", "--data-dir", str(data), "--cheats-community-install", rom]
|
||||
p = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
|
||||
out = p.stdout + p.stderr
|
||||
if "owner violation" in out or "ThreadSanitizer" in out:
|
||||
print("FAIL threading error (rc=%d)\n%s" % (p.returncode, out)); sys.exit(1)
|
||||
return p.returncode, out
|
||||
|
||||
|
||||
def main():
|
||||
exe, site, rom, crc, work = sys.argv[1:6]
|
||||
threaded = len(sys.argv) > 6 and sys.argv[6] == "on"
|
||||
site, work = pathlib.Path(site), pathlib.Path(work)
|
||||
shutil.rmtree(work, ignore_errors=True)
|
||||
srv = http.server.ThreadingHTTPServer(("127.0.0.1", 0), functools.partial(Quiet, directory=str(site)))
|
||||
@@ -41,7 +48,7 @@ def main():
|
||||
threading.Thread(target=srv.serve_forever, daemon=True).start()
|
||||
|
||||
data = work / "data"
|
||||
rc, out = run(exe, data, rom, url)
|
||||
rc, out = run(exe, data, rom, url, threaded)
|
||||
pack = data / "cheats/community/packs" / (crc + ".cht")
|
||||
if rc != 0 or "COMMUNITY pack %s installed" % crc not in out:
|
||||
print("FAIL install (rc=%d)\n%s" % (rc, out)); return 1
|
||||
@@ -52,18 +59,18 @@ def main():
|
||||
|
||||
with pack.open("ab") as f: # a hand edit: the file no longer matches its sidecar (EditedLocally)
|
||||
f.write(b"# my tweak\n")
|
||||
rc, out = run(exe, data, rom, url)
|
||||
rc, out = run(exe, data, rom, url, threaded)
|
||||
if rc != 0 or "COMMUNITY pack %s installed" % crc not in out:
|
||||
print("FAIL reinstall over a locally edited pack (rc=%d)\n%s" % (rc, out)); return 1
|
||||
if pack.read_bytes() != (site / "packs" / (crc + ".cht")).read_bytes():
|
||||
print("FAIL the locally edited pack was not replaced"); return 1
|
||||
|
||||
srv.shutdown(); srv.server_close() # from here on nothing answers
|
||||
rc, out = run(exe, data, rom, url)
|
||||
rc, out = run(exe, data, rom, url, threaded)
|
||||
if rc != 0 or "COMMUNITY pack %s installed" % crc not in out:
|
||||
print("FAIL offline reuse of the installed pack (rc=%d)\n%s" % (rc, out)); return 1
|
||||
|
||||
rc, out = run(exe, work / "fresh", rom, url)
|
||||
rc, out = run(exe, work / "fresh", rom, url, threaded)
|
||||
if rc == 0 or (work / "fresh/cheats/community/packs" / (crc + ".cht")).exists():
|
||||
print("FAIL a fresh data dir with no server must not install (rc=%d)\n%s" % (rc, out)); return 1
|
||||
print("ok")
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
// two app processes on localhost, one Host and one Join through
|
||||
// the command-line twin of the window; both must report the link UP.
|
||||
// Does not use UDP broadcast: the client is given the address.
|
||||
// vj_netplay_test pair-shared EXE ROM [THREAD] two copies on ONE data folder (instance.h); THREAD = on runs both with the emulation thread
|
||||
// vj_netplay_test control EXE ROM [THREAD] the same with nobody to talk to: must FAIL (so the check can fail).
|
||||
// vj_netplay_test discovery EXE ROM a host and a client that must FIND it by LAN discovery and join
|
||||
// it. Exit 77 (ctest: skipped) when this machine does not deliver
|
||||
@@ -652,7 +653,7 @@ static int runPair(const std::string& exe, const std::string& rom, const std::st
|
||||
|
||||
// Two copies of the app on ONE data folder. The test takes instance 1 itself, so both children are
|
||||
// secondaries (2 and 3): they must coexist, link up, and leave the shared files alone.
|
||||
static int runPairShared(const std::string& exe, const std::string& rom) {
|
||||
static int runPairShared(const std::string& exe, const std::string& rom, const std::string& thread = "off") {
|
||||
namespace fs = std::filesystem;
|
||||
const int port = pickFreePort();
|
||||
const int disc = pickFreePort(port);
|
||||
@@ -674,10 +675,16 @@ static int runPairShared(const std::string& exe, const std::string& rom) {
|
||||
|
||||
const std::string p = std::to_string(port);
|
||||
const std::string d = dir.string();
|
||||
Child host = spawn(appArgs(exe, rom, {"--data-dir", d, "--netplay", "host", "--netplay-port", p, "--netplay-expect-link", "30"}));
|
||||
std::vector<std::string> hostX = {"--data-dir", d, "--netplay", "host", "--netplay-port", p, "--netplay-expect-link", "30"};
|
||||
std::vector<std::string> joinX = {"--data-dir", d, "--netplay", "join", "--netplay-addr", "127.0.0.1",
|
||||
"--netplay-port", p, "--netplay-expect-link", "30"};
|
||||
if (thread == "on") {
|
||||
hostX.insert(hostX.end(), {"--emu-thread", "on"});
|
||||
joinX.insert(joinX.end(), {"--emu-thread", "on"});
|
||||
}
|
||||
Child host = spawn(appArgs(exe, rom, hostX));
|
||||
SDL_Delay(300);
|
||||
Child join = spawn(appArgs(exe, rom, {"--data-dir", d, "--netplay", "join", "--netplay-addr", "127.0.0.1",
|
||||
"--netplay-port", p, "--netplay-expect-link", "30"}));
|
||||
Child join = spawn(appArgs(exe, rom, joinX));
|
||||
bool finished = waitBoth(host, join, 90);
|
||||
reap(host);
|
||||
reap(join);
|
||||
@@ -771,7 +778,7 @@ static int runDiscovery(const std::string& exe, const std::string& rom) {
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
std::fprintf(stderr, "usage: vj_netplay_test unit ROM | pair EXE ROM [DEVICE [HOST JOIN]] | control EXE ROM [THREAD] | discovery EXE ROM\n");
|
||||
std::fprintf(stderr, "usage: vj_netplay_test unit ROM | pair EXE ROM [DEVICE [HOST JOIN]] | pair-shared EXE ROM [THREAD] | control EXE ROM [THREAD] | discovery EXE ROM\n");
|
||||
return 2;
|
||||
}
|
||||
netInit();
|
||||
@@ -785,7 +792,7 @@ int main(int argc, char** argv) {
|
||||
} else if (mode == "pair" && argc >= 4) {
|
||||
rc = runPair(argv[2], argv[3], argc >= 5 ? argv[4] : "jaglink", argc >= 6 ? argv[5] : "off", argc >= 7 ? argv[6] : "off");
|
||||
} else if (mode == "pair-shared" && argc >= 4) {
|
||||
rc = runPairShared(argv[2], argv[3]);
|
||||
rc = runPairShared(argv[2], argv[3], argc >= 5 ? argv[4] : "off");
|
||||
} else if (mode == "control" && argc >= 4) {
|
||||
rc = runControl(argv[2], argv[3], argc >= 5 ? argv[4] : "off");
|
||||
} else if (mode == "discovery" && argc >= 4) {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
quit_during_call.py -- a quit (SIGTERM) that arrives while the UI thread waits in a slow EmuThread::call (#967, #1039).
|
||||
|
||||
VJ_TEST_SLOW_CALL_MS stalls the load call() on the emulation thread (as smoke_busy_threaded does). Once the app has
|
||||
logged "[emu] busy" the UI thread is parked in that call; the script sends SIGTERM then. The headless signal handler
|
||||
only sets the quit flag, so the call must finish on its own, the frame loops must be skipped, and the app must leave
|
||||
cleanly: exit 0, the "quit requested" line, no owner violation, and neither 3 s warning (a thread that did not stop,
|
||||
a call that was not answered).
|
||||
|
||||
usage: quit_during_call.py <virtualjaguar> <rom>
|
||||
"""
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
SLOW_MS = 2000
|
||||
|
||||
|
||||
def main():
|
||||
exe, rom = sys.argv[1:3]
|
||||
env = dict(os.environ, VJ_TEST_SLOW_CALL_MS=str(SLOW_MS))
|
||||
p = subprocess.Popen([exe, "--headless", "--emu-thread", "on", "--frames", "30", rom], env=env,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
|
||||
lines = []
|
||||
busy = threading.Event()
|
||||
|
||||
def reader():
|
||||
for line in p.stdout:
|
||||
lines.append(line)
|
||||
if "[emu] busy" in line:
|
||||
busy.set()
|
||||
|
||||
t = threading.Thread(target=reader, daemon=True)
|
||||
t.start()
|
||||
try:
|
||||
if not busy.wait(30):
|
||||
print("FAIL the app never logged the busy state\n" + "".join(lines))
|
||||
return 1
|
||||
t0 = time.monotonic()
|
||||
p.send_signal(signal.SIGTERM) # the UI thread is inside the call right now
|
||||
try:
|
||||
rc = p.wait(timeout=20)
|
||||
except subprocess.TimeoutExpired:
|
||||
print("FAIL the app did not exit within 20 s of SIGTERM\n" + "".join(lines))
|
||||
return 1
|
||||
t.join(5)
|
||||
out = "".join(lines)
|
||||
if rc != 0:
|
||||
print("FAIL exit code %d, expected 0\n%s" % (rc, out)); return 1
|
||||
for bad in ("owner violation", "ThreadSanitizer", "did not stop within 3 s", "did not answer within 3 s"):
|
||||
if bad in out:
|
||||
print("FAIL the log says %r\n%s" % (bad, out)); return 1
|
||||
if "headless: quit requested, exiting cleanly" not in out:
|
||||
print("FAIL no \"quit requested\" line (the run ended some other way)\n" + out); return 1
|
||||
print("PASS: quit during a slow call exits 0 (%.1f s after the signal)" % (time.monotonic() - t0))
|
||||
return 0
|
||||
finally:
|
||||
if p.poll() is None:
|
||||
p.kill()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user