/**
* Copyright 2009 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.document.util;
import org.waveprotocol.wave.model.undo.UndoManager;
import org.waveprotocol.wave.model.undo.UndoManagerFactory;
import org.waveprotocol.wave.model.undo.UndoManagerPlus;
import junit.framework.TestCase;
import org.waveprotocol.wave.model.document.operation.DocOp;
import org.waveprotocol.wave.model.document.operation.algorithm.Composer;
import org.waveprotocol.wave.model.document.operation.impl.DocOpBuilder;
import org.waveprotocol.wave.model.operation.OpComparators;
import org.waveprotocol.wave.model.util.Pair;
import java.util.Arrays;
import java.util.List;
/**
* Tests for UndoManager.
*
*/
public class UndoManagerTest extends TestCase {
public void testUndoRedo() {
UndoManager<DocOp> undoManager = UndoManagerFactory.createUndoManager();
undoManager.checkpoint();
undoManager.undoableOp(insert(3, 10));
undoManager.checkpoint();
undoManager.undoableOp(insert(5, 11));
undoManager.checkpoint();
undoManager.undoableOp(insert(4, 12));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(4, 12), undoManager.undo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(5, 11), undoManager.undo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(3, 10), undoManager.undo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(3, 10), undoManager.redo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(5, 11), undoManager.redo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(4, 12), undoManager.redo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(4, 12), undoManager.undo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(5, 11), undoManager.undo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(3, 10), undoManager.undo()));
}
public void testUndoRedoWithNonundoableOps() {
UndoManager<DocOp> undoManager = UndoManagerFactory.createUndoManager();
undoManager.checkpoint();
undoManager.undoableOp(insert(3, 10));
undoManager.nonUndoableOp(insert(1, 11));
undoManager.checkpoint();
undoManager.undoableOp(insert(5, 12));
undoManager.nonUndoableOp(insert(1, 13));
undoManager.checkpoint();
undoManager.undoableOp(insert(4, 14));
undoManager.nonUndoableOp(insert(1, 15));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(5, 15), undoManager.undo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(7, 14), undoManager.undo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(6, 13), undoManager.undo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(6, 13), undoManager.redo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(7, 14), undoManager.redo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(5, 15), undoManager.redo()));
}
public void testUndoRedoWithConsecutiveNonundoableOps() {
UndoManager<DocOp> undoManager = UndoManagerFactory.createUndoManager();
undoManager.checkpoint();
undoManager.undoableOp(insert(3, 10));
undoManager.nonUndoableOp(insert(1, 11));
undoManager.nonUndoableOp(insert(2, 12));
undoManager.checkpoint();
undoManager.undoableOp(insert(6, 13));
undoManager.nonUndoableOp(delete(10, 13));
undoManager.nonUndoableOp(insert(1, 13));
undoManager.nonUndoableOp(insert(10, 14));
undoManager.checkpoint();
undoManager.undoableOp(insert(5, 15));
undoManager.nonUndoableOp(insert(1, 16));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(6, 16), undoManager.undo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(8, 15), undoManager.undo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(7, 14), undoManager.undo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(7, 14), undoManager.redo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(8, 15), undoManager.redo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(6, 16), undoManager.redo()));
}
public void testUndoRedoInterspersedWithNonundoableOps() {
UndoManager<DocOp> undoManager = UndoManagerFactory.createUndoManager();
undoManager.checkpoint();
undoManager.undoableOp(insert(3, 10));
undoManager.nonUndoableOp(insert(1, 11));
undoManager.checkpoint();
undoManager.undoableOp(insert(5, 12));
undoManager.nonUndoableOp(insert(1, 13));
undoManager.checkpoint();
undoManager.undoableOp(insert(4, 14));
undoManager.nonUndoableOp(insert(1, 15));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(5, 15), undoManager.undo()));
undoManager.nonUndoableOp(insert(1, 15));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(8, 15), undoManager.undo()));
undoManager.nonUndoableOp(insert(1, 15));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(8, 15), undoManager.undo()));
undoManager.nonUndoableOp(insert(1, 15));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(9, 16), undoManager.redo()));
undoManager.nonUndoableOp(insert(1, 17));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(11, 18), undoManager.redo()));
undoManager.nonUndoableOp(insert(1, 19));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(10, 20), undoManager.redo()));
}
public void testUndoRedoWithNondenseCheckpointing() {
UndoManager<DocOp> undoManager = UndoManagerFactory.createUndoManager();
undoManager.checkpoint();
undoManager.undoableOp(insert(3, 10));
undoManager.nonUndoableOp(insert(1, 11));
undoManager.checkpoint();
undoManager.undoableOp(insert(8, 12));
undoManager.nonUndoableOp(insert(1, 13));
undoManager.undoableOp(insert(2, 14));
undoManager.nonUndoableOp(insert(10, 15));
undoManager.undoableOp(insert(6, 16));
undoManager.nonUndoableOp(insert(1, 17));
undoManager.undoableOp(delete(13, 17));
undoManager.undoableOp(delete(3, 16));
undoManager.checkpoint();
undoManager.undoableOp(insert(4, 16));
undoManager.nonUndoableOp(insert(1, 17));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(5, 17), undoManager.undo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(7, 16), undoManager.undo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(7, 15), undoManager.undo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(7, 15), undoManager.redo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(7, 16), undoManager.redo()));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(5, 17), undoManager.redo()));
}
public void testUndoRedoWithNondenseCheckpointsInterspersedWithNonundoableOps() {
UndoManager<DocOp> undoManager = UndoManagerFactory.createUndoManager();
undoManager.checkpoint();
undoManager.undoableOp(insert(3, 10));
undoManager.nonUndoableOp(insert(1, 11));
undoManager.checkpoint();
undoManager.undoableOp(insert(8, 12));
undoManager.nonUndoableOp(insert(1, 13));
undoManager.undoableOp(insert(2, 14));
undoManager.nonUndoableOp(insert(10, 15));
undoManager.undoableOp(insert(6, 16));
undoManager.nonUndoableOp(insert(1, 17));
undoManager.undoableOp(delete(13, 17));
undoManager.undoableOp(delete(3, 16));
undoManager.checkpoint();
undoManager.undoableOp(insert(4, 16));
undoManager.nonUndoableOp(insert(1, 17));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(5, 17), undoManager.undo()));
undoManager.nonUndoableOp(insert(1, 17));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(8, 17), undoManager.undo()));
undoManager.nonUndoableOp(insert(1, 17));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(9, 17), undoManager.undo()));
undoManager.nonUndoableOp(insert(1, 17));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(10, 18), undoManager.redo()));
undoManager.nonUndoableOp(insert(1, 19));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(11, 20), undoManager.redo()));
undoManager.nonUndoableOp(insert(1, 21));
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(10, 22), undoManager.redo()));
}
public void testPlusMethods() {
UndoManagerPlus<DocOp> undoManager = UndoManagerFactory.createUndoManager();
undoManager.checkpoint();
undoManager.undoableOp(insert(3, 10));
undoManager.nonUndoableOp(insert(1, 11));
undoManager.checkpoint();
undoManager.undoableOp(insert(8, 12));
undoManager.nonUndoableOp(insert(1, 13));
undoManager.undoableOp(insert(2, 14));
undoManager.nonUndoableOp(insert(10, 15));
undoManager.undoableOp(insert(6, 16));
undoManager.nonUndoableOp(insert(1, 17));
undoManager.undoableOp(delete(13, 17));
undoManager.undoableOp(delete(3, 16));
undoManager.checkpoint();
undoManager.undoableOp(insert(4, 16));
undoManager.nonUndoableOp(insert(1, 17));
Pair<DocOp, DocOp> pair = undoManager.undoPlus();
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(5, 17), pair.first));
compareDocOpList(Arrays.asList(
insert(1, 16)
), pair.second);
undoManager.nonUndoableOp(insert(1, 17));
pair = undoManager.undoPlus();
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(8, 17), pair.first));
compareDocOpList(Arrays.asList(
insert(1, 12),
insert(9, 13),
insert(1, 14),
insert(1, 15),
insert(1, 16)
), pair.second);
undoManager.nonUndoableOp(insert(1, 17));
pair = undoManager.undoPlus();
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(delete(9, 17), pair.first));
compareDocOpList(Arrays.asList(
insert(1, 10),
insert(1, 11),
insert(8, 12),
insert(1, 13),
insert(1, 14),
insert(1, 15),
insert(1, 16)
), pair.second);
undoManager.nonUndoableOp(insert(1, 17));
pair = undoManager.redoPlus();
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(10, 18), pair.first));
compareDocOpList(Arrays.asList(
insert(1, 18)
), pair.second);
undoManager.nonUndoableOp(insert(1, 19));
pair = undoManager.redoPlus();
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(11, 20), pair.first));
compareDocOpList(Arrays.asList(
insert(1, 18),
insert(1, 19),
insert(1, 20)
), pair.second);
undoManager.nonUndoableOp(insert(1, 21));
pair = undoManager.redoPlus();
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(insert(10, 22), pair.first));
compareDocOpList(Arrays.asList(
insert(1, 18),
insert(1, 19),
insert(1, 20),
insert(1, 21),
insert(1, 22)
), pair.second);
}
private static void compareDocOpList(List<DocOp> ops, DocOp op) {
assertTrue(OpComparators.SYNTACTIC_IDENTITY.equal(Composer.compose(ops), op));
}
private static DocOp insert(int location, int size) {
return new DocOpBuilder()
.retain(location)
.characters("a")
.retain(size - location)
.build();
}
private static DocOp delete(int location, int size) {
return new DocOpBuilder()
.retain(location)
.deleteCharacters("a")
.retain(size - location)
.build();
}
}