// 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 #include 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 // Legal names containing a ".." substring that is not the whole name: a // name can only ever be one path component (separators are banned), so // ".." as a component is the only expressible traversal. CHECK(isValidEntryName("take..final.wav")); CHECK(isValidEntryName("loop...wav")); CHECK(isValidEntryName("a..b")); } static void testEntryNameRejectsSeparatorsAndDots() { CHECK(!isValidEntryName("")); CHECK(!isValidEntryName(".")); CHECK(!isValidEntryName("..")); CHECK(!isValidEntryName("..\\evil.wav")); CHECK(!isValidEntryName("../evil.wav")); CHECK(!isValidEntryName("dir/inner.wav")); CHECK(!isValidEntryName("dir\\inner.wav")); CHECK(!isValidEntryName("/rooted.wav")); CHECK(!isValidEntryName("\\rooted.wav")); // Embedded NUL: every plausible filesystem call (ofstream, fopen, // CreateFileW off .c_str()) truncates at it, so two names differing only // after the NUL would collide on one file. CHECK(!isValidEntryName(std::string("a\0b.wav", 7))); // Other control bytes (newline here) are equally hostile to logs/UI. CHECK(!isValidEntryName("a\nb.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 } static void testEntryNameRejectsWindowsHostileNames() { // Reserved characters. CHECK(!isValidEntryName("a*b.wav")); CHECK(!isValidEntryName("a?b.wav")); CHECK(!isValidEntryName("a|b.wav")); CHECK(!isValidEntryName("a.wav")); CHECK(!isValidEntryName("\"q\".wav")); // Trailing dot or space (silently stripped at creation on Windows). CHECK(!isValidEntryName("trailing ")); CHECK(!isValidEntryName("trailing.")); CHECK(!isValidEntryName(" ")); CHECK(!isValidEntryName(" ")); // DOS device names, case-insensitive, with and without an extension. CHECK(!isValidEntryName("NUL")); CHECK(!isValidEntryName("CON")); CHECK(!isValidEntryName("con.wav")); CHECK(!isValidEntryName("PRN")); CHECK(!isValidEntryName("AUX")); CHECK(!isValidEntryName("COM1")); CHECK(!isValidEntryName("com1.txt")); CHECK(!isValidEntryName("LPT1")); // Not a device name: a real filename that merely starts with one. CHECK(isValidEntryName("console.wav")); } int main() { testClassifyReadable(); testClassifyTooNew(); testClassifyMalformed(); testEntryNameAccepts(); testEntryNameRejectsSeparatorsAndDots(); testEntryNameRejectsAbsolutePrefixes(); testEntryNameRejectsWindowsHostileNames(); 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; }