Strip FX writes from the Design View mode switch; split view.cpp into park/lanes/reconcile/tracks and add the record-scoped undo repair pass
This commit is contained in:
@@ -1,197 +0,0 @@
|
||||
// Standalone tests for the Design View park surface's pure decisions — no
|
||||
// REAPER, no test framework.
|
||||
//
|
||||
// The properties under test: a pre-park snapshot is never taken from a chain a
|
||||
// park has already touched, the refusal that follows names its tracks and is
|
||||
// reported once per changed set, and one mode apply leaves at most ONE undo
|
||||
// point — none at all when a reapply found everything already where it wanted it.
|
||||
|
||||
#include "../src/shell/view/view_fx_park.h"
|
||||
#include "../src/shell/view/view.h" // applyMintsUndoPoint — the apply's undo-point fold
|
||||
|
||||
#include "core/view/view_mode_model.h" // makeParkPlan — the park flags' only producer
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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)
|
||||
|
||||
// -- may this chain be snapshotted? -------------------------------------------
|
||||
//
|
||||
// Rationale: the snapshot-source invariant (view_fx_park.h, this directory's
|
||||
// CLAUDE.md).
|
||||
|
||||
// The live values the fold reads, keyed by Flag — so an assertion names the flag
|
||||
// it varies rather than a position in makeParkPlan's op order.
|
||||
struct FlagValues {
|
||||
int showInTcp = 0, showInMixer = 0, mainSend = 0, fxEnable = 0;
|
||||
|
||||
int operator()(Flag f) const {
|
||||
switch (f) {
|
||||
case Flag::ShowInTcp: return showInTcp;
|
||||
case Flag::ShowInMixer: return showInMixer;
|
||||
case Flag::MainSend: return mainSend;
|
||||
case Flag::FxEnable: return fxEnable;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
static void testAChainSittingAtEveryValueTheParkWouldWriteReadsAsParked() {
|
||||
// The four zeros are what a parked track's driven flags actually read; if
|
||||
// makeParkPlan ever writes something else, this is the test that says so.
|
||||
const TrackPlan park = makeParkPlan("{A}");
|
||||
|
||||
CHECK(park.flags.size() == 4);
|
||||
CHECK(parkFlagsAlreadyApplied(park.flags, FlagValues{0, 0, 0, 0}));
|
||||
}
|
||||
|
||||
static void testOneFlagStillAtTheUsersValueMeansNoParkReachedTheChain() {
|
||||
const TrackPlan park = makeParkPlan("{A}");
|
||||
|
||||
// Each case varies exactly one flag, so together they also prove all four are
|
||||
// in the plan: a missing op makes its case read parked and fail here.
|
||||
CHECK(!parkFlagsAlreadyApplied(park.flags, FlagValues{1, 0, 0, 0}));
|
||||
CHECK(!parkFlagsAlreadyApplied(park.flags, FlagValues{0, 1, 0, 0}));
|
||||
CHECK(!parkFlagsAlreadyApplied(park.flags, FlagValues{0, 0, 1, 0}));
|
||||
CHECK(!parkFlagsAlreadyApplied(park.flags, FlagValues{0, 0, 0, 1}));
|
||||
}
|
||||
|
||||
static void testAnEmptyPlanProvesNothing() {
|
||||
CHECK(!parkFlagsAlreadyApplied(std::vector<TrackFlagOp>{}, FlagValues{0, 0, 0, 0}));
|
||||
}
|
||||
|
||||
static void testEitherHalfOfTheChainReadingParkedIsEnoughToRefuse() {
|
||||
// Disjunction pinned; rationale at chainReadsParked (view_fx_park.h).
|
||||
CHECK(!chainReadsParked(/*parkFlagsRead=*/false, /*anyFxOffline=*/false));
|
||||
CHECK(chainReadsParked(/*parkFlagsRead=*/true, /*anyFxOffline=*/false));
|
||||
CHECK(chainReadsParked(/*parkFlagsRead=*/false, /*anyFxOffline=*/true));
|
||||
CHECK(chainReadsParked(/*parkFlagsRead=*/true, /*anyFxOffline=*/true));
|
||||
}
|
||||
|
||||
static void testACleanChainIsSnapshottedThenParked() {
|
||||
CHECK(decidePark(/*haveSnapshot=*/false, /*chainReadsParked=*/false) ==
|
||||
ParkAction::SnapshotThenPark);
|
||||
}
|
||||
|
||||
static void testAHeldSnapshotIsNeverOverwrittenWhateverTheChainReads() {
|
||||
// The held snapshot IS the pre-park truth, so the chain is not consulted —
|
||||
// which is what lets the shell skip the live flag reads on this path.
|
||||
CHECK(decidePark(/*haveSnapshot=*/true, /*chainReadsParked=*/false) == ParkAction::ParkOnly);
|
||||
CHECK(decidePark(/*haveSnapshot=*/true, /*chainReadsParked=*/true) == ParkAction::ParkOnly);
|
||||
}
|
||||
|
||||
static void testAParkedChainWithNoSnapshotIsRefusedRatherThanResnapshotted() {
|
||||
// The defect this whole section exists for: the truth is gone, so the only
|
||||
// non-destructive act is to leave the track alone. Snapshotting here commits
|
||||
// park state as the user's state and no later restore can undo it.
|
||||
CHECK(decidePark(/*haveSnapshot=*/false, /*chainReadsParked=*/true) == ParkAction::Refuse);
|
||||
}
|
||||
|
||||
static void testRefusalNamesEveryRefusedTrackAndSaysNothingWhenNoneWere() {
|
||||
CHECK(describeRefusedParks({}).empty());
|
||||
|
||||
const std::string one = describeRefusedParks({"Bass"});
|
||||
CHECK(one.find("1 track ") != std::string::npos);
|
||||
CHECK(one.find("Bass") != std::string::npos);
|
||||
|
||||
const std::string two = describeRefusedParks({"Bass", "Drum bus"});
|
||||
CHECK(two.find("2 tracks ") != std::string::npos);
|
||||
CHECK(two.find("Bass") != std::string::npos);
|
||||
CHECK(two.find("Drum bus") != std::string::npos);
|
||||
}
|
||||
|
||||
static void testRefusalRecoveryNamesThePerFxHalfAndAssertsNoCause() {
|
||||
const std::string msg = describeRefusedParks({"Bass"});
|
||||
|
||||
// I_FXEN is the chain bypass: a user who restores only the four flags leaves
|
||||
// every individually offlined FX offline and walks straight back into a
|
||||
// refusal, so the recovery has to spell the per-FX step out.
|
||||
CHECK(msg.find("every FX in its chain online") != std::string::npos);
|
||||
// And it must not name a cause: the pair has several routes, and on a track
|
||||
// the user themselves keeps hidden/bypassed there was no lost state at all.
|
||||
CHECK(msg.find("undo") == std::string::npos);
|
||||
CHECK(msg.find("lost") == std::string::npos);
|
||||
}
|
||||
|
||||
// -- the refusal memo's print-suppression gate --------------------------------
|
||||
//
|
||||
// reportRefusedParks' own memo (owner + last-reported names, both REAPER-side
|
||||
// statics) can't be driven from here, but the pure gate behind it can: prints
|
||||
// (i.e. updates the memo) on any change, stays silent on an exact repeat, and
|
||||
// treats a fresh empty set as a change too, so a later real refusal is never
|
||||
// mistaken for a repeat of one that already healed.
|
||||
|
||||
static void testUnchangedOwnerAndSetStaysSilent() {
|
||||
CHECK(!shouldReport(/*sameOwnerAsLast=*/true, {"Bass"}, {"Bass"}));
|
||||
}
|
||||
|
||||
static void testADifferentNamedSetReports() {
|
||||
CHECK(shouldReport(/*sameOwnerAsLast=*/true, {"Bass"}, {"Bass", "Drum bus"}));
|
||||
}
|
||||
|
||||
static void testADifferentOwnerReportsEvenWithTheSameNames() {
|
||||
// Two alternating project tabs must not suppress each other's first refusal.
|
||||
CHECK(shouldReport(/*sameOwnerAsLast=*/false, {"Bass"}, {"Bass"}));
|
||||
}
|
||||
|
||||
static void testAFreshEmptySetReportsAndResetsTheMemo() {
|
||||
CHECK(shouldReport(/*sameOwnerAsLast=*/true, {"Bass"}, {}));
|
||||
// Already empty, same owner: nothing changed, stays silent.
|
||||
CHECK(!shouldReport(/*sameOwnerAsLast=*/true, {}, {}));
|
||||
}
|
||||
|
||||
// -- the apply's one undo point ----------------------------------------------
|
||||
//
|
||||
// These pin applyMintsUndoPoint's fold only. The `wroteAnything` verdict it
|
||||
// consumes — writeIfChanged / parkTrack / restoreTrack, all running against live
|
||||
// REAPER calls — cannot be pinned here; docs/VERIFICATION.md §"Mode switching"
|
||||
// is the sole cover for that plumbing being wired correctly.
|
||||
|
||||
static void testARealSwitchAlwaysLeavesAPointEvenWithNothingToWrite() {
|
||||
// An explicitly fired action stays undoable even when the plan found nothing
|
||||
// to write — the discard form is for reapplies only.
|
||||
CHECK(applyMintsUndoPoint(/*realSwitch=*/true, /*wroteAnything=*/false));
|
||||
CHECK(applyMintsUndoPoint(/*realSwitch=*/true, /*wroteAnything=*/true));
|
||||
}
|
||||
|
||||
static void testAReapplyThatWroteSomethingLeavesAPoint() {
|
||||
// A tag/untag reapply that actually parked a track: real project state moved.
|
||||
CHECK(applyMintsUndoPoint(/*realSwitch=*/false, /*wroteAnything=*/true));
|
||||
}
|
||||
|
||||
static void testAReapplyThatWroteNothingLeavesNoPointAtAll() {
|
||||
// The project-load reapply over a project saved fully parked: every flag and
|
||||
// every FX already sit where the plan wants them. Opening a project must not
|
||||
// cost the user an undo step.
|
||||
CHECK(!applyMintsUndoPoint(/*realSwitch=*/false, /*wroteAnything=*/false));
|
||||
}
|
||||
|
||||
int main() {
|
||||
testAChainSittingAtEveryValueTheParkWouldWriteReadsAsParked();
|
||||
testOneFlagStillAtTheUsersValueMeansNoParkReachedTheChain();
|
||||
testAnEmptyPlanProvesNothing();
|
||||
testEitherHalfOfTheChainReadingParkedIsEnoughToRefuse();
|
||||
testACleanChainIsSnapshottedThenParked();
|
||||
testAHeldSnapshotIsNeverOverwrittenWhateverTheChainReads();
|
||||
testAParkedChainWithNoSnapshotIsRefusedRatherThanResnapshotted();
|
||||
testRefusalNamesEveryRefusedTrackAndSaysNothingWhenNoneWere();
|
||||
testRefusalRecoveryNamesThePerFxHalfAndAssertsNoCause();
|
||||
|
||||
testUnchangedOwnerAndSetStaysSilent();
|
||||
testADifferentNamedSetReports();
|
||||
testADifferentOwnerReportsEvenWithTheSameNames();
|
||||
testAFreshEmptySetReportsAndResetsTheMemo();
|
||||
|
||||
testARealSwitchAlwaysLeavesAPointEvenWithNothingToWrite();
|
||||
testAReapplyThatWroteSomethingLeavesAPoint();
|
||||
testAReapplyThatWroteNothingLeavesNoPointAtAll();
|
||||
|
||||
if (g_fail == 0) std::printf("All tests passed.\n");
|
||||
return g_fail ? 1 : 0;
|
||||
}
|
||||
@@ -816,11 +816,11 @@ using LiveFlags = std::map<std::string, int>;
|
||||
// Returns nothing; mutates `vm` snapshots/active mode and `live` flags in place,
|
||||
// exactly mirroring view.cpp's park then restore then setActiveMode ordering.
|
||||
//
|
||||
// Guarded, the park mirrors decidePark's three ways: a held snapshot parks
|
||||
// without recapturing, a chain that already reads parked with no snapshot is
|
||||
// REFUSED (left exactly as found), and only a clean chain is snapshotted. The one
|
||||
// `live` flag stands in for the whole park-flag-plus-FX fold the shell reads —
|
||||
// the model half of the defect is identical either way.
|
||||
// Guarded, the park recaptures only when the model holds no record — the property
|
||||
// the shell's parkTrack keeps. Its second guarded branch (a parked-looking chain
|
||||
// with no record is left alone) outlived the shell rule it mirrored and is kept
|
||||
// here only as a second unguarded-vs-guarded contrast; the model half of the defect
|
||||
// is identical either way.
|
||||
void simulateApplyMode(ViewModeModel& vm, LiveFlags& live, const FolderTree& tree,
|
||||
const std::string& target, bool guard) {
|
||||
TogglePlan plan = vm.planToggle(tree, target);
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
// Standalone tests for the Design View park surface's pure decisions — no REAPER, no
|
||||
// test framework.
|
||||
//
|
||||
// The properties under test: the four flags a park drives address exactly the four
|
||||
// REAPER parameters the tool owns (never mute, never solo), and one mode apply leaves
|
||||
// at most ONE undo point — none at all when a reapply found everything already where
|
||||
// it wanted it.
|
||||
|
||||
#include "../src/shell/view/view_park.h"
|
||||
#include "../src/shell/view/view.h" // applyMintsUndoPoint — the apply's undo-point fold
|
||||
|
||||
#include "core/view/view_mode_model.h" // makeParkPlan — the park flags' only producer
|
||||
|
||||
#include <cstdio>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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)
|
||||
|
||||
// -- what a park is allowed to address ----------------------------------------
|
||||
//
|
||||
// trackFlagParm is the ONE mapping the park's pre-park read, the restore's write and
|
||||
// the reconcile's repair all share; a mistyped parameter there is silent at compile
|
||||
// time and drives the wrong track property forever.
|
||||
|
||||
static void testEachDrivenFlagAddressesItsOwnReaperParameter() {
|
||||
const std::set<std::string> parms{trackFlagParm(Flag::ShowInTcp),
|
||||
trackFlagParm(Flag::ShowInMixer),
|
||||
trackFlagParm(Flag::MainSend),
|
||||
trackFlagParm(Flag::FxEnable)};
|
||||
CHECK(parms.size() == 4); // no two flags collapse onto one parameter
|
||||
CHECK(parms.count("B_SHOWINTCP") == 1);
|
||||
CHECK(parms.count("B_SHOWINMIXER") == 1);
|
||||
CHECK(parms.count("B_MAINSEND") == 1);
|
||||
CHECK(parms.count("I_FXEN") == 1);
|
||||
}
|
||||
|
||||
static void testNoDrivenFlagCanEverAddressMuteOrSolo() {
|
||||
// The never-touch-mute invariant, held at the one place a Flag becomes a REAPER
|
||||
// parameter name.
|
||||
for (const Flag f : {Flag::ShowInTcp, Flag::ShowInMixer, Flag::MainSend, Flag::FxEnable}) {
|
||||
const std::string parm = trackFlagParm(f);
|
||||
CHECK(parm != "B_MUTE");
|
||||
CHECK(parm != "I_SOLO");
|
||||
}
|
||||
}
|
||||
|
||||
static void testAParkPlanDrivesEveryDrivenFlagToZero() {
|
||||
// The four zeros are what a parked track's driven flags actually read; if
|
||||
// makeParkPlan ever writes something else, this is the test that says so.
|
||||
const TrackPlan park = makeParkPlan("{A}");
|
||||
|
||||
CHECK(park.flags.size() == 4);
|
||||
std::set<std::string> parms;
|
||||
for (const TrackFlagOp& op : park.flags) {
|
||||
CHECK(op.guid == "{A}");
|
||||
CHECK(op.value == 0);
|
||||
parms.insert(trackFlagParm(op.flag));
|
||||
}
|
||||
CHECK(parms.size() == 4);
|
||||
}
|
||||
|
||||
// -- the apply's one undo point ----------------------------------------------
|
||||
//
|
||||
// These pin applyMintsUndoPoint's fold only. The `wroteAnything` verdict it consumes
|
||||
// — writeIfChanged / parkTrack / restoreTrack, all running against live REAPER calls
|
||||
// — cannot be pinned here; docs/VERIFICATION.md §"Mode switching" is the sole cover
|
||||
// for that plumbing being wired correctly.
|
||||
|
||||
static void testARealSwitchAlwaysLeavesAPointEvenWithNothingToWrite() {
|
||||
// An explicitly fired action stays undoable even when the plan found nothing to
|
||||
// write — the discard form is for reapplies only.
|
||||
CHECK(applyMintsUndoPoint(/*realSwitch=*/true, /*wroteAnything=*/false));
|
||||
CHECK(applyMintsUndoPoint(/*realSwitch=*/true, /*wroteAnything=*/true));
|
||||
}
|
||||
|
||||
static void testAReapplyThatWroteSomethingLeavesAPoint() {
|
||||
// A tag/untag reapply that actually parked a track: real project state moved.
|
||||
CHECK(applyMintsUndoPoint(/*realSwitch=*/false, /*wroteAnything=*/true));
|
||||
}
|
||||
|
||||
static void testAReapplyThatWroteNothingLeavesNoPointAtAll() {
|
||||
// The project-load reapply over a project saved fully parked: every flag already
|
||||
// sits where the plan wants it. Opening a project must not cost the user an undo
|
||||
// step.
|
||||
CHECK(!applyMintsUndoPoint(/*realSwitch=*/false, /*wroteAnything=*/false));
|
||||
}
|
||||
|
||||
int main() {
|
||||
testEachDrivenFlagAddressesItsOwnReaperParameter();
|
||||
testNoDrivenFlagCanEverAddressMuteOrSolo();
|
||||
testAParkPlanDrivesEveryDrivenFlagToZero();
|
||||
|
||||
testARealSwitchAlwaysLeavesAPointEvenWithNothingToWrite();
|
||||
testAReapplyThatWroteSomethingLeavesAPoint();
|
||||
testAReapplyThatWroteNothingLeavesNoPointAtAll();
|
||||
|
||||
if (g_fail == 0) std::printf("All tests passed.\n");
|
||||
return g_fail ? 1 : 0;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
// Standalone tests for the Design View repair pass's pure decision — no REAPER, no
|
||||
// test framework.
|
||||
//
|
||||
// The two properties the pass exists for: it drives a flag ONLY where a park record
|
||||
// names the track (so a track the tool never parked can never have its main send
|
||||
// re-enabled under it), and it fires on applied-output drift rather than on "an undo
|
||||
// happened". Both AppliedStates are produced by the real fingerprint
|
||||
// (view::appliedState) over real models, so a change to what the fingerprint carries
|
||||
// shows up here rather than being papered over by hand-built fixtures.
|
||||
|
||||
#include "../src/shell/view/view_reconcile.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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)
|
||||
|
||||
namespace {
|
||||
|
||||
TrackSnapshot captured(int showInTcp, int showInMixer, int mainSend, int fxEnable) {
|
||||
TrackSnapshot snap;
|
||||
snap.showInTcp = showInTcp;
|
||||
snap.showInMixer = showInMixer;
|
||||
snap.mainSend = mainSend;
|
||||
snap.fxEnable = fxEnable;
|
||||
return snap;
|
||||
}
|
||||
|
||||
bool hasOp(const std::vector<TrackFlagOp>& ops, const std::string& guid, Flag flag, int value) {
|
||||
for (const TrackFlagOp& op : ops)
|
||||
if (op.guid == guid && op.flag == flag && op.value == value) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool namesTrack(const std::vector<TrackFlagOp>& ops, const std::string& guid) {
|
||||
for (const TrackFlagOp& op : ops)
|
||||
if (op.guid == guid) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// -- the trigger: applied-output drift, not "an undo happened" -----------------
|
||||
|
||||
static void testAnUndoThatLeftOurAppliedOutputAloneRepairsNothing() {
|
||||
// The rejected trigger is "did an undo occur" — an undo that touched nothing we
|
||||
// drive still answers yes, and repairing on it rewrites state the user owns.
|
||||
ViewModeModel model;
|
||||
model.setActiveMode(kDesignModeId);
|
||||
model.storeSnapshot("{A}", captured(1, 1, 1, 1));
|
||||
|
||||
const view::AppliedState state = view::appliedState(model);
|
||||
CHECK(planReconcile(state, state).empty());
|
||||
}
|
||||
|
||||
static void testADriftedActiveModeAloneIsNotAReasonToDriveATrack() {
|
||||
// Drift is the gate, not the instruction: the record still has an owner, so the
|
||||
// reapply that follows this pass is what drives it.
|
||||
ViewModeModel applied;
|
||||
applied.setActiveMode(kDesignModeId);
|
||||
applied.storeSnapshot("{A}", captured(1, 1, 1, 1));
|
||||
|
||||
ViewModeModel reloaded; // active mode Arrange — the fingerprint differs
|
||||
reloaded.storeSnapshot("{A}", captured(1, 1, 1, 1));
|
||||
|
||||
CHECK(view::appliedState(applied) != view::appliedState(reloaded));
|
||||
CHECK(planReconcile(view::appliedState(applied), view::appliedState(reloaded)).empty());
|
||||
}
|
||||
|
||||
// -- the scope rule: a record names the track, or nothing is written -----------
|
||||
|
||||
static void testARecordTheReloadedModelLostIsHandedBackItsCapturedValues() {
|
||||
// The undo case: REAPER rolled view_state back to before the switch, so the model
|
||||
// no longer records the park — but the flags are still driven on the track, and
|
||||
// this memo is the only surviving account of what they were.
|
||||
ViewModeModel applied;
|
||||
applied.setActiveMode(kDesignModeId);
|
||||
applied.storeSnapshot("{A}", captured(1, 1, 1, 1));
|
||||
|
||||
const ViewModeModel reloaded; // Arrange, no records at all
|
||||
|
||||
const std::vector<TrackFlagOp> ops =
|
||||
planReconcile(view::appliedState(applied), view::appliedState(reloaded));
|
||||
|
||||
CHECK(ops.size() == 4);
|
||||
CHECK(hasOp(ops, "{A}", Flag::ShowInTcp, 1));
|
||||
CHECK(hasOp(ops, "{A}", Flag::ShowInMixer, 1));
|
||||
CHECK(hasOp(ops, "{A}", Flag::MainSend, 1));
|
||||
CHECK(hasOp(ops, "{A}", Flag::FxEnable, 1));
|
||||
}
|
||||
|
||||
static void testARepairedFlagReturnsToItsCapturedValueNeverToOn() {
|
||||
// A track the user had OUT of the master mix and FX-bypassed before we parked it:
|
||||
// the repair must hand back exactly that, not a hardcoded "on".
|
||||
ViewModeModel applied;
|
||||
applied.setActiveMode(kDesignModeId);
|
||||
applied.storeSnapshot("{A}", captured(1, 0, 0, 0));
|
||||
|
||||
const ViewModeModel reloaded;
|
||||
|
||||
const std::vector<TrackFlagOp> ops =
|
||||
planReconcile(view::appliedState(applied), view::appliedState(reloaded));
|
||||
|
||||
CHECK(hasOp(ops, "{A}", Flag::MainSend, 0));
|
||||
CHECK(hasOp(ops, "{A}", Flag::ShowInMixer, 0));
|
||||
CHECK(hasOp(ops, "{A}", Flag::FxEnable, 0));
|
||||
CHECK(!hasOp(ops, "{A}", Flag::MainSend, 1));
|
||||
}
|
||||
|
||||
static void testATrackNeitherStateRecordsIsNeverDrivenAtAll() {
|
||||
// The regression this rule is built against: a track the model merely calls
|
||||
// active is NOT a track the tool parked. It holds no captured value, so asserting
|
||||
// B_MAINSEND=1 on it would silently re-enable a send the user disabled by hand.
|
||||
ViewModeModel applied;
|
||||
applied.setActiveMode(kDesignModeId);
|
||||
applied.storeSnapshot("{A}", captured(1, 1, 1, 1));
|
||||
|
||||
ViewModeModel reloaded;
|
||||
reloaded.membership().tag("{B}", kArrangeModeId); // active, never parked, no record
|
||||
|
||||
const std::vector<TrackFlagOp> ops =
|
||||
planReconcile(view::appliedState(applied), view::appliedState(reloaded));
|
||||
|
||||
CHECK(namesTrack(ops, "{A}"));
|
||||
CHECK(!namesTrack(ops, "{B}"));
|
||||
}
|
||||
|
||||
static void testASpentFlagHalfIsNotARecordToRepairFrom() {
|
||||
// A restore already handed the flags back and zeroed them on the record; the FX
|
||||
// half outlives that. Repairing from the spent half would drive those zeros —
|
||||
// i.e. re-park the track — so the fingerprint must not carry it.
|
||||
ViewModeModel applied;
|
||||
applied.setActiveMode(kDesignModeId);
|
||||
applied.storeSnapshot("{A}", captured(1, 1, 1, 1));
|
||||
applied.releaseSnapshotFlags("{A}");
|
||||
|
||||
const ViewModeModel reloaded;
|
||||
|
||||
CHECK(planReconcile(view::appliedState(applied), view::appliedState(reloaded)).empty());
|
||||
}
|
||||
|
||||
int main() {
|
||||
testAnUndoThatLeftOurAppliedOutputAloneRepairsNothing();
|
||||
testADriftedActiveModeAloneIsNotAReasonToDriveATrack();
|
||||
|
||||
testARecordTheReloadedModelLostIsHandedBackItsCapturedValues();
|
||||
testARepairedFlagReturnsToItsCapturedValueNeverToOn();
|
||||
testATrackNeitherStateRecordsIsNeverDrivenAtAll();
|
||||
testASpentFlagHalfIsNotARecordToRepairFrom();
|
||||
|
||||
if (g_fail == 0) std::printf("All tests passed.\n");
|
||||
return g_fail ? 1 : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user