// Standalone tests for reasampler::view's derived FX state — no REAPER, no test // framework. Two halves: what desiredFxState decides is owed, and how one work // item resolves against the chain as it stands (the chain mutates while a track // is parked, which is the whole reason a slot cannot be the key). #include "../src/core/view/fx_state.h" #include #include using namespace reasampler; using namespace reasampler::view; static int g_fail = 0; #define CHECK(cond) do { if(!(cond)) { \ std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0) // -- helpers ----------------------------------------------------------------- namespace { TrackSnapshot recordWithFx(std::vector fx) { TrackSnapshot snap; snap.showInTcp = 1; snap.showInMixer = 1; snap.mainSend = 1; snap.fxEnable = 1; snap.fxOffline = std::move(fx); return snap; } // A Design leaf parked with three identified FX, the middle one offline before // the park because the USER put it there. ViewModeModel parkedWithThreeFx() { ViewModeModel vm; vm.membership().tag("{T}", kDesignModeId); vm.storeSnapshot("{T}", recordWithFx({{"{FX-A}", 0}, {"{FX-B}", 1}, {"{FX-C}", 0}})); return vm; } const FxTrackWork* workFor(const std::vector& work, const std::string& guid) { for (const FxTrackWork& w : work) if (w.trackGuid == guid) return &w; return nullptr; } bool writeAt(const FxRestoreResolution& res, int fxIndex, bool offline) { for (const FxOfflineWrite& w : res.writes) if (w.fxIndex == fxIndex) return w.offline == offline; return false; } bool anyWriteAt(const FxRestoreResolution& res, int fxIndex) { for (const FxOfflineWrite& w : res.writes) if (w.fxIndex == fxIndex) return true; return false; } } // namespace // -- desiredFxState: what is owed -------------------------------------------- static void testActiveTrackIsOwedOnlineInactiveIsOwedOffline() { ViewModeModel vm = parkedWithThreeFx(); vm.membership().tag("{P}", kArrangeModeId); vm.storeSnapshot("{P}", recordWithFx({{"{FX-P}", 0}})); // Active mode is Arrange: {T} (Design) stays parked, {P} is coming back. const std::vector work = desiredFxState(vm, kArrangeModeId, {"{T}", "{P}"}); CHECK(work.size() == 2); CHECK(workFor(work, "{P}") && workFor(work, "{P}")->kind == FxWorkKind::Online); CHECK(workFor(work, "{T}") && workFor(work, "{T}")->kind == FxWorkKind::Offline); // Offline work names no targets: only the shell can read the live chain, and // that chain IS the target set. CHECK(workFor(work, "{T}") && workFor(work, "{T}")->targets.empty()); CHECK(workFor(work, "{P}") && workFor(work, "{P}")->targets.size() == 1); } static void testOnlineWorkSortsBeforeOfflineWork() { // GUIDs chosen so plain GUID order would put every Offline track FIRST — the // ordering under test has to beat the map's own order to pass. ViewModeModel vm; vm.membership().tag("{A-park}", kDesignModeId); vm.membership().tag("{B-park}", kDesignModeId); vm.membership().tag("{Y-back}", kArrangeModeId); vm.membership().tag("{Z-back}", kArrangeModeId); for (const char* g : {"{A-park}", "{B-park}", "{Y-back}", "{Z-back}"}) vm.storeSnapshot(g, recordWithFx({{"{FX}", 0}})); const std::vector work = desiredFxState(vm, kArrangeModeId, {"{A-park}", "{B-park}", "{Y-back}", "{Z-back}"}); CHECK(work.size() == 4); // Onlining restores state the user owns and is 2-6x the cost of offlining; // offlining is opportunistic reclaim and may be deferred indefinitely. CHECK(work[0].trackGuid == "{Y-back}" && work[0].kind == FxWorkKind::Online); CHECK(work[1].trackGuid == "{Z-back}" && work[1].kind == FxWorkKind::Online); CHECK(work[2].trackGuid == "{A-park}" && work[2].kind == FxWorkKind::Offline); CHECK(work[3].trackGuid == "{B-park}" && work[3].kind == FxWorkKind::Offline); // Deterministic: a re-derivation yields the identical list, so a drain's // position in it survives one. CHECK(desiredFxState(vm, kArrangeModeId, {"{A-park}", "{B-park}", "{Y-back}", "{Z-back}"}) == work); } static void testUserOfflinedPluginIsNeverBlanketOnlined() { // The property this whole split exists for. {FX-B} was offline before the // park because the user offlined it; coming back must leave it offline. ViewModeModel vm = parkedWithThreeFx(); CHECK(vm.setActiveMode(kDesignModeId)); const std::vector work = desiredFxState(vm, kDesignModeId, {"{T}"}); CHECK(work.size() == 1); CHECK(work[0].kind == FxWorkKind::Online); const FxRestoreResolution res = resolveFxWork(work[0], {"{FX-A}", "{FX-B}", "{FX-C}"}); CHECK(res.writes.size() == 3); CHECK(res.drops.total() == 0); CHECK(writeAt(res, 0, false)); CHECK(writeAt(res, 1, true)); // stays offline — NOT blanket-onlined CHECK(writeAt(res, 2, false)); } static void testReleasedFxHalfIsOwedNothing() { // The chain is already back online; the record survives only for its flags. ViewModeModel vm = parkedWithThreeFx(); CHECK(vm.setActiveMode(kDesignModeId)); vm.releaseSnapshotFx("{T}"); CHECK(desiredFxState(vm, kDesignModeId, {"{T}"}).empty()); CHECK(fxRestoreTargets(vm, "{T}").empty()); // ...and the mirror: a record whose FLAG half is spent still owes its FX. ViewModeModel vm2 = parkedWithThreeFx(); CHECK(vm2.setActiveMode(kDesignModeId)); vm2.releaseSnapshotFlags("{T}"); const std::vector work = desiredFxState(vm2, kDesignModeId, {"{T}"}); CHECK(work.size() == 1); CHECK(work.size() == 1 && work[0].targets.size() == 3); } static void testDeadAndUnparkedTracksAreOwedNothing() { ViewModeModel vm = parkedWithThreeFx(); // The track is gone from the project: there is nothing to write to. CHECK(desiredFxState(vm, kArrangeModeId, {}).empty()); // A track with no park record is owed nothing either way — that is also how a // REFUSED park stays refused: it wrote no record, so no reclaim targets it. ViewModeModel bare; bare.membership().tag("{U}", kDesignModeId); CHECK(desiredFxState(bare, kArrangeModeId, {"{U}"}).empty()); } // -- resolveFxWork: against the chain as it stands ---------------------------- static void testOfflineWorkTargetsEveryLiveSlot() { ViewModeModel vm = parkedWithThreeFx(); // active mode Arrange ⇒ {T} parks const std::vector work = desiredFxState(vm, kArrangeModeId, {"{T}"}); CHECK(work.size() == 1); if (work.empty()) return; // Two plugins added while parked, one of which REAPER reports no GUID for. // Reclaim has to reach those too, which is why offline work keys by slot. const FxRestoreResolution res = resolveFxWork(work[0], {"{FX-A}", "", "{FX-B}", "{FX-C}", "{FX-NEW}"}); CHECK(res.writes.size() == 5); CHECK(res.drops.total() == 0); for (int i = 0; i < 5; ++i) CHECK(writeAt(res, i, true)); } static void testReorderRestoreLandsEachPluginItsOwnState() { ViewModeModel vm = parkedWithThreeFx(); CHECK(vm.setActiveMode(kDesignModeId)); const std::vector work = desiredFxState(vm, kDesignModeId, {"{T}"}); CHECK(work.size() == 1); if (work.empty()) return; // Reordered to C, A, B while parked. const FxRestoreResolution res = resolveFxWork(work[0], {"{FX-C}", "{FX-A}", "{FX-B}"}); CHECK(res.writes.size() == 3); CHECK(res.drops.total() == 0); CHECK(writeAt(res, 1, false)); // A CHECK(writeAt(res, 2, true)); // B's captured offline followed B, not slot 1 CHECK(writeAt(res, 0, false)); // C } static void testDeletedFxIsDroppedExplicitly() { ViewModeModel vm = parkedWithThreeFx(); CHECK(vm.setActiveMode(kDesignModeId)); const std::vector work = desiredFxState(vm, kDesignModeId, {"{T}"}); CHECK(work.size() == 1); if (work.empty()) return; // B deleted while parked. const FxRestoreResolution res = resolveFxWork(work[0], {"{FX-A}", "{FX-C}"}); CHECK(res.writes.size() == 2); CHECK(res.drops.missingIdentity == 1); CHECK(writeAt(res, 0, false)); // A CHECK(writeAt(res, 1, false)); // C — and NOT B's captured `true` CHECK(!describeFxRestoreDrops(res.drops, 1).empty()); } static void testAddedFxIsLeftAlone() { ViewModeModel vm = parkedWithThreeFx(); CHECK(vm.setActiveMode(kDesignModeId)); const std::vector work = desiredFxState(vm, kDesignModeId, {"{T}"}); CHECK(work.size() == 1); if (work.empty()) return; const FxRestoreResolution res = resolveFxWork(work[0], {"{FX-NEW}", "{FX-A}", "{FX-B}", "{FX-C}"}); CHECK(res.writes.size() == 3); CHECK(res.drops.total() == 0); CHECK(!anyWriteAt(res, 0)); // the added FX is never written CHECK(writeAt(res, 1, false)); CHECK(writeAt(res, 2, true)); CHECK(writeAt(res, 3, false)); } static void testSlotKeyedRecordStillRestoresByPosition() { // A record lifted from a project saved before FX identity existed: position // is all its bytes can support, and the targets carry that keying through. ViewModeModel vm; vm.membership().tag("{T}", kDesignModeId); CHECK(vm.setActiveMode(kDesignModeId)); TrackSnapshot snap = recordWithFx({{"", 0}, {"", 1}, {"", 0}}); snap.fxKeying = FxKeying::Slot; vm.storeSnapshot("{T}", snap); const std::vector targets = fxRestoreTargets(vm, "{T}"); CHECK(targets.size() == 3); CHECK(!targets.empty() && targets[0].keying == FxKeying::Slot); const std::vector work = desiredFxState(vm, kDesignModeId, {"{T}"}); CHECK(work.size() == 1); if (work.empty()) return; const FxRestoreResolution res = resolveFxWork(work[0], {"{FX-X}", "{FX-Y}", "{FX-Z}"}); CHECK(res.writes.size() == 3); CHECK(res.drops.total() == 0); CHECK(writeAt(res, 0, false)); CHECK(writeAt(res, 1, true)); CHECK(writeAt(res, 2, false)); } int main() { testActiveTrackIsOwedOnlineInactiveIsOwedOffline(); testOnlineWorkSortsBeforeOfflineWork(); testUserOfflinedPluginIsNeverBlanketOnlined(); testReleasedFxHalfIsOwedNothing(); testDeadAndUnparkedTracksAreOwedNothing(); testOfflineWorkTargetsEveryLiveSlot(); testReorderRestoreLandsEachPluginItsOwnState(); testDeletedFxIsDroppedExplicitly(); testAddedFxIsLeftAlone(); testSlotKeyedRecordStillRestoresByPosition(); if (g_fail == 0) std::printf("All tests passed.\n"); return g_fail ? 1 : 0; }