Files
CoopAllTheThings/tests/ipc_server_test.cpp
BlackMark 30eccf749d Apply clang-format across the whole tree
Run clang-format (the repo's .clang-format: LLVM base, 120 cols, tabs,
Allman functions) over every source file so the tree is formatter-clean.
Whitespace only -- no behavior change; full x64 + x86 suites pass.

Also set SortIncludes: false in .clang-format. Windows include order is
load-bearing (windows.h must precede tlhelp32.h / mmreg.h / xinput.h /
dinput.h; winsock2.h must precede windows.h), and the default
alphabetical sort reorders tlhelp32.h ahead of windows.h -- a build
break. Leaving order alone keeps the manual, correct grouping.
2026-07-12 11:52:53 +02:00

96 lines
3.3 KiB
C++

// 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 <cstdint>
#include <cstdio>
#include <windows.h>
#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<SharedBlock>();
// 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;
}