Files
CoopAllTheThings/tests/tool_paths_test.cpp
BlackMark 2802fbddf6 Add misc unit tests + harden the WAV chunk walk
Fills small coverage gaps:
- shared_memory_test: SharedMemory create-or-open aliasing, move (steal + empty
  the source, no double-free), reset, open-missing.
- wav_test: malformed input -- truncation, bad magic, missing data chunk,
  over-long data size (clamps), odd-sized chunk (word-align skip), and a corrupt
  ~4 GB chunk_size. The reader gains an advance guard so that last case can't wrap
  pos on a 32-bit size_t (x86) or spin the walk; it stops cleanly.
- tool_paths_test: deployed_artifact_path resolution -- next-to-exe, one-dir-up,
  and the not-found fallback -- with real marker files.
- audio_ring_test: an overrun-at-the-seam case (write head near the end: a
  wrapping push that fits vs. an over-capacity wrapping push dropped whole),
  exercising the wrap split + overrun together, not just at offset 0.

The injector bitness check isn't added as a unit test: is_wow64_process is
file-local and the real WOW64 path needs a 32-bit target, so it stays
inspection-covered (and exercised by the x86 injection path).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 02:05:35 +02:00

85 lines
2.5 KiB
C++

// Unit test for the deployed-artifact path resolution (common/include/coop/tool_paths.hpp): a probe
// staged under bin/<config>/tools/ must still find coop_hook.dll etc. at the deployable root one level
// up. Drives the real probe logic by dropping marker files next to the exe and one directory up.
#include <cstdio>
#include <string>
#include <windows.h>
#include "coop/tool_paths.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 touch(const std::wstring& path)
{
HANDLE h = CreateFileW(path.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (h == INVALID_HANDLE_VALUE)
{
return false;
}
CloseHandle(h);
return true;
}
std::wstring parent_of(std::wstring dir) // dir has a trailing separator
{
if (!dir.empty())
{
dir.pop_back();
}
const std::size_t slash = dir.find_last_of(L"\\/");
return slash == std::wstring::npos ? std::wstring() : dir.substr(0, slash + 1);
}
} // namespace
int main()
{
const std::wstring here = exe_directory();
check(!here.empty() && (here.back() == L'\\' || here.back() == L'/'), "exe_directory ends with a separator");
const std::wstring tag = std::to_wstring(GetCurrentProcessId());
const std::wstring near_name = L"coop_tp_near_" + tag + L".marker";
const std::wstring up_name = L"coop_tp_up_" + tag + L".marker";
const std::wstring missing_name = L"coop_tp_missing_" + tag + L".marker";
const std::wstring up_dir = parent_of(here);
const bool have_up = !up_dir.empty();
// next-to-exe wins.
check(touch(here + near_name), "created a marker next to the exe");
check(deployed_artifact_path(near_name.c_str()) == here + near_name, "resolves an artifact next to the exe");
// one directory up (the deployable root) when it isn't next to the exe.
if (have_up)
{
check(touch(up_dir + up_name), "created a marker one directory up");
check(deployed_artifact_path(up_name.c_str()) == up_dir + up_name,
"falls back to the artifact one directory up");
}
// missing -> the next-to-exe path (so the caller can report a sensible 'not found').
check(deployed_artifact_path(missing_name.c_str()) == here + missing_name,
"missing artifact -> next-to-exe fallback path");
DeleteFileW((here + near_name).c_str());
if (have_up)
{
DeleteFileW((up_dir + up_name).c_str());
}
std::printf(g_failures == 0 ? "PASS tool_paths_test\n" : "FAILED tool_paths_test (%d)\n", g_failures);
return g_failures == 0 ? 0 : 1;
}