a14d33aa75
RENDER_FORMAT now uses the base64 float-WAV config (raw bytes were rejected into a 16-bit/44.1 fallback); RENDER_SRATE pinned to the project rate. Unsaved projects prompt Save then refuse if cancelled — no default-location fallback. Harden deriveBankPaths against empty projectDir.
126 lines
5.4 KiB
C++
126 lines
5.4 KiB
C++
// Standalone tests for reasampler::capture_paths — no REAPER, no framework.
|
|
// The capture shell is DAW-bound and only verifiable in REAPER; this covers the
|
|
// one genuinely pure piece: the bank-folder / unique-name / project-relative
|
|
// path arithmetic that feeds BankIndex::add's relative-only invariant.
|
|
|
|
#include "../src/capture_paths.h"
|
|
|
|
#include <cstdio>
|
|
#include <string>
|
|
|
|
using namespace reasampler;
|
|
|
|
static int g_fail = 0;
|
|
#define CHECK(cond) do { if(!(cond)) { \
|
|
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
|
|
|
|
static void testNormalizeSlashes() {
|
|
CHECK(normalizeSlashes("C:\\a\\b") == "C:/a/b");
|
|
CHECK(normalizeSlashes("a/b/c") == "a/b/c");
|
|
CHECK(normalizeSlashes("a/b/") == "a/b"); // trailing slash stripped
|
|
CHECK(normalizeSlashes("a\\b\\") == "a/b"); // backslash + trailing
|
|
CHECK(normalizeSlashes("/") == "/"); // lone root preserved
|
|
CHECK(normalizeSlashes("") == ""); // empty stays empty
|
|
}
|
|
|
|
static void testSanitizeStem() {
|
|
// Safe characters survive verbatim.
|
|
CHECK(sanitizeStem("Kick_01.take-2") == "Kick_01.take-2");
|
|
// Spaces, slashes, quotes, control chars become '_'.
|
|
CHECK(sanitizeStem("my mix") == "my_mix");
|
|
CHECK(sanitizeStem("a/b\\c") == "a_b_c");
|
|
CHECK(sanitizeStem("q\"uote") == "q_uote");
|
|
CHECK(sanitizeStem(std::string("nul\0byte", 8)) == "nul_byte");
|
|
// Nothing usable -> stable default.
|
|
CHECK(sanitizeStem("") == "capture");
|
|
CHECK(sanitizeStem(" ") == "capture");
|
|
// All-separator (no alphanumeric) -> default, so the name is meaningful.
|
|
CHECK(sanitizeStem("...") == "capture");
|
|
CHECK(sanitizeStem("-_-") == "capture");
|
|
}
|
|
|
|
static void testDeriveRelativePathIsProjectRelative() {
|
|
BankPaths p = deriveBankPaths("C:\\Users\\d\\proj", "master mix", "1753080000");
|
|
// Relative path is under the fixed bank subfolder, forward-slashed, .wav.
|
|
CHECK(p.relativePath == "reasampler_bank/master_mix_1753080000.wav");
|
|
// It must NOT be absolute by any of BankIndex::add's rejection rules:
|
|
// no leading '/', no drive letter, no backslash, no UNC prefix.
|
|
CHECK(p.relativePath.find(':') == std::string::npos);
|
|
CHECK(p.relativePath.find('\\') == std::string::npos);
|
|
CHECK(!p.relativePath.empty() && p.relativePath[0] != '/');
|
|
CHECK(p.relativePath.rfind("\\\\", 0) != 0);
|
|
}
|
|
|
|
static void testDeriveAbsoluteDirJoinsProjectDir() {
|
|
BankPaths p = deriveBankPaths("C:\\Users\\d\\proj", "kick", "");
|
|
// Backslashes normalized; bank subfolder appended; no trailing slash.
|
|
CHECK(p.absoluteDir == "C:/Users/d/proj/reasampler_bank");
|
|
// No unique tag -> stem has no trailing "_".
|
|
CHECK(p.fileName == "kick.wav");
|
|
CHECK(p.relativePath == "reasampler_bank/kick.wav");
|
|
}
|
|
|
|
static void testDeriveHandlesTrailingSlashProjectDir() {
|
|
// A project dir with a trailing slash must not double up in the join.
|
|
BankPaths p = deriveBankPaths("/home/d/proj/", "mix", "7");
|
|
CHECK(p.absoluteDir == "/home/d/proj/reasampler_bank");
|
|
CHECK(p.fileName == "mix_7.wav");
|
|
}
|
|
|
|
static void testDeriveEmptyProjectDirIsRejected() {
|
|
// Precondition: deriveBankPaths requires a non-empty projectDir.
|
|
// In debug builds the assert(!dir.empty()) fires immediately and aborts
|
|
// the process — that IS the check, so we don't call into it there.
|
|
// In release/NDEBUG builds the assert is elided; we verify the fallback
|
|
// contract: absoluteDir is left empty (not a bare "reasampler_bank") so
|
|
// any caller that ignores the precondition fails loudly at the render/stat
|
|
// step rather than silently writing to CWD.
|
|
#ifdef NDEBUG
|
|
BankPaths p = deriveBankPaths("", "mix", "");
|
|
CHECK(p.absoluteDir.empty());
|
|
CHECK(p.relativePath == "reasampler_bank/mix.wav");
|
|
#endif
|
|
// Debug: assert fires on the call above — contract verified by the crash.
|
|
}
|
|
|
|
static void testDeterministicForSameInputs() {
|
|
// Same inputs -> same derived paths (feeds deterministic file naming).
|
|
BankPaths a = deriveBankPaths("C:/p", "mix", "42");
|
|
BankPaths b = deriveBankPaths("C:/p", "mix", "42");
|
|
CHECK(a.absoluteDir == b.absoluteDir);
|
|
CHECK(a.relativePath == b.relativePath);
|
|
CHECK(a.fileName == b.fileName);
|
|
}
|
|
|
|
static void testFileStem() {
|
|
// fileStem is the stem component of fileName (no extension). The capture
|
|
// backend passes fileStem directly to RENDER_PATTERN because REAPER appends
|
|
// the format extension itself — the backend must not re-derive or re-strip it.
|
|
BankPaths p = deriveBankPaths("C:/p", "master mix", "123");
|
|
CHECK(p.fileStem == "master_mix_123");
|
|
CHECK(p.fileName == "master_mix_123.wav");
|
|
// fileStem + ".wav" must equal fileName (the invariant the backend relies on).
|
|
CHECK(p.fileStem + ".wav" == p.fileName);
|
|
|
|
// No tag: stem only.
|
|
BankPaths q = deriveBankPaths("C:/p", "kick", "");
|
|
CHECK(q.fileStem == "kick");
|
|
CHECK(q.fileName == "kick.wav");
|
|
CHECK(q.fileStem + ".wav" == q.fileName);
|
|
}
|
|
|
|
int main() {
|
|
testNormalizeSlashes();
|
|
testSanitizeStem();
|
|
testDeriveRelativePathIsProjectRelative();
|
|
testDeriveAbsoluteDirJoinsProjectDir();
|
|
testDeriveHandlesTrailingSlashProjectDir();
|
|
testDeriveEmptyProjectDirIsRejected();
|
|
testDeterministicForSameInputs();
|
|
testFileStem();
|
|
|
|
if (g_fail == 0) std::printf("capture_paths: all tests passed\n");
|
|
else std::printf("capture_paths: %d CHECK(s) FAILED\n", g_fail);
|
|
return g_fail ? 1 : 0;
|
|
}
|