feat(view): D2 view shell — apply mode toggle plan to live project
Reads I_FOLDERDEPTH into a FolderTree (pure view_tree helper, unit-tested), snapshots to-be-parked tracks before parking, runs the D1 planner, and applies park/restore flag + per-FX-offline writes under an undo block. Master and mute/solo untouched; acts only on tagged tracks.
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
// Standalone tests for reasampler::buildFolderTree — no REAPER, no framework.
|
||||
// The D2 view shell is DAW-bound and only verifiable in REAPER; this covers the
|
||||
// one genuinely pure piece: the I_FOLDERDEPTH walk that turns REAPER's linear
|
||||
// track stream into the parent<->child FolderTree the pure model consumes.
|
||||
|
||||
#include "../src/view_tree.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
using namespace reasampler;
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(cond) do { if(!(cond)) { \
|
||||
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
|
||||
|
||||
// Find a node by guid; returns nullptr if absent.
|
||||
static const FolderNode* nodeOf(const FolderTree& t, const std::string& guid) {
|
||||
for (const auto& n : t.nodes)
|
||||
if (n.guid == guid) return &n;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// All top-level leaves: three depth-0 tracks, no folders. Every parentGuid empty,
|
||||
// nothing is a parent, order preserved.
|
||||
static void testFlatLeaves() {
|
||||
FolderTree t = buildFolderTree({{"a", 0}, {"b", 0}, {"c", 0}});
|
||||
CHECK(t.nodes.size() == 3);
|
||||
CHECK(t.nodes[0].guid == "a" && t.nodes[1].guid == "b" && t.nodes[2].guid == "c");
|
||||
for (const auto& n : t.nodes) {
|
||||
CHECK(n.parentGuid.empty());
|
||||
CHECK(!n.isParent);
|
||||
}
|
||||
}
|
||||
|
||||
// One folder: parent P (depth 1) contains child C, which closes the folder (-1).
|
||||
// P is a parent and is itself top-level; C's parent is P.
|
||||
static void testSingleFolder() {
|
||||
FolderTree t = buildFolderTree({{"P", 1}, {"C", -1}});
|
||||
const FolderNode* p = nodeOf(t, "P");
|
||||
const FolderNode* c = nodeOf(t, "C");
|
||||
CHECK(p && c);
|
||||
CHECK(p->isParent);
|
||||
CHECK(p->parentGuid.empty()); // the folder parent is itself top-level
|
||||
CHECK(!c->isParent);
|
||||
CHECK(c->parentGuid == "P"); // child sits under the folder
|
||||
}
|
||||
|
||||
// Folder with two children; a following top-level leaf must land back at the root,
|
||||
// proving the close (-1) popped the folder off the stack.
|
||||
static void testFolderThenSibling() {
|
||||
FolderTree t = buildFolderTree({{"P", 1}, {"C1", 0}, {"C2", -1}, {"S", 0}});
|
||||
CHECK(nodeOf(t, "C1")->parentGuid == "P");
|
||||
CHECK(nodeOf(t, "C2")->parentGuid == "P");
|
||||
CHECK(nodeOf(t, "S")->parentGuid.empty()); // popped back to root
|
||||
}
|
||||
|
||||
// Nested folders: P > Q > leaf. The inner leaf closes BOTH folders at once (-2).
|
||||
// A sibling after it returns to the root.
|
||||
static void testNestedDeepClose() {
|
||||
FolderTree t = buildFolderTree({{"P", 1}, {"Q", 1}, {"L", -2}, {"S", 0}});
|
||||
CHECK(nodeOf(t, "P")->isParent && nodeOf(t, "P")->parentGuid.empty());
|
||||
CHECK(nodeOf(t, "Q")->isParent && nodeOf(t, "Q")->parentGuid == "P");
|
||||
CHECK(nodeOf(t, "L")->parentGuid == "Q"); // deepest leaf under Q
|
||||
CHECK(!nodeOf(t, "L")->isParent);
|
||||
CHECK(nodeOf(t, "S")->parentGuid.empty()); // both levels popped
|
||||
}
|
||||
|
||||
// Nested with a stepwise close: inner folder closes (-1), then a sibling at the
|
||||
// OUTER level, then that folder closes (-1). Proves partial pops track correctly.
|
||||
static void testNestedStepwiseClose() {
|
||||
FolderTree t = buildFolderTree(
|
||||
{{"P", 1}, {"Q", 1}, {"L", -1}, {"M", -1}});
|
||||
CHECK(nodeOf(t, "Q")->parentGuid == "P");
|
||||
CHECK(nodeOf(t, "L")->parentGuid == "Q"); // L inside inner folder Q
|
||||
CHECK(nodeOf(t, "M")->parentGuid == "P"); // after Q closed, M is under P
|
||||
}
|
||||
|
||||
// Malformed stream: a close deeper than the open stack must clamp to empty, never
|
||||
// underflow. A following leaf is still top-level and the walk stays total.
|
||||
static void testMalformedCloseClamps() {
|
||||
FolderTree t = buildFolderTree({{"P", 1}, {"C", -5}, {"S", 0}});
|
||||
CHECK(nodeOf(t, "C")->parentGuid == "P");
|
||||
CHECK(nodeOf(t, "S")->parentGuid.empty()); // clamped, not underflowed
|
||||
CHECK(t.nodes.size() == 3);
|
||||
}
|
||||
|
||||
// Empty input yields an empty tree (no fault).
|
||||
static void testEmpty() {
|
||||
FolderTree t = buildFolderTree({});
|
||||
CHECK(t.nodes.empty());
|
||||
}
|
||||
|
||||
// An empty folder (parent immediately closes itself, depth accounting P=1 then a
|
||||
// child that both belongs to and closes it). Here P opens and the sole child
|
||||
// closes; verifies a folder with exactly one leaf child.
|
||||
static void testSingleChildFolder() {
|
||||
FolderTree t = buildFolderTree({{"P", 1}, {"only", -1}});
|
||||
CHECK(nodeOf(t, "only")->parentGuid == "P");
|
||||
CHECK(nodeOf(t, "P")->isParent);
|
||||
}
|
||||
|
||||
int main() {
|
||||
testFlatLeaves();
|
||||
testSingleFolder();
|
||||
testFolderThenSibling();
|
||||
testNestedDeepClose();
|
||||
testNestedStepwiseClose();
|
||||
testMalformedCloseClamps();
|
||||
testEmpty();
|
||||
testSingleChildFolder();
|
||||
|
||||
if (g_fail == 0) {
|
||||
std::printf("view_tree: all tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
std::printf("view_tree: %d checks FAILED\n", g_fail);
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user