public void UndoRedoMultiTest() { var undoable = new TestUndoable(); var manager = new UndoManager(undoable); const int count = 6; for (int i = 0; i < count; i++) { using (var undo = manager.BeginTransaction(string.Format("Revision {0}", i + 1))) { undoable.Revise(); undo.Commit(); } } for (int i = 0; i < count; i++) { manager.UndoRestore(i); string[] undoDescriptions = manager.UndoDescriptions.ToArray(); for (int j = 0; j < undoDescriptions.Length; j++) Assert.AreEqual(string.Format("Revision {0}", count - j - i - 1), undoDescriptions[j]); string[] redoDescriptions = manager.RedoDescriptions.ToArray(); for (int j = 0; j < redoDescriptions.Length; j++) Assert.AreEqual(string.Format("Revision {0}", count + j - i), redoDescriptions[j]); Assert.AreEqual(count - i - 1, manager.UndoCount); Assert.AreEqual(i + 1, manager.RedoCount); Assert.AreEqual(count - i - 1, undoable.RevisionIndex); manager.RedoRestore(i); Assert.AreEqual(count, manager.UndoCount); Assert.AreEqual(0, manager.RedoCount); Assert.AreEqual(count, undoable.RevisionIndex); } // Try undoing beyond end of stack try { manager.UndoRestore(count); Assert.Fail("Expected exception"); } catch (IndexOutOfRangeException) { Assert.AreEqual(count, manager.UndoCount); } manager.UndoRestore(count / 2); // Try redoing beyond end of stack try { manager.RedoRestore(count); Assert.Fail("Expected exception"); } catch (IndexOutOfRangeException) { Assert.AreEqual(count / 2 + 1, manager.RedoCount); } manager.Clear(); Assert.AreEqual(0, manager.UndoCount); Assert.AreEqual(0, manager.RedoCount); }
public void UndoTransactionTest() { var undoable = new TestUndoable(); var manager = new UndoManager(undoable); manager.StacksChanged += undoable.UndoStacksChanged; Assert.IsNull(manager.UndoDescription); Assert.AreEqual(0, manager.UndoDescriptions.ToArray().Length); Assert.IsNull(manager.RedoDescription); Assert.AreEqual(0, manager.RedoDescriptions.ToArray().Length); // Test functions which should be no-ops with nothing to do. manager.Undo(); manager.UndoRestore(10); manager.Redo(); manager.RedoRestore(10); const string description = "Success"; using (var undo = manager.BeginTransaction(description)) { Assert.IsTrue(manager.Recording); undoable.Revise(); undoable.AllowStackChanges = true; undo.Commit(); undoable.AllowStackChanges = false; } Assert.IsFalse(manager.Recording); // Undo stack should contain new record Assert.AreEqual(description, manager.UndoDescription); using (manager.BeginTransaction("Implicit rollback")) { Assert.IsTrue(manager.Recording); // No commit call } Assert.IsFalse(manager.Recording); // Undo stack shouldn't change Assert.AreEqual(description, manager.UndoDescription); using (var undo = manager.BeginTransaction("Explicit rollback")) { Assert.IsTrue(manager.Recording); undo.Rollback(); Assert.IsFalse(manager.Recording); } // Undo stack shouldn't change Assert.AreEqual(description, manager.UndoDescription); var undoFree = manager.BeginTransaction("Rollback without Dispose"); Assert.IsTrue(manager.Recording); undoFree.Rollback(); Assert.IsFalse(manager.Recording); // Undo stack shouldn't change Assert.AreEqual(description, manager.UndoDescription); undoFree = manager.BeginTransaction("Commit without Dispose"); Assert.IsTrue(manager.Recording); undoable.Revise(); undoable.AllowStackChanges = true; undoFree.Commit(); undoable.AllowStackChanges = false; Assert.IsFalse(manager.Recording); // Undo stack should have extra record Assert.AreEqual(2, manager.UndoCount); undoable.AllowStackChanges = true; manager.Undo(); Assert.AreEqual(description, manager.UndoDescription); Assert.AreEqual(1, undoable.RevisionIndex); manager.Redo(); Assert.AreEqual(2, undoable.RevisionIndex); Assert.AreEqual(2, manager.UndoCount); manager.Undo(); Assert.AreEqual(1, manager.RedoCount); undoable.AllowStackChanges = false; var descriptionNested = "Nested transactions"; using (var undo = manager.BeginTransaction(descriptionNested)) { Assert.IsTrue(manager.Recording); using (var undoInner = manager.BeginTransaction("Inner transaction")) { undoable.Revise(); undoInner.Commit(); // Should be ignored Assert.IsTrue(manager.Recording); } Assert.IsTrue(manager.Recording); undoable.AllowStackChanges = true; undo.Commit(); undoable.AllowStackChanges = false; Assert.IsFalse(manager.Recording); } Assert.AreEqual(descriptionNested, manager.UndoDescription); // Make sure undo stack was cleared Assert.AreEqual(0, manager.RedoCount); Assert.AreEqual(0, manager.RedoDescriptions.ToArray().Length); Assert.IsNull(manager.RedoDescription); try { using (var undo = manager.BeginTransaction("Commit after rollback")) { Assert.IsTrue(manager.Recording); undo.Rollback(); Assert.IsFalse(manager.Recording); undo.Commit(); Assert.Fail("Exception exptected"); } } catch (Exception) { Assert.AreEqual(2, manager.UndoCount); } try { descriptionNested = "Double commit"; using (var undo = manager.BeginTransaction(descriptionNested)) { Assert.IsTrue(manager.Recording); using (var undoInner = manager.BeginTransaction("Inner transaction")) { undoable.Revise(); undoInner.Commit(); Assert.IsTrue(manager.Recording); } undoable.AllowStackChanges = true; undo.Commit(); undoable.AllowStackChanges = false; Assert.IsFalse(manager.Recording); undo.Commit(); Assert.Fail("Exception exptected"); } } catch (Exception) { Assert.AreEqual(3, manager.UndoCount); Assert.AreEqual(3, manager.UndoDescriptions.ToArray().Length); Assert.AreEqual(descriptionNested, manager.UndoDescription); } Assert.AreEqual(7, undoable.CountStackChanges); }