http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/conversation/testing/FakeConversationView.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/conversation/testing/FakeConversationView.java b/wave/src/test/java/org/waveprotocol/wave/model/conversation/testing/FakeConversationView.java new file mode 100644 index 0000000..85b2fd9 --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/conversation/testing/FakeConversationView.java @@ -0,0 +1,159 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.conversation.testing; + +import org.waveprotocol.wave.model.conversation.ObservableConversationView; +import org.waveprotocol.wave.model.conversation.WaveBasedConversationView; +import org.waveprotocol.wave.model.conversation.WaveletBasedConversation; +import org.waveprotocol.wave.model.id.IdGenerator; +import org.waveprotocol.wave.model.id.WaveId; +import org.waveprotocol.wave.model.schema.SchemaProvider; +import org.waveprotocol.wave.model.schema.conversation.ConversationSchemas; +import org.waveprotocol.wave.model.testing.FakeIdGenerator; +import org.waveprotocol.wave.model.testing.FakeWaveView; +import org.waveprotocol.wave.model.wave.ParticipantId; +import org.waveprotocol.wave.model.wave.data.DocumentFactory; +import org.waveprotocol.wave.model.wave.opbased.ObservableWaveView; + +import java.util.Collection; + +/** + * A fake conversation view. The view is fully functioning but not attached to + * any communication channels. + * + * @author [email protected] (Alex North) + */ +public final class FakeConversationView implements ObservableConversationView { + + private final static SchemaProvider DEFAULT_SCHEMAS = new ConversationSchemas(); + + public final static class Builder { + private SchemaProvider schemas; + private IdGenerator idGenerator; + private WaveId waveId; + private ParticipantId viewer; + private DocumentFactory<?> docFactory; + + private Builder() { + } + + public Builder with(DocumentFactory<?> docFactory) { + this.docFactory = docFactory; + return this; + } + + public Builder with(SchemaProvider schemas) { + this.schemas = schemas; + return this; + } + + public Builder with(IdGenerator idGenerator) { + this.idGenerator = idGenerator; + return this; + } + + public Builder with(WaveId wid) { + this.waveId = wid; + return this; + } + + public Builder with(ParticipantId viewer) { + this.viewer = viewer; + return this; + } + + public FakeConversationView build() { + if (schemas == null) { + schemas = DEFAULT_SCHEMAS; + } + if (idGenerator == null) { + idGenerator = FakeIdGenerator.create(); + } + if (waveId == null) { + waveId = idGenerator.newWaveId(); + } + + FakeWaveView waveView = FakeWaveView.builder(schemas) // \u2620 + .with(docFactory) // \u2620 + .with(idGenerator) // \u2620 + .with(waveId) // \u2620 + .with(viewer) // \u2620 + .build(); + + return new FakeConversationView(WaveBasedConversationView.create(waveView, idGenerator)); + } + } + + /** Creates a new conversation view builder. */ + public static Builder builder() { + return new Builder(); + } + + /** The backing conversation view. */ + private final WaveBasedConversationView view; + + private FakeConversationView(WaveBasedConversationView view) { + this.view = view; + } + + @Override + public String getId() { + return view.getId(); + } + + @Override + public WaveletBasedConversation createConversation() { + return view.createConversation(); + } + + @Override + public WaveletBasedConversation createRoot() { + return view.createRoot(); + } + + @Override + public WaveletBasedConversation getConversation(String conversationId) { + return view.getConversation(conversationId); + } + + @Override + public Collection<? extends WaveletBasedConversation> getConversations() { + return view.getConversations(); + } + + @Override + public WaveletBasedConversation getRoot() { + return view.getRoot(); + } + + @Override + public void addListener(Listener listener) { + view.addListener(listener); + } + + @Override + public void removeListener(Listener listener) { + view.removeListener(listener); + } + + public ObservableWaveView getWaveView() { + return view.getWaveView(); + } +}
http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/testing/BasicFactories.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/testing/BasicFactories.java b/wave/src/test/java/org/waveprotocol/wave/model/testing/BasicFactories.java new file mode 100644 index 0000000..f23e93b --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/testing/BasicFactories.java @@ -0,0 +1,239 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.testing; + +import org.waveprotocol.wave.model.document.Document; +import org.waveprotocol.wave.model.document.ObservableDocument; +import org.waveprotocol.wave.model.document.indexed.IndexedDocument; +import org.waveprotocol.wave.model.document.operation.DocInitialization; +import org.waveprotocol.wave.model.document.operation.DocOp; +import org.waveprotocol.wave.model.document.operation.automaton.DocumentSchema; +import org.waveprotocol.wave.model.document.raw.impl.Element; +import org.waveprotocol.wave.model.document.raw.impl.Node; +import org.waveprotocol.wave.model.document.raw.impl.Text; +import org.waveprotocol.wave.model.document.util.DocProviders; +import org.waveprotocol.wave.model.document.util.DocumentImpl; +import org.waveprotocol.wave.model.document.util.DocumentProvider; +import org.waveprotocol.wave.model.operation.SilentOperationSink; +import org.waveprotocol.wave.model.schema.SchemaCollection; +import org.waveprotocol.wave.model.schema.SchemaProvider; +import org.waveprotocol.wave.model.util.Preconditions; +import org.waveprotocol.wave.model.wave.data.DocumentFactory; +import org.waveprotocol.wave.model.wave.data.MuteDocumentFactory; +import org.waveprotocol.wave.model.wave.data.impl.ObservablePluggableMutableDocument; +import org.waveprotocol.wave.model.wave.data.impl.PluggableMutableDocument; +import org.waveprotocol.wave.model.wave.data.impl.WaveletDataImpl; + +import java.util.Map; + +/** + * Static factory for creating various document factories and builders whose + * document schemas come from the provider set using + * {@link #setSchemaProvider(SchemaProvider)}. If no provider is set the empty + * provider is used. This should only be used for tests of the core model. + * + */ +public class BasicFactories { + + /** + * Provider of {@link Document}s based on the {@link DocProviders#POJO} DOM + * implementation. + */ + private static final DocumentProvider<Document> DOC_PROVIDER = new DocumentProvider<Document>() { + @Override + public Document create(String tagName, Map<String, String> attributes) { + IndexedDocument<Node, Element, Text> doc = DocProviders.POJO.create(tagName, attributes); + return new DocumentImpl(DocProviders.createTrivialSequencer(doc), doc); + } + + @Override + public Document parse(String text) { + IndexedDocument<Node, Element, Text> doc = DocProviders.POJO.parse(text); + return new DocumentImpl(DocProviders.createTrivialSequencer(doc), doc); + } + }; + + /** + * Provider of {@link ObservableDocument}s based on the + * {@link DocProviders#POJO} DOM implementation and a trivial sequence. + */ + private static final DocumentProvider<ObservablePluggableMutableDocument> OBS_DOC_PROVIDER = + new DocumentProvider<ObservablePluggableMutableDocument>() { + @Override + public ObservablePluggableMutableDocument create( + String tagName, Map<String, String> attributes) { + // FIXME(ohler): this is inefficient. + return build(DocProviders.POJO.create(tagName, attributes).asOperation()); + } + + @Override + public ObservablePluggableMutableDocument parse(String text) { + // FIXME(ohler): this is inefficient. + return build(DocProviders.POJO.parse(text).asOperation()); + } + + private ObservablePluggableMutableDocument build(DocInitialization init) { + ObservablePluggableMutableDocument doc = + new ObservablePluggableMutableDocument(DocumentSchema.NO_SCHEMA_CONSTRAINTS, init); + doc.init(SilentOperationSink.VOID); + return doc; + } + }; + + private static SchemaProvider schemas = SchemaCollection.empty(); + + /** + * Sets the schema provider that will provide schemas for the factories + * returned from the methods of this class. + */ + public static void setSchemaProvider(SchemaProvider value) { + schemas = value; + } + + /** + * Returns the current schema provider. + */ + protected static SchemaProvider getSchemas() { + return schemas; + } + + /** + * Returns a new fake wave view builder whose document schemas comes from the + * current provider. + */ + public static FakeWaveView.Builder fakeWaveViewBuilder() { + return FakeWaveView.builder(getSchemas()); + } + + /** + * Returns a new op-based wavelet factory builder whose document schemas comes + * from the current provider. + */ + public static OpBasedWaveletFactory.Builder opBasedWaveletFactoryBuilder() { + return OpBasedWaveletFactory.builder(getSchemas()); + } + + /** + * Returns a new wavelet data impl factory whose document schemas comes from + * the current provider. + */ + public static WaveletDataImpl.Factory waveletDataImplFactory() { + return WaveletDataImpl.Factory.create(observablePluggableMutableDocumentFactory()); + } + + /** + * Returns a mute document factory whose document schemas comes from the + * current provider. + */ + public static MuteDocumentFactory muteDocumentFactory() { + return new MuteDocumentFactory(getSchemas()); + } + + /** + * Returns a fake document factory whose document schemas comes from the + * current provider. + */ + public static FakeDocument.Factory fakeDocumentFactory() { + return FakeDocument.Factory.create(getSchemas()); + } + + /** + * Returns a plugable mutable document factory whose document schemas comes + * from the current provider. + */ + public static DocumentFactory<? extends PluggableMutableDocument> + pluggableMutableDocumentFactory() { + return PluggableMutableDocument.createFactory(getSchemas()); + } + + /** + * Returns an observable pluggable mutable document factory whose document + * schemas comes from the current provider. + */ + public static DocumentFactory<? extends ObservablePluggableMutableDocument> + observablePluggableMutableDocumentFactory() { + return ObservablePluggableMutableDocument.createFactory(getSchemas()); + } + + /** + * Returns a provider of {@link Document}s. + * + * Provided documents have no schema constraints: consider using + * {@link MuteDocumentFactory} instead. + * + * TODO(anorth): Remove this method in favor of one specifying a schema. + */ + public static DocumentProvider<Document> documentProvider() { + return DOC_PROVIDER; + } + + /** + * Returns a provider of observable mutable documents. + * + * Provided documents have no schema constraints: consider using + * {@link MuteDocumentFactory} instead. + * + * TODO(anorth): Change generic type to ObservableDocument after fixing + * callers. + * + * TODO(anorth): Remove this method in favor of one specifying a schema. + */ + public static DocumentProvider<ObservablePluggableMutableDocument> observableDocumentProvider() { + return OBS_DOC_PROVIDER; + } + + /** + * Creates an observable mutable document with some schema, content, and sink. + */ + public static ObservableDocument createDocument(DocumentSchema schema, + String initialContent, SilentOperationSink<? super DocOp> sink) { + Preconditions.checkNotNull(sink, "Sink can't be null"); + DocInitialization init = DocProviders.POJO.parse(initialContent).asOperation(); + ObservablePluggableMutableDocument doc = new ObservablePluggableMutableDocument(schema, init); + doc.init(sink); + return doc; + } + + /** + * Creates an observable mutable document with some schema and a sink. + */ + public static ObservableDocument createDocument( + DocumentSchema schema, SilentOperationSink<? super DocOp> sink) { + return createDocument(schema, "", sink); + } + + /** + * Creates an observable mutable document with some schema. + */ + public static ObservableDocument createDocument(DocumentSchema schema) { + return createDocument(schema, "", SilentOperationSink.VOID); + } + + /** + * Creates an observable mutable document with some schema and initial content + */ + public static ObservableDocument createDocument( + DocumentSchema schema, String initialContent) { + return createDocument(schema, initialContent, SilentOperationSink.VOID); + } + + protected BasicFactories() { + } +} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/testing/DeltaTestUtil.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/testing/DeltaTestUtil.java b/wave/src/test/java/org/waveprotocol/wave/model/testing/DeltaTestUtil.java new file mode 100755 index 0000000..2bd7021 --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/testing/DeltaTestUtil.java @@ -0,0 +1,199 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.testing; + +import org.waveprotocol.wave.model.document.operation.DocOp; +import org.waveprotocol.wave.model.document.operation.impl.DocOpBuilder; +import org.waveprotocol.wave.model.operation.wave.AddParticipant; +import org.waveprotocol.wave.model.operation.wave.BlipContentOperation; +import org.waveprotocol.wave.model.operation.wave.NoOp; +import org.waveprotocol.wave.model.operation.wave.RemoveParticipant; +import org.waveprotocol.wave.model.operation.wave.TransformedWaveletDelta; +import org.waveprotocol.wave.model.operation.wave.WaveletBlipOperation; +import org.waveprotocol.wave.model.operation.wave.WaveletDelta; +import org.waveprotocol.wave.model.operation.wave.WaveletOperation; +import org.waveprotocol.wave.model.operation.wave.WaveletOperationContext; +import org.waveprotocol.wave.model.util.CollectionUtils; +import org.waveprotocol.wave.model.version.HashedVersion; +import org.waveprotocol.wave.model.wave.Constants; +import org.waveprotocol.wave.model.wave.ParticipantId; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; + + +/** + * A bunch of utility functions to make testing easier. + * + * @author [email protected] (David Wang) + */ +public class DeltaTestUtil { + private static final WaveletOperationContext DUMMY = new WaveletOperationContext(null, 0L, 0L); + + private final ParticipantId author; + private final Random random = new Random(42); + + /** + * Creates a {@link DeltaTestUtil} with which operations authored by the given + * author can readily be made. + */ + public DeltaTestUtil(String author) { + this(new ParticipantId(author)); + } + + /** + * Creates a {@link DeltaTestUtil} with which operations authored by the given + * author can readily be made. + */ + public DeltaTestUtil(ParticipantId author) { + this.author = author; + } + + public ParticipantId getAuthor() { + return author; + } + + /** + * Creates an XmlDelete with the given data. + */ + public WaveletOperation delete(int posStart, String characters, int remaining) { + DocOp op = new DocOpBuilder() + .retain(posStart) + .deleteCharacters(characters) + .retain(remaining) + .build(); + BlipContentOperation blipOp = new BlipContentOperation( + new WaveletOperationContext(author, 0L, 1), op); + WaveletBlipOperation waveOp = new WaveletBlipOperation("blip id", blipOp); + return waveOp; + } + + /** + * Wrap an op with a delta. + */ + public TransformedWaveletDelta delta(long targetVersion, WaveletOperation op) { + return TransformedWaveletDelta.cloneOperations(author, + HashedVersion.unsigned(targetVersion + 1), 0L, Arrays.asList(op)); + } + + /** + * Create a delta with a single NoOp operation. + * + * @param initialVersion The version before the operation. + */ + public TransformedWaveletDelta noOpDelta(long initialVersion) { + return makeTransformedDelta(0L, HashedVersion.unsigned(initialVersion + 1), 1); + } + + /** Create a NoOp operation. */ + public NoOp noOp() { + return new NoOp(new WaveletOperationContext(author, 0L, 1L)); + } + + /** Create an AddParticipant operation. */ + public AddParticipant addParticipant(ParticipantId participant) { + return new AddParticipant(new WaveletOperationContext(author, 0L, 1L), participant); + } + + /** Creates a RemoveParticipant operation. */ + public RemoveParticipant removeParticipant(ParticipantId participant) { + return new RemoveParticipant(new WaveletOperationContext(author, 0L, 1L), participant); + } + + /** + * A docop that is empty. i.e. does nothing to the document. The document must + * also be empty, otherwise the operation is invalid. + */ + public WaveletOperation noOpDocOp(String blipId) { + WaveletOperationContext context = new WaveletOperationContext(author, 0L, 1L); + BlipContentOperation blipOp = new BlipContentOperation(context, (new DocOpBuilder()).build()); + return new WaveletBlipOperation(blipId, blipOp); + } + + /** + * Creates an XmlInsert with the given data. + */ + public WaveletOperation insert(int pos, String text, int remaining, + HashedVersion resultingVersion) { + DocOpBuilder builder = new DocOpBuilder(); + builder.retain(pos).characters(text); + if (remaining > 0) { + builder.retain(remaining); + } + BlipContentOperation blipOp = new BlipContentOperation( + new WaveletOperationContext(author, 0L, 1, resultingVersion), builder.build()); + WaveletBlipOperation waveOp = new WaveletBlipOperation("blip id", blipOp); + return waveOp; + } + + /** + * Builds a random client delta. + */ + public WaveletDelta makeDelta(HashedVersion targetVersion, long timestamp, int numOps) { + List<WaveletOperation> ops = CollectionUtils.newArrayList(); + WaveletOperationContext context = + new WaveletOperationContext(author, Constants.NO_TIMESTAMP, 1); + for (int i = 0; i < numOps; ++i) { + ops.add(randomOp(context)); + } + return new WaveletDelta(author, targetVersion, ops); + } + + /** + * Builds a no-op client delta. + */ + public WaveletDelta makeNoOpDelta(HashedVersion targetVersion, long timestamp, int numOps) { + List<WaveletOperation> ops = CollectionUtils.newArrayList(); + WaveletOperationContext context = + new WaveletOperationContext(author, Constants.NO_TIMESTAMP, 1); + for (int i = 0; i < numOps; ++i) { + ops.add(new NoOp(context)); + } + return new WaveletDelta(author, targetVersion, ops); + } + + /** + * Builds a random transformed delta. + */ + public TransformedWaveletDelta makeTransformedDelta(long applicationTimestamp, + HashedVersion resultingVersion, int numOps) { + List<WaveletOperation> ops = CollectionUtils.newArrayList(); + for (int i = 0; i < numOps; ++i) { + ops.add(randomOp(DUMMY)); + } + return TransformedWaveletDelta.cloneOperations(author, resultingVersion, applicationTimestamp, + ops); + } + + /** + * Creates a random op. The result is unlikely to be applicable to any + * wavelet, but is generated such that we are fairly certain that it will be + * unique so we can identify it when it completes a round-trip. + */ + private WaveletOperation randomOp(WaveletOperationContext context) { + DocOp blipOp = new DocOpBuilder() + .retain(Math.abs(random.nextInt()) / 2 + 1) + .characters("createRndOp#" + random.nextInt()) + .build(); + return new WaveletBlipOperation("createRndId#" + random.nextInt(), + new BlipContentOperation(context, blipOp)); + } +} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/testing/DocOpCreator.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/testing/DocOpCreator.java b/wave/src/test/java/org/waveprotocol/wave/model/testing/DocOpCreator.java new file mode 100644 index 0000000..31f589b --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/testing/DocOpCreator.java @@ -0,0 +1,266 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.testing; + +import org.waveprotocol.wave.model.document.operation.Attributes; +import org.waveprotocol.wave.model.document.operation.AttributesUpdate; +import org.waveprotocol.wave.model.document.operation.DocOp; +import org.waveprotocol.wave.model.document.operation.EvaluatingDocOpCursor; +import org.waveprotocol.wave.model.document.operation.impl.AnnotationBoundaryMapImpl; +import org.waveprotocol.wave.model.document.operation.impl.AttributesUpdateImpl; +import org.waveprotocol.wave.model.document.operation.impl.DocOpBuffer; +import org.waveprotocol.wave.model.util.Preconditions; + +/** + * A convenience class for creating document operations. + * + */ +public class DocOpCreator { + + /** + * A builder for BufferedDocOps which is used by the static convenience + * methods of DocOpCreator for creating operations. This builder allows + * calling "retain" with an argument of 0 and "characters" and + * "deleteCharacters" with an empty string argument, in order to make the + * building process easier in some circumstances. + */ + private static class SimplifyingDocOpBuilder { + + private final EvaluatingDocOpCursor<DocOp> buffer = new DocOpBuffer(); + + public final DocOp build() { + return buffer.finish(); + } + + public final SimplifyingDocOpBuilder retain(int itemCount) { + Preconditions.checkArgument(itemCount >= 0, "Negative item count"); + if (itemCount > 0) { + buffer.retain(itemCount); + } + return this; + } + + public final SimplifyingDocOpBuilder characters(String characters) { + if (characters.length() > 0) { + buffer.characters(characters); + } + return this; + } + + public final SimplifyingDocOpBuilder elementStart(String type, Attributes attrs) { + buffer.elementStart(type, attrs); + return this; + } + + public final SimplifyingDocOpBuilder elementEnd() { + buffer.elementEnd(); + return this; + } + + public final SimplifyingDocOpBuilder deleteCharacters(String characters) { + if (characters.length() > 0) { + buffer.deleteCharacters(characters); + } + return this; + } + + public final SimplifyingDocOpBuilder deleteElementStart(String type, Attributes attrs) { + buffer.deleteElementStart(type, attrs); + return this; + } + + public final SimplifyingDocOpBuilder deleteElementEnd() { + buffer.deleteElementEnd(); + return this; + } + + public final SimplifyingDocOpBuilder replaceAttributes(Attributes oldAttrs, + Attributes newAttrs) { + buffer.replaceAttributes(oldAttrs, newAttrs); + return this; + } + + public final SimplifyingDocOpBuilder updateAttributes(AttributesUpdate update) { + buffer.updateAttributes(update); + return this; + } + + public final SimplifyingDocOpBuilder setAnnotation(int itemCount, String key, String oldValue, + String newValue) { + Preconditions.checkArgument(itemCount >= 0, "Negative item count"); + if (itemCount > 0) { + buffer.annotationBoundary(AnnotationBoundaryMapImpl.builder() + .updateValues(key, oldValue, newValue) + .build()); + buffer.retain(itemCount); + buffer.annotationBoundary(AnnotationBoundaryMapImpl.builder() + .initializationEnd(key) + .build()); + } + return this; + } + + } + + /** + * Creates a document operation that inserts the given characters at the given + * location. + * + * @param size The initial size of the document. + * @param location The location at which to insert characters. + * @param characters The characters to insert. + * @return The document operation. + */ + public static DocOp insertCharacters(int size, int location, String characters) { + return new SimplifyingDocOpBuilder() + .retain(location) + .characters(characters) + .retain(size - location) + .build(); + } + + /** + * Creates a document operation that inserts an element at the given location. + * + * @param size The initial size of the document. + * @param location The location at which to insert the element. + * @param type The type of the element. + * @param attributes The attributes of the element. + * @return The document operation. + */ + public static DocOp insertElement(int size, int location, String type, + Attributes attributes) { + return new SimplifyingDocOpBuilder() + .retain(location) + .elementStart(type, attributes) + .elementEnd() + .retain(size - location) + .build(); + } + + /** + * Creates a document operation that deletes the characters denoted by the + * given range. + * + * @param size The initial size of the document. + * @param location The location the characters to delete. + * @param characters The characters to delete. + * @return The document operation. + */ + public static DocOp deleteCharacters(int size, int location, String characters) { + return new SimplifyingDocOpBuilder() + .retain(location) + .deleteCharacters(characters) + .retain(size - location - characters.length()) + .build(); + } + + /** + * Creates a document operation that deletes an empty element at a given + * location. + * + * @param size The initial size of the document. + * @param location The location of the element to delete. + * @param type The type of the element. + * @param attributes The attributes of the element. + * @return The document operation. + */ + public static DocOp deleteElement(int size, int location, String type, + Attributes attributes) { + return new SimplifyingDocOpBuilder() + .retain(location) + .deleteElementStart(type, attributes) + .deleteElementEnd() + .retain(size - location - 2) + .build(); + } + + /** + * Creates a document operation that replace all the attributes of an element. + * + * @param size The initial size of the document. + * @param location The location of the element whose attributes are to be set. + * @param oldAttr The old attributes of the element. + * @param newAttr The new attributes that the element should have. + * @return The document operation. + */ + public static DocOp replaceAttributes(int size, int location, Attributes oldAttr, + Attributes newAttr) { + return new SimplifyingDocOpBuilder() + .retain(location) + .replaceAttributes(oldAttr, newAttr) + .retain(size - location - 1) + .build(); + } + + /** + * Creates a document operation that sets an attribute of an element. + * + * @param size The initial size of the document. + * @param location The location of the element whose attribute is to be set. + * @param name The name of the attribute to set. + * @param oldValue The old value of the attribute. + * @param newValue The value to which to set the attribute. + * @return The document operation. + */ + public static DocOp setAttribute(int size, int location, String name, String oldValue, + String newValue) { + return new SimplifyingDocOpBuilder() + .retain(location) + .updateAttributes(new AttributesUpdateImpl(name, oldValue, newValue)) + .retain(size - location - 1) + .build(); + } + + /** + * Creates a document operation that sets an annotation over a range. + * + * @param size The initial size of the document. + * @param start The location of the start of the range on which the annotation + * is to be set. + * @param end The location of the end of the range on which the annotation is + * to be set. + * @param key The annotation key. + * @param oldValue The old annotation value. + * @param newValue The new annotation value. + * @return The document operation. + */ + public static DocOp setAnnotation(int size, int start, int end, String key, + String oldValue, String newValue) { + return new SimplifyingDocOpBuilder() + .retain(start) + .setAnnotation(end - start, key, oldValue, newValue) + .retain(size - end) + .build(); + } + + /** + * Creates a document operation that acts as the identity on a document. + * + * @param size The size of the document. + * @return The document operation. + */ + public static DocOp identity(int size) { + return new SimplifyingDocOpBuilder() + .retain(size) + .build(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/testing/ExtraAsserts.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/testing/ExtraAsserts.java b/wave/src/test/java/org/waveprotocol/wave/model/testing/ExtraAsserts.java new file mode 100644 index 0000000..989240b --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/testing/ExtraAsserts.java @@ -0,0 +1,92 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.testing; + +import org.waveprotocol.wave.model.wave.data.BlipData; + +import junit.framework.Assert; + +import org.waveprotocol.wave.model.document.MutableDocument; +import org.waveprotocol.wave.model.document.ReadableWDocument; +import org.waveprotocol.wave.model.document.operation.impl.DocOpUtil; +import org.waveprotocol.wave.model.document.util.DocCompare; +import org.waveprotocol.wave.model.document.util.XmlStringBuilder; + +/** + * Extra assertions that are useful for tests involving the model. + * + */ +public final class ExtraAsserts { + + /** + * Asserts that the structure of the document and the builder are the same. + */ + public static <N, E extends N, T extends N> void assertStructureEquivalent( + XmlStringBuilder expected, MutableDocument<N, E, T> doc) { + String expectedStr = expected.getXmlString(); + if (!DocCompare.equivalent(DocCompare.STRUCTURE, expectedStr, doc)) { + String docStr = doc.toXmlString(); + String message = "Expected [" + expectedStr + "], found [" + docStr + "]"; + Assert.fail(message); + } + } + + /** + * Asserts that the structure of the two documents are the same. + */ + public static <N1, N2> void assertStructureEquivalent(ReadableWDocument<N1, ?, ?> doc1, + ReadableWDocument<N2, ?, ?> doc2) { + if (!DocCompare.equivalent(DocCompare.STRUCTURE, doc1, doc2)) { + String doc1Str = doc1.toXmlString(); + String doc2Str = doc2.toXmlString(); + String message = "Expected [" + doc1Str + "] found [" + doc2Str + "]"; + Assert.fail(message); + } + } + + /** + * Asserts that the content, both structure and annotations, of the document + * and the builder are the same. + */ + public static <N, E extends N, T extends N> void assertEqual( + XmlStringBuilder expected, MutableDocument<N, E, T> doc) { + String expectedStr = expected.getXmlString(); + if (!DocCompare.equivalent(DocCompare.ALL, expectedStr, doc)) { + String docStr = doc.toXmlString(); + String message = "Expected [" + expectedStr + "], found [" + docStr + "]"; + Assert.fail(message); + } + } + + // Static utility class + private ExtraAsserts() { } + + /** + * Checks the content of a document and asserts it matches the given expected + * content. + * + * @param expectedContent The expected content. + * @param root The content to check. + */ + public static void checkContent(String expectedContent, BlipData root) { + Assert.assertEquals(expectedContent, DocOpUtil.toXmlString(root.getContent().asOperation())); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/testing/Factory.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/testing/Factory.java b/wave/src/test/java/org/waveprotocol/wave/model/testing/Factory.java new file mode 100644 index 0000000..7ae8f7a --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/testing/Factory.java @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.testing; + +/** + * Generic factory interface. The intended use within this test package is + * to allow black-box tests, which only test an interface, to be decoupled from + * the construction of the particular instance of that interface to test. + * + * @param <T> type of created instances + */ +public interface Factory<T> { + /** + * Creates an instance. + */ + T create(); +} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeDocument.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeDocument.java b/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeDocument.java new file mode 100644 index 0000000..33de8c1 --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeDocument.java @@ -0,0 +1,81 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.testing; + +import org.waveprotocol.wave.model.document.operation.DocInitialization; +import org.waveprotocol.wave.model.document.operation.DocOp; +import org.waveprotocol.wave.model.document.operation.automaton.DocumentSchema; +import org.waveprotocol.wave.model.id.WaveletId; +import org.waveprotocol.wave.model.operation.OperationException; +import org.waveprotocol.wave.model.schema.SchemaProvider; +import org.waveprotocol.wave.model.wave.data.DocumentFactory; +import org.waveprotocol.wave.model.wave.data.impl.ObservablePluggableMutableDocument; + +/** + * A document implementation and factory for use in tests. + * + */ +public class FakeDocument extends ObservablePluggableMutableDocument { + + public static class Factory implements DocumentFactory<FakeDocument> { + + private final SchemaProvider schemas; + + public static Factory create(SchemaProvider schemas) { + return new Factory(schemas); + } + + private Factory(SchemaProvider schemas) { + this.schemas = schemas; + } + + private DocumentSchema getSchemaForId(WaveletId waveletId, String documentId) { + DocumentSchema result = schemas.getSchemaForId(waveletId, documentId); + return (result != null) ? result : DocumentSchema.NO_SCHEMA_CONSTRAINTS; + } + + @Override + public FakeDocument create(final WaveletId waveletId, final String blipId, + DocInitialization content) { + return new FakeDocument(content, getSchemaForId(waveletId, blipId)); + } + } + + private DocOp consumed; + + public FakeDocument(DocInitialization initial, DocumentSchema schema) { + super(schema, initial); + } + + @Override + public void consume(DocOp op) throws OperationException { + super.consume(op); + this.consumed = op; + } + + public DocOp getConsumed() { + return consumed; + } + + @Override + public String toString() { + return toXmlString(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeHashedVersionFactory.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeHashedVersionFactory.java b/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeHashedVersionFactory.java new file mode 100644 index 0000000..e9d25b8 --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeHashedVersionFactory.java @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.testing; + +import org.waveprotocol.wave.model.id.WaveletName; +import org.waveprotocol.wave.model.version.HashedVersion; +import org.waveprotocol.wave.model.version.HashedVersionFactory; + +/** + * A hashed version factory which generates unsigned versions. + * + * @author [email protected] (Alex North) + */ +public final class FakeHashedVersionFactory implements HashedVersionFactory { + + public static final HashedVersionFactory INSTANCE = new FakeHashedVersionFactory(); + + @Override + public HashedVersion createVersionZero(WaveletName waveletName) { + return HashedVersion.unsigned(0); + } + + @Override + public HashedVersion create(byte[] appliedDeltaBytes, HashedVersion versionAppliedAt, + int opsApplied) { + return HashedVersion.unsigned(versionAppliedAt.getVersion() + opsApplied); + } +} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeIdGenerator.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeIdGenerator.java b/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeIdGenerator.java new file mode 100644 index 0000000..f46802c --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeIdGenerator.java @@ -0,0 +1,44 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.testing; + +import org.waveprotocol.wave.model.id.IdGenerator; +import org.waveprotocol.wave.model.id.IdGeneratorImpl; +import org.waveprotocol.wave.model.id.IdGeneratorImpl.Seed; + + +/** + * Id generator suitable for use in testing. + * + */ +public final class FakeIdGenerator { + + // Prevent instantiation + private FakeIdGenerator() {} + + public static IdGenerator create() { + return new IdGeneratorImpl("example.com", new Seed() { + @Override + public String get() { + return "seed"; + } + }); + } +} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeSilentOperationSink.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeSilentOperationSink.java b/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeSilentOperationSink.java new file mode 100644 index 0000000..43bbd34 --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeSilentOperationSink.java @@ -0,0 +1,69 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.testing; + +import org.waveprotocol.wave.model.operation.Operation; +import org.waveprotocol.wave.model.operation.SilentOperationSink; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * A place where you can get a concrete OperationSink.Silent for testing. + * + * @author [email protected] (David Wang) + */ +public class FakeSilentOperationSink<T extends Operation<?>> implements SilentOperationSink<T> { + private LinkedList<T> ops = new LinkedList<T>(); + + /** + * For unit testing + * @return the most recently consumed op + */ + public T getConsumedOp() { + int size = ops.size(); + return (size == 0) ? null : (ops.get(size - 1)); + } + + /** + * {@inheritDoc} + */ + public void consume(T op) { + ops.addLast(op); + } + + /** + * Clears the list of saved operations. + */ + public void clear() { + ops.clear(); + } + + /** + * Gets the list of operations consumed by this sink since it was last + * cleared. + * + * @return the ops, from first consumed through most recently consumed. + */ + public List<T> getOps() { + return Collections.unmodifiableList(ops); + } +} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeWaveView.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeWaveView.java b/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeWaveView.java new file mode 100644 index 0000000..5f74e11 --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeWaveView.java @@ -0,0 +1,229 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.testing; + +import org.waveprotocol.wave.model.id.IdGenerator; +import org.waveprotocol.wave.model.id.WaveId; +import org.waveprotocol.wave.model.id.WaveletId; +import org.waveprotocol.wave.model.operation.SilentOperationSink; +import org.waveprotocol.wave.model.operation.wave.WaveletOperation; +import org.waveprotocol.wave.model.operation.wave.WaveletOperationContext; +import org.waveprotocol.wave.model.schema.SchemaProvider; +import org.waveprotocol.wave.model.wave.ObservableWavelet; +import org.waveprotocol.wave.model.wave.ParticipantId; +import org.waveprotocol.wave.model.wave.WaveViewListener; +import org.waveprotocol.wave.model.wave.data.DocumentFactory; +import org.waveprotocol.wave.model.wave.data.impl.WaveletDataImpl; +import org.waveprotocol.wave.model.wave.opbased.ObservableWaveView; +import org.waveprotocol.wave.model.wave.opbased.OpBasedWavelet; +import org.waveprotocol.wave.model.wave.opbased.WaveViewImpl; + +/** + * Dummy implementation of a wave view. + * + */ +public final class FakeWaveView implements ObservableWaveView, Factory<OpBasedWavelet> { + + public final static class Builder { + private final SchemaProvider schemas; + private IdGenerator idGenerator; + private WaveId waveId; + private ParticipantId viewer; + private SilentOperationSink<? super WaveletOperation> sink; + private WaveViewImpl.WaveletConfigurator configurator; + private DocumentFactory<?> docFactory; + + private Builder(SchemaProvider schemas) { + this.schemas = schemas; + } + + public Builder with(DocumentFactory<?> docFactory) { + this.docFactory = docFactory; + return this; + } + + public Builder with(IdGenerator idGenerator) { + this.idGenerator = idGenerator; + return this; + } + + public Builder with(WaveId wid) { + this.waveId = wid; + return this; + } + + public Builder with(ParticipantId viewer) { + this.viewer = viewer; + return this; + } + + public Builder with(SilentOperationSink<? super WaveletOperation> sink) { + this.sink = sink; + return this; + } + + public Builder with(WaveViewImpl.WaveletConfigurator configurator) { + this.configurator = configurator; + return this; + } + + public FakeWaveView build() { + if (idGenerator == null) { + idGenerator = FakeIdGenerator.create(); + } + if (waveId == null) { + waveId = idGenerator.newWaveId(); + } + if (viewer == null) { + viewer = FAKE_PARTICIPANT; + } + if (sink == null) { + sink = SilentOperationSink.VOID; + } + if (configurator == null) { + configurator = WaveViewImpl.WaveletConfigurator.ADD_CREATOR; + } + if (docFactory == null) { + // Document factory that accepts output-sink registrations. + docFactory = FakeDocument.Factory.create(schemas); + } + + // Wavelet factory that does all the work. + OpBasedWaveletFactory waveletFactory = OpBasedWaveletFactory // \u2620 + .builder(schemas) // \u2620 + .with(WaveletDataImpl.Factory.create(docFactory)) // \u2620 + .with(sink) // \u2620 + .with(viewer) // \u2620 + .build(); + + // And the view implementation using that factory. + WaveViewImpl<OpBasedWavelet> view = + WaveViewImpl.create(waveletFactory, waveId, idGenerator, viewer, configurator); + + return new FakeWaveView(waveletFactory, view); + } + } + + private static final ParticipantId FAKE_PARTICIPANT = new ParticipantId("[email protected]"); + + private final OpBasedWaveletFactory factory; + private final WaveViewImpl<? extends OpBasedWavelet> view; + + /** + * Creates a wave view. + * + * @param factory factory exposing testing hacks + * @param view real view implementation + */ + private FakeWaveView(OpBasedWaveletFactory factory, WaveViewImpl<? extends OpBasedWavelet> view) { + this.factory = factory; + this.view = view; + } + + /** + * @return a builder for a fake wave view. + */ + public static Builder builder(SchemaProvider schemas) { + return new Builder(schemas); + } + + // + // Expose as basic wavelet factory for wavelet-specific tests. + // + + @Override + public OpBasedWavelet create() { + return createWavelet(); + } + + // + // Testing hacks. + // + + public MockParticipationHelper getLastAuthoriser() { + return factory.getLastAuthoriser(); + } + + public WaveletOperationContext.Factory getLastContextFactory() { + return factory.getLastContextFactory(); + } + + public OpBasedWavelet createWavelet(WaveletId id) { + return view.createWavelet(id); + } + + public void removeWavelet(ObservableWavelet wavelet) { + view.removeWavelet(wavelet); + } + + // + // Delegate view implementation to view. + // + + @Override + public OpBasedWavelet createRoot() { + return view.createRoot(); + } + + @Override + public OpBasedWavelet createUserData() { + return view.createUserData(); + } + + @Override + public OpBasedWavelet createWavelet() { + return view.createWavelet(); + } + + @Override + public OpBasedWavelet getRoot() { + return view.getRoot(); + } + + @Override + public OpBasedWavelet getUserData() { + return view.getUserData(); + } + + @Override + public OpBasedWavelet getWavelet(WaveletId waveletId) { + return view.getWavelet(waveletId); + } + + @Override + public Iterable<? extends OpBasedWavelet> getWavelets() { + return view.getWavelets(); + } + + @Override + public WaveId getWaveId() { + return view.getWaveId(); + } + + @Override + public void addListener(WaveViewListener listener) { + view.addListener(listener); + } + + @Override + public void removeListener(WaveViewListener listener) { + view.removeListener(listener); + } +} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeWaveletDataListener.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeWaveletDataListener.java b/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeWaveletDataListener.java new file mode 100644 index 0000000..b281a10 --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeWaveletDataListener.java @@ -0,0 +1,320 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.testing; + +import org.waveprotocol.wave.model.version.HashedVersion; +import org.waveprotocol.wave.model.wave.ParticipantId; +import org.waveprotocol.wave.model.wave.data.BlipData; +import org.waveprotocol.wave.model.wave.data.WaveletData; +import org.waveprotocol.wave.model.wave.data.WaveletDataListener; + +/** + * Stub implementation of {@link WaveletDataListener}. Each notification method + * saves the passed parameters for later inspection by accessors. + * + * @author [email protected] (David Wang) + */ +public class FakeWaveletDataListener implements WaveletDataListener { + /** + * The last participantId received from + * {@link #onParticipantAdded(WaveletData, ParticipantId)} + */ + private ParticipantId participantAdded; + + /** + * The last participantId received from + * {@link #onParticipantRemoved(WaveletData, ParticipantId)} + */ + private ParticipantId participantRemoved; + + /** + * The last blip received from {@link #onBlipDataAdded(WaveletData, BlipData)} + */ + private BlipData blipDataAdded; + + /** + * The last oldTitle received from {@link #onTitleChanged(WaveletData, String, String)}. + */ + private String oldTitle; + + /** + * The last newTitle received from {@link #onTitleChanged(WaveletData, String, String)}. + */ + private String newTitle; + + /** + * The last blip target received from any other onBlipXxx method. + */ + private BlipData blipModified; + + /** + * The last old modified time received by + * {@link #onLastModifiedTimeChanged(WaveletData, long, long)} + */ + private long oldLastModifiedTime; + + /** + * The last new modified time received by + * {@link #onLastModifiedTimeChanged(WaveletData, long, long)} + */ + private long newLastModifiedTime; + + /** + * The last contributor received by + * {@link #onBlipDataContributorAdded(WaveletData, BlipData, ParticipantId)} + */ + private ParticipantId blipContributorAdded; + + /** + * The last contributor received by + * {@link #onBlipDataContributorRemoved(WaveletData, BlipData, ParticipantId)} + */ + private ParticipantId blipContributorRemoved; + + /** + * The last old timestamp received by + * {@link #onBlipDataTimestampModified(WaveletData, BlipData, long, long)} + */ + private long blipOldTimestamp; + + /** + * The last new timestamp received by + * {@link #onBlipDataTimestampModified(WaveletData, BlipData, long, long)} + */ + private long blipNewTimestamp; + /** + * The last old version received by + * {@link #onBlipDataVersionModified(WaveletData, BlipData, long, long)} + */ + private long blipOldVersion; + + /** + * The last new version received by + * {@link #onBlipDataVersionModified(WaveletData, BlipData, long, long)} + */ + private long blipNewVersion; + + private long oldVersion; + private long newVersion; + + private HashedVersion oldHashedVersion; + private HashedVersion newHashedVersion; + + @Override + public void onParticipantAdded(WaveletData wavelet, ParticipantId participantId) { + this.participantAdded = participantId; + } + + @Override + public void onParticipantRemoved(WaveletData wavelet, ParticipantId participantId) { + this.participantRemoved = participantId; + } + + @Override + public void onLastModifiedTimeChanged(WaveletData waveletData, long oldTime, long newTime) { + this.oldLastModifiedTime = oldTime; + this.newLastModifiedTime = newTime; + } + + @Override + public void onVersionChanged(WaveletData wavelet, long oldVersion, long newVersion) { + this.oldVersion = oldVersion; + this.newVersion = newVersion; + } + + @Override + public void onHashedVersionChanged(WaveletData waveletData, HashedVersion oldHashedVersion, + HashedVersion newHashedVersion) { + this.oldHashedVersion = oldHashedVersion; + this.newHashedVersion = newHashedVersion; + } + + @Override + public void onBlipDataAdded(WaveletData waveletData, BlipData blip) { + this.blipDataAdded = blip; + } + + @Override + public void onBlipDataContributorAdded( + WaveletData waveletData, BlipData blip, ParticipantId contributor) { + this.blipModified = blip; + this.blipContributorAdded = contributor; + } + + @Override + public void onBlipDataContributorRemoved( + WaveletData waveletData, BlipData blip, ParticipantId contributor) { + this.blipModified = blip; + this.blipContributorRemoved = contributor; + } + + @Override + public void onBlipDataTimestampModified( + WaveletData waveletData, BlipData blip, long oldTime, long newTime) { + this.blipModified = blip; + this.blipOldTimestamp = oldTime; + this.blipNewTimestamp = newTime; + } + + @Override + public void onBlipDataVersionModified( + WaveletData waveletData, BlipData blip, long oldVersion, long newVersion) { + this.blipModified = blip; + this.blipOldVersion = oldVersion; + this.blipNewVersion = newVersion; + } + + @Deprecated + @Override + public void onRemoteBlipDataContentModified(WaveletData waveletData, BlipData blip) { + this.blipModified = blip; + } + + @Override + public void onBlipDataSubmitted(WaveletData waveletData, BlipData blip) { + this.blipModified = blip; + } + + /** + * @return the last participantId received by + * {@link #onParticipantAdded(WaveletData, ParticipantId)} + */ + public ParticipantId getParticipantAdded() { + return participantAdded; + } + + /** + * @return the last participantId received by + * {@link #onParticipantRemoved(WaveletData, ParticipantId)} + */ + public ParticipantId getParticipantRemoved() { + return participantRemoved; + } + + /** + * @return the last blip received by {@link #onBlipDataAdded(WaveletData, BlipData)} + */ + public BlipData getBlipDataAdded() { + return blipDataAdded; + } + + /** + * @return the last blip received by any of the other onBlipDataXxx methods. + */ + public BlipData getBlipModified() { + return blipModified; + } + + /** + * @return the last newTitle received by + * {@link #onTitleChanged(WaveletData, String, String)}. + */ + public String getNewTitle() { + return newTitle; + } + + /** + * @return the last oldTitle received by + * {@link #onTitleChanged(WaveletData, String, String)}. + */ + public String getOldTitle() { + return oldTitle; + } + + /** + * @return the last old time received by + * {@link #onLastModifiedTimeChanged(WaveletData, long, long)} + */ + public long getOldLastModifiedTime() { + return oldLastModifiedTime; + } + + /** + * @return the last new time received by + * {@link #onLastModifiedTimeChanged(WaveletData, long, long)} + */ + public long getNewLastModifiedTime() { + return newLastModifiedTime; + } + + /** + * @return the last participant received by + * {@link #onBlipDataContributorAdded(WaveletData, BlipData, ParticipantId)} + */ + public ParticipantId getBlipContributorAdded() { + return blipContributorAdded; + } + + /** + * @return the last participant receieved by + * {@link #onBlipDataContributorRemoved(WaveletData, BlipData, ParticipantId)} + */ + public ParticipantId getBlipContributorRemoved() { + return blipContributorRemoved; + } + + /** + * @return the last old timestamp received by + * {@link #onBlipDataTimestampModified(WaveletData, BlipData, long, long)} + */ + public long getBlipOldTimestamp() { + return blipOldTimestamp; + } + + /** + * @return the last new timestamp received by + * {@link #onBlipDataTimestampModified(WaveletData, BlipData, long, long)} + */ + public long getBlipNewTimestamp() { + return blipNewTimestamp; + } + + /** + * @return the last new version received by + * {@link #onBlipDataVersionModified(WaveletData, BlipData, long, long)} + */ + public long getBlipOldVersion() { + return blipOldVersion; + } + + /** + * @return the last old version received by + * {@link #onBlipDataVersionModified(WaveletData, BlipData, long, long)} + */ + public long getBlipNewVersion() { + return blipNewVersion; + } + + public long getNewVersion() { + return newVersion; + } + + public long getOldVersion() { + return oldVersion; + } + + public HashedVersion getNewHashedVersion() { + return newHashedVersion; + } + + public HashedVersion getOldHashedVersion() { + return oldHashedVersion; + } +} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeWaveletListener.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeWaveletListener.java b/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeWaveletListener.java new file mode 100644 index 0000000..8d66614 --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/testing/FakeWaveletListener.java @@ -0,0 +1,58 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.testing; + +import org.waveprotocol.wave.model.wave.ObservableWavelet; +import org.waveprotocol.wave.model.wave.WaveletListener; +import org.waveprotocol.wave.model.wave.opbased.WaveletListenerImpl; + +import org.waveprotocol.wave.model.wave.ParticipantId; + +/** + * Stub implementation of {@link WaveletListener}. Each notification method + * saves the passed parameters for later inspection by accessors. + * + * @author [email protected] (David Wang) + */ +public class FakeWaveletListener extends WaveletListenerImpl { + /** The last participant received from + * {@link #onParticipantAdded(ObservableWavelet, ParticipantId)} + */ + private ParticipantId participant; + + @Override + public void onParticipantAdded(ObservableWavelet wavelet, ParticipantId participant) { + this.participant = participant; + } + + /** + * @return the last {@code participant} received by + * {@link #onParticipantAdded(ObservableWavelet, ParticipantId)}. + */ + public ParticipantId getParticipant() { + return participant; + } + + + /** Resets all fields. */ + public void reset() { + this.participant = null; + } +} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/testing/GenericGWTTestBase.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/testing/GenericGWTTestBase.java b/wave/src/test/java/org/waveprotocol/wave/model/testing/GenericGWTTestBase.java new file mode 100644 index 0000000..98ce42b --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/testing/GenericGWTTestBase.java @@ -0,0 +1,77 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.testing; + +import com.google.gwt.junit.client.GWTTestCase; + +/** + * The base class for running a model-related test case as a GWT test case. + * + * A {@link GenericGWTTestBase} contains a {@link GenericTestBase}, to which it forwards all + * relevant testing methods. This base class holds the reference to the + * contained test, and forwards {@link GWTTestCase#gwtSetUp()} and + * {@link GWTTestCase#gwtTearDown()} to it. + * + * To run a vanilla JUnit test case as a GWTTestCase, simply write the JUnit + * test as an extension of {@link GenericTestBase}, and create a parallel extension + * of this class that wraps an instance of the plain test case, and forwards + * all test methods to it. + * + * @param <T> wrapped test case class + */ +public abstract class GenericGWTTestBase<T extends GenericTestBase<?>> extends GWTTestCase { + /** The wrapped vanilla test case. */ + protected final T target; + + /** + * The default constructor. + */ + protected GenericGWTTestBase(T target) { + this.target = target; + } + + /** + * Forwards to wrapped test's {@link GenericTestBase#setUp()}. + */ + @Override + protected void gwtSetUp() throws Exception { + target.setUp(); + } + + /** + * Forwards to wrapped test's {@link GenericTestBase#tearDown()}. + */ + @Override + protected void gwtTearDown() throws Exception { + target.tearDown(); + } + + /** + * Specifies a module to use when running this test case. The returned + * module must cause the source for this class to be included. + * + * @see com.google.gwt.junit.client.GWTTestCase#getModuleName() + */ + @Override + public String getModuleName() { + return "org.waveprotocol.wave.model.tests"; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/testing/GenericTestBase.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/testing/GenericTestBase.java b/wave/src/test/java/org/waveprotocol/wave/model/testing/GenericTestBase.java new file mode 100644 index 0000000..980a987 --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/testing/GenericTestBase.java @@ -0,0 +1,70 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.testing; + +import junit.framework.TestCase; + +/** + * Generic base implementation for a test case that tests the behaviour of a + * single type. This implementation holds a reference to a factory for + * creating instances of that interface, and uses that factory to instantiates + * the instance to test in {@link #setUp()}. + * + * @param <T> interface type being tested + */ +public abstract class GenericTestBase<T> extends TestCase { + /** Factory used to create each wave to be tested. */ + protected final Factory<? extends T> factory; + + // State initialized in setUp() + + /** Target to test. */ + protected T target; + + /** + * Creates this test case, which runs on the wave-datas created by a factory. + * + * @param factory factory for creating the wave-datas to test + */ + protected GenericTestBase(Factory<? extends T> factory) { + this.factory = factory; + } + + /** + * {@inheritDoc} + * + * This implementation uses the test's factory to creates a test target. + */ + @Override + protected void setUp() { + target = factory.create(); + } + + /** + * {@inheritDoc} + */ + @Override + protected void tearDown() throws Exception { + // This is only overridden to expose tearDown to GWTTestBase (which should + // be in GWTTestBase's scope anyway, since it extends TestCase, but for + // some reason it isn't). + super.tearDown(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/testing/MockParticipationHelper.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/testing/MockParticipationHelper.java b/wave/src/test/java/org/waveprotocol/wave/model/testing/MockParticipationHelper.java new file mode 100644 index 0000000..bca7824 --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/testing/MockParticipationHelper.java @@ -0,0 +1,111 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.testing; + +import org.waveprotocol.wave.model.wave.ParticipantId; +import org.waveprotocol.wave.model.wave.ParticipationHelper; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.NoSuchElementException; +import java.util.Set; + +/** + * Mock {@link ParticipationHelper}. + * + */ +public class MockParticipationHelper implements ParticipationHelper { + + /** + * Frame used with {@link MockParticipationHelper} to record expectations + * and desired results. + */ + public static class Frame { + private final Set<ParticipantId> candidates; + private final ParticipantId editor; + private final ParticipantId result; + + /** + * Creates a frame that will either return a given participant or throw an + * {@link IllegalStateException} if no participant is given. + * + * @param result participant to return from this frame, or null if an + * {@link IllegalStateException} should be thrown. + * @param editor required for this frame to apply. + * @param candidates required for this frame to apply. + */ + public Frame(ParticipantId result, ParticipantId editor, + ParticipantId... candidates) { + this.result = result; + this.editor = editor; + this.candidates = new HashSet<ParticipantId>(Arrays.asList(candidates)); + } + + /** Returns the result or throws the exception dictated by this frame. */ + public ParticipantId apply() { + if (result == null) { + throw new IllegalStateException("Authoriser set to throw exception on this frame."); + } else { + return result; + } + } + + /** Checks whether the given arguments match those expected by this frame. */ + public boolean matches(ParticipantId editor, Set<ParticipantId> candidates) { + return editor.equals(this.editor) && candidates.equals(this.candidates); + } + } + + private final LinkedList<Frame> frames = new LinkedList<Frame>(); + + /** + * {@inheritDoc} + * + * Makes a decision by comparing against the next frame in the stub. If + * successful, that frame will then be discarded. + * + * @return the return participant of the frame if the arguments match those of + * the frame and the frame includes a return participant. + * @throws IllegalStateException if the arguments match those of the frame and + * the frame is designed to throw such an exception. + * @throws AssertionError if the arguments do not match those of the frame. + * @throws NoSuchElementException if there are no frames left. + */ + @Override + public ParticipantId getAuthoriser(ParticipantId editor, Set<ParticipantId> candidates) { + if (frames.isEmpty()) { + throw new NoSuchElementException("No frames left to compare with getAuthoriser(" + + editor + ", " + candidates + ")"); + } else { + Frame frame = frames.removeFirst(); + if (frame.matches(editor, candidates)) { + return frame.apply(); + } else { + throw new AssertionError(); + } + } + } + + /** Adds a given frame to the end of the list of those expected by this stub. */ + public void program(Frame frame) { + frames.addLast(frame); + } +} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/testing/MockWaveletOperationContextFactory.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/testing/MockWaveletOperationContextFactory.java b/wave/src/test/java/org/waveprotocol/wave/model/testing/MockWaveletOperationContextFactory.java new file mode 100644 index 0000000..df78ca4 --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/testing/MockWaveletOperationContextFactory.java @@ -0,0 +1,61 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.testing; + +import org.waveprotocol.wave.model.operation.wave.AbstractWaveletOperationContextFactory; +import org.waveprotocol.wave.model.wave.ParticipantId; + +/** + * WaveletOperationContext.Factory that supports setting the timestamp + * and default participant id to use. + * + */ +public class MockWaveletOperationContextFactory extends AbstractWaveletOperationContextFactory { + private long timeMillis; + private ParticipantId participantId; + + @Override + protected long currentTimeMillis() { + return timeMillis; + } + + @Override + public ParticipantId getParticipantId() { + return participantId; + } + + /** + * Sets the timestamp for future WaveletOperationContext objects generated + * by this factory. + */ + public MockWaveletOperationContextFactory setCurrentTimeMillis(long timeMillis) { + this.timeMillis = timeMillis; + return this; + } + + /** + * Sets the participant for future WaveletOperationContext objects generated + * by this factory. + */ + public MockWaveletOperationContextFactory setParticipantId(ParticipantId participantId) { + this.participantId = participantId; + return this; + } +} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/testing/ModelTestUtils.java ---------------------------------------------------------------------- diff --git a/wave/src/test/java/org/waveprotocol/wave/model/testing/ModelTestUtils.java b/wave/src/test/java/org/waveprotocol/wave/model/testing/ModelTestUtils.java new file mode 100644 index 0000000..8df4800 --- /dev/null +++ b/wave/src/test/java/org/waveprotocol/wave/model/testing/ModelTestUtils.java @@ -0,0 +1,61 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.testing; + +import org.waveprotocol.wave.model.document.operation.DocInitialization; +import org.waveprotocol.wave.model.document.operation.impl.AttributesImpl; +import org.waveprotocol.wave.model.document.operation.impl.DocInitializationBuilder; + +/** + * A utility class containing convenient methods for creating and checking blip + * document content. + * + */ +public final class ModelTestUtils { + + private ModelTestUtils() { + } + + /** + * Creates a document with the given content. + * + * @param contentText The content that the document should have. + * @return The document with the given content. + */ + public static DocInitialization createContent(String contentText) { + if (contentText.isEmpty()) { + return (new DocInitializationBuilder()) + .elementStart("body", new AttributesImpl()) + .elementStart("line", new AttributesImpl()) + .elementEnd() + .elementEnd() + .build(); + } else { + return new DocInitializationBuilder() + .elementStart("body", new AttributesImpl()) + .elementStart("line", new AttributesImpl()) + .elementEnd() + .characters(contentText) + .elementEnd() + .build(); + } + } + +}
