diff options
author | Neil <nyamatongwe@gmail.com> | 2023-12-30 15:55:18 +1100 |
---|---|---|
committer | Neil <nyamatongwe@gmail.com> | 2023-12-30 15:55:18 +1100 |
commit | 0c3b3ebea9cdc63aa003145a53c9a3d3f89a6400 (patch) | |
tree | 8f8edfc6a729839180033b9ee7dbd3d527203fc6 /test | |
parent | a6e76015545771c1f18187e2b4b4e6bb0e4d509f (diff) | |
download | scintilla-mirror-0c3b3ebea9cdc63aa003145a53c9a3d3f89a6400.tar.gz |
Add tests for undo history, coalescing, grouping, tentative.
Diffstat (limited to 'test')
-rw-r--r-- | test/unit/testCellBuffer.cxx | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/test/unit/testCellBuffer.cxx b/test/unit/testCellBuffer.cxx index eaa5b0f31..510a1c097 100644 --- a/test/unit/testCellBuffer.cxx +++ b/test/unit/testCellBuffer.cxx @@ -210,6 +210,222 @@ TEST_CASE("CellBuffer") { } +bool Equal(const Action &a, ActionType at, Sci::Position position, std::string_view value) noexcept { + // Currently ignores mayCoalesce + if (a.at != at) + return false; + if (a.position != position) + return false; + if (a.lenData != static_cast<Sci::Position>(value.length())) + return false; + if (memcmp(a.data.get(), value.data(), a.lenData) != 0) + return false; + return true; +} + +void TentativeUndo(UndoHistory &uh) { + const int steps = uh.TentativeSteps(); + for (int step = 0; step < steps; step++) { + /* const Action &actionStep = */ uh.GetUndoStep(); + uh.CompletedUndoStep(); + } + uh.TentativeCommit(); +} + +TEST_CASE("UndoHistory") { + + UndoHistory uh; + + SECTION("Basics") { + REQUIRE(uh.IsSavePoint()); + REQUIRE(uh.AfterSavePoint()); + REQUIRE(!uh.BeforeSavePoint()); + REQUIRE(!uh.BeforeReachableSavePoint()); + REQUIRE(!uh.CanUndo()); + REQUIRE(!uh.CanRedo()); + + bool startSequence = false; + const char *val = uh.AppendAction(ActionType::insert, 0, "ab", 2, startSequence, true); + REQUIRE(memcmp(val, "ab", 2) == 0); + REQUIRE(startSequence); + REQUIRE(!uh.IsSavePoint()); + REQUIRE(uh.AfterSavePoint()); + REQUIRE(uh.CanUndo()); + REQUIRE(!uh.CanRedo()); + val = uh.AppendAction(ActionType::remove, 0, "ab", 2, startSequence, true); + REQUIRE(memcmp(val, "ab", 2) == 0); + REQUIRE(startSequence); + + // Undoing + { + const int steps = uh.StartUndo(); + REQUIRE(steps == 1); + const Action &action = uh.GetUndoStep(); + REQUIRE(Equal(action, ActionType::remove, 0, "ab")); + uh.CompletedUndoStep(); + } + { + const int steps = uh.StartUndo(); + REQUIRE(steps == 1); + const Action &action = uh.GetUndoStep(); + REQUIRE(Equal(action, ActionType::insert, 0, "ab")); + uh.CompletedUndoStep(); + } + + REQUIRE(uh.IsSavePoint()); + + // Redoing + { + const int steps = uh.StartRedo(); + REQUIRE(steps == 1); + const Action &action = uh.GetRedoStep(); + REQUIRE(Equal(action, ActionType::insert, 0, "ab")); + uh.CompletedRedoStep(); + } + { + const int steps = uh.StartRedo(); + REQUIRE(steps == 1); + const Action &action = uh.GetRedoStep(); + REQUIRE(Equal(action, ActionType::remove, 0, "ab")); + uh.CompletedRedoStep(); + } + + REQUIRE(!uh.IsSavePoint()); + } + + SECTION("Coalesce") { + + bool startSequence = false; + const char *val = uh.AppendAction(ActionType::insert, 0, "ab", 2, startSequence, true); + REQUIRE(memcmp(val, "ab", 2) == 0); + REQUIRE(startSequence); + REQUIRE(!uh.IsSavePoint()); + REQUIRE(uh.AfterSavePoint()); + REQUIRE(uh.CanUndo()); + REQUIRE(!uh.CanRedo()); + val = uh.AppendAction(ActionType::insert, 2, "cd", 2, startSequence, true); + REQUIRE(memcmp(val, "cd", 2) == 0); + REQUIRE(!startSequence); + + // Undoing + { + const int steps = uh.StartUndo(); + REQUIRE(steps == 2); + const Action &action2 = uh.GetUndoStep(); + REQUIRE(Equal(action2, ActionType::insert, 2, "cd")); + uh.CompletedUndoStep(); + const Action &action1 = uh.GetUndoStep(); + REQUIRE(Equal(action1, ActionType::insert, 0, "ab")); + uh.CompletedUndoStep(); + } + + REQUIRE(uh.IsSavePoint()); + + // Redoing + { + const int steps = uh.StartRedo(); + REQUIRE(steps == 2); + const Action &action1 = uh.GetRedoStep(); + REQUIRE(Equal(action1, ActionType::insert, 0, "ab")); + uh.CompletedRedoStep(); + const Action &action2 = uh.GetRedoStep(); + REQUIRE(Equal(action2, ActionType::insert, 2, "cd")); + uh.CompletedRedoStep(); + } + + REQUIRE(!uh.IsSavePoint()); + + } + + SECTION("Grouping") { + + uh.BeginUndoAction(); + + bool startSequence = false; + const char *val = uh.AppendAction(ActionType::insert, 0, "ab", 2, startSequence, true); + REQUIRE(memcmp(val, "ab", 2) == 0); + REQUIRE(startSequence); + REQUIRE(!uh.IsSavePoint()); + REQUIRE(uh.AfterSavePoint()); + REQUIRE(uh.CanUndo()); + REQUIRE(!uh.CanRedo()); + val = uh.AppendAction(ActionType::remove, 0, "ab", 2, startSequence, true); + REQUIRE(memcmp(val, "ab", 2) == 0); + REQUIRE(!startSequence); + val = uh.AppendAction(ActionType::insert, 0, "cde", 3, startSequence, true); + REQUIRE(memcmp(val, "cde", 3) == 0); + REQUIRE(!startSequence); + + uh.EndUndoAction(); + + // Undoing + { + const int steps = uh.StartUndo(); + REQUIRE(steps == 3); + const Action &action3 = uh.GetUndoStep(); + REQUIRE(Equal(action3, ActionType::insert, 0, "cde")); + uh.CompletedUndoStep(); + const Action &action2 = uh.GetUndoStep(); + REQUIRE(Equal(action2, ActionType::remove, 0, "ab")); + uh.CompletedUndoStep(); + const Action &action1 = uh.GetUndoStep(); + REQUIRE(Equal(action1, ActionType::insert, 0, "ab")); + uh.CompletedUndoStep(); + } + + REQUIRE(uh.IsSavePoint()); + + // Redoing + { + const int steps = uh.StartRedo(); + REQUIRE(steps == 3); + const Action &action1 = uh.GetRedoStep(); + REQUIRE(Equal(action1, ActionType::insert, 0, "ab")); + uh.CompletedRedoStep(); + const Action &action2 = uh.GetRedoStep(); + REQUIRE(Equal(action2, ActionType::remove, 0, "ab")); + uh.CompletedRedoStep(); + const Action &action3 = uh.GetRedoStep(); + REQUIRE(Equal(action3, ActionType::insert, 0, "cde")); + uh.CompletedRedoStep(); + } + + REQUIRE(!uh.IsSavePoint()); + + } + + SECTION("Tentative") { + + REQUIRE(!uh.TentativeActive()); + REQUIRE(uh.TentativeSteps() == -1); + uh.TentativeStart(); + REQUIRE(uh.TentativeActive()); + REQUIRE(uh.TentativeSteps() == 0); + bool startSequence = false; + uh.AppendAction(ActionType::insert, 0, "ab", 2, startSequence, true); + REQUIRE(uh.TentativeActive()); + REQUIRE(uh.TentativeSteps() == 1); + REQUIRE(uh.CanUndo()); + uh.TentativeCommit(); + REQUIRE(!uh.TentativeActive()); + REQUIRE(uh.TentativeSteps() == -1); + REQUIRE(uh.CanUndo()); + + // TentativeUndo is the other important operation but it is performed by Document so add a local equivalent + uh.TentativeStart(); + uh.AppendAction(ActionType::remove, 0, "ab", 2, startSequence, false); + uh.AppendAction(ActionType::insert, 0, "ab", 2, startSequence, true); + REQUIRE(uh.TentativeActive()); + // The first TentativeCommit didn't seal off the first action so it is still undoable + REQUIRE(uh.TentativeSteps() == 3); + REQUIRE(uh.CanUndo()); + TentativeUndo(uh); + REQUIRE(!uh.TentativeActive()); + REQUIRE(uh.TentativeSteps() == -1); + REQUIRE(uh.CanUndo()); + } +} + TEST_CASE("CharacterIndex") { CellBuffer cb(true, false); |