Refuse to park a track whose pre-park snapshot is gone, rather than re-snapshotting the already-parked chain as the user's state
This commit is contained in:
@@ -4,7 +4,8 @@
|
||||
// The properties under test: a mode switch leaves its per-FX offline work here,
|
||||
// so a second switch arriving before the first drained must leave every track in
|
||||
// the state the SECOND switch specifies — never the first's, never both replayed;
|
||||
// and a cancel must not strand the pre-park FX state it was the last record of.
|
||||
// a cancel must not strand the pre-park FX state it was the last record of; and
|
||||
// a pre-park snapshot is never taken from a chain a park has already touched.
|
||||
|
||||
#include "../src/shell/view/view_fx_park.h"
|
||||
|
||||
@@ -233,6 +234,67 @@ static void testRestorePlanOpsRebuildTheSlotKeyedSnapshotVerbatim() {
|
||||
CHECK(rebuilt.states == snap.fxOffline);
|
||||
}
|
||||
|
||||
// -- may this chain be snapshotted? -------------------------------------------
|
||||
//
|
||||
// The pre-park snapshot is restore's only source of truth, so one taken from an
|
||||
// already-parked chain makes every later restore write hidden/out-of-mix/
|
||||
// FX-disabled back, permanently. The park site cannot infer a clean chain from
|
||||
// an absent snapshot — discardDeferredFxParks drops intents whose flag writes
|
||||
// already landed — so the chain itself has to be asked.
|
||||
|
||||
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}", /*fxCount=*/0);
|
||||
|
||||
CHECK(park.flags.size() == 4);
|
||||
CHECK(parkFlagsAlreadyApplied(park.flags, {0, 0, 0, 0}));
|
||||
}
|
||||
|
||||
static void testOneFlagStillAtTheUsersValueMeansNoParkReachedTheChain() {
|
||||
const TrackPlan park = makeParkPlan("{A}", /*fxCount=*/0);
|
||||
|
||||
// Flag order is ShowInTcp, ShowInMixer, MainSend, FxEnable — each alone is
|
||||
// enough to prove the chain was never parked.
|
||||
CHECK(!parkFlagsAlreadyApplied(park.flags, {1, 0, 0, 0}));
|
||||
CHECK(!parkFlagsAlreadyApplied(park.flags, {0, 1, 0, 0}));
|
||||
CHECK(!parkFlagsAlreadyApplied(park.flags, {0, 0, 1, 0}));
|
||||
CHECK(!parkFlagsAlreadyApplied(park.flags, {0, 0, 0, 1}));
|
||||
}
|
||||
|
||||
static void testAnEmptyOrMismatchedPlanProvesNothing() {
|
||||
const TrackPlan park = makeParkPlan("{A}", /*fxCount=*/0);
|
||||
|
||||
CHECK(!parkFlagsAlreadyApplied({}, {}));
|
||||
CHECK(!parkFlagsAlreadyApplied(park.flags, {0, 0, 0}));
|
||||
}
|
||||
|
||||
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 testRefusalIsReportedWithItsTrackCountAndSaysNothingWhenNoneWereRefused() {
|
||||
CHECK(describeRefusedParks(0).empty());
|
||||
CHECK(describeRefusedParks(-1).empty());
|
||||
CHECK(describeRefusedParks(1).find("1 track ") != std::string::npos);
|
||||
CHECK(describeRefusedParks(3).find("3 tracks ") != std::string::npos);
|
||||
}
|
||||
|
||||
// -- re-entrancy -------------------------------------------------------------
|
||||
|
||||
static void testTakeDetachesEverythingAndLeavesTheQueueEmpty() {
|
||||
@@ -297,6 +359,14 @@ int main() {
|
||||
testSlotKeyedRestoreDoesNotBecomeIdentityKeyedWithNoIdentities();
|
||||
testRestorePlanOpsRebuildTheIdentityKeyedSnapshotVerbatim();
|
||||
testRestorePlanOpsRebuildTheSlotKeyedSnapshotVerbatim();
|
||||
testAChainSittingAtEveryValueTheParkWouldWriteReadsAsParked();
|
||||
testOneFlagStillAtTheUsersValueMeansNoParkReachedTheChain();
|
||||
testAnEmptyOrMismatchedPlanProvesNothing();
|
||||
testACleanChainIsSnapshottedThenParked();
|
||||
testAHeldSnapshotIsNeverOverwrittenWhateverTheChainReads();
|
||||
testAParkedChainWithNoSnapshotIsRefusedRatherThanResnapshotted();
|
||||
testRefusalIsReportedWithItsTrackCountAndSaysNothingWhenNoneWereRefused();
|
||||
|
||||
testTakeDetachesEverythingAndLeavesTheQueueEmpty();
|
||||
testIntentsArrivingDuringADrainSurviveIt();
|
||||
testAReEntrantParkCancelsOnlyWhatIsStillPending();
|
||||
|
||||
Reference in New Issue
Block a user