feat(version): V4 beta-in-isolation channel via -DREASAMPLER_CHANNEL

Compile-time channel flag forks a fully isolated reaper_reasampler_beta
(namespace, command-id prefix, action names, dock ident, -beta render) from
one auditable app_version definition. Stable identity byte-unchanged.
This commit is contained in:
2026-07-26 16:06:24 -04:00
parent 0a9d8200c7
commit 5c0e3a8ccf
13 changed files with 648 additions and 224 deletions
+90 -5
View File
@@ -17,13 +17,94 @@ static int g_fail = 0;
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
// --- appVersion: exact-string fidelity, leading zero preserved ----------------
//
// The channel is a COMPILE-TIME fact (REASAMPLER_CHANNEL_IS_BETA, threaded through the same
// configure_file'd header): the stable `build` tree compiles these tests with the flag = 0,
// the `build-beta` tree with = 1. So each config's ctest run validates ITS OWN channel's
// derivation. The channel-dependent assertions below branch on isBeta() so the ONE test
// source is correct in both configs — and each branch would fail if the derivation regressed
// (a beta build rendering "0.9.01" without the suffix, or stable rendering "-beta", trips it).
static void testVersionConstantRendersExactString() {
// The Daniel-fixed string: EXACTLY "0.9.01", two-digit zero-padded patch. This is
// the whole point of sourcing the STRING (not reconstructing from numeric components):
// a normalized "0.9.1" here would be a leading-zero-fidelity regression, and this
// assertion fails against the actual CMake-configured value, not a re-derivation.
CHECK(appVersion() == "0.9.01");
// The Daniel-fixed base string: EXACTLY "0.9.01", two-digit zero-padded patch — the
// numeric triple, IDENTICAL on both channels (it is also the stamp value). This is the
// whole point of sourcing the STRING (not reconstructing from numeric components): a
// normalized "0.9.1" here would be a leading-zero-fidelity regression. stampVersion()
// is the pure numeric triple regardless of channel.
CHECK(stampVersion() == "0.9.01");
}
// --- channel-derived rendering (V4) -------------------------------------------
static void testChannelDerivedRendering() {
// The DISPLAY render. Stable: exactly the numeric string. Beta: numeric + "-beta"
// (a plain suffix, V2). This is the show-version + panel-readout value. Each branch is
// the assertion the OTHER config's build would need to fail — i.e. a stable build that
// wrongly rendered "-beta", or a beta build that dropped it, is caught here.
if (isBeta()) {
CHECK(channel() == Channel::Beta);
CHECK(appVersion() == "0.9.01-beta");
} else {
CHECK(channel() == Channel::Stable);
CHECK(appVersion() == "0.9.01");
}
// The STAMP value is the numeric triple on BOTH channels — never suffixed — so it stays
// classifiable (see the stamp-classifiability test) and byte-identical to stable.
CHECK(stampVersion() == "0.9.01");
}
static void testChannelDerivedIdentityStrings() {
// Namespace, command-id prefix, action-name prefix, binary + dock idents all fork from
// the one channel bit. Stable values are BYTE-IDENTICAL to the pre-V4 build — any drift
// in the stable branch is a shipped-identity defect.
if (isBeta()) {
CHECK(extStateNamespace() == "reasampler_beta");
CHECK(commandIdPrefix() == "CEREBELLUM_REASAMPLER_BETA_");
CHECK(actionDisplayPrefix() == "ReaSampler beta: ");
CHECK(binaryName() == "reaper_reasampler_beta");
CHECK(dockTitle() == "ReaSampler Bank beta");
CHECK(dockIdent() == "reasampler_bank_panel_beta");
} else {
CHECK(extStateNamespace() == "reasampler");
CHECK(commandIdPrefix() == "CEREBELLUM_REASAMPLER_");
CHECK(actionDisplayPrefix() == "ReaSampler: ");
CHECK(binaryName() == "reaper_reasampler");
CHECK(dockTitle() == "ReaSampler Bank");
CHECK(dockIdent() == "reasampler_bank_panel");
}
}
static void testChannelQualifiedIdAndNameComposition() {
// The two composition helpers the shells funnel through. A representative shipped id
// (CAPTURE_TRACK) and phrase must compose to the exact channel-qualified strings — this
// is what guarantees stable rebuilds its shipped id and beta gets the isolated one.
if (isBeta()) {
CHECK(channelCommandId("CAPTURE_TRACK") == "CEREBELLUM_REASAMPLER_BETA_CAPTURE_TRACK");
CHECK(channelActionName("capture selected track(s)") ==
"ReaSampler beta: capture selected track(s)");
} else {
CHECK(channelCommandId("CAPTURE_TRACK") == "CEREBELLUM_REASAMPLER_CAPTURE_TRACK");
CHECK(channelActionName("capture selected track(s)") ==
"ReaSampler: capture selected track(s)");
}
}
static void testStampClassifiesAsStampedOnOwnChannel() {
// The V4 stamp-classifiability requirement: the value a channel WRITES (stampVersion())
// must classify as Stamped when that same channel reads it back — on BOTH channels. A
// "-beta"-suffixed stamp would classify as Unknown, so this fails if a build ever stamped
// appVersion() instead of stampVersion().
WritingVersion wv = classifyWritingVersion(stampVersion());
CHECK(wv.kind == WritingVersion::Kind::Stamped);
CHECK(wv.raw == "0.9.01");
CHECK(wv.parsed.major == 0 && wv.parsed.minor == 9 && wv.parsed.patch == 1);
// The OTHER channel's raw stamp value is handled without throwing per the design: since
// both channels stamp the identical numeric triple, the other channel's value is the same
// "0.9.01" and also classifies Stamped. (The DISPLAY string "0.9.01-beta", by contrast,
// is intentionally NOT the stamp value — confirm it classifies Unknown, proving why the
// stamp must stay the numeric triple.)
CHECK(classifyWritingVersion("0.9.01-beta").kind == WritingVersion::Kind::Unknown);
}
// --- parseVersion: well-formed, leading zeros, and rejection ------------------
@@ -122,6 +203,10 @@ static void testStampedStampIsOrderableAgainstCurrent() {
int main() {
testVersionConstantRendersExactString();
testChannelDerivedRendering();
testChannelDerivedIdentityStrings();
testChannelQualifiedIdAndNameComposition();
testStampClassifiesAsStampedOnOwnChannel();
testParseWellFormed();
testParseRejectsMalformed();
testOrderingByPatch();
+16 -11
View File
@@ -252,11 +252,15 @@ static void testTableHasBothScopes() {
std::set<std::string> ids;
int item = 0, track = 0;
for (const auto& def : table) {
// Every id is a non-empty CEREBELLUM_REASAMPLER_ string and is UNIQUE
// (duplicate ids would collide on registration).
std::string id = def.commandString;
CHECK(id.rfind("CEREBELLUM_REASAMPLER_", 0) == 0);
CHECK(ids.insert(id).second); // false if duplicate
// Every command SUFFIX (Phase V, V4 — the channel prefix is prepended by the shell)
// is a non-empty, UNIQUE string (duplicate suffixes would collide once composed).
std::string suffix = def.commandSuffix;
CHECK(!suffix.empty());
// The suffix is NOT prefixed with the channel family here — that is composed at
// register time. A leftover "CEREBELLUM_REASAMPLER_" in the table would be a
// double-prefix bug, so assert its ABSENCE.
CHECK(suffix.rfind("CEREBELLUM_REASAMPLER_", 0) != 0);
CHECK(ids.insert(suffix).second); // false if duplicate
// Every scope resolves to a supported offline source.
CHECK(renderSettingsFor(sourceModeForScope(def.scope), 1.0).supported);
@@ -269,16 +273,17 @@ static void testTableHasBothScopes() {
}
static void testScopeActionIdsAreTheShippedStrings() {
// Pin the shipped CAPTURE_ITEM / CAPTURE_TRACK ids so a future edit that silently
// changes them (breaking user keybindings) fails the gate.
// Pin the shipped CAPTURE_ITEM / CAPTURE_TRACK SUFFIXES so a future edit that silently
// changes them (breaking user keybindings once composed with the channel prefix) fails
// the gate. The full stable id is prefix + suffix ("CEREBELLUM_REASAMPLER_CAPTURE_ITEM").
const auto& table = captureActionTable();
std::string itemId, trackId;
for (const auto& def : table) {
if (def.scope == CaptureScope::Item) itemId = def.commandString;
if (def.scope == CaptureScope::Track) trackId = def.commandString;
if (def.scope == CaptureScope::Item) itemId = def.commandSuffix;
if (def.scope == CaptureScope::Track) trackId = def.commandSuffix;
}
CHECK(itemId == "CEREBELLUM_REASAMPLER_CAPTURE_ITEM");
CHECK(trackId == "CEREBELLUM_REASAMPLER_CAPTURE_TRACK");
CHECK(itemId == "CAPTURE_ITEM");
CHECK(trackId == "CAPTURE_TRACK");
}
int main() {