S4 Tier 0: the bank plays — VST3 marshals MIDI to the S3 core, reads the live bank + resolves WAV the M4 way, mono downmix, lock-free load handoff, LICE sample-pick

This commit is contained in:
2026-07-26 16:43:04 -04:00
parent b0fc052113
commit 0cde457224
24 changed files with 1239 additions and 302 deletions
+5 -65
View File
@@ -1,11 +1,12 @@
// Standalone tests for reasampler::vst::bridge_marshal — no VST3, no REAPER, no test
// framework. Same fast assert loop as the sibling pure tests: assert the REAPER
// bridge-read marshalling (GetProjExtState result decode + a small JSON string-field
// reader) directly, so the DAW-facing shell only has to invoke the API.
// bridge-read marshalling (GetProjExtState result decode) directly, so the DAW-facing
// shell only has to invoke the API.
//
// Covers: decodeGetProjExtState hit/absent/zero-return/empty-buffer (the stale-buffer
// guard); extractJsonStringField present/absent/escapes/whitespace/value-vs-key
// disambiguation/non-string-value/malformed.
// guard). The S1 spike's extractJsonStringField string-scan reader was retired in S4
// (the instrument now parses the bank through the shared bank_book JSON path), so its
// cases are gone with it.
#include "../src/vst/bridge_marshal.h"
@@ -44,72 +45,11 @@ static void testDecodeEmptyBuffer() {
CHECK(!v.has_value());
}
// --- extractJsonStringField ---------------------------------------------------
static void testExtractPresent() {
const std::string json = R"({"guid":"ABC-123","name":"kick"})";
auto g = extractJsonStringField(json, "guid");
CHECK(g && *g == "ABC-123");
auto n = extractJsonStringField(json, "name");
CHECK(n && *n == "kick");
}
static void testExtractAbsent() {
const std::string json = R"({"guid":"ABC-123"})";
CHECK(!extractJsonStringField(json, "missing").has_value());
}
static void testExtractWhitespaceTolerant() {
const std::string json = "{ \"guid\" : \"X\" , \"n\":\"y\" }";
auto g = extractJsonStringField(json, "guid");
CHECK(g && *g == "X");
}
static void testExtractEscapes() {
// \" \\ \/ \n \t all decode.
const std::string json = R"({"path":"a\\b\/c\"d\ne"})";
auto p = extractJsonStringField(json, "path");
CHECK(p && *p == "a\\b/c\"d\ne");
}
static void testExtractValueContainingKeyText() {
// A VALUE that contains the key text must not be mistaken for the member. Here the
// first "guid" occurrence is inside another value; the real member comes later.
const std::string json = R"({"note":"the guid is here","guid":"REAL"})";
auto g = extractJsonStringField(json, "guid");
CHECK(g && *g == "REAL");
}
static void testExtractNonStringValue() {
// A numeric/object value is not a string — return nullopt rather than garbage.
const std::string json = R"({"count":42,"name":"ok"})";
CHECK(!extractJsonStringField(json, "count").has_value());
// The sibling string field still reads.
auto n = extractJsonStringField(json, "name");
CHECK(n && *n == "ok");
}
static void testExtractMalformed() {
CHECK(!extractJsonStringField(R"({"guid":"unterminated)", "guid").has_value());
CHECK(!extractJsonStringField(R"({"guid":)", "guid").has_value());
CHECK(!extractJsonStringField(R"({"guid")", "guid").has_value());
CHECK(!extractJsonStringField("", "guid").has_value());
// Dangling escape at end of string.
CHECK(!extractJsonStringField(R"({"guid":"abc\)", "guid").has_value());
}
int main() {
testDecodeHit();
testDecodeAbsentKey();
testDecodeNegativeReturn();
testDecodeEmptyBuffer();
testExtractPresent();
testExtractAbsent();
testExtractWhitespaceTolerant();
testExtractEscapes();
testExtractValueContainingKeyText();
testExtractNonStringValue();
testExtractMalformed();
if (g_fail == 0) std::printf("bridge_marshal: all tests passed\n");
return g_fail != 0;