// Standalone tests for reasampler::track_topology — no REAPER, no framework. // Covers the direct-child walk over a flat I_FOLDERDEPTH delta list: the set a // ranged item render must silence so a folder parent's children stay out of the // capture. #include "../src/core/capture/track_topology.h" #include #include #include using namespace reasampler::capture; static int g_fail = 0; #define CHECK(cond) do { if(!(cond)) { \ std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0) static bool sameIndices(const std::vector& got, const std::vector& want) { return got == want; } static void testFlatProjectHasNoChildren() { // No track opens a folder, so no track has children — including the one asked // about. A flat project must produce an empty silence set, not a stray one. const std::vector depths{0, 0, 0}; CHECK(sameIndices(directChildIndices(depths, 0), {})); CHECK(sameIndices(directChildIndices(depths, 1), {})); } static void testFolderParentReturnsItsDirectChildren() { // 0: folder parent, 1: child, 2: last child (closes the folder), 3: unrelated. const std::vector depths{1, 0, -1, 0}; CHECK(sameIndices(directChildIndices(depths, 0), {1, 2})); // Track 3 sits outside the folder entirely and owns nothing. CHECK(sameIndices(directChildIndices(depths, 3), {})); } static void testGrandchildrenAreExcluded() { // 0: outer folder, 1: inner folder (a direct child), 2: grandchild closing the // inner folder, 3: second direct child closing the outer. The grandchild reaches // track 0 only through track 1, so silencing track 1's send covers it — listing // it too would be redundant, and the walk must not. const std::vector depths{1, 1, -1, -1}; CHECK(sameIndices(directChildIndices(depths, 0), {1, 3})); // Asked about the inner folder, the grandchild IS the direct child. CHECK(sameIndices(directChildIndices(depths, 1), {2})); } static void testMultiLevelCloseEndsTheOuterFolderToo() { // A -2 closes two folders at once (SDK: "last in the innermost and next-innermost // folders"), so track 3 is outside track 0's folder even though no track between // them closed it singly. const std::vector depths{1, 1, -2, 0}; CHECK(sameIndices(directChildIndices(depths, 0), {1})); CHECK(sameIndices(directChildIndices(depths, 1), {2})); } static void testNonFolderAndOutOfRangeReturnEmpty() { const std::vector depths{1, 0, -1}; CHECK(sameIndices(directChildIndices(depths, 1), {})); // a plain child CHECK(sameIndices(directChildIndices(depths, 2), {})); // the folder's last track CHECK(sameIndices(directChildIndices(depths, -1), {})); // no such track CHECK(sameIndices(directChildIndices(depths, 3), {})); // past the end CHECK(sameIndices(directChildIndices({}, 0), {})); // empty project } static void testUnterminatedFolderSwallowsTheRest() { // A folder that never closes owns every remaining track, which is how REAPER // reads the same list — the walk must not stop early and leave a child audible. const std::vector depths{1, 0, 0}; CHECK(sameIndices(directChildIndices(depths, 0), {1, 2})); } static void testSiblingFolderAfterParentClosesIsNotIncluded() { // 0: folder parent, 1: child, 2: last child (closes it). 3: an unrelated sibling // folder that opens AFTER track 0's folder has already closed, 4: its child, 5: // its last child. A walk that fails to stop at track 0's own close would keep // consuming and wrongly pull the sibling's children in too. const std::vector depths{1, 0, -1, 1, 0, -1}; CHECK(sameIndices(directChildIndices(depths, 0), {1, 2})); } // --- siblingPlacement ------------------------------------------------------- // // Every case asserts the property that actually matters, not just the numbers: the // new track sits at the SOURCE's own nesting level, and the delta total is // unchanged so no track after the insertion moves. `levelsAfter` rebuilds the // post-insertion list and reads the levels straight off it. static std::vector depthsAfter(const std::vector& depths, const SiblingPlacement& p) { std::vector out = depths; if (p.precedingIndex >= 0) out[static_cast(p.precedingIndex)] = p.precedingDepth; out.insert(out.begin() + p.insertIndex, p.newDepth); return out; } static int sumOf(const std::vector& v) { int s = 0; for (int d : v) s += d; return s; } // Absolute nesting level of track `idx` in a delta list. static int levelAt(const std::vector& depths, int idx) { int level = 0; for (int i = 0; i < idx; ++i) level += depths[static_cast(i)]; return level; } // The whole contract in one call: the new track is a sibling (same level as the // source) and nothing downstream shifted (delta total preserved). static void checkIsSibling(const std::vector& before, int srcIdx) { const SiblingPlacement p = siblingPlacement(before, srcIdx); const std::vector after = depthsAfter(before, p); CHECK(sumOf(after) == sumOf(before)); CHECK(levelAt(after, p.insertIndex) == levelAt(before, srcIdx)); } static void testSiblingOfANormalTrackGoesDirectlyBelowIt() { // Three normal tracks at top level; the source is the middle one. const std::vector depths{0, 0, 0}; const SiblingPlacement p = siblingPlacement(depths, 1); CHECK(p.insertIndex == 2); CHECK(p.precedingIndex == 1); CHECK(p.precedingDepth == 0); // unchanged CHECK(p.newDepth == 0); checkIsSibling(depths, 1); } static void testSiblingOfAMidFolderTrackStaysInsideTheFolder() { // 0: parent, 1: child (the source), 2: last child closing the folder. const std::vector depths{1, 0, -1}; const SiblingPlacement p = siblingPlacement(depths, 1); CHECK(p.insertIndex == 2); CHECK(p.precedingDepth == 0); CHECK(p.newDepth == 0); // still inside; track 2 still closes the folder checkIsSibling(depths, 1); } static void testSiblingOfTheLastTrackInAFolderInheritsTheClosingDelta() { // The source carries the folder's close, so a naive insert-after would drop the // new track OUTSIDE the folder and bypass the folder bus entirely. const std::vector depths{1, -1, 0}; const SiblingPlacement p = siblingPlacement(depths, 1); CHECK(p.insertIndex == 2); CHECK(p.precedingDepth == 0); // the source no longer closes the folder CHECK(p.newDepth == -1); // the new track does checkIsSibling(depths, 1); } static void testSiblingOfTheLastTrackInTwoFoldersMovesTheWholeClose() { // 0: outer parent, 1: inner parent, 2: last in BOTH folders (the source). const std::vector depths{1, 1, -2}; const SiblingPlacement p = siblingPlacement(depths, 2); CHECK(p.insertIndex == 3); CHECK(p.precedingDepth == 0); CHECK(p.newDepth == -2); // the -2 travels intact checkIsSibling(depths, 2); } static void testSiblingOfAFolderParentLandsAfterTheWholeFolder() { // Inserting straight after a folder parent would make the new track its FIRST // CHILD, re-summing the render through the parent's FX and fader. const std::vector depths{1, 0, -1, 0}; const SiblingPlacement p = siblingPlacement(depths, 0); CHECK(p.insertIndex == 3); // past the whole folder, not at index 1 CHECK(p.precedingIndex == 2); CHECK(p.precedingDepth == -1); // unchanged — track 2 still closes the folder CHECK(p.newDepth == 0); checkIsSibling(depths, 0); } static void testSiblingOfTheLastTrackInTheProjectAppends() { const std::vector depths{0, 0}; const SiblingPlacement p = siblingPlacement(depths, 1); CHECK(p.insertIndex == 2); // == count: appended CHECK(p.precedingDepth == 0); CHECK(p.newDepth == 0); checkIsSibling(depths, 1); } static void testSiblingOfTheLastTrackInTheProjectInsideAFolder() { // The project's last track also closes a folder — the close must still travel. const std::vector depths{1, -1}; const SiblingPlacement p = siblingPlacement(depths, 1); CHECK(p.insertIndex == 2); CHECK(p.precedingDepth == 0); CHECK(p.newDepth == -1); checkIsSibling(depths, 1); } static void testMalformedDeltaListClampsRatherThanAsserting() { // Deltas summing to -3: more closes than opens, which no well-formed project // produces. The result must still be a legal in-range placement. const std::vector depths{0, -2, -1}; const SiblingPlacement p = siblingPlacement(depths, 1); CHECK(p.insertIndex >= 0 && p.insertIndex <= static_cast(depths.size())); CHECK(p.precedingIndex == p.insertIndex - 1); // Clamped at zero rather than tracking a negative nesting level. CHECK(levelAt(depthsAfter(depths, p), p.insertIndex) >= 0); // An unterminated folder (deltas summing to +1) is the other direction. const std::vector open{1, 0}; const SiblingPlacement q = siblingPlacement(open, 1); CHECK(q.insertIndex == 2); CHECK(q.newDepth <= 0); // never invents a second folder open } static void testOutOfRangeSourceIndexClamps() { const std::vector depths{0, 0}; // Past the end clamps to the last track; negative clamps to the first. CHECK(siblingPlacement(depths, 99).insertIndex == 2); CHECK(siblingPlacement(depths, -5).insertIndex == 1); // An empty project has nothing to precede the new track. CHECK(siblingPlacement({}, 0).insertIndex == 0); CHECK(siblingPlacement({}, 0).precedingIndex == -1); } int main() { testFlatProjectHasNoChildren(); testFolderParentReturnsItsDirectChildren(); testGrandchildrenAreExcluded(); testMultiLevelCloseEndsTheOuterFolderToo(); testNonFolderAndOutOfRangeReturnEmpty(); testUnterminatedFolderSwallowsTheRest(); testSiblingFolderAfterParentClosesIsNotIncluded(); testSiblingOfANormalTrackGoesDirectlyBelowIt(); testSiblingOfAMidFolderTrackStaysInsideTheFolder(); testSiblingOfTheLastTrackInAFolderInheritsTheClosingDelta(); testSiblingOfTheLastTrackInTwoFoldersMovesTheWholeClose(); testSiblingOfAFolderParentLandsAfterTheWholeFolder(); testSiblingOfTheLastTrackInTheProjectAppends(); testSiblingOfTheLastTrackInTheProjectInsideAFolder(); testMalformedDeltaListClampsRatherThanAsserting(); testOutOfRangeSourceIndexClamps(); if (g_fail == 0) std::printf("track_topology: all tests passed\n"); return g_fail == 0 ? 0 : 1; }