d9ad8e4adf
ICaptureBackend/CaptureRequest seam + OfflineRenderBackend driving snapshotted RENDER_* with dither/normalize forced off for bit-identical output, RenderFailed via filesystem check, and a pure capture_paths lib with tests. Spike action registered.
119 lines
5.0 KiB
C++
119 lines
5.0 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 testDeriveEmptyProjectDirFallsBackToRelative() {
|
|
// Defensive: with no project dir, absoluteDir is just the bank subfolder
|
|
// (the shell rejects the no-path case before this, but the arithmetic must
|
|
// not emit a leading slash that would read as absolute).
|
|
BankPaths p = deriveBankPaths("", "mix", "");
|
|
CHECK(p.absoluteDir == "reasampler_bank");
|
|
CHECK(p.relativePath == "reasampler_bank/mix.wav");
|
|
}
|
|
|
|
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();
|
|
testDeriveEmptyProjectDirFallsBackToRelative();
|
|
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;
|
|
}
|