diff --git a/recompiler/tests/test_aot_overlay_discovery.py b/recompiler/tests/test_aot_overlay_discovery.py index f84c9ed8..bd125706 100644 --- a/recompiler/tests/test_aot_overlay_discovery.py +++ b/recompiler/tests/test_aot_overlay_discovery.py @@ -11,6 +11,18 @@ import sys import tempfile import time +if os.name == 'nt': + import ctypes + # Several publication checks deliberately probe non-PE synthetic DLLs. + # CTest's Windows child error mode can display a loader error dialog and + # block LoadLibrary indefinitely. Keep the real loader/export validation, + # but make its expected failures noninteractive in this test process. + kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) + kernel32.GetErrorMode.restype = ctypes.c_uint + kernel32.SetErrorMode.argtypes = [ctypes.c_uint] + kernel32.SetErrorMode.restype = ctypes.c_uint + kernel32.SetErrorMode(kernel32.GetErrorMode() | 0x0001 | 0x8000) + ROOT = pathlib.Path(__file__).resolve().parents[2] SPEC = importlib.util.spec_from_file_location( diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 394a4cf8..5827de52 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -342,8 +342,7 @@ if(BUILD_TESTING) target_compile_options(gpu_textured_dot_nw_shift_exec_test PRIVATE -ffunction-sections -fdata-sections) target_link_options(gpu_textured_dot_nw_shift_exec_test PRIVATE - -Wl,--gc-sections - -Wl,--error-limit=0) + -Wl,--gc-sections) endif() add_test(NAME gpu_textured_dot_nw_shift_exec_test COMMAND gpu_textured_dot_nw_shift_exec_test) diff --git a/runtime/runtime.cmake b/runtime/runtime.cmake index a0398a5a..dcbf0302 100644 --- a/runtime/runtime.cmake +++ b/runtime/runtime.cmake @@ -1629,7 +1629,12 @@ function(psxrecomp_add_runtime_target target) string(REPLACE "\\" "/" _psxrt_ico_fwd "${PSXRT_APP_ICON}") set(_psxrt_rc "${CMAKE_CURRENT_BINARY_DIR}/${target}_app_icon.rc") file(WRITE "${_psxrt_rc}" "IDI_ICON1 ICON \"${_psxrt_ico_fwd}\"\n") - target_sources(${target} PRIVATE "${_psxrt_rc}") + # The icon has no dependency on runtime macros or headers. + # Compile it separately: windres launches a preprocessor through + # a shell, which cannot round-trip the executable's quoted + # string definitions (notably the pipe-separated BIOS stems). + add_library(${target}_app_icon OBJECT "${_psxrt_rc}") + target_sources(${target} PRIVATE $) message(STATUS "psxrecomp ${target}: APP_ICON=${PSXRT_APP_ICON} (RC=${CMAKE_RC_COMPILER})") else() message(WARNING diff --git a/runtime/tests/test_cdrom_lid_integration.py b/runtime/tests/test_cdrom_lid_integration.py index 38eda4bf..955a733f 100644 --- a/runtime/tests/test_cdrom_lid_integration.py +++ b/runtime/tests/test_cdrom_lid_integration.py @@ -26,9 +26,11 @@ def function_body(name: str) -> str: reinsert = function_body("debug_force_cd_reinsert") -assert "cdrom_lid_begin_open" in reinsert -assert "s_lid_irq_pending = 1" in reinsert -assert "set_irq(CDIRQ_ACK)" not in reinsert +assert "begin_tray_open_cycle()" in reinsert +tray_open = function_body("begin_tray_open_cycle") +assert "cdrom_lid_begin_open" in tray_open +assert "s_lid_irq_pending = 1" in tray_open +assert "set_irq(CDIRQ_ACK)" not in reinsert + tray_open present = function_body("present_lid_open_irq_if_ready") assert "response_push(0x08)" in present diff --git a/runtime/tests/test_gpu_textured_dot_nw_shift_exec.c b/runtime/tests/test_gpu_textured_dot_nw_shift_exec.c index 4986df9f..62294947 100644 --- a/runtime/tests/test_gpu_textured_dot_nw_shift_exec.c +++ b/runtime/tests/test_gpu_textured_dot_nw_shift_exec.c @@ -15,6 +15,24 @@ uint32_t g_debug_current_func_addr; uint32_t g_debug_last_store_pc; CPUState *debug_cpu_ptr; int g_psx_vram_dirty_tracking; +uint32_t g_psx_ram_size = 0x00200000u; +uint32_t g_psx_ram_mask = 0x001FFFFFu; + +/* This fixture exercises stock GPU packets without a GL context or mod bank. + * Keep the gpu.c dependencies explicit, including on linkers that retain + * additional exported command handlers despite --gc-sections. */ +GrBackend gr_backend(void) { return GR_BACKEND_SOFTWARE; } +int gl_renderer_texture_banks_supported(void) { return 0; } +int gl_renderer_select_texture_bank(uint16_t id) { (void)id; return 0; } +uint16_t mod_texture_packet_bank(uint32_t source, const uint32_t *words, + uint32_t count) { + (void)source; (void)words; (void)count; + return 0; +} +int mod_texture_packet_precision(uint32_t source, float q[3], float xy[6]) { + (void)source; (void)q; (void)xy; + return 0; +} static uint32_t test_ram[0x00200000u / 4u]; diff --git a/runtime/tests/test_launch_disc_path.py b/runtime/tests/test_launch_disc_path.py index 739b000f..4d0d7dc8 100644 --- a/runtime/tests/test_launch_disc_path.py +++ b/runtime/tests/test_launch_disc_path.py @@ -46,7 +46,8 @@ int main(int argc, char** argv) { env = os.environ.copy() compiler = shutil.which(args.compiler) or args.compiler env['PATH'] = str(Path(compiler).parent) + os.pathsep + env.get('PATH', '') - subprocess.run([compiler, '-std=c++17', '-O2', '-I' + str(root/'include'), str(source), + subprocess.run([compiler, '-std=c++17', '-O2', '-I' + str(root/'include'), + '-I' + str(root.parent/'recompiler/include'), str(source), str(root/'src/disc_path.cpp'), str(root/'src/cue_sheet.cpp'), '-o', str(program)], check=True, env=env) for stem, tracks in [('disc', 12), ('album', 8)]: diff --git a/runtime/tests/test_mod_catalog_layout.py b/runtime/tests/test_mod_catalog_layout.py index 37f62d67..e26d22c5 100644 --- a/runtime/tests/test_mod_catalog_layout.py +++ b/runtime/tests/test_mod_catalog_layout.py @@ -124,6 +124,7 @@ def run_select( driver = tmp / "select_driver.cmake" driver.write_text( + 'cmake_minimum_required(VERSION 3.20)\n' f'include("{SELECT.as_posix()}")\n' "psx_select_builtin_mod_ids(_out LABEL psx-runtime" + cm_list("AVAILABLE", available) diff --git a/runtime/tests/test_packagers_never_format_cache_tag.py b/runtime/tests/test_packagers_never_format_cache_tag.py index e93d8873..77c1e631 100644 --- a/runtime/tests/test_packagers_never_format_cache_tag.py +++ b/runtime/tests/test_packagers_never_format_cache_tag.py @@ -104,7 +104,7 @@ SCANNED_NAMES = ('CMakeLists.txt',) SKIP_DIRS = {'.git', 'build', 'generated', 'node_modules', '__pycache__', 'lib', '_deps', 'beetle-psx', 'recomp-ui', 'psxrecomp-v4', - 'ghidra', 'seeds', 'assets'} + 'ghidra', 'seeds', 'assets', 'overlay_toolchain'} # A release packager that touches the OVERLAY CACHE must route through the # framework's shared staging surface rather than reimplementing it. The @@ -128,6 +128,17 @@ TAG_CONSUMER_RE = re.compile( SHARED_SURFACE_RE = re.compile(r'release_overlay_stage|release_stage\.py') +def check_consumer_detection(): + # Config validation is not cache staging. Retain positive controls so the + # narrower heuristic cannot silently exempt real cache consumers. + assert not TAG_CONSUMER_RE.search('overlay_cache = true') + assert not TAG_CONSUMER_RE.search('${SHIP_WITHOUT_OVERLAY_CACHE_BECAUSE}') + for example in ('${overlay_cache}', '$overlay_cache', '$Overlay_Cache', + 'overlay_cache/gcc', 'cache_tag()', '$CgTag', 'cg_tag=foo', + 'overlay_toolchain'): + assert TAG_CONSUMER_RE.search(example), example + + def _is_scanned(name): return name.endswith(SCANNED_SUFFIXES) or name in SCANNED_NAMES @@ -178,6 +189,7 @@ def framework_root(): def main(argv=None): + check_consumer_detection() ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) ap.add_argument('--root', action='append', default=[], help='extra tree to check (repeatable). In an extra tree ' diff --git a/runtime/tests/test_savestate_status_protocol.py b/runtime/tests/test_savestate_status_protocol.py index fe19988c..f893263e 100644 --- a/runtime/tests/test_savestate_status_protocol.py +++ b/runtime/tests/test_savestate_status_protocol.py @@ -11,12 +11,14 @@ REWIND = (ROOT / "src/psx_rewind.c").read_text(encoding="utf-8") DIRTY = (ROOT / "src/dirty_ram_interp.c").read_text(encoding="utf-8") BOOT_STATE_H = (ROOT / "include/boot_state.h").read_text(encoding="utf-8") DMA = (ROOT / "src/dma.c").read_text(encoding="utf-8") +BOOT_STATE = (ROOT / "src/boot_state.c").read_text(encoding="utf-8") # The per-word DMA2 cursor and XA DATA_END pending bit grow the snapshot wire. -# Lock the format change to v7 so an old file is rejected before any state -# section is applied. +# Keep the v7 minimum: v8 adds enhancement memory, while vanilla writers still +# emit v7. Older files must be rejected before any live state is applied. assert "#define DMA_GPU_LL_WIRE (4u + (10u * 4u))" in DMA -assert "#define BOOT_STATE_VERSION 7u" in BOOT_STATE_H +assert "#define BOOT_STATE_VERSION 8u" in BOOT_STATE_H +assert "psx_mod_memory_snapshot_bytes() ? BOOT_STATE_VERSION : 7u" in BOOT_STATE assert "#define BOOT_STATE_VERSION_MIN_READ 7u" in BOOT_STATE_H assert "Reject\n * them at the header before any section changes the live machine." in BOOT_STATE_H diff --git a/tools/tests/test_bundled_release_layout.py b/tools/tests/test_bundled_release_layout.py index 30e0b419..0429b977 100644 --- a/tools/tests/test_bundled_release_layout.py +++ b/tools/tests/test_bundled_release_layout.py @@ -33,10 +33,20 @@ sys.path.insert(0, str(TOOLKIT)) TEMPLATE = FW / "docs" / "ci" / "templates" / "game-release.yml" CHECK_GENERATED = FW / "tools" / "ci" / "check_generated.sh" BOOT = "SLUS_012.34" +GIT = shutil.which("git") +BASH = shutil.which("bash") +if os.name == "nt": + # PATH can resolve bash to the WSL launcher and git to an MSYS shim. + # Neither reliably accepts the native Windows fixture/script paths. + git_root = Path(os.environ.get("ProgramFiles", "C:/Program Files")) / "Git" + if (git_root / "cmd/git.exe").is_file(): + GIT = str(git_root / "cmd/git.exe") + if (git_root / "bin/bash.exe").is_file(): + BASH = str(git_root / "bin/bash.exe") def _git(root, *args): - return subprocess.run(["git", "-C", str(root), *args], capture_output=True, + return subprocess.run([GIT, "-C", str(root), *args], capture_output=True, text=True, check=False) @@ -167,7 +177,7 @@ class ScaffoldContract(unittest.TestCase): class CheckGeneratedScript(unittest.TestCase): def run_check(self, root): - return subprocess.run(["bash", str(CHECK_GENERATED), "--root", str(root)], + return subprocess.run([BASH, CHECK_GENERATED.as_posix(), "--root", root.as_posix()], capture_output=True, text=True, check=False) def test_passes_on_committed_game_c(self): @@ -278,12 +288,12 @@ class PackagerScriptContract(unittest.TestCase): "tools/ci/generate_openbios.sh"): p = FW / rel self.assertTrue(os.access(p, os.X_OK), f"{rel} not executable") - r = subprocess.run(["bash", "-n", str(p)], capture_output=True, text=True) + r = subprocess.run([BASH, "-n", p.as_posix()], capture_output=True, text=True) self.assertEqual(r.returncode, 0, r.stderr) if __name__ == "__main__": - if shutil.which("git") is None or shutil.which("bash") is None: + if GIT is None or BASH is None: print("git and bash are required", file=sys.stderr) sys.exit(2) unittest.main(verbosity=1)