/**
* Copyright 2010 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 org.waveprotocol.wave.model.operation.wave.AddParticipant;
import org.waveprotocol.wave.model.operation.wave.BlipContentOperation;
import org.waveprotocol.wave.model.operation.wave.WaveletBlipOperation;
import org.waveprotocol.wave.model.operation.wave.WaveletOperation;
import org.waveprotocol.wave.model.operation.wave.WaveletOperationContext;
import junit.framework.TestCase;
import org.waveprotocol.wave.model.document.operation.DocOp;
import org.waveprotocol.wave.model.document.operation.impl.DocOpBuilder;
import org.waveprotocol.wave.model.operation.OperationPair;
import org.waveprotocol.wave.model.operation.TransformException;
import org.waveprotocol.wave.model.operation.core.CoreWaveletDocumentOperation;
import org.waveprotocol.wave.model.wave.ParticipantId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
*/
public class WaveAggregateOpTest extends TestCase {
private static final String CREATOR1_ID = "creator1";
private static final String CREATOR2_ID = "creator2";
private static final String TARGET1 = "target1";
private static final String TARGET2 = "target2";
private static final WaveletOperationContext CREATOR1_CONTEXT =
new WaveletOperationContext(new ParticipantId(CREATOR1_ID), 0, 0);
private static final WaveletOperationContext CREATOR2_CONTEXT =
new WaveletOperationContext(new ParticipantId(CREATOR2_ID), 0, 0);
private DocOp insertDocOp(int location, int size) {
return new DocOpBuilder()
.retain(location)
.characters("a")
.retain(size - location)
.build();
}
private DocOp deleteDocOp(int location, int size) {
return new DocOpBuilder()
.retain(location)
.deleteCharacters("a")
.retain(size - location)
.build();
}
public void testCompose() {
{
WaveAggregateOp op1 = new WaveOpBuilder(CREATOR1_ID).addParticipant(TARGET1).build();
WaveAggregateOp op2 = new WaveOpBuilder(CREATOR2_ID).addParticipant(TARGET2).build();
WaveAggregateOp composed = compose(op1, op2);
List<WaveletOperation> expected = Arrays.<WaveletOperation>asList(
new AddParticipant(CREATOR1_CONTEXT, new ParticipantId(TARGET1)),
new AddParticipant(CREATOR2_CONTEXT, new ParticipantId(TARGET2))
);
assertEquals(expected, composed.toWaveletOperations());
}
{
DocOp insertDocOp = insertDocOp(1, 5);
WaveAggregateOp op1 = new WaveOpBuilder(CREATOR1_ID).addParticipant(TARGET1).build();
WaveAggregateOp op2 = new WaveOpBuilder(CREATOR2_ID).docOp("doc", insertDocOp).build();
WaveAggregateOp composed = compose(op1, op2);
List<WaveletOperation> expected = Arrays.<WaveletOperation>asList(
new AddParticipant(CREATOR1_CONTEXT, new ParticipantId(TARGET1)),
new WaveletBlipOperation("doc", new BlipContentOperation(CREATOR2_CONTEXT, insertDocOp))
);
assertEquals(expected, composed.toWaveletOperations());
}
// Test compose of document op from the same creator
{
DocOp insertDocOp1 = insertDocOp(1, 3);
DocOp insertDocOp2 = insertDocOp(3, 4);
DocOp deleteDocOp = deleteDocOp(2, 5);
WaveAggregateOp op1 = new WaveOpBuilder(CREATOR1_ID).docOp("doc", insertDocOp1).build();
WaveAggregateOp op2 = new WaveOpBuilder(CREATOR1_ID)
.docOp("doc", insertDocOp2)
.docOp("doc", deleteDocOp)
.build();
WaveAggregateOp composed = compose(op1, op2);
DocOp expectedDocOp = new DocOpBuilder()
.retain(1)
.characters("a")
.deleteCharacters("a")
.characters("a")
.retain(1)
.build();
List<WaveletOperation> expected = Arrays.<WaveletOperation>asList(
new WaveletBlipOperation("doc", new BlipContentOperation(CREATOR1_CONTEXT, expectedDocOp))
);
assertEquals(expected, composed.toWaveletOperations());
}
}
public void testTransform() {
// Test that orthogonal ops are preserved.
{
WaveAggregateOp op1 = new WaveOpBuilder(CREATOR1_ID).addParticipant(TARGET1).build();
WaveAggregateOp op2 = new WaveOpBuilder(CREATOR2_ID).addParticipant(TARGET2).build();
OperationPair<WaveAggregateOp> transformed;
try {
transformed = WaveAggregateOp.transform(op1, op2);
} catch (TransformException e) {
fail("transform failed:" + e);
return;
}
List<WaveletOperation> expectedClient = Arrays.<WaveletOperation> asList(
new AddParticipant(CREATOR1_CONTEXT, new ParticipantId(TARGET1)));
List<WaveletOperation> expectedServer = Arrays.<WaveletOperation> asList(
new AddParticipant(CREATOR2_CONTEXT, new ParticipantId(TARGET2)));
assertEquals(expectedClient, transformed.clientOp().toWaveletOperations());
assertEquals(expectedServer, transformed.serverOp().toWaveletOperations());
}
// Test case where client ops are transformed away, and some server ops are kept
{
WaveAggregateOp op1 = new WaveOpBuilder(CREATOR1_ID).addParticipant(TARGET1).build();
WaveAggregateOp op2 =
new WaveOpBuilder(CREATOR2_ID).addParticipant(TARGET1).addParticipant(TARGET2).build();
OperationPair<WaveAggregateOp> transformed;
try {
transformed = WaveAggregateOp.transform(op1, op2);
} catch (TransformException e) {
fail("transform failed:" + e);
return;
}
List<WaveletOperation> expectedServer =
Arrays.<WaveletOperation> asList(new AddParticipant(CREATOR2_CONTEXT, new ParticipantId(
TARGET2)));
assertEquals(0, transformed.clientOp().toWaveletOperations().size());
assertEquals(expectedServer, transformed.serverOp().toWaveletOperations());
}
// Same as above, but server ops originate from 2 different creators. Test
// that the creators are preserved correctly through transform and
// composition.
{
WaveAggregateOp op1 = new WaveOpBuilder(CREATOR1_ID).addParticipant(TARGET1).build();
WaveAggregateOp op2a =
new WaveOpBuilder(CREATOR2_ID).addParticipant(TARGET1).build();
WaveAggregateOp op2b = new WaveOpBuilder(CREATOR1_ID).addParticipant(TARGET2).build();
WaveAggregateOp op2 = compose(op2a, op2b);
OperationPair<WaveAggregateOp> transformed;
try {
transformed = WaveAggregateOp.transform(op1, op2);
} catch (TransformException e) {
fail("transform failed:" + e);
return;
}
List<WaveletOperation> expectedServer =
Arrays.<WaveletOperation> asList(new AddParticipant(CREATOR1_CONTEXT, new ParticipantId(
TARGET2)));
assertEquals(0, transformed.clientOp().toWaveletOperations().size());
assertEquals(expectedServer, transformed.serverOp().toWaveletOperations());
}
}
private static class WaveOpBuilder {
final ParticipantId creator;
List<AggregateOperation> ops = new ArrayList<AggregateOperation>();
WaveOpBuilder(String userName) {
creator = new ParticipantId(userName);
}
public WaveOpBuilder docOp(String docId, DocOp docOp) {
ops.add(new AggregateOperation(new CoreWaveletDocumentOperation(docId, docOp)));
return this;
}
WaveOpBuilder aggregateOp(AggregateOperation op) {
ops.add(op);
return this;
}
WaveOpBuilder addParticipant(String participantId) {
ops.add(AggregateOpTestUtil.addParticipant(participantId));
return this;
}
WaveOpBuilder removeParticipant(String participantId) {
ops.add(AggregateOpTestUtil.removeParticipant(participantId));
return this;
}
WaveOpBuilder insert(String id, int location, int size) {
ops.add(AggregateOpTestUtil.insert(id, location, size));
return this;
}
WaveOpBuilder delete(String id, int location, int size) {
ops.add(AggregateOpTestUtil.delete(id, location, size));
return this;
}
WaveAggregateOp build() {
return new WaveAggregateOp(AggregateOperation.compose(ops), creator);
}
}
private static WaveAggregateOp compose(WaveAggregateOp... ops) {
return WaveAggregateOp.compose(Arrays.asList(ops));
}
private void assertEquals(List<WaveletOperation> ops1, List<WaveletOperation> ops2) {
if (ops1.size() != ops2.size()) {
fail("size differ: " + "(" + ops1.size() + ", " + ops2.size() + ") " + ops1 + " " + ops2);
}
for (int i = 0; i < ops1.size(); ++i) {
assertEquals(ops1.get(i), ops2.get(i));
}
}
private void assertEquals(WaveletOperation op1, WaveletOperation op2) {
if (!haveSameCreator(op1, op2)) {
fail("creator differ ( " + op1.getContext().getCreator() + ", "
+ op2.getContext().getCreator() + ") ");
}
if (!op1.equals(op2)) {
fail("op differ ( " + op1 + ", " + op2);
}
}
private boolean haveSameCreator(WaveletOperation op1, WaveletOperation op2) {
return op1.getContext().getCreator().equals(op2.getContext().getCreator());
}
}