diff --git a/recompiler/CMakeLists.txt b/recompiler/CMakeLists.txt index 179df0ae..39a57238 100644 --- a/recompiler/CMakeLists.txt +++ b/recompiler/CMakeLists.txt @@ -619,12 +619,16 @@ if(BUILD_TESTING) COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../tools/tests/test_overlay_guard_bytes_declaration.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/..) - if(Python3_VERSION VERSION_GREATER_EQUAL "3.11") - add_test( - NAME aot_overlay_methods - COMMAND ${Python3_EXECUTABLE} - ${CMAKE_CURRENT_SOURCE_DIR}/../tools/tests/test_aot_overlay_pipeline.py - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/..) + # The pipeline reads mod manifests with tomllib (Python 3.11+). An + # older interpreter keeps the test registered but DISABLED, so it is + # reported as not run instead of silently missing from `ctest -N`. + add_test( + NAME aot_overlay_methods + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/../tools/tests/test_aot_overlay_pipeline.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/..) + if(Python3_VERSION VERSION_LESS "3.11") + set_tests_properties(aot_overlay_methods PROPERTIES DISABLED TRUE) endif() # --static must walk the whole of compile_overlays.main() and exit 0: # a DLL-only reconciliation at its tail once read an unbound cache_dir @@ -692,6 +696,50 @@ if(BUILD_TESTING) ${CMAKE_CURRENT_SOURCE_DIR}/../tools/tests/test_stall_report.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/..) + # ---- offline tool unit tests (tools/tests, tools/, tests/) ----------- + # Pure-Python unit tests of the offline tools: no built target and no + # generated BIOS, so they belong in this BIOS-free tree. They had no + # add_test() entry and were only run by whoever remembered to invoke + # pytest by hand. check_test_registration.cmake now globs these + # directories too, so a new one fails configure until it is listed. + # test_psx_chd skips its decode cases unless a shared libchdr is found + # (PSXRECOMP_LIBCHDR); its container/selection cases always run. + foreach(_t IN ITEMS + aligned_lzss_banks + cursor_writers + disc_companion + duckstation_oracle + gpu_frame + menu_scan + mips_tagged_relocations + overlay_widescreen_callbacks + pad_delivery + pad_trace + psx_chd + sbi_registry + static_fragment_variant_keys) + add_test( + NAME ${_t} + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/../tools/tests/test_${_t}.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/..) + endforeach() + add_test( + NAME compile_overlays_static_split + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/../tools/test_compile_overlays_static_split.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/..) + add_test( + NAME aot_overlay_spike_coverage_report + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/../tools/aot_overlay_spike/test_coverage_report.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/..) + add_test( + NAME disc_inventory + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/../tests/test_disc_inventory.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/..) + # ---- codegen tests that were written but never registered ----------- # These existed in recompiler/tests/ with no add_test() entry, so they # had no way to run and no way to fail. Each was executed manually diff --git a/runtime/check_test_registration.cmake b/runtime/check_test_registration.cmake index a26d87f3..8fa9d4a5 100644 --- a/runtime/check_test_registration.cmake +++ b/runtime/check_test_registration.cmake @@ -88,12 +88,20 @@ function(psxrecomp_check_all_tests_registered) # CONFIGURE_DEPENDS matters more than usual here: without it, ADDING a test # file would not re-run configure, so this check would sleep through the # exact event it exists to catch. + # The offline tools' unit tests live beside the tools (tools/tests, + # tools/, one level of tools//) and in the top-level tests/. They + # are registered in recompiler/CMakeLists.txt, the BIOS-free tree. file(GLOB _found CONFIGURE_DEPENDS "${_root}/runtime/tests/test_*.c" "${_root}/runtime/tests/test_*.cpp" "${_root}/runtime/tests/test_*.py" "${_root}/recompiler/tests/*_test.cpp" - "${_root}/recompiler/tests/test_*.py") + "${_root}/recompiler/tests/test_*.py" + "${_root}/tools/tests/test_*.py" + "${_root}/tools/test_*.py" + "${_root}/tools/*/test_*.py" + "${_root}/tests/test_*.py") + list(REMOVE_DUPLICATES _found) # ---- diff ------------------------------------------------------------- set(_orphans "") diff --git a/tools/tests/test_compile_overlays_static_tail.py b/tools/tests/test_compile_overlays_static_tail.py index 656f62e6..0eeaf243 100644 --- a/tools/tests/test_compile_overlays_static_tail.py +++ b/tools/tests/test_compile_overlays_static_tail.py @@ -13,9 +13,14 @@ check before doing anything else. CTest passes it the same way the other overlay codegen tests get it. Usage: test_compile_overlays_static_tail.py --recompiler + +The recompiler may also come from PSXRECOMP_GAME_RECOMPILER, or be found in a +framework build directory, so a plain unittest/pytest collection runs the test +instead of handing subprocess a None path (the old failure mode: TypeError). """ import argparse import json +import os from pathlib import Path import subprocess import sys @@ -24,11 +29,33 @@ import unittest TOOLS = Path(__file__).resolve().parents[1] ROOT = TOOLS.parent +RECOMPILER_ENV = "PSXRECOMP_GAME_RECOMPILER" RECOMPILER = None +def find_recompiler(): + """--recompiler, then the environment, then a framework build tree.""" + if RECOMPILER: + return RECOMPILER + env = os.environ.get(RECOMPILER_ENV, "").strip() + if env: + return env + names = ("psxrecomp-game.exe", "psxrecomp-game") + for build in sorted(ROOT.glob("build*")) + sorted((ROOT / "recompiler").glob("build*")): + for name in names: + candidate = build / name + if candidate.is_file(): + return str(candidate) + return None + + class StaticTailTest(unittest.TestCase): def test_static_run_with_no_captures_exits_zero(self): + recompiler = find_recompiler() + self.assertTrue(recompiler and Path(recompiler).is_file(), + f"psxrecomp-game not found: pass --recompiler, set " + f"{RECOMPILER_ENV}, or build the recompiler under " + f"{ROOT}/build*") with tempfile.TemporaryDirectory() as tmp: tmp = Path(tmp) caps = tmp / "captures.json" @@ -43,7 +70,7 @@ class StaticTailTest(unittest.TestCase): "--captures", str(caps), "--out-dir", str(out), "--game-toml", str(toml), - "--recompiler", RECOMPILER, + "--recompiler", recompiler, "--runtime-include", str(ROOT / "runtime" / "include")], capture_output=True, text=True, timeout=120) detail = proc.stdout[-1500:] + proc.stderr[-1500:] diff --git a/tools/tests/test_cursor_writers.py b/tools/tests/test_cursor_writers.py index 1d50a491..85ca0077 100644 --- a/tools/tests/test_cursor_writers.py +++ b/tools/tests/test_cursor_writers.py @@ -180,3 +180,7 @@ class PerPressVerdictTest(unittest.TestCase): w(0x8004A5F5, 7, 6, "0xB", seq=3, frame=500), w(0x8004A5F5, 6, 5, "0xC", seq=4, frame=500)] self.assertEqual(cw.verdict(ws, 0x8004A5F5)[0], "two_paths") + + +if __name__ == "__main__": + unittest.main()