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.
96 lines
3.3 KiB
C++
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;
|
|
}
|