Land src/core/package: the pure RSBK container — format ladder, JSON manifest, framing/layout codec
Two-integer ladder (formatVersion/minReaderVersion), bare-name-only entries validated on encode and decode, prefix decode that proves exact file size without ever reading a payload.
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
// Standalone tests for reasampler::package's format contract — no REAPER, no
|
||||
// test framework. Pins the version-ladder classification (both integers, every
|
||||
// branch) and the entry-name rule that makes path expression structurally
|
||||
// impossible in a package.
|
||||
|
||||
#include "../src/core/package/package_format.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
using namespace reasampler::package;
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(cond) do { if(!(cond)) { \
|
||||
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
|
||||
|
||||
// --- classifyPackageVersion --------------------------------------------------
|
||||
|
||||
static void testClassifyReadable() {
|
||||
CHECK(classifyPackageVersion(kPackageFormatVersion, kPackageMinReaderVersion) ==
|
||||
PackageReadability::Readable);
|
||||
// The additive-forward-compat direction: a newer writer whose minReader
|
||||
// still reaches back to this build reads fine.
|
||||
CHECK(classifyPackageVersion(kPackageFormatVersion + 5, kPackageMinReaderVersion) ==
|
||||
PackageReadability::Readable);
|
||||
// Boundary: minReader exactly this build's format version.
|
||||
CHECK(classifyPackageVersion(kPackageFormatVersion + 1, kPackageFormatVersion) ==
|
||||
PackageReadability::Readable);
|
||||
}
|
||||
|
||||
static void testClassifyTooNew() {
|
||||
// Boundary: one past this build's format version refuses.
|
||||
CHECK(classifyPackageVersion(kPackageFormatVersion + 1, kPackageFormatVersion + 1) ==
|
||||
PackageReadability::TooNew);
|
||||
CHECK(classifyPackageVersion(99, 42) == PackageReadability::TooNew);
|
||||
}
|
||||
|
||||
static void testClassifyMalformed() {
|
||||
// Zero versions: no honest writer emits them (the ladder starts at 1).
|
||||
CHECK(classifyPackageVersion(0, 0) == PackageReadability::Malformed);
|
||||
CHECK(classifyPackageVersion(1, 0) == PackageReadability::Malformed);
|
||||
CHECK(classifyPackageVersion(0, 1) == PackageReadability::Malformed);
|
||||
// A writer cannot require a reader newer than what it wrote.
|
||||
CHECK(classifyPackageVersion(1, 2) == PackageReadability::Malformed);
|
||||
// Incoherence outranks TooNew: even with both above this build, minReader >
|
||||
// formatVersion is Malformed, not a refusal message.
|
||||
CHECK(classifyPackageVersion(5, 9) == PackageReadability::Malformed);
|
||||
}
|
||||
|
||||
// --- isValidEntryName --------------------------------------------------------
|
||||
|
||||
static void testEntryNameAccepts() {
|
||||
CHECK(isValidEntryName("kick.wav"));
|
||||
CHECK(isValidEntryName("Snare 03 (wet).wav"));
|
||||
CHECK(isValidEntryName("no-extension"));
|
||||
CHECK(isValidEntryName(".hidden")); // a leading dot is a bare name
|
||||
CHECK(isValidEntryName("a.b.c.wav")); // single dots are fine
|
||||
CHECK(isValidEntryName(std::string(kMaxEntryNameBytes, 'x'))); // at the cap
|
||||
}
|
||||
|
||||
static void testEntryNameRejectsSeparatorsAndDots() {
|
||||
CHECK(!isValidEntryName(""));
|
||||
CHECK(!isValidEntryName("."));
|
||||
CHECK(!isValidEntryName(".."));
|
||||
CHECK(!isValidEntryName("..\\evil.wav"));
|
||||
CHECK(!isValidEntryName("../evil.wav"));
|
||||
CHECK(!isValidEntryName("a..b.wav")); // any ".." occurrence rejects
|
||||
CHECK(!isValidEntryName("dir/inner.wav"));
|
||||
CHECK(!isValidEntryName("dir\\inner.wav"));
|
||||
CHECK(!isValidEntryName("/rooted.wav"));
|
||||
CHECK(!isValidEntryName("\\rooted.wav"));
|
||||
}
|
||||
|
||||
static void testEntryNameRejectsAbsolutePrefixes() {
|
||||
CHECK(!isValidEntryName("C:\\abs.wav"));
|
||||
CHECK(!isValidEntryName("C:/abs.wav"));
|
||||
CHECK(!isValidEntryName("c:relative-to-drive.wav")); // ':' bans drive forms
|
||||
CHECK(!isValidEntryName("\\\\server\\share.wav")); // UNC
|
||||
CHECK(!isValidEntryName(std::string(kMaxEntryNameBytes + 1, 'x'))); // over cap
|
||||
}
|
||||
|
||||
int main() {
|
||||
testClassifyReadable();
|
||||
testClassifyTooNew();
|
||||
testClassifyMalformed();
|
||||
testEntryNameAccepts();
|
||||
testEntryNameRejectsSeparatorsAndDots();
|
||||
testEntryNameRejectsAbsolutePrefixes();
|
||||
|
||||
if (g_fail == 0) {
|
||||
std::printf("package_format_tests: all passed\n");
|
||||
return 0;
|
||||
}
|
||||
std::printf("package_format_tests: %d failure(s)\n", g_fail);
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user