// Unit test for the host-side IpcServer control operations that drive graceful disconnect // (hook/src reconciles against these): request_unhook_all() must set every subsystem's // "disabled" flag so the injected DLL removes all hooks, and all_hooks_removed() must report // whether the DLL's hook registry currently has anything installed. No game / no DLL -- the // test opens a second view of the same section to act as the hook would (publish a registry, // read the control flags). Deterministic, no threads. #include #include #include #include "coop/protocol.hpp" #include "coop/shared_memory.hpp" #include "ipc/ipc_server.hpp" using namespace coop; namespace { int g_failures = 0; void check(bool ok, const char* what) { std::printf("%s %s\n", ok ? " ok:" : "FAIL:", what); if (!ok) { ++g_failures; } } bool all_disabled(const SharedBlock* b) { for (std::uint32_t s = 0; s < HookSubsys_Count; ++s) { if (b->control.subsystem_disabled[s].load(std::memory_order_acquire) != 1u) { return false; } } return true; } bool none_disabled(const SharedBlock* b) { for (std::uint32_t s = 0; s < HookSubsys_Count; ++s) { if (b->control.subsystem_disabled[s].load(std::memory_order_acquire) != 0u) { return false; } } return true; } } // namespace int main() { const unsigned long pid = GetCurrentProcessId(); // section name is per-pid; no real game needed IpcServer server; if (!server.start(pid)) { std::printf("FAIL: IpcServer::start\n"); return 1; } // A second view of the same section, standing in for the injected hook: it reads the control // flags the host writes and publishes the hook registry the host reads back. SharedMemory hook_view; if (!hook_view.open(shared_memory_name(pid), sizeof(SharedBlock))) { std::printf("FAIL: open hook view\n"); return 1; } auto* block = hook_view.as(); // Fresh section: nothing disabled (0 = install), so the game gets every subsystem by default. check(none_disabled(block), "fresh section requests all subsystems installed"); // Graceful disconnect step 1: ask the DLL to remove every hook. server.request_unhook_all(); check(all_disabled(block), "request_unhook_all() disables every subsystem"); // all_hooks_removed() reflects the DLL's registry. Empty registry => nothing installed. check(server.all_hooks_removed(), "all_hooks_removed() true when the registry is empty"); // Hook publishes one installed entry => something is still hooked. block->status.hook_entry_count = 1; block->status.hook_entries[0].installed = 1u; check(!server.all_hooks_removed(), "all_hooks_removed() false while a hook is still installed"); // Hook finishes removing it => back to "nothing installed". block->status.hook_entries[0].installed = 0u; check(server.all_hooks_removed(), "all_hooks_removed() true once every entry is uninstalled"); // Reconnect re-enables: clearing a subsystem's disabled flag requests it back (symmetric op). server.set_subsystem_enabled(HookSubsys_Input, true); check(block->control.subsystem_disabled[HookSubsys_Input].load(std::memory_order_acquire) == 0u, "re-enabling a subsystem clears its disabled flag (reconnect path)"); server.stop(); std::printf(g_failures == 0 ? "PASS ipc_server_test\n" : "FAILED ipc_server_test (%d)\n", g_failures); return g_failures == 0 ? 0 : 1; }