/** * Copyright 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.waveprotocol.wave.model.wave.undo; import static org.waveprotocol.wave.model.wave.undo.AggregateOpTestUtil.addParticipant; import static org.waveprotocol.wave.model.wave.undo.AggregateOpTestUtil.areEqual; import static org.waveprotocol.wave.model.wave.undo.AggregateOpTestUtil.compose; import static org.waveprotocol.wave.model.wave.undo.AggregateOpTestUtil.delete; import static org.waveprotocol.wave.model.wave.undo.AggregateOpTestUtil.insert; import static org.waveprotocol.wave.model.wave.undo.AggregateOpTestUtil.removeParticipant; import org.waveprotocol.wave.model.undo.UndoManager; import org.waveprotocol.wave.model.undo.UndoManagerPlus; import junit.framework.TestCase; import org.waveprotocol.wave.model.util.Pair; /** * Tests for UndoManagerFactory. This also tests the package-private aggregate * operations and their composition, transform, and inversion algorithms. * */ public class UndoManagerFactoryTest extends TestCase { public void testUndoRedo() { UndoManager<AggregateOperation> undoManager = UndoManagerFactory.createUndoManager(); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 3, 10)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 5, 11)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 4, 12)); assertOpsEqual(delete("i", 4, 12), undoManager.undo()); assertOpsEqual(delete("i", 5, 11), undoManager.undo()); assertOpsEqual(delete("i", 3, 10), undoManager.undo()); assertOpsEqual(insert("i", 3, 10), undoManager.redo()); assertOpsEqual(insert("i", 5, 11), undoManager.redo()); assertOpsEqual(insert("i", 4, 12), undoManager.redo()); assertOpsEqual(delete("i", 4, 12), undoManager.undo()); assertOpsEqual(delete("i", 5, 11), undoManager.undo()); assertOpsEqual(delete("i", 3, 10), undoManager.undo()); } public void testUndoRedoWithNonundoableOps() { UndoManager<AggregateOperation> undoManager = UndoManagerFactory.createUndoManager(); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 3, 10)); undoManager.nonUndoableOp(insert("i", 1, 11)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 5, 12)); undoManager.nonUndoableOp(insert("i", 1, 13)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 4, 14)); undoManager.nonUndoableOp(insert("i", 1, 15)); assertOpsEqual(delete("i", 5, 15), undoManager.undo()); assertOpsEqual(delete("i", 7, 14), undoManager.undo()); assertOpsEqual(delete("i", 6, 13), undoManager.undo()); assertOpsEqual(insert("i", 6, 13), undoManager.redo()); assertOpsEqual(insert("i", 7, 14), undoManager.redo()); assertOpsEqual(insert("i", 5, 15), undoManager.redo()); } public void testUndoRedoWithConsecutiveNonundoableOps() { UndoManager<AggregateOperation> undoManager = UndoManagerFactory.createUndoManager(); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 3, 10)); undoManager.nonUndoableOp(insert("i", 1, 11)); undoManager.nonUndoableOp(insert("i", 2, 12)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 6, 13)); undoManager.nonUndoableOp(delete("i", 10, 13)); undoManager.nonUndoableOp(insert("i", 1, 13)); undoManager.nonUndoableOp(insert("i", 10, 14)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 5, 15)); undoManager.nonUndoableOp(insert("i", 1, 16)); assertOpsEqual(delete("i", 6, 16), undoManager.undo()); assertOpsEqual(delete("i", 8, 15), undoManager.undo()); assertOpsEqual(delete("i", 7, 14), undoManager.undo()); assertOpsEqual(insert("i", 7, 14), undoManager.redo()); assertOpsEqual(insert("i", 8, 15), undoManager.redo()); assertOpsEqual(insert("i", 6, 16), undoManager.redo()); } public void testUndoRedoInterspersedWithNonundoableOps() { UndoManager<AggregateOperation> undoManager = UndoManagerFactory.createUndoManager(); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 3, 10)); undoManager.nonUndoableOp(insert("i", 1, 11)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 5, 12)); undoManager.nonUndoableOp(insert("i", 1, 13)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 4, 14)); undoManager.nonUndoableOp(insert("i", 1, 15)); assertOpsEqual(delete("i", 5, 15), undoManager.undo()); undoManager.nonUndoableOp(insert("i", 1, 15)); assertOpsEqual(delete("i", 8, 15), undoManager.undo()); undoManager.nonUndoableOp(insert("i", 1, 15)); assertOpsEqual(delete("i", 8, 15), undoManager.undo()); undoManager.nonUndoableOp(insert("i", 1, 15)); assertOpsEqual(insert("i", 9, 16), undoManager.redo()); undoManager.nonUndoableOp(insert("i", 1, 17)); assertOpsEqual(insert("i", 11, 18), undoManager.redo()); undoManager.nonUndoableOp(insert("i", 1, 19)); assertOpsEqual(insert("i", 10, 20), undoManager.redo()); } public void testUndoRedoWithNondenseCheckpointing() { UndoManager<AggregateOperation> undoManager = UndoManagerFactory.createUndoManager(); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 3, 10)); undoManager.nonUndoableOp(insert("i", 1, 11)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 8, 12)); undoManager.nonUndoableOp(insert("i", 1, 13)); undoManager.undoableOp(insert("i", 2, 14)); undoManager.nonUndoableOp(insert("i", 10, 15)); undoManager.undoableOp(insert("i", 6, 16)); undoManager.nonUndoableOp(insert("i", 1, 17)); undoManager.undoableOp(delete("i", 13, 17)); undoManager.undoableOp(delete("i", 3, 16)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 4, 16)); undoManager.nonUndoableOp(insert("i", 1, 17)); assertOpsEqual(delete("i", 5, 17), undoManager.undo()); assertOpsEqual(delete("i", 7, 16), undoManager.undo()); assertOpsEqual(delete("i", 7, 15), undoManager.undo()); assertOpsEqual(insert("i", 7, 15), undoManager.redo()); assertOpsEqual(insert("i", 7, 16), undoManager.redo()); assertOpsEqual(insert("i", 5, 17), undoManager.redo()); } public void testUndoRedoWithNondenseCheckpointsInterspersedWithNonundoableOps() { UndoManager<AggregateOperation> undoManager = UndoManagerFactory.createUndoManager(); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 3, 10)); undoManager.nonUndoableOp(insert("i", 1, 11)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 8, 12)); undoManager.nonUndoableOp(insert("i", 1, 13)); undoManager.undoableOp(insert("i", 2, 14)); undoManager.nonUndoableOp(insert("i", 10, 15)); undoManager.undoableOp(insert("i", 6, 16)); undoManager.nonUndoableOp(insert("i", 1, 17)); undoManager.undoableOp(delete("i", 13, 17)); undoManager.undoableOp(delete("i", 3, 16)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 4, 16)); undoManager.nonUndoableOp(insert("i", 1, 17)); assertOpsEqual(delete("i", 5, 17), undoManager.undo()); undoManager.nonUndoableOp(insert("i", 1, 17)); assertOpsEqual(delete("i", 8, 17), undoManager.undo()); undoManager.nonUndoableOp(insert("i", 1, 17)); assertOpsEqual(delete("i", 9, 17), undoManager.undo()); undoManager.nonUndoableOp(insert("i", 1, 17)); assertOpsEqual(insert("i", 10, 18), undoManager.redo()); undoManager.nonUndoableOp(insert("i", 1, 19)); assertOpsEqual(insert("i", 11, 20), undoManager.redo()); undoManager.nonUndoableOp(insert("i", 1, 21)); assertOpsEqual(insert("i", 10, 22), undoManager.redo()); } public void testPlusMethods() { UndoManagerPlus<AggregateOperation> undoManager = UndoManagerFactory.createUndoManager(); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 3, 10)); undoManager.nonUndoableOp(insert("i", 1, 11)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 8, 12)); undoManager.nonUndoableOp(insert("i", 1, 13)); undoManager.undoableOp(insert("i", 2, 14)); undoManager.nonUndoableOp(insert("i", 10, 15)); undoManager.undoableOp(insert("i", 6, 16)); undoManager.nonUndoableOp(insert("i", 1, 17)); undoManager.undoableOp(delete("i", 13, 17)); undoManager.undoableOp(delete("i", 3, 16)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 4, 16)); undoManager.nonUndoableOp(insert("i", 1, 17)); Pair<AggregateOperation, AggregateOperation> pair = undoManager.undoPlus(); assertOpsEqual(delete("i", 5, 17), pair.first); assertOpsEqual(insert("i", 1, 16), pair.second); undoManager.nonUndoableOp(insert("i", 1, 17)); pair = undoManager.undoPlus(); assertOpsEqual(delete("i", 8, 17), pair.first); assertOpsEqual(compose( insert("i", 1, 12), insert("i", 9, 13), insert("i", 1, 14), insert("i", 1, 15), insert("i", 1, 16) ), pair.second); undoManager.nonUndoableOp(insert("i", 1, 17)); pair = undoManager.undoPlus(); assertOpsEqual(delete("i", 9, 17), pair.first); assertOpsEqual(compose( insert("i", 1, 10), insert("i", 1, 11), insert("i", 8, 12), insert("i", 1, 13), insert("i", 1, 14), insert("i", 1, 15), insert("i", 1, 16) ), pair.second); undoManager.nonUndoableOp(insert("i", 1, 17)); pair = undoManager.redoPlus(); assertOpsEqual(insert("i", 10, 18), pair.first); assertOpsEqual(insert("i", 1, 18), pair.second); undoManager.nonUndoableOp(insert("i", 1, 19)); pair = undoManager.redoPlus(); assertOpsEqual(insert("i", 11, 20), pair.first); assertOpsEqual(compose( insert("i", 1, 18), insert("i", 1, 19), insert("i", 1, 20) ), pair.second); undoManager.nonUndoableOp(insert("i", 1, 21)); pair = undoManager.redoPlus(); assertOpsEqual(insert("i", 10, 22), pair.first); assertOpsEqual(compose( insert("i", 1, 18), insert("i", 1, 19), insert("i", 1, 20), insert("i", 1, 21), insert("i", 1, 22) ), pair.second); } public void testUndoRedoInvolvingMultipleWavelets() { UndoManager<AggregateOperation> undoManager = UndoManagerFactory.createUndoManager(); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 3, 10)); undoManager.undoableOp(insert("j", 5, 10)); undoManager.nonUndoableOp(insert("i", 1, 11)); undoManager.nonUndoableOp(insert("j", 1, 11)); undoManager.nonUndoableOp(insert("k", 1, 11)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 8, 12)); undoManager.undoableOp(insert("j", 5, 12)); undoManager.nonUndoableOp(insert("i", 1, 13)); undoManager.undoableOp(insert("i", 2, 14)); undoManager.nonUndoableOp(insert("i", 10, 15)); undoManager.undoableOp(insert("i", 6, 16)); undoManager.nonUndoableOp(insert("i", 1, 17)); undoManager.undoableOp(delete("i", 13, 17)); undoManager.undoableOp(delete("i", 3, 16)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 4, 16)); undoManager.nonUndoableOp(insert("i", 1, 17)); assertOpsEqual(delete("i", 5, 17), undoManager.undo()); undoManager.nonUndoableOp(insert("i", 1, 17)); assertOpsEqual(compose(delete("i", 8, 17), delete("j", 5, 12)), undoManager.undo()); undoManager.nonUndoableOp(insert("i", 1, 17)); assertOpsEqual(compose(delete("i", 9, 17), delete("j", 6, 11)), undoManager.undo()); undoManager.nonUndoableOp(insert("i", 1, 17)); assertOpsEqual(compose(insert("i", 10, 18), insert("j", 6, 11)), undoManager.redo()); undoManager.nonUndoableOp(insert("i", 1, 19)); assertOpsEqual(compose(insert("i", 11, 20), insert("j", 5, 12)), undoManager.redo()); undoManager.nonUndoableOp(insert("i", 1, 21)); assertOpsEqual(insert("i", 10, 22), undoManager.redo()); } public void testUndoRedoInvolvingParticipants() { UndoManager<AggregateOperation> undoManager = UndoManagerFactory.createUndoManager(); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 3, 10)); undoManager.undoableOp(addParticipant("x")); undoManager.undoableOp(insert("j", 5, 10)); undoManager.undoableOp(addParticipant("y")); undoManager.nonUndoableOp(insert("i", 1, 11)); undoManager.undoableOp(addParticipant("z")); undoManager.nonUndoableOp(insert("j", 1, 11)); undoManager.undoableOp(removeParticipant("y")); undoManager.nonUndoableOp(insert("k", 1, 11)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 8, 12)); undoManager.undoableOp(addParticipant("xx")); undoManager.nonUndoableOp(removeParticipant("xx")); undoManager.undoableOp(removeParticipant("yy")); undoManager.nonUndoableOp(addParticipant("yy")); undoManager.undoableOp(addParticipant("ww")); undoManager.undoableOp(insert("j", 5, 12)); undoManager.undoableOp(removeParticipant("xxx")); undoManager.undoableOp(removeParticipant("yyy")); undoManager.undoableOp(removeParticipant("zzz")); undoManager.undoableOp(addParticipant("yyy")); undoManager.nonUndoableOp(insert("i", 1, 13)); undoManager.undoableOp(insert("i", 2, 14)); undoManager.nonUndoableOp(insert("i", 10, 15)); undoManager.undoableOp(insert("i", 6, 16)); undoManager.nonUndoableOp(insert("i", 1, 17)); undoManager.undoableOp(delete("i", 13, 17)); undoManager.undoableOp(delete("i", 3, 16)); undoManager.checkpoint(); undoManager.undoableOp(insert("i", 4, 16)); undoManager.nonUndoableOp(insert("i", 1, 17)); assertOpsEqual(delete("i", 5, 17), undoManager.undo()); undoManager.nonUndoableOp(insert("i", 1, 17)); assertOpsEqual(compose( delete("i", 8, 17), delete("j", 5, 12), removeParticipant("ww"), addParticipant("xxx"), addParticipant("zzz") ), undoManager.undo()); undoManager.nonUndoableOp(insert("i", 1, 17)); assertOpsEqual(compose( delete("i", 9, 17), delete("j", 6, 11), removeParticipant("x"), removeParticipant("z") ), undoManager.undo()); undoManager.nonUndoableOp(insert("i", 1, 17)); assertOpsEqual(compose( insert("i", 10, 18), insert("j", 6, 11), addParticipant("x"), addParticipant("z") ), undoManager.redo()); undoManager.nonUndoableOp(insert("i", 1, 19)); assertOpsEqual(compose( insert("i", 11, 20), insert("j", 5, 12), addParticipant("ww"), removeParticipant("xxx"), removeParticipant("zzz") ), undoManager.redo()); undoManager.nonUndoableOp(insert("i", 1, 21)); assertOpsEqual(insert("i", 10, 22), undoManager.redo()); } private static void assertOpsEqual(AggregateOperation op1, AggregateOperation op2) { assertTrue(areEqual(op1, op2)); } }