// Unit test for the per-game audio override store (save/load round-trip, case-insensitive // lookup by image name, and the differing-overwrite detection that drives the warning). #include #include #include #include #include #include "audio/audio_overrides.hpp" using namespace coop; namespace { int g_failures = 0; void check(bool ok, const char* what) { if (!ok) { std::printf("FAIL: %s\n", what); ++g_failures; } else { std::printf(" ok: %s\n", what); } } std::wstring temp_path() { wchar_t dir[MAX_PATH] = {}; GetTempPathW(MAX_PATH, dir); return std::wstring(dir) + L"coop_overrides_test_" + std::to_wstring(GetCurrentProcessId()) + L".ini"; } } // namespace int main() { const std::wstring path = temp_path(); DeleteFileW(path.c_str()); const AudioFormatOverride brotato{44100, 2, 32, WAVE_FORMAT_IEEE_FLOAT}; const AudioFormatOverride snb{48000, 2, 16, WAVE_FORMAT_PCM}; const AudioFormatOverride cafe_ov{96000, 2, 32, WAVE_FORMAT_IEEE_FLOAT}; // "cafe.exe" with the e replaced by U+00E9 ('e' with acute). Under the old 7-bit narrow, // 0x00E9 & 0x7F == 0x69 == 'i', so this collapsed onto "cafi.exe" -- a silent key collision. // Built from a code point so the source stays pure ASCII (no literal/escape encoding hazards). const wchar_t cafe_buf[] = {L'c', L'a', L'f', static_cast(0x00E9), L'.', L'e', L'x', L'e', L'\0'}; const wchar_t* kCafe = cafe_buf; { AudioOverrideStore store(path); store.load(); // missing file -> empty AudioFormatOverride got; check(!store.find(L"brotato.exe", got), "empty store: no entry"); bool differed = true; store.set(L"C:\\games\\Brotato.exe", brotato, &differed); // full path -> basename key check(!differed, "first set: not a differing overwrite"); store.set(L"snb.exe", snb, &differed); check(!differed, "new game set: not a differing overwrite"); // Same value again -> not differing. store.set(L"brotato.exe", brotato, &differed); check(!differed, "identical re-set: not differing"); // Different value -> differing (drives the warning). const AudioFormatOverride brotato2{48000, 2, 32, WAVE_FORMAT_IEEE_FLOAT}; store.set(L"brotato.exe", brotato2, &differed); check(differed, "changed value: flagged as differing overwrite"); // Non-ASCII names must keep their own entry (no high-bit collision with the ASCII look-alike). store.set(kCafe, cafe_ov, &differed); store.set(L"cafi.exe", snb, &differed); AudioFormatOverride na; check(store.find(kCafe, na) && na == cafe_ov, "non-ASCII name keeps its own override"); check(store.find(L"cafi.exe", na) && na == snb, "the ASCII look-alike keeps a separate override"); } // Reload from disk in a fresh store: persistence + case-insensitive basename lookup. { AudioOverrideStore store(path); store.load(); AudioFormatOverride got; check(store.find(L"BROTATO.EXE", got), "reload: found case-insensitively"); check(got == AudioFormatOverride{48000, 2, 32, WAVE_FORMAT_IEEE_FLOAT}, "reload: latest value persisted"); check(store.find(L"D:\\steam\\snb.exe", got) && got == snb, "reload: found by full path basename"); check(!store.find(L"unknown.exe", got), "reload: unknown game absent"); // The non-ASCII name must survive the UTF-8 persist/reload round-trip. check(store.find(kCafe, got) && got == cafe_ov, "reload: non-ASCII name round-trips losslessly"); } DeleteFileW(path.c_str()); if (g_failures == 0) { std::printf("PASS audio_overrides_test\n"); return 0; } std::printf("FAILED audio_overrides_test (%d)\n", g_failures); return 1; }