rename view_model -> view_mode_model; add shell-expands-FX test and parse-site comments

This commit is contained in:
2026-07-22 20:37:17 -04:00
parent 33ffb45a45
commit 25cfb62d46
4 changed files with 93 additions and 49 deletions
@@ -1,4 +1,4 @@
// Standalone tests for reasampler::view_model — no REAPER, no test framework.
// Standalone tests for reasampler::ViewModeModel — no REAPER, no test framework.
// Mirror of test_bank_model: iterate the hard logic outside the DAW.
//
// Covers (PLAN.md D1 test cases):
@@ -10,8 +10,9 @@
// 4. show-both leaf never appears in a park op-list and is visible in all modes.
// 5. Unknown/stale GUID tolerated (ignore-and-prune, no crash).
// 6. JSON round-trip lossless: modes + membership + show-both + snapshots + active.
// 7. planToggle park path: fxOffline is empty (shell-expands-FX contract).
#include "../src/view_model.h"
#include "../src/view_mode_model.h"
#include <algorithm>
#include <cstdio>
@@ -53,7 +54,7 @@ static int flagValue(const TrackPlan& p, Flag f) {
// -- 1. N-mode proven --------------------------------------------------------
static void testNModeRegistryAndMembership() {
ViewModel vm;
ViewModeModel vm;
// Seeded: Arrange + Design.
CHECK(vm.modes().size() == 2);
CHECK(vm.modes().contains(kArrangeModeId));
@@ -105,7 +106,7 @@ static void testNModeRegistryAndMembership() {
// -- 2. Parent derivation ----------------------------------------------------
static void testParentDerivationMultiMode() {
ViewModel vm;
ViewModeModel vm;
// Folder {F} holds two leaves: {L1} in Arrange (untagged default), {L2} in Design.
FolderTree tree;
tree.nodes.push_back(FolderNode{"{F}", "", /*isParent=*/true});
@@ -131,7 +132,7 @@ static void testParentDerivationMultiMode() {
nested.nodes.push_back(FolderNode{"{G}", "", true});
nested.nodes.push_back(FolderNode{"{F2}", "{G}", true});
nested.nodes.push_back(FolderNode{"{L3}", "{F2}", false});
ViewModel vm2;
ViewModeModel vm2;
vm2.membership().tag("{L3}", kDesignModeId);
auto d2 = vm2.visibleTracks(nested, kDesignModeId);
CHECK(visibleHas(d2, "{L3}"));
@@ -189,7 +190,7 @@ static void testRestoreRoundTripSnapshotValues() {
// End-to-end via planToggle: a leaf tagged Design, snapshotted, parked while in
// Arrange, then restored when we toggle back to Design.
ViewModel vm;
ViewModeModel vm;
FolderTree tree;
tree.nodes.push_back(FolderNode{"{DES}", "", false});
vm.membership().tag("{DES}", kDesignModeId);
@@ -214,7 +215,7 @@ static void testRestoreRoundTripSnapshotValues() {
// -- 4. show-both leaf --------------------------------------------------------
static void testShowBothNeverParkedVisibleEverywhere() {
ViewModel vm;
ViewModeModel vm;
CHECK(vm.modes().add(Mode{"mixdown", "Mixdown", 2}));
FolderTree tree;
@@ -243,7 +244,7 @@ static void testShowBothNeverParkedVisibleEverywhere() {
// -- 5. Unknown/stale GUID tolerated -----------------------------------------
static void testStaleGuidTolerated() {
ViewModel vm;
ViewModeModel vm;
// Tag two leaves, but the tree only knows one — the other GUID is stale (its
// track was deleted / restructured while parked).
vm.membership().tag("{LIVE}", kDesignModeId);
@@ -273,7 +274,7 @@ static void testStaleGuidTolerated() {
// -- 6. JSON round-trip lossless ---------------------------------------------
static void testJsonRoundTrip() {
ViewModel vm;
ViewModeModel vm;
// Modes: seeded pair + a third; also an out-of-order ordinal to prove sorting
// survives round-trip.
CHECK(vm.modes().add(Mode{"print", "Print \"stem\"\n", 5}));
@@ -301,7 +302,7 @@ static void testJsonRoundTrip() {
CHECK(vm.setActiveMode("mixdown"));
std::string json = vm.serialize();
auto back = ViewModel::deserialize(json);
auto back = ViewModeModel::deserialize(json);
CHECK(back.has_value());
CHECK(back && *back == vm);
// String form is stable across a second round-trip.
@@ -323,14 +324,14 @@ static void testJsonRoundTrip() {
}
static void testEmptyModelRoundTrip() {
ViewModel vm; // default: Arrange + Design seeded, active = Arrange, no members
ViewModeModel vm; // default: Arrange + Design seeded, active = Arrange, no members
std::string json = vm.serialize();
auto back = ViewModel::deserialize(json);
auto back = ViewModeModel::deserialize(json);
CHECK(back.has_value());
CHECK(back && *back == vm);
// Lenient empty root ⇒ a default-seeded model.
auto empty = ViewModel::deserialize("{}");
auto empty = ViewModeModel::deserialize("{}");
CHECK(empty.has_value());
CHECK(empty && empty->modes().size() == 2);
CHECK(empty && empty->activeModeId() == kArrangeModeId);
@@ -352,11 +353,36 @@ static void testMalformedJson() {
"{\"modes\":[]}trailing", // trailing garbage
};
for (const char* j : bad) {
auto r = ViewModel::deserialize(j);
auto r = ViewModeModel::deserialize(j);
CHECK(!r.has_value());
}
}
// -- 7. planToggle park path: fxOffline is empty (shell-expands-FX contract) --
//
// planToggle calls makeParkPlan(guid, /*fxCount=*/0) for each inactive leaf.
// The D2 shell is responsible for expanding per-FX offline ops using
// TrackFX_GetCount — the pure model has no access to REAPER FX counts at plan
// time. This test pins that contract so a regression that passes a non-zero
// count (and emits FX ops prematurely) is caught immediately.
// The direct makeParkPlan(guid, 3) path (non-zero fxCount) is covered by
// testRestoreRoundTripSnapshotValues above.
static void testPlanToggleParkHasEmptyFxOffline() {
ViewModeModel vm;
FolderTree tree;
tree.nodes.push_back(FolderNode{"{LEAF}", "", false});
vm.membership().tag("{LEAF}", kDesignModeId);
// Toggle to Arrange: {LEAF} is inactive ⇒ park plan emitted.
auto plan = vm.planToggle(tree, kArrangeModeId);
CHECK(plan.park.size() == 1);
// The park plan must have an empty fxOffline — the shell expands FX ops.
CHECK(plan.park[0].fxOffline.empty());
// Scalar flags must still be present (the four park zeros).
CHECK(plan.park[0].flags.size() == 4);
}
int main() {
testNModeRegistryAndMembership();
testParentDerivationMultiMode();
@@ -366,6 +392,7 @@ int main() {
testJsonRoundTrip();
testEmptyModelRoundTrip();
testMalformedJson();
testPlanToggleParkHasEmptyFxOffline();
if (g_fail == 0) std::printf("All tests passed.\n");
return g_fail ? 1 : 0;