/** * 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.operation.wave; import junit.framework.TestCase; import org.waveprotocol.wave.model.document.operation.DocOp; import org.waveprotocol.wave.model.document.util.EmptyDocument; import org.waveprotocol.wave.model.operation.OperationException; import org.waveprotocol.wave.model.testing.BasicFactories; import org.waveprotocol.wave.model.testing.ModelTestUtils; import org.waveprotocol.wave.model.testing.WaveletDataFactory; import org.waveprotocol.wave.model.version.HashedVersion; import org.waveprotocol.wave.model.wave.Constants; import org.waveprotocol.wave.model.wave.ParticipantId; import org.waveprotocol.wave.model.wave.data.BlipData; import org.waveprotocol.wave.model.wave.data.impl.WaveletDataImpl; import java.util.List; /** * Test for WaveBlipOperation. * * @author zdwang@google.com (David Wang) */ public class VersionUpdateOpTest extends TestCase { private static final String ROOT_BLIP = "root"; private static final byte[] SIGNATURE = new byte[] { 4, 4, 4, 4 }; private static final byte[] SIGNATURE2 = new byte[] { 8, 8, 8, 8 }; private static final ParticipantId CREATOR = new ParticipantId("lars@gwave.com"); private static final ParticipantId FRED = new ParticipantId("fred@gwave.com"); /** * An operation context typical of a locally-generated op; doesn't update * metadata. */ private static final WaveletOperationContext LOCAL_CONTEXT = new WaveletOperationContext(CREATOR, Constants.NO_TIMESTAMP, 0); private WaveletDataImpl waveletData; @Override protected void setUp() throws Exception { super.setUp(); waveletData = WaveletDataFactory.of(BasicFactories.waveletDataImplFactory()).create(); } /** * Tests version update from a wavelet op ack. */ public void testVersionUpdateOpFromWaveletOpAck() throws OperationException { assertWaveletMetadataIsInitialState(); // Apply an op that has no version information. WaveletOperationContext context = LOCAL_CONTEXT; WaveletOperation op = addParticipant(context); assertWaveletMetadataIsInitialState(); // Create version update op from ack. WaveletOperation updateOp = op.createVersionUpdateOp(5, HashedVersion.of(5, SIGNATURE)); updateOp.apply(waveletData); assertWaveletVersion(5); assertWaveletSignature(HashedVersion.of(5, SIGNATURE)); } /** * Tests version update from a blip no-op ack. */ public void testVersionUpdateOpFromBlipNoOpAck() throws Exception { assertWaveletMetadataIsInitialState(); // Apply a local op that has no version information. WaveletOperation waveletOp = touchBlip(LOCAL_CONTEXT, ROOT_BLIP); assertWaveletMetadataIsInitialState(); // Create version update op from ack. WaveletOperation updateOp = waveletOp.createVersionUpdateOp(1, HashedVersion.of(1, SIGNATURE)); updateOp.apply(waveletData); assertWaveletVersion(1); assertWaveletSignature(HashedVersion.of(1, SIGNATURE)); assertBlipVersion(ROOT_BLIP, 0); } /** * Tests version update from a blip content operation ack. */ public void testVersionUpdateOpFromBlipContentOperationAck() throws Exception { assertWaveletMetadataIsInitialState(); // Apply a document mutation. WaveletOperation waveletOp = mutateBlip(LOCAL_CONTEXT, ROOT_BLIP); assertWaveletMetadataIsInitialState(); // Create version update op from ack. WaveletOperation updateOp = waveletOp.createVersionUpdateOp(1, HashedVersion.of(1, SIGNATURE)); updateOp.apply(waveletData); assertWaveletVersion(1); assertWaveletSignature(HashedVersion.of(1, SIGNATURE)); assertBlipVersion(ROOT_BLIP, 1); } /** * Test a version is set properly if there are version info in Submit */ public void testVersionUpdateOpFromSubmitAck() throws Exception { assertWaveletMetadataIsInitialState(); // Apply a submit. BlipOperation blipOp = new SubmitBlip(LOCAL_CONTEXT); WaveletOperation waveletOp = new WaveletBlipOperation(ROOT_BLIP, blipOp); waveletOp.apply(waveletData); assertWaveletMetadataIsInitialState(); // Create version update op from ack. WaveletOperation updateOp = waveletOp.createVersionUpdateOp(1, HashedVersion.of(1, SIGNATURE)); updateOp.apply(waveletData); assertWaveletVersion(1); assertWaveletSignature(HashedVersion.of(1, SIGNATURE)); assertBlipVersion(ROOT_BLIP, 0); } public void testMultipleVersionUpdates() throws OperationException { assertWaveletMetadataIsInitialState(); // First add participant. WaveletOperation addParticipantOp = addParticipant(LOCAL_CONTEXT); WaveletOperation addParticipantUpdateOp = addParticipantOp.createVersionUpdateOp(1, HashedVersion.of(1, SIGNATURE)); addParticipantUpdateOp.apply(waveletData); assertWaveletVersion(1); assertWaveletSignature(HashedVersion.of(1, SIGNATURE)); // Then mutate blip. WaveletOperation mutateBlipOp = mutateBlip(LOCAL_CONTEXT, ROOT_BLIP); WaveletOperation mutateBlipUpdateOp = mutateBlipOp.createVersionUpdateOp(1, HashedVersion.of(2, SIGNATURE2)); mutateBlipUpdateOp.apply(waveletData); assertWaveletVersion(2); assertWaveletSignature(HashedVersion.of(2, SIGNATURE2)); assertBlipVersion(ROOT_BLIP, 2); } /** * Test a version is set properly if we reverse the version op. */ public void testReverse() throws Exception { assertWaveletMetadataIsInitialState(); // Apply a document mutation. WaveletOperation waveletOp = mutateBlip(LOCAL_CONTEXT, ROOT_BLIP); assertWaveletMetadataIsInitialState(); // Create version update op from ack. WaveletOperation updateOp = waveletOp.createVersionUpdateOp(1, HashedVersion.of(1, SIGNATURE)); List<? extends WaveletOperation> reverse = updateOp.applyAndReturnReverse(waveletData); assertWaveletVersion(1); assertWaveletSignature(HashedVersion.of(1, SIGNATURE)); assertBlipVersion(ROOT_BLIP, 1); // apply the reverse and see the version is back to it was before assertEquals(1, reverse.size()); reverse.get(0).apply(waveletData); assertWaveletMetadataIsInitialState(); } private void assertWaveletVersion(long waveletVersion) { assertEquals(waveletVersion, waveletData.getVersion()); } private void assertWaveletSignature(HashedVersion distinctVersion) { assertEquals(distinctVersion, waveletData.getHashedVersion()); } private void assertBlipVersion(String blipId, long lastModifiedVersion) { assertEquals(lastModifiedVersion, waveletData.getDocument(blipId).getLastModifiedVersion()); } private void assertWaveletMetadataIsInitialState() { assertWaveletVersion(0); assertWaveletSignature(HashedVersion.unsigned(0)); BlipData blip = waveletData.getDocument(ROOT_BLIP); if (blip != null) { assertBlipVersion(ROOT_BLIP, 0); } } /** * Applies an op to add FRED to the wavelet with some context. * * @return the operation applied */ private WaveletOperation addParticipant(WaveletOperationContext context) throws OperationException { WaveletOperation waveletOp = new AddParticipant(context, FRED); waveletOp.apply(waveletData); return waveletOp; } /** * Applies an empty document op to a blip with some context. * * @return the operation applied */ private WaveletOperation touchBlip(WaveletOperationContext context, String blipId) throws OperationException { WaveletOperation waveletOp = new WaveletBlipOperation(blipId, new BlipContentOperation(context, EmptyDocument.EMPTY_DOCUMENT)); waveletOp.apply(waveletData); return waveletOp; } private WaveletOperation mutateBlip(WaveletOperationContext context, String blipId) throws OperationException { DocOp docOp = ModelTestUtils.createContent("Hello"); BlipOperation blipOp = new BlipContentOperation(LOCAL_CONTEXT, docOp); WaveletOperation waveletBlipOp = new WaveletBlipOperation(blipId, blipOp); waveletBlipOp.apply(waveletData); return waveletBlipOp; } }