#!/usr/bin/env python3 """Test native bank storage/acknowledgments with ASan/UBSan, without the VM. Uses deps/json/single_include/nlohmann/json.hpp if restored, or --json-header pointing to the single header at the revision in the plugin's upstream.json. """ import argparse import os from pathlib import Path import re import shutil import subprocess import tempfile ROOT = Path(__file__).resolve().parents[1] parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--json-header", help="Header path; {game} expands to t5/t6 for their respective pinned headers") args = parser.parse_args() with tempfile.TemporaryDirectory(prefix="tws-bank-") as temp: directory = Path(temp) for game in ("t5", "t6"): plugin = ROOT / game / "plugins" / f"tws-{game}-gsc-utils" header = Path(args.json_header.format(game=game)) if args.json_header else plugin / "deps/json/single_include/nlohmann/json.hpp" if not header.is_file(): parser.error(f"Missing {header}; restore dependencies or pass --json-header") shutil.copyfile(header, directory / "json.hpp") (directory / "stdinc.hpp").write_text("#include \n#include \n#include \n#include \n") executable = directory / game subprocess.run([os.environ.get("CXX", "c++"), "-std=c++20", "-Wall", "-Wextra", "-Werror", "-fsanitize=address,undefined", "-I", str(directory), "-I", str(plugin / "src/utils"), str(plugin / "src/utils/bank.cpp"), str(ROOT / "tools/tests/bank_cases.cpp"), "-o", str(executable)], check=True) work = directory / f"{game}-data" subprocess.run([str(executable), str(work)], check=True) # Separate OS processes prove snapshots/identities don't depend on VM or # DLL memory. A newly booted producer recovers before its own start. subprocess.run([str(executable), str(work), "produce"], check=True) subprocess.run([str(executable), str(work), "recover"], check=True) subprocess.run([str(executable), str(work), "empty"], check=True) # Compile the actual registration adapter as well, substituting only # engine interfaces. This does not validate the Windows ABI/VM linker. stub = (ROOT / "tools/tests/http_component_stub.hpp").read_text() stub = stub.replace("std::vector get_keys() const", "void emplace_back(const script_value& value) { (*data)[std::to_string(data->size())] = value; }\n std::vector get_keys() const") (directory / "component_stub.hpp").write_text(stub) component = re.sub(r'^#include[^\n]*\n', '', (plugin / "src/component/bank.cpp").read_text(), flags=re.MULTILINE) source = directory / "component.cpp" source.write_text('#include "component_stub.hpp"\n#include "bank.hpp"\n' + component) subprocess.run([os.environ.get("CXX", "c++"), "-std=c++20", "-Wall", "-Wextra", "-Werror", "-fsyntax-only", "-I", str(directory), "-I", str(plugin / "src/utils"), str(source)], check=True) print(f"PASS {game}: native storage, acknowledgment, process restart and component checks", flush=True)