/** * 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.examples.fedone.waveserver; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.protobuf.ByteString; import junit.framework.TestCase; import org.waveprotocol.wave.examples.fedone.common.HashedVersion; import org.waveprotocol.wave.examples.fedone.common.WaveletOperationSerializer; import static org.waveprotocol.wave.examples.fedone.common.WaveletOperationSerializer.serialize; import org.waveprotocol.wave.examples.fedone.model.util.HashedVersionZeroFactoryImpl; import org.waveprotocol.wave.federation.Proto.ProtocolSignature; import org.waveprotocol.wave.federation.Proto.ProtocolSignedDelta; import org.waveprotocol.wave.federation.Proto.ProtocolWaveletDelta; import org.waveprotocol.wave.model.document.operation.BufferedDocOp; import org.waveprotocol.wave.model.document.operation.impl.DocOpBuilder; import org.waveprotocol.wave.model.id.WaveId; import org.waveprotocol.wave.model.id.WaveletId; import org.waveprotocol.wave.model.id.WaveletName; import org.waveprotocol.wave.model.operation.OperationException; import org.waveprotocol.wave.model.operation.wave.AddParticipant; import org.waveprotocol.wave.model.operation.wave.RemoveParticipant; import org.waveprotocol.wave.model.operation.wave.WaveletDelta; import org.waveprotocol.wave.model.operation.wave.WaveletDocumentOperation; import org.waveprotocol.wave.model.operation.wave.WaveletOperation; import org.waveprotocol.wave.model.wave.ParticipantId; import java.util.Collections; import java.util.List; /** * Tests for local and remote wavelet containers. * * */ public class WaveletContainerTest extends TestCase { private static final String domain = "wave.google.com"; private static final WaveletName waveletName = WaveletName.of( new WaveId(domain, "waveid"), new WaveletId(domain, "waveletid")); private static final ParticipantId author = new ParticipantId("admin@" + domain); private static final List<ParticipantId> participants = ImmutableList.of( new ParticipantId("foo@" + domain), new ParticipantId("bar@example.com")); private static final HashedVersion version0 = new HashedVersionZeroFactoryImpl().createVersionZero(waveletName); private static final ByteString fakeSigner1 = ByteString.EMPTY; private static final ByteString fakeSigner2 = ByteString.copyFrom(new byte[] {1}); private static final ProtocolSignature fakeSignature1 = ProtocolSignature.newBuilder() .setSignatureBytes(ByteString.EMPTY) .setSignerId(fakeSigner1) .setSignatureAlgorithm(ProtocolSignature.SignatureAlgorithm.SHA1_RSA) .build(); private static final ProtocolSignature fakeSignature2 = ProtocolSignature.newBuilder() .setSignatureBytes(ByteString.copyFrom(new byte[] {1})) .setSignerId(fakeSigner2) .setSignatureAlgorithm(ProtocolSignature.SignatureAlgorithm.SHA1_RSA) .build(); private List<WaveletOperation> addParticipantOps; private List<WaveletOperation> removeParticipantOps; private List<WaveletOperation> doubleRemoveParticipantOps; private ProtocolWaveletDelta addParticipantDelta; private ProtocolWaveletDelta removeParticipantDelta; private ProtocolWaveletDelta doubleRemoveParticipantDelta; private LocalWaveletContainerImpl localWavelet; private RemoteWaveletContainerImpl remoteWavelet; @Override public void setUp() throws Exception { super.setUp(); localWavelet = new LocalWaveletContainerImpl(waveletName); remoteWavelet = new RemoteWaveletContainerImpl(waveletName); addParticipantOps = Lists.newArrayList(); removeParticipantOps = Lists.newArrayList(); for (ParticipantId p : participants) { addParticipantOps.add(new AddParticipant(p)); removeParticipantOps.add(new RemoveParticipant(p)); } Collections.reverse(removeParticipantOps); doubleRemoveParticipantOps = Lists.newArrayList(removeParticipantOps); doubleRemoveParticipantOps.addAll(removeParticipantOps); addParticipantDelta = WaveletOperationSerializer.serialize( new WaveletDelta(author, addParticipantOps), version0); removeParticipantDelta = WaveletOperationSerializer.serialize( new WaveletDelta(author, removeParticipantOps), version0); doubleRemoveParticipantDelta = WaveletOperationSerializer.serialize( new WaveletDelta(author, doubleRemoveParticipantOps), version0); } // Tests public void testLocalApplyWaveletOperation() throws Exception { assertSuccessfulApplyWaveletOperations(localWavelet); } public void testRemoteApplyWaveletOperation() throws Exception { assertSuccessfulApplyWaveletOperations(remoteWavelet); } public void testLocalFailedWaveletOperations() throws Exception { assertFailedWaveletOperations(localWavelet); } public void testRemoteFailedWaveletOperations() throws Exception { assertFailedWaveletOperations(localWavelet); } public void testSuccessfulLocalRequest() throws Exception { ProtocolSignedDelta addDelta = ProtocolSignedDelta.newBuilder() .addSignature(fakeSignature1) .setDelta(addParticipantDelta.toByteString()) .build(); localWavelet.submitRequest(waveletName, addDelta); assertEquals(localWavelet.getCurrentVersion().getVersion(), 2); assertTrue(localWavelet.isDeltaSigner( serialize(localWavelet.getCurrentVersion()),fakeSigner1)); assertFalse(localWavelet.isDeltaSigner( serialize(localWavelet.getCurrentVersion()), fakeSigner2)); HashedVersion oldVersion = localWavelet.getCurrentVersion(); ProtocolSignedDelta removeDelta = ProtocolSignedDelta.newBuilder() .addSignature(fakeSignature2) .setDelta(ProtocolWaveletDelta.newBuilder(removeParticipantDelta).setHashedVersion( serialize(localWavelet.getCurrentVersion())).build().toByteString()) .build(); localWavelet.submitRequest(waveletName, removeDelta); assertEquals(localWavelet.getCurrentVersion().getVersion(), 4); assertTrue(localWavelet.isDeltaSigner(serialize(oldVersion), fakeSigner1)); assertFalse(localWavelet.isDeltaSigner(serialize(oldVersion), fakeSigner2)); assertTrue(localWavelet.isDeltaSigner( serialize(localWavelet.getCurrentVersion()), fakeSigner2)); assertFalse(localWavelet.isDeltaSigner( serialize(localWavelet.getCurrentVersion()), fakeSigner1)); } public void testFailedLocalWaveletRequest() throws Exception { ProtocolSignedDelta removeDelta = ProtocolSignedDelta.newBuilder() .addSignature(fakeSignature1) .setDelta(removeParticipantDelta.toByteString()) .build(); try { localWavelet.submitRequest(waveletName, removeDelta); fail("Should fail"); } catch (OperationException e) { // Correct } assertEquals(localWavelet.getCurrentVersion(), version0); ProtocolSignedDelta addDelta = ProtocolSignedDelta.newBuilder() .addSignature(fakeSignature1) .setDelta(addParticipantDelta.toByteString()) .build(); localWavelet.submitRequest(waveletName, addDelta); try { ProtocolSignedDelta addAgainDelta = ProtocolSignedDelta.newBuilder() .addSignature(fakeSignature2) .setDelta(ProtocolWaveletDelta.newBuilder(addParticipantDelta) .setHashedVersion(serialize(localWavelet.getCurrentVersion())) .build().toByteString()) .build(); localWavelet.submitRequest(waveletName, addAgainDelta); fail("Should fail"); } catch (OperationException e) { // Correct } assertEquals(localWavelet.getCurrentVersion().getVersion(), 2); assertTrue(localWavelet.isDeltaSigner( serialize(localWavelet.getCurrentVersion()), fakeSigner1)); assertFalse(localWavelet.isDeltaSigner( serialize(localWavelet.getCurrentVersion()), fakeSigner2)); HashedVersion oldVersion = localWavelet.getCurrentVersion(); ProtocolSignedDelta rollbackDelta = ProtocolSignedDelta.newBuilder() .addSignature(fakeSignature1) .setDelta(ProtocolWaveletDelta.newBuilder(doubleRemoveParticipantDelta) .setHashedVersion(serialize(localWavelet.getCurrentVersion())) .build().toByteString()) .build(); try { localWavelet.submitRequest(waveletName, rollbackDelta); fail("Should fail"); } catch (OperationException e) { // Correct } assertEquals(localWavelet.getCurrentVersion(), oldVersion); } public void testLocalEmptyDelta() throws Exception { ProtocolSignedDelta emptyDelta = ProtocolSignedDelta.newBuilder() .addSignature(fakeSignature1) .setDelta(ProtocolWaveletDelta.newBuilder() .setAuthor(author.toString()) .setHashedVersion(serialize(version0)) .build().toByteString()) .build(); try { localWavelet.submitRequest(waveletName, emptyDelta); fail("Should fail"); } catch (EmptyDeltaException e) { // Correct } } public void testOperationsOfDifferentSizes() throws EmptyDeltaException, OperationException { String docId = "b+somedoc"; BufferedDocOp docOp1 = new DocOpBuilder().characters("hi").build(); BufferedDocOp docOp2 = new DocOpBuilder().characters("bye").build(); localWavelet.applyWaveletOperations(ImmutableList.<WaveletOperation> of( new WaveletDocumentOperation(docId, docOp1))); try { // Version will still be 0 (applyWaveletOperations doesn't affect it) so "hi" and "bye" // won't compose properly. localWavelet.applyWaveletOperations(ImmutableList.<WaveletOperation> of( new WaveletDocumentOperation(docId, docOp2))); fail("Composition of \"hi\" and \"bye\" did not throw OperationException"); } catch (OperationException expected) { // Correct } } // Utilities /** * Check that a container succeeds when adding non-existent participants and removing existing * participants. */ private void assertSuccessfulApplyWaveletOperations(WaveletContainerImpl with) throws Exception { with.applyWaveletOperations(addParticipantOps); assertEquals(with.getParticipants(), participants); with.applyWaveletOperations(removeParticipantOps); assertEquals(with.getParticipants(), Collections.emptyList()); } /** * Check that a container fails when removing non-existent participants and adding duplicate * participants, and that the partipant list is preserved correctly. */ private void assertFailedWaveletOperations(WaveletContainerImpl with) throws Exception { try { with.applyWaveletOperations(removeParticipantOps); fail("Should fail"); } catch (OperationException e) { // Correct } assertEquals(localWavelet.getParticipants(), Collections.emptyList()); with.applyWaveletOperations(addParticipantOps); try { with.applyWaveletOperations(addParticipantOps); fail("Should fail"); } catch (OperationException e) { // Correct } assertEquals(with.getParticipants(), participants); try { with.applyWaveletOperations(doubleRemoveParticipantOps); fail("Should fail"); } catch (OperationException e) { // Correct } assertEquals(with.getParticipants(), participants); } }