http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/main/java/org/waveprotocol/wave/model/testing/WaveletDataFactory.java
----------------------------------------------------------------------
diff --git 
a/wave/src/main/java/org/waveprotocol/wave/model/testing/WaveletDataFactory.java
 
b/wave/src/main/java/org/waveprotocol/wave/model/testing/WaveletDataFactory.java
deleted file mode 100644
index bf8ff93..0000000
--- 
a/wave/src/main/java/org/waveprotocol/wave/model/testing/WaveletDataFactory.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * 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.version.HashedVersion;
-import org.waveprotocol.wave.model.wave.ParticipantId;
-import org.waveprotocol.wave.model.wave.data.ObservableWaveletData;
-import org.waveprotocol.wave.model.wave.data.WaveletData;
-import org.waveprotocol.wave.model.wave.data.impl.EmptyWaveletSnapshot;
-
-/**
- * Exposes any {@link ObservableWaveletData.Factory} as a {@link Factory}, by
- * injecting suitable dependencies for testing.
- *
- */
-public final class WaveletDataFactory<T extends WaveletData> implements 
Factory<T> {
-  private final static WaveId WAVE_ID;
-  private final static WaveletId WAVELET_ID;
-  private static final ParticipantId PARTICIPANT_ID = new 
ParticipantId("f...@example.com");
-
-  static {
-    IdGenerator gen = FakeIdGenerator.create();
-    WAVE_ID = gen.newWaveId();
-    WAVELET_ID = gen.newConversationWaveletId();
-  }
-
-  private final WaveletData.Factory<T> factory;
-
-  private WaveletDataFactory(WaveletData.Factory<T> factory) {
-    this.factory = factory;
-  }
-
-  public static <T extends WaveletData> Factory<T> of(WaveletData.Factory<T> 
factory) {
-    return new WaveletDataFactory<T>(factory);
-  }
-
-  @Override
-  public T create() {
-    return factory.create(new EmptyWaveletSnapshot(WAVE_ID, WAVELET_ID, 
PARTICIPANT_ID,
-        HashedVersion.unsigned(0), 0));
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/main/resources/org/apache/wave/box/server/rpc/avatar/unknown.jpg
----------------------------------------------------------------------
diff --git 
a/wave/src/main/resources/org/apache/wave/box/server/rpc/avatar/unknown.jpg 
b/wave/src/main/resources/org/apache/wave/box/server/rpc/avatar/unknown.jpg
new file mode 100644
index 0000000..0f39513
Binary files /dev/null and 
b/wave/src/main/resources/org/apache/wave/box/server/rpc/avatar/unknown.jpg 
differ

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/main/resources/org/waveprotocol/box/attachment/Attachment.gwt.xml
----------------------------------------------------------------------
diff --git 
a/wave/src/main/resources/org/waveprotocol/box/attachment/Attachment.gwt.xml 
b/wave/src/main/resources/org/waveprotocol/box/attachment/Attachment.gwt.xml
index 2783343..3a5d2d1 100644
--- a/wave/src/main/resources/org/waveprotocol/box/attachment/Attachment.gwt.xml
+++ b/wave/src/main/resources/org/waveprotocol/box/attachment/Attachment.gwt.xml
@@ -23,4 +23,5 @@
 <module>
   <inherits name="org.waveprotocol.wave.communication.Communication"/>
   <source path="" excludes="gson/** proto/**"/>
+  <source path="" excludes="**/AttachmentProto.java"/>
 </module>

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/main/resources/org/waveprotocol/box/common/comms/WaveClientRpc.gwt.xml
----------------------------------------------------------------------
diff --git 
a/wave/src/main/resources/org/waveprotocol/box/common/comms/WaveClientRpc.gwt.xml
 
b/wave/src/main/resources/org/waveprotocol/box/common/comms/WaveClientRpc.gwt.xml
index 7393ff6..cfc5633 100644
--- 
a/wave/src/main/resources/org/waveprotocol/box/common/comms/WaveClientRpc.gwt.xml
+++ 
b/wave/src/main/resources/org/waveprotocol/box/common/comms/WaveClientRpc.gwt.xml
@@ -25,4 +25,5 @@
   <!-- DTO deps below. -->
   <inherits name="org.waveprotocol.wave.communication.Communication"/>
   <source path="" excludes="gson/** proto/**"/>
+  <source path="" excludes="**/WaveClientRpc.java"/>
 </module>

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/main/resources/org/waveprotocol/box/profile/Profile.gwt.xml
----------------------------------------------------------------------
diff --git 
a/wave/src/main/resources/org/waveprotocol/box/profile/Profile.gwt.xml 
b/wave/src/main/resources/org/waveprotocol/box/profile/Profile.gwt.xml
index b02f733..49ad4bb 100644
--- a/wave/src/main/resources/org/waveprotocol/box/profile/Profile.gwt.xml
+++ b/wave/src/main/resources/org/waveprotocol/box/profile/Profile.gwt.xml
@@ -24,5 +24,5 @@
   <!-- DTO deps below. -->
   <inherits name="org.waveprotocol.wave.communication.Communication"/>
   <source path="" excludes="gson/** proto/**"/>
-  <source path=""/>
+  <source path="" excludes="**/ProfilesProto.java"/>
 </module>

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/main/resources/org/waveprotocol/box/search/Search.gwt.xml
----------------------------------------------------------------------
diff --git a/wave/src/main/resources/org/waveprotocol/box/search/Search.gwt.xml 
b/wave/src/main/resources/org/waveprotocol/box/search/Search.gwt.xml
index 7393ff6..ad022d2 100644
--- a/wave/src/main/resources/org/waveprotocol/box/search/Search.gwt.xml
+++ b/wave/src/main/resources/org/waveprotocol/box/search/Search.gwt.xml
@@ -25,4 +25,5 @@
   <!-- DTO deps below. -->
   <inherits name="org.waveprotocol.wave.communication.Communication"/>
   <source path="" excludes="gson/** proto/**"/>
+  <source path="" excludes="**/SearchProto.java"/>
 </module>

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/main/resources/org/waveprotocol/box/stat/Stat.gwt.xml
----------------------------------------------------------------------
diff --git a/wave/src/main/resources/org/waveprotocol/box/stat/Stat.gwt.xml 
b/wave/src/main/resources/org/waveprotocol/box/stat/Stat.gwt.xml
index a459a43..3139114 100644
--- a/wave/src/main/resources/org/waveprotocol/box/stat/Stat.gwt.xml
+++ b/wave/src/main/resources/org/waveprotocol/box/stat/Stat.gwt.xml
@@ -12,5 +12,6 @@
 <!-- limitations under the License.                                         -->
 
 <module>
+    <inherits name="com.google.common.collect.Collect"/>
     <source path=""/>
 </module>

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/main/resources/org/waveprotocol/wave/client/doodad/Doodad.gwt.xml
----------------------------------------------------------------------
diff --git 
a/wave/src/main/resources/org/waveprotocol/wave/client/doodad/Doodad.gwt.xml 
b/wave/src/main/resources/org/waveprotocol/wave/client/doodad/Doodad.gwt.xml
index 26cf342..01c645b 100644
--- a/wave/src/main/resources/org/waveprotocol/wave/client/doodad/Doodad.gwt.xml
+++ b/wave/src/main/resources/org/waveprotocol/wave/client/doodad/Doodad.gwt.xml
@@ -29,6 +29,7 @@
 <inherits name="org.waveprotocol.wave.client.widget.button.Button" />
 <inherits name="org.waveprotocol.wave.model.conversation.Conversation" />
 <inherits name="org.waveprotocol.wave.util.escapers.Escapers" />
+<inherits name="org.waveprotocol.box.webclient.WebClient"/>
 
 <source path=""/>
 

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/main/resources/org/waveprotocol/wave/client/editor/Editor.gwt.xml
----------------------------------------------------------------------
diff --git 
a/wave/src/main/resources/org/waveprotocol/wave/client/editor/Editor.gwt.xml 
b/wave/src/main/resources/org/waveprotocol/wave/client/editor/Editor.gwt.xml
index 24af85f..a0a7a4b 100644
--- a/wave/src/main/resources/org/waveprotocol/wave/client/editor/Editor.gwt.xml
+++ b/wave/src/main/resources/org/waveprotocol/wave/client/editor/Editor.gwt.xml
@@ -29,6 +29,7 @@
 <inherits name="org.waveprotocol.wave.client.common.scrub.Scrub" />
 <inherits name="org.waveprotocol.wave.client.common.util.Util" />
 <inherits name="org.waveprotocol.wave.client.debug.logger.Logger" />
+<inherits name="org.waveprotocol.wave.client.doodad.Doodad" />
 <inherits name="org.waveprotocol.wave.client.editor.constants.Constants" />
 <inherits 
name="org.waveprotocol.wave.client.editor.content.paragraph.constants.Constants"
 />
 <inherits name="org.waveprotocol.wave.client.editor.selection.html.Html" />

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/main/resources/org/waveprotocol/wave/client/scheduler/Scheduler.gwt.xml
----------------------------------------------------------------------
diff --git 
a/wave/src/main/resources/org/waveprotocol/wave/client/scheduler/Scheduler.gwt.xml
 
b/wave/src/main/resources/org/waveprotocol/wave/client/scheduler/Scheduler.gwt.xml
index 2d013bd..57ce7cd 100644
--- 
a/wave/src/main/resources/org/waveprotocol/wave/client/scheduler/Scheduler.gwt.xml
+++ 
b/wave/src/main/resources/org/waveprotocol/wave/client/scheduler/Scheduler.gwt.xml
@@ -20,7 +20,10 @@
 
 -->
 
-<module><inherits name="com.google.gwt.user.User" /><inherits 
name="org.waveprotocol.wave.client.common.util.Util" /> <source path=""/>
+<module>
+ <inherits name="com.google.gwt.user.User" />
+ <inherits name="org.waveprotocol.wave.client.common.util.Util" />
+ <inherits name="org.waveprotocol.box.stat.Stat" />
+ <source path=""/>
  <source path="knobs"/>
-
 </module>

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/main/resources/org/waveprotocol/wave/client/scheduler/testing/Testing.gwt.xml
----------------------------------------------------------------------
diff --git 
a/wave/src/main/resources/org/waveprotocol/wave/client/scheduler/testing/Testing.gwt.xml
 
b/wave/src/main/resources/org/waveprotocol/wave/client/scheduler/testing/Testing.gwt.xml
index 1b37066..1b0896c 100644
--- 
a/wave/src/main/resources/org/waveprotocol/wave/client/scheduler/testing/Testing.gwt.xml
+++ 
b/wave/src/main/resources/org/waveprotocol/wave/client/scheduler/testing/Testing.gwt.xml
@@ -20,6 +20,8 @@
 
 -->
 
-<module><inherits name="com.google.gwt.user.User" /> <source path=""/>
+<module><inherits name="com.google.gwt.user.User" />
+    <inherits name="org.waveprotocol.wave.client.scheduler.Scheduler"/>
+    <source path=""/>
 
 </module>

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/main/resources/org/waveprotocol/wave/client/wavepanel/WavePanel.gwt.xml
----------------------------------------------------------------------
diff --git 
a/wave/src/main/resources/org/waveprotocol/wave/client/wavepanel/WavePanel.gwt.xml
 
b/wave/src/main/resources/org/waveprotocol/wave/client/wavepanel/WavePanel.gwt.xml
index 1b37066..30524e2 100644
--- 
a/wave/src/main/resources/org/waveprotocol/wave/client/wavepanel/WavePanel.gwt.xml
+++ 
b/wave/src/main/resources/org/waveprotocol/wave/client/wavepanel/WavePanel.gwt.xml
@@ -20,6 +20,9 @@
 
 -->
 
-<module><inherits name="com.google.gwt.user.User" /> <source path=""/>
+<module><inherits name="com.google.gwt.user.User" />
+    <inherits name="org.waveprotocol.wave.client.uibuilder.UiBuilder"/>
+    <inherits name="org.waveprotocol.box.stat.Stat"/>
+    <source path=""/>
 
 </module>

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/main/resources/org/waveprotocol/wave/client/wavepanel/event/Event.gwt.xml
----------------------------------------------------------------------
diff --git 
a/wave/src/main/resources/org/waveprotocol/wave/client/wavepanel/event/Event.gwt.xml
 
b/wave/src/main/resources/org/waveprotocol/wave/client/wavepanel/event/Event.gwt.xml
index 1ef9aa9..bf53a20 100644
--- 
a/wave/src/main/resources/org/waveprotocol/wave/client/wavepanel/event/Event.gwt.xml
+++ 
b/wave/src/main/resources/org/waveprotocol/wave/client/wavepanel/event/Event.gwt.xml
@@ -20,6 +20,9 @@
 
 -->
 
-<module><inherits name="com.google.gwt.user.User" /><inherits 
name="com.google.common.base.Base" /><inherits 
name="org.waveprotocol.wave.client.common.util.Util" /><inherits 
name="org.waveprotocol.wave.client.uibuilder.UiBuilder" /><inherits 
name="org.waveprotocol.wave.client.widget.common.Common" /><inherits 
name="org.waveprotocol.wave.model.util.Util" /> <source path=""/>
+<module><inherits name="com.google.gwt.user.User" /><inherits 
name="com.google.common.base.Base" /><inherits 
name="org.waveprotocol.wave.client.common.util.Util" /><inherits 
name="org.waveprotocol.wave.client.uibuilder.UiBuilder" /><inherits 
name="org.waveprotocol.wave.client.widget.common.Common" /><inherits 
name="org.waveprotocol.wave.model.util.Util" />
+    <inherits name="org.waveprotocol.box.stat.Stat"/>
+    <inherits name="org.waveprotocol.box.stat.Stat"/>
+    <source path=""/>
 
 </module>

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/com/google/wave/api/BlipRobotTest.java
----------------------------------------------------------------------
diff --git a/wave/src/test/java/com/google/wave/api/BlipRobotTest.java 
b/wave/src/test/java/com/google/wave/api/BlipRobotTest.java
index d8b33f8..1d48b25 100644
--- a/wave/src/test/java/com/google/wave/api/BlipRobotTest.java
+++ b/wave/src/test/java/com/google/wave/api/BlipRobotTest.java
@@ -458,7 +458,7 @@ public class BlipRobotTest extends TestCase {
 
     Blip expectedBlip = new Blip("blip1", Arrays.asList("blip2", "blip3"),
         "\nhello world!\n another line", Arrays.asList("ro...@test.com", 
"u...@test.com"),
-        "u...@test.com", 1000l, 123l, null, null, Arrays.asList(new 
Annotation("key", "val", 2, 3)),
+        "u...@test.com", 1000l, 123l, null, "<some string>", Arrays.asList(new 
Annotation("key", "val", 2, 3)),
         elements, new ArrayList<String>(), wavelet);
 
     Blip actualBlip = Blip.deserialize(wavelet.getOperationQueue(), wavelet,

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/box/server/frontend/ClientFrontendImplTest.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/box/server/frontend/ClientFrontendImplTest.java
 
b/wave/src/test/java/org/waveprotocol/box/server/frontend/ClientFrontendImplTest.java
index 8a3f1d6..54556e8 100644
--- 
a/wave/src/test/java/org/waveprotocol/box/server/frontend/ClientFrontendImplTest.java
+++ 
b/wave/src/test/java/org/waveprotocol/box/server/frontend/ClientFrontendImplTest.java
@@ -330,8 +330,7 @@ public class ClientFrontendImplTest extends TestCase {
 
   private static List<TransformedWaveletDelta> isDeltasStartingAt(final long 
version) {
     return argThat(new ArgumentMatcher<List<TransformedWaveletDelta>>() {
-      @Override
-      public boolean matches(Object sequence) {
+      public boolean matches(List<TransformedWaveletDelta> sequence) {
         if (sequence != null) {
           DeltaSequence s = (DeltaSequence) sequence;
           return (s.size() > 0) && (s.getStartVersion() == version);

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/box/server/robots/active/ActiveApiServletTest.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/box/server/robots/active/ActiveApiServletTest.java
 
b/wave/src/test/java/org/waveprotocol/box/server/robots/active/ActiveApiServletTest.java
index 9c60294..540ed7b 100644
--- 
a/wave/src/test/java/org/waveprotocol/box/server/robots/active/ActiveApiServletTest.java
+++ 
b/wave/src/test/java/org/waveprotocol/box/server/robots/active/ActiveApiServletTest.java
@@ -119,7 +119,7 @@ public class ActiveApiServletTest extends TestCase {
     String operationId = "op1";
     OperationRequest operation = new OperationRequest("wavelet.create", 
operationId);
     List<OperationRequest> operations = Collections.singletonList(operation);
-    
when(robotSerializer.deserializeOperations(anyString())).thenReturn(operations);
+    when(robotSerializer.deserializeOperations(any())).thenReturn(operations);
     String responseValue = "response value";
     when(robotSerializer.serialize(any(), any(Type.class), 
any(ProtocolVersion.class))).thenReturn(
         responseValue);

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/box/server/robots/dataapi/DataApiServletTest.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/box/server/robots/dataapi/DataApiServletTest.java
 
b/wave/src/test/java/org/waveprotocol/box/server/robots/dataapi/DataApiServletTest.java
index 40e2e63..3bd3e52 100644
--- 
a/wave/src/test/java/org/waveprotocol/box/server/robots/dataapi/DataApiServletTest.java
+++ 
b/wave/src/test/java/org/waveprotocol/box/server/robots/dataapi/DataApiServletTest.java
@@ -121,7 +121,7 @@ public class DataApiServletTest extends TestCase {
     String operationId = "op1";
     OperationRequest operation = new OperationRequest("wavelet.create", 
operationId);
     List<OperationRequest> operations = Collections.singletonList(operation);
-    
when(robotSerializer.deserializeOperations(anyString())).thenReturn(operations);
+    when(robotSerializer.deserializeOperations(any())).thenReturn(operations);
     String responseValue = "response value";
     when(robotSerializer.serialize(any(), any(Type.class), 
any(ProtocolVersion.class))).thenReturn(
         responseValue);

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/box/server/robots/operations/FetchProfilesServiceTest.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/box/server/robots/operations/FetchProfilesServiceTest.java
 
b/wave/src/test/java/org/waveprotocol/box/server/robots/operations/FetchProfilesServiceTest.java
index 6b55728..da6fc93 100644
--- 
a/wave/src/test/java/org/waveprotocol/box/server/robots/operations/FetchProfilesServiceTest.java
+++ 
b/wave/src/test/java/org/waveprotocol/box/server/robots/operations/FetchProfilesServiceTest.java
@@ -19,7 +19,7 @@
 
 package org.waveprotocol.box.server.robots.operations;
 
-import static org.mockito.Matchers.argThat;
+import static org.mockito.hamcrest.MockitoHamcrest.argThat;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/box/server/robots/operations/SearchServiceTest.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/box/server/robots/operations/SearchServiceTest.java
 
b/wave/src/test/java/org/waveprotocol/box/server/robots/operations/SearchServiceTest.java
index 7b2f848..bc5113f 100644
--- 
a/wave/src/test/java/org/waveprotocol/box/server/robots/operations/SearchServiceTest.java
+++ 
b/wave/src/test/java/org/waveprotocol/box/server/robots/operations/SearchServiceTest.java
@@ -19,7 +19,7 @@
 
 package org.waveprotocol.box.server.robots.operations;
 
-import static org.mockito.Matchers.argThat;
+import static org.mockito.hamcrest.MockitoHamcrest.argThat;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/box/server/robots/passive/RobotTest.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/box/server/robots/passive/RobotTest.java 
b/wave/src/test/java/org/waveprotocol/box/server/robots/passive/RobotTest.java
index d551a3e..c5fa248 100644
--- 
a/wave/src/test/java/org/waveprotocol/box/server/robots/passive/RobotTest.java
+++ 
b/wave/src/test/java/org/waveprotocol/box/server/robots/passive/RobotTest.java
@@ -204,7 +204,7 @@ public class RobotTest extends TestCase {
     EventMessageBundle messages = new 
EventMessageBundle(ROBOT_NAME.toEmailAddress(), "");
     messages.addEvent(new DocumentChangedEvent(null, null, ALEX.getAddress(), 
0L, "b+1234"));
     when(eventGenerator.generateEvents(
-        any(WaveletAndDeltas.class), anyMap(), 
any(EventDataConverter.class))).thenReturn(messages);
+        any(), anyMap(), any())).thenReturn(messages);
 
     OperationRequest op = new OperationRequest("wavelet.fetch", "op1");
     List<OperationRequest> ops = Collections.singletonList(op);

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/box/server/robots/testing/OperationServiceHelper.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/box/server/robots/testing/OperationServiceHelper.java
 
b/wave/src/test/java/org/waveprotocol/box/server/robots/testing/OperationServiceHelper.java
new file mode 100644
index 0000000..814f972
--- /dev/null
+++ 
b/wave/src/test/java/org/waveprotocol/box/server/robots/testing/OperationServiceHelper.java
@@ -0,0 +1,120 @@
+/**
+ * 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.box.server.robots.testing;
+
+import static org.mockito.Mockito.mock;
+
+import com.google.wave.api.data.converter.EventDataConverter;
+import com.google.wave.api.data.converter.v22.EventDataConverterV22;
+
+import org.waveprotocol.box.server.robots.OperationContextImpl;
+import org.waveprotocol.box.server.robots.RobotWaveletData;
+import org.waveprotocol.box.server.robots.operations.OperationService;
+import org.waveprotocol.box.server.robots.util.ConversationUtil;
+import org.waveprotocol.box.server.waveserver.WaveletProvider;
+import org.waveprotocol.wave.model.conversation.ObservableConversation;
+import org.waveprotocol.wave.model.conversation.WaveletBasedConversation;
+import org.waveprotocol.wave.model.id.IdURIEncoderDecoder;
+import org.waveprotocol.wave.model.id.WaveletName;
+import org.waveprotocol.wave.model.operation.SilentOperationSink;
+import 
org.waveprotocol.wave.model.operation.wave.BasicWaveletOperationContextFactory;
+import org.waveprotocol.wave.model.operation.wave.WaveletOperation;
+import org.waveprotocol.wave.model.testing.BasicFactories;
+import org.waveprotocol.wave.model.testing.FakeIdGenerator;
+import org.waveprotocol.wave.model.version.HashedVersionFactory;
+import org.waveprotocol.wave.model.version.HashedVersionFactoryImpl;
+import org.waveprotocol.wave.model.wave.ParticipantId;
+import org.waveprotocol.wave.model.wave.ParticipationHelper;
+import org.waveprotocol.wave.model.wave.data.DocumentFactory;
+import org.waveprotocol.wave.model.wave.data.DocumentOperationSink;
+import org.waveprotocol.wave.model.wave.data.ObservableWaveletData;
+import org.waveprotocol.wave.model.wave.data.WaveletData;
+import org.waveprotocol.wave.model.wave.data.impl.EmptyWaveletSnapshot;
+import org.waveprotocol.wave.model.wave.data.impl.WaveletDataImpl;
+import org.waveprotocol.wave.model.wave.opbased.OpBasedWavelet;
+import org.waveprotocol.wave.util.escapers.jvm.JavaUrlCodec;
+
+/**
+ * Helper for testing {@link OperationService}. Puts a single empty
+ * conversational wavelet with one participant in the operation context.
+ *
+ * @author ljvder...@google.com (Lennard de Rijk)
+ */
+public class OperationServiceHelper {
+
+  private static final IdURIEncoderDecoder URI_CODEC =
+      new IdURIEncoderDecoder(new JavaUrlCodec());
+  private static final HashedVersionFactory HASH_FACTORY = new 
HashedVersionFactoryImpl(URI_CODEC);
+  private static final DocumentFactory<? extends DocumentOperationSink> 
DOCUMENT_FACTORY =
+      BasicFactories.observablePluggableMutableDocumentFactory();
+
+  private final WaveletProvider waveletProvider;
+  private final OperationContextImpl context;
+
+  /**
+   * Constructs a new {@link OperationServiceHelper} with a wavelet with the
+   * name and participant that are passed in.
+   *
+   * @param waveletName the name of the empty wavelet to open in the context.
+   * @param participant the participant that should be on that empty wavelet.
+   */
+  public OperationServiceHelper(WaveletName waveletName, ParticipantId 
participant) {
+    waveletProvider = mock(WaveletProvider.class);
+    EventDataConverter converter = new EventDataConverterV22();
+
+    ObservableWaveletData waveletData = 
WaveletDataImpl.Factory.create(DOCUMENT_FACTORY).create(
+        new EmptyWaveletSnapshot(waveletName.waveId, waveletName.waveletId, 
participant,
+            HASH_FACTORY.createVersionZero(waveletName), 0L));
+    waveletData.addParticipant(participant);
+
+    BasicWaveletOperationContextFactory CONTEXT_FACTORY =
+        new BasicWaveletOperationContextFactory(participant);
+
+    SilentOperationSink<WaveletOperation> executor =
+        SilentOperationSink.Executor.<WaveletOperation, 
WaveletData>build(waveletData);
+    OpBasedWavelet wavelet =
+        new OpBasedWavelet(waveletData.getWaveId(), waveletData, 
CONTEXT_FACTORY,
+            ParticipationHelper.DEFAULT, executor, SilentOperationSink.VOID);
+
+    // Make a conversation with an empty root blip
+    WaveletBasedConversation.makeWaveletConversational(wavelet);
+    ConversationUtil conversationUtil = new 
ConversationUtil(FakeIdGenerator.create());
+    ObservableConversation conversation = 
conversationUtil.buildConversation(wavelet).getRoot();
+    conversation.getRootThread().appendBlip();
+
+    context = new OperationContextImpl(waveletProvider, converter, 
conversationUtil);
+    context.putWavelet(waveletName.waveId, waveletName.waveletId,
+        new RobotWaveletData(waveletData, 
HASH_FACTORY.createVersionZero(waveletName)));
+  }
+
+  /**
+   * @return the {@link WaveletProvider} mock
+   */
+  public WaveletProvider getWaveletProvider() {
+    return waveletProvider;
+  }
+
+  /**
+   * @return the {@link OperationContextImpl} with the empty wavelet opened.
+   */
+  public OperationContextImpl getContext() {
+    return context;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/box/server/rpc/AuthenticationServletTest.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/box/server/rpc/AuthenticationServletTest.java
 
b/wave/src/test/java/org/waveprotocol/box/server/rpc/AuthenticationServletTest.java
index 89c705e..3b006c2 100644
--- 
a/wave/src/test/java/org/waveprotocol/box/server/rpc/AuthenticationServletTest.java
+++ 
b/wave/src/test/java/org/waveprotocol/box/server/rpc/AuthenticationServletTest.java
@@ -185,7 +185,7 @@ public class AuthenticationServletTest extends TestCase {
     // Servlet control flow forces us to set these return values first and
     // verify the logged in user was set afterwards.
     if (expectSuccess) {
-      
when(manager.getLoggedInUser(Mockito.any(HttpSession.class))).thenReturn(USER);
+      when(manager.getLoggedInUser(Mockito.any())).thenReturn(USER);
       when(session.getAttribute("user")).thenReturn(USER);
     }
     servlet.doPost(req, resp);

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/box/server/rpc/testing/FakeServerRpcController.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/box/server/rpc/testing/FakeServerRpcController.java
 
b/wave/src/test/java/org/waveprotocol/box/server/rpc/testing/FakeServerRpcController.java
new file mode 100644
index 0000000..fa45cd6
--- /dev/null
+++ 
b/wave/src/test/java/org/waveprotocol/box/server/rpc/testing/FakeServerRpcController.java
@@ -0,0 +1,84 @@
+/**
+ * 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.box.server.rpc.testing;
+
+import static org.waveprotocol.box.server.util.testing.TestingConstants.USER;
+
+import com.google.protobuf.RpcCallback;
+
+import org.waveprotocol.box.server.rpc.ServerRpcController;
+import org.waveprotocol.wave.model.wave.ParticipantId;
+
+
+/**
+  * An {@code RpcController} that just handles error text and failure 
condition.
+  */
+public class FakeServerRpcController implements ServerRpcController {
+  private boolean failed = false;
+  private String errorText = null;
+
+  @Override
+  public String errorText() {
+    return errorText;
+  }
+
+  @Override
+  public boolean failed() {
+    return failed;
+  }
+
+  @Override
+  public boolean isCanceled() {
+    return false;
+  }
+
+  @Override
+  public void notifyOnCancel(RpcCallback<Object> arg) {
+  }
+
+  @Override
+  public void reset() {
+    failed = false;
+    errorText = null;
+  }
+
+  @Override
+  public void setFailed(String error) {
+    failed = true;
+    errorText = error;
+  }
+
+  @Override
+  public void startCancel() {
+  }
+
+  @Override
+  public ParticipantId getLoggedInUser() {
+    return ParticipantId.ofUnsafe(USER);
+  }
+
+  @Override
+  public void cancel() {
+  }
+
+  @Override
+  public void run() {
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/box/server/util/testing/ExceptionLogHandler.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/box/server/util/testing/ExceptionLogHandler.java
 
b/wave/src/test/java/org/waveprotocol/box/server/util/testing/ExceptionLogHandler.java
new file mode 100644
index 0000000..7ea3550
--- /dev/null
+++ 
b/wave/src/test/java/org/waveprotocol/box/server/util/testing/ExceptionLogHandler.java
@@ -0,0 +1,65 @@
+/**
+ * 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.box.server.util.testing;
+
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+
+/**
+ * A log handler which throws a runtime exception at and above a set log level.
+ * Useful for catching erroneous conditions during testing, which are only
+ * reported to the logs.
+ *
+ * @author mk.mat...@gmail.com (Michael Kuntzman)
+ */
+public class ExceptionLogHandler extends Handler {
+  /** The integer value of the minimum fatal log level. */
+  private final int fatalLevel;
+
+  /**
+   * Constructs an ExceptionLogHandler that throws a runtime exception at and 
above the specified
+   * log level.
+   *
+   * @param fatalLevel the minimum log level for which to throw an exception.
+   */
+  public ExceptionLogHandler(Level fatalLevel) {
+    this.fatalLevel = fatalLevel.intValue();
+  }
+
+  /**
+   * @throws RuntimeException if the log record is at or above the fatal log 
level.
+   */
+  @Override
+  public void publish(LogRecord record) {
+    if (record.getLevel().intValue() >= fatalLevel) {
+      throw new RuntimeException(record.getLevel() + ": " + 
record.getMessage(),
+          record.getThrown());
+    }
+  }
+
+  @Override
+  public void flush() {
+  }
+
+  @Override
+  public void close() {
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/box/server/util/testing/Matchers.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/box/server/util/testing/Matchers.java 
b/wave/src/test/java/org/waveprotocol/box/server/util/testing/Matchers.java
new file mode 100644
index 0000000..72be27a
--- /dev/null
+++ b/wave/src/test/java/org/waveprotocol/box/server/util/testing/Matchers.java
@@ -0,0 +1,172 @@
+/**
+ * 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.box.server.util.testing;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+
+import java.util.Collection;
+
+/**
+ * Additional matchers to go with JUnit4's assertThat and assumeThat.
+ *
+ * @author mk.mat...@gmail.com (Michael Kuntzman)
+ */
+// TODO(Michael): Maybe move this class to the libraries repository/branch.
+public class Matchers {
+  /**
+   * Nicer aliases for some of the methods in this class, which may conflict 
with methods in other
+   * packages (potential conficts noted for each alias).
+   */
+  public static class Aliases {
+    /**
+     * Alias for "containsString". May conflict with 
"org.mockito.Mockito.contains".
+     *
+     * @param substring to look for.
+     * @return a matcher for checking that a string contains the specified 
substring.
+     */
+    public static TypeSafeMatcher<String> contains(final String substring) {
+      return containsString(substring);
+    }
+
+    /**
+     * Alias for "matchesRegex". May conflict with 
"org.mockito.Mockito.matches".
+     *
+     * @param regularExpression to match against.
+     * @return a matcher for checking that a string matches the specified 
regular expression.
+     */
+    public static TypeSafeMatcher<String> matches(final String 
regularExpression) {
+      return matchesRegex(regularExpression);
+    }
+  }
+
+  /**
+   * A more user-friendly version of 
org.junit.matchers.JUnitMatchers.hasItem(T element). Allows a
+   * more verbose failure than assertTrue(collection.contains(item)). The 
matcher produces
+   * "Expected: a collection containing '...' got: '...'", whereas assertTrue 
produces merely
+   * "AssertionFailedError".
+   * Usage: static import, then assertThat(collection, contains(item)).
+   *
+   * @param item to look for.
+   * @return a matcher for checking that a collection contains the specified 
item.
+   */
+  public static <T> TypeSafeMatcher<Collection<? super T>> contains(final T 
item) {
+    return new TypeSafeMatcher<Collection<? super T>>() {
+          @Override
+          public boolean matchesSafely(Collection<? super T> collection) {
+            return collection.contains(item);
+          }
+
+          @Override
+          public void describeTo(Description description) {
+            description.appendText("a collection containing 
").appendValue(item);
+          }
+        };
+  }
+
+  /**
+   * Same as JUnitMatchers.containsString. Allows a more verbose failure than
+   * assertTrue(str.contains(substring)).
+   * Usage: static import, then assertThat(str, containsString(substring)).
+   *
+   * @param substring to look for.
+   * @return a matcher for checking that a string contains the specified 
substring.
+   */
+  public static TypeSafeMatcher<String> containsString(final String substring) 
{
+    return new TypeSafeMatcher<String>() {
+          @Override
+          public boolean matchesSafely(String str) {
+            return str.contains(substring);
+          }
+
+          @Override
+          public void describeTo(Description description) {
+            description.appendText("a string containing 
").appendValue(substring);
+          }
+        };
+  }
+
+  /**
+   * The negative version of "contains" for a collection. Allows a more 
verbose failure than
+   * assertFalse(collection.contains(item)).
+   * Usage: static import, then assertThat(collection, doesNotContain(item)).
+   *
+   * @param item to look for.
+   * @return a matcher for checking that a collection does not contain the 
specified item.
+   */
+  public static <T> TypeSafeMatcher<Collection<? super T>> 
doesNotContain(final T item) {
+    return new TypeSafeMatcher<Collection<? super T>>() {
+          @Override
+          public boolean matchesSafely(Collection<? super T> collection) {
+            return !collection.contains(item);
+          }
+
+          @Override
+          public void describeTo(Description description) {
+            description.appendText("a collection NOT containing 
").appendValue(item);
+          }
+        };
+  }
+
+  /**
+   * The negative version of "contains" for a string (or "containsString"). 
Allows a more verbose
+   * failure than assertFalse(str.contains(substring)).
+   * Usage: static import, then assertThat(str, doesNotContain(substring)).
+   *
+   * @param substring to look for.
+   * @return a matcher for checking that a string contains the specified 
substring.
+   */
+  public static TypeSafeMatcher<String> doesNotContain(final String substring) 
{
+    return new TypeSafeMatcher<String>() {
+          @Override
+          public boolean matchesSafely(String str) {
+            return !str.contains(substring);
+          }
+
+          @Override
+          public void describeTo(Description description) {
+            description.appendText("a string NOT containing 
").appendValue(substring);
+          }
+        };
+  }
+
+  /**
+   * Allows a more verbose failure than assertTrue(str.matches(regex)). The 
matcher produces
+   * "Expected: a string matching regex '...' got: '...'", whereas assertTrue 
produces merely
+   * "AssertionFailedError".
+   * Usage: static import, then assertThat(str, matchesRegex(regex)).
+   *
+   * @param regularExpression to match against.
+   * @return a matcher for checking that a string matches the specified 
regular expression.
+   */
+  public static TypeSafeMatcher<String> matchesRegex(final String 
regularExpression) {
+    return new TypeSafeMatcher<String>() {
+          @Override
+          public boolean matchesSafely(String str) {
+            return str.matches(regularExpression);
+          }
+
+          @Override
+          public void describeTo(Description description) {
+            description.appendText("a string matching regex 
").appendValue(regularExpression);
+          }
+        };
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/box/server/util/testing/TestingConstants.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/box/server/util/testing/TestingConstants.java
 
b/wave/src/test/java/org/waveprotocol/box/server/util/testing/TestingConstants.java
new file mode 100644
index 0000000..ed0d95f
--- /dev/null
+++ 
b/wave/src/test/java/org/waveprotocol/box/server/util/testing/TestingConstants.java
@@ -0,0 +1,66 @@
+/**
+ * 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.box.server.util.testing;
+
+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.wave.ParticipantId;
+
+/**
+ * Commonly used constants for unit testing. Some constants taken from
+ * previously existing test cases.
+ *
+ * @author mk.mat...@gmail.com (Michael Kuntzman)
+ */
+// TODO(Michael): Maybe move this class to the libraries repository/branch.
+public interface TestingConstants {
+  public static final String BLIP_ID = "b+blip";
+
+  public static final String MESSAGE = "The quick brown fox jumps over the 
lazy dog";
+
+  public static final String MESSAGE2 = "Why's the rum gone?";
+
+  public static final String MESSAGE3 = "There is no spoon";
+
+  public static final String DOMAIN = "host.com";
+
+  public static final String OTHER_USER_NAME = "other";
+
+  public static final String OTHER_USER = OTHER_USER_NAME + "@" + DOMAIN;
+
+  public static final ParticipantId OTHER_PARTICIPANT = new 
ParticipantId(OTHER_USER);
+
+  public static final int PORT = 9876;
+
+  public static final String USER_NAME = "user";
+
+  public static final String USER = USER_NAME + "@" + DOMAIN;
+
+  public static final char[] PASSWORD = "password".toCharArray();
+
+  public static final ParticipantId PARTICIPANT = new ParticipantId(USER);
+
+  public static final WaveId WAVE_ID = WaveId.of(DOMAIN, "w+wave");
+
+  public static final WaveletId WAVELET_ID = WaveletId.of(DOMAIN, "wavelet");
+
+  public static final WaveletName WAVELET_NAME = WaveletName.of(WAVE_ID, 
WAVELET_ID);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/client/editor/extract/PasteFormatRendererGwtTest.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/wave/client/editor/extract/PasteFormatRendererGwtTest.java
 
b/wave/src/test/java/org/waveprotocol/wave/client/editor/extract/PasteFormatRendererGwtTest.java
index 83d34c3..d5275cf 100644
--- 
a/wave/src/test/java/org/waveprotocol/wave/client/editor/extract/PasteFormatRendererGwtTest.java
+++ 
b/wave/src/test/java/org/waveprotocol/wave/client/editor/extract/PasteFormatRendererGwtTest.java
@@ -87,7 +87,7 @@ public class PasteFormatRendererGwtTest extends GWTTestCase {
     testHelper("<body><line/>before<line t=\"li\"/>one<line 
t=\"li\"/>two<line/>after</body>",
         "before<br><ul><li>one</li><li>two</li></ul>after<br>");
     testHelper("<body><line t=\"li\"/><input>hello</input> 
world<line/>after</body>",
-    "<ul><li><span>hello</span> world</li></ul>after<br>");
+    "<ul><li><span contentEditable=\"true\" style=\"white-space: pre-wrap;\" 
class=\"SWCNL\">hello</span> world</li></ul>after<br>");
   }
 
   private void testHelper(String content, String expectedResult) {

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/client/editor/integration/ParagraphGwtTest.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/wave/client/editor/integration/ParagraphGwtTest.java
 
b/wave/src/test/java/org/waveprotocol/wave/client/editor/integration/ParagraphGwtTest.java
index 8ea86ee..8fb854c 100644
--- 
a/wave/src/test/java/org/waveprotocol/wave/client/editor/integration/ParagraphGwtTest.java
+++ 
b/wave/src/test/java/org/waveprotocol/wave/client/editor/integration/ParagraphGwtTest.java
@@ -84,7 +84,7 @@ public class ParagraphGwtTest extends ElementTestBase {
 //    testTaller("<p></p><p></p>", "<p></p>", minHeight);
 //    testTaller("<p></p><p></p><p></p>", "<p></p><p></p>", minHeight);
 
-    testEqualHeight(format("<p></p>"), format("<p>aXj</p>"));
+    testEqualHeight(format("<p>a</p>"), format("<p>aXj</p>"));
 //    testEqualHeight("<p></p><p></p>", "<p>aXj</p><p>aXjADFSG</p>");
 
     testContentWrap("<p>|</p>");

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/client/scheduler/IdempotentSchedulerTest.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/wave/client/scheduler/IdempotentSchedulerTest.java
 
b/wave/src/test/java/org/waveprotocol/wave/client/scheduler/IdempotentSchedulerTest.java
index 27f062b..3082226 100644
--- 
a/wave/src/test/java/org/waveprotocol/wave/client/scheduler/IdempotentSchedulerTest.java
+++ 
b/wave/src/test/java/org/waveprotocol/wave/client/scheduler/IdempotentSchedulerTest.java
@@ -19,17 +19,17 @@
 
 package org.waveprotocol.wave.client.scheduler;
 
+import junit.framework.TestCase;
 import org.waveprotocol.wave.client.scheduler.Scheduler.IncrementalTask;
 
-import org.jmock.Expectations;
-import org.jmock.integration.junit3.MockObjectTestCase;
+import static org.mockito.Mockito.*;
 
 /**
  * Test case for IdempotentScheduler.
  *
  */
 
-public class IdempotentSchedulerTest extends MockObjectTestCase {
+public class IdempotentSchedulerTest extends TestCase {
 
   // Mocks.
   private IncrementalTask task;
@@ -49,84 +49,57 @@ public class IdempotentSchedulerTest extends 
MockObjectTestCase {
   }
 
   public void testSchedulesItselfAsTheTask() {
-    checking(new Expectations() {{
-        oneOf(timer).isScheduled(with(is));
-        will(returnValue(false));
-        oneOf(timer).scheduleRepeating(is, delay, delay);
-    }});
-
     is.schedule();
+
+    verify(timer, times(1)).isScheduled(is);
+    verify(timer, times(1)).scheduleRepeating(is, delay, delay);
   }
 
   public void testCancelsItselfAsTheTask() {
-    checking(new Expectations() {{
-      oneOf(timer).isScheduled(with(is));
-      will(returnValue(true));
-      oneOf(timer).cancel(is);
-    }});
-
+    when(timer.isScheduled(is)).thenReturn(true);
     is.cancel();
+    verify(timer, times(1)).cancel(is);
   }
   public void testSchedulerExecutesTask() {
-    checking(new Expectations() {{
-      oneOf(task).execute();
-    }});
-
     is.execute();
+    verify(task, times(1)).execute();
   }
 
   public void testMultipleScheduleCallsScheduleExactlyOnce() {
-    checking(new Expectations() {{
-      oneOf(timer).isScheduled(with(is));
-      will(returnValue(false));
-      oneOf(timer).scheduleRepeating(is, delay, delay);
-      oneOf(timer).isScheduled(with(is));
-      will(returnValue(true));
-    }});
+    when(timer.isScheduled(is)).thenReturn(true, false);
 
     is.schedule();
     is.schedule();
+
+    verify(timer, times(2)).isScheduled(is);
+    verify(timer, times(1)).scheduleRepeating(is, delay, delay);
   }
 
   public void testCancelWillNotCancelIfNotScheduled() {
-    checking(new Expectations() {{
-      oneOf(timer).isScheduled(with(is));
-      will(returnValue(false));
-    }});
-
+    when(timer.isScheduled(is)).thenReturn(false);
     is.cancel();
+    verify(timer, never()).cancel(is);
   }
 
   public void testWillRescheduleAfterTaskCompletion() {
-    checking(new Expectations() {{
-      oneOf(timer).isScheduled(with(is));
-      will(returnValue(false));
-      oneOf(timer).scheduleRepeating(is, delay, delay);
-      oneOf(task).execute();
-      will(returnValue(false));  // Indicates termination
-      oneOf(timer).isScheduled(with(is));
-      will(returnValue(false));
-      oneOf(timer).scheduleRepeating(is, delay, delay);
-    }});
+    when(timer.isScheduled(is)).thenReturn(false);
+    when(task.execute()).thenReturn(false);
 
     is.schedule();
     is.execute();  // Simulates call by timer.
     is.schedule();
+
+    verify(timer, times(2)).scheduleRepeating(is, delay, delay);
   }
 
   public void testWillRescheduleAfterCancel() {
-    checking(new Expectations() {{
-      oneOf(timer).isScheduled(with(is)); will(returnValue(false));
-      oneOf(timer).scheduleRepeating(is, delay, delay);
-      oneOf(timer).isScheduled(with(is)); will(returnValue(true));
-      oneOf(timer).cancel(is);
-      oneOf(timer).isScheduled(with(is));
-      will(returnValue(false));
-      oneOf(timer).scheduleRepeating(is, delay, delay);
-    }});
+    when(timer.isScheduled(is)).thenReturn(false, true, false);
 
     is.schedule();
     is.cancel();
     is.schedule();
+
+    verify(timer, times(1)).cancel(is);
+    verify(timer, times(2)).scheduleRepeating(is, delay, delay);
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/client/scheduler/knobs/ControllerTest.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/wave/client/scheduler/knobs/ControllerTest.java
 
b/wave/src/test/java/org/waveprotocol/wave/client/scheduler/knobs/ControllerTest.java
index 7cc062d..2f17a92 100644
--- 
a/wave/src/test/java/org/waveprotocol/wave/client/scheduler/knobs/ControllerTest.java
+++ 
b/wave/src/test/java/org/waveprotocol/wave/client/scheduler/knobs/ControllerTest.java
@@ -19,21 +19,21 @@
 
 package org.waveprotocol.wave.client.scheduler.knobs;
 
+import junit.framework.TestCase;
 import org.waveprotocol.wave.client.scheduler.Scheduler.Priority;
 import org.waveprotocol.wave.client.scheduler.Scheduler.Schedulable;
-
-import org.jmock.Expectations;
-import org.jmock.integration.junit3.MockObjectTestCase;
 import org.waveprotocol.wave.model.util.ReadableStringSet;
 
 import java.util.Collection;
 
+import static org.mockito.Mockito.*;
+
 /**
  * Tests the controller components for the scheduler.
  *
  */
 
-public class ControllerTest extends MockObjectTestCase {
+public class ControllerTest extends TestCase {
 
   /**
    * Stub implementation of a per-level UI control.  Used both as a dummy and a
@@ -107,54 +107,20 @@ public class ControllerTest extends MockObjectTestCase {
     knobView = mock(KnobView.class);
     knobsView = mock(KnobsView.class);
     stubView = new StubLevelView();
+    for (Priority p : Priority.values()) {
+      when(knobsView.create(p)).thenReturn(p.equals(Priority.MEDIUM) ? 
stubView : new StubLevelView());
+    }
   }
 
-  /**
-   * Tells jmock to accept the things that happen to the mock level-view
-   * when it gets injected into a level presenter.
-   */
-  private void allowKnobViewSetup() {
-    /// Sigh, Jmock = tight coupling with impl details :(
-    checking(new Expectations() {{
-      // Stuff that happens in constructor
-      one(knobView).init(with(any(KnobPresenter.class)));
-      one(knobView).enable();
-      one(knobView).showCount(0);
-      one(knobView).hideJobs();
-    }});
-  }
-
-  /**
-   * Tells jmock to accept the things that happen to the mock knob-view
-   * when it gets injected into a presenter.  Also injects {@link #stubView} as
-   * the per-level view of the MEDIUM priority level.
-   */
-  private void allowKnobsViewSetup() {
-    checking(new Expectations() {{
-      for (Priority p : Priority.values()) {
-        one(knobsView).create(p);
-            will(returnValue(p.equals(Priority.MEDIUM) ? stubView : new 
StubLevelView()));
-       }
-    }});
-  }
-
-  //
-  // Tests below.
-  //
-
   public void testLevelPresenterInitialStateAndClicking() {
-    allowKnobViewSetup();
     KnobPresenter presenter = new KnobPresenter(knobView);
 
-    checking(new Expectations() {{
-      one(knobView).disable();
-    }});
     presenter.onClicked();
+    verify(knobView, times(1)).disable();
+
 
-    checking(new Expectations() {{
-      one(knobView).enable();
-    }});
     presenter.onClicked();
+    verify(knobView, times(2)).enable(); // is enabled by default
   }
 
   public void testLevelEnabledAndDisabled() {
@@ -167,12 +133,10 @@ public class ControllerTest extends MockObjectTestCase {
   }
 
   public void testKnobsAddLevelForEachPriority() {
-    allowKnobsViewSetup();
     KnobsPresenter presenter = new KnobsPresenter(knobsView);
   }
 
   public void testClickingOnLevelTogglesRunnability() {
-    allowKnobsViewSetup();
     KnobsPresenter presenter = new KnobsPresenter(knobsView);
 
     assertNotNull(stubView.getListener());
@@ -184,7 +148,6 @@ public class ControllerTest extends MockObjectTestCase {
   }
 
   public void testUpdatingMediumJobCountUpdatesView() {
-    allowKnobsViewSetup();
     KnobsPresenter presenter = new KnobsPresenter(knobsView);
 
     Schedulable a = new Schedulable(){};

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/client/scheduler/testing/FakeTimerServiceTest.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/wave/client/scheduler/testing/FakeTimerServiceTest.java
 
b/wave/src/test/java/org/waveprotocol/wave/client/scheduler/testing/FakeTimerServiceTest.java
index 83fd4de..580b25d 100644
--- 
a/wave/src/test/java/org/waveprotocol/wave/client/scheduler/testing/FakeTimerServiceTest.java
+++ 
b/wave/src/test/java/org/waveprotocol/wave/client/scheduler/testing/FakeTimerServiceTest.java
@@ -19,20 +19,19 @@
 
 package org.waveprotocol.wave.client.scheduler.testing;
 
+import junit.framework.TestCase;
+import org.mockito.InOrder;
 import org.waveprotocol.wave.client.scheduler.Scheduler.IncrementalTask;
 import org.waveprotocol.wave.client.scheduler.Scheduler.Task;
 
-import org.jmock.integration.junit3.MockObjectTestCase;
-import org.jmock.Expectations;
-import org.jmock.Sequence;
+import static org.mockito.Mockito.*;
 
 /**
  * The FakeTimerService is a complicated enough fake that it deserves its own
  * tests. (Normally I wouldn't test fakes.)
  *
  */
-
-public class FakeTimerServiceTest extends MockObjectTestCase {
+public class FakeTimerServiceTest extends TestCase {
   private Task oneoff;
   private IncrementalTask repeating;
   private FakeTimerService timer;
@@ -43,6 +42,7 @@ public class FakeTimerServiceTest extends MockObjectTestCase {
 
     oneoff = mock(Task.class, "oneoff");
     repeating = mock(IncrementalTask.class, "repeating");
+    when(repeating.execute()).thenReturn(true);
     timer = new FakeTimerService();
   }
 
@@ -50,31 +50,22 @@ public class FakeTimerServiceTest extends 
MockObjectTestCase {
     timer.scheduleDelayed(oneoff, 500);
     timer.scheduleRepeating(repeating, 0, 1000);
 
-    checking(new Expectations() {{
-      one(oneoff).execute();
-      exactly(2).of(repeating).execute();
-      will(returnValue(true));
-    }});
     timer.tick(1000);
     timer.tick(500);
     timer.tick(499);
-    checking(new Expectations() {{
-      one(repeating).execute();
-      will(returnValue(true));
-    }});
+    verify(oneoff, times(1)).execute();
+    verify(repeating, times(2)).execute();
+
     timer.tick(1);
     timer.tick(1);
     timer.tick(1);
-    checking(new Expectations() {{
-      one(repeating).execute();
-      will(returnValue(true));
-    }});
+    verify(repeating, times(3)).execute();
+
     timer.tick(999);
-    checking(new Expectations() {{
-      exactly(3).of(repeating).execute();
-      will(returnValue(true));
-    }});
+    verify(repeating, times(4)).execute();
+
     timer.tick(3000);
+    verify(repeating, times(7)).execute();
   }
 
   public void testCancel() {
@@ -83,22 +74,17 @@ public class FakeTimerServiceTest extends 
MockObjectTestCase {
     timer.scheduleRepeating(repeating, 0, 1000);
     timer.cancel(repeating);
 
-    checking(new Expectations() {{
-      never(repeating).execute();
-      never(oneoff).execute();
-    }});
-
     timer.tick(10 * 1000);
+
+    verify(oneoff, never()).execute();
+    verify(repeating, never()).execute();
   }
 
   public void testScheduleWillExecuteImmediatelyOnAnyTick() {
     timer.schedule(oneoff);
 
-    checking(new Expectations() {{
-      one(oneoff).execute();
-    }});
-
     timer.tick(0);
+    verify(oneoff, times(1)).execute();
   }
 
     /** Tests that scheduling a task with negative start time throws an 
exception. */
@@ -134,14 +120,12 @@ public class FakeTimerServiceTest extends 
MockObjectTestCase {
   public void testInterval0() {
     timer.scheduleRepeating(repeating, 500, 0);
     timer.tick(499);
-    checking(new Expectations() {{
-      exactly(5).of(repeating).execute();
-      will(returnValue(true));
-      one(repeating).execute();
-      will(returnValue(false));
-      never(repeating).execute();
-    }});
+
+    verify(repeating, never()).execute();
+    when(repeating.execute()).thenReturn(true, true, true, true, false);
+
     timer.tick(1);
+    verify(repeating, times(5)).execute();
   }
 
   /**
@@ -153,42 +137,39 @@ public class FakeTimerServiceTest extends 
MockObjectTestCase {
     final IncrementalTask[] tasks = new IncrementalTask[taskCount];
     for (int i = 0; i < taskCount; i++) {
       tasks[i] = mock(IncrementalTask.class, "repeating_" + i);
+      if (i==0) {
+        when(tasks[i].execute()).thenReturn(true, false);
+      } else {
+        when(tasks[i].execute()).thenReturn(false);
+      }
       timer.scheduleRepeating(tasks[i], 1, 0);
     }
-    final Sequence seq = sequence("callOrder");
-    checking(new Expectations() {{
-      for (IncrementalTask task : tasks) {
-        one(task).execute();
-        will(returnValue(true));
-        inSequence(seq);
-      }
-      for (IncrementalTask task : tasks) {
-        one(task).execute();
-        will(returnValue(false));
-        inSequence(seq);
 
-        never(task).execute();
-      }
-    }});
+    InOrder inOrder = inOrder((Object[]) tasks);
+
     timer.tick(1);
+
+    for (int i = 0; i < taskCount; i++) {
+      inOrder.verify(tasks[i]).execute();
+    }
+    inOrder.verify(tasks[0]).execute();
+
+    verify(tasks[0], times(2)).execute();
   }
 
   public void testReschedulingCancelsFirst() {
     timer.scheduleDelayed(oneoff, 500);
 
-    checking(new Expectations() {{
-      never(oneoff).execute();
-    }});
-
     timer.tick(499);
+    verify(oneoff, never()).execute();
+
     timer.scheduleDelayed(oneoff, 1000);
-    timer.tick(2);
 
-    checking(new Expectations() {{
-      one(oneoff).execute();
-    }});
+    timer.tick(2);
+    verify(oneoff, never()).execute();
 
     timer.tick(1000);
+    verify(oneoff, times(1)).execute();
   }
 
   /**
@@ -200,11 +181,10 @@ public class FakeTimerServiceTest extends 
MockObjectTestCase {
     timer.scheduleDelayed(oneoff, time);
     final Task anotherTask = mock(Task.class, "another_task");
     timer.scheduleDelayed(anotherTask, time);
-    checking(new Expectations() {{
-      one(oneoff).execute();
-      one(anotherTask).execute();
-    }});
+
     timer.tick(time);
+    verify(oneoff, times(1)).execute();
+    verify(anotherTask, times(1)).execute();
   }
 
   /** Tests that multiple processes can be run at the same time. */
@@ -214,19 +194,12 @@ public class FakeTimerServiceTest extends 
MockObjectTestCase {
     timer.scheduleRepeating(repeating, 0, time);
     timer.scheduleRepeating(anotherTask, time, time);
 
-    checking(new Expectations() {{
-      one(repeating).execute();
-      will(returnValue(true));
-    }});
     timer.tick(0);
+    verify(repeating, times(1)).execute();
 
-    checking(new Expectations() {{
-      one(repeating).execute();
-      will(returnValue(true));
-      one(anotherTask).execute();
-      will(returnValue(true));
-    }});
     timer.tick(time);
+    verify(repeating, times(2)).execute();
+    verify(anotherTask, times(1)).execute();
   }
 
   /**
@@ -235,12 +208,11 @@ public class FakeTimerServiceTest extends 
MockObjectTestCase {
    */
   public void testRepeatedTaskMakesUpForMissedExecutions() {
     timer.scheduleRepeating(repeating, 5, 10);
+
     timer.tick(4);
-    checking(new Expectations() {{
-      exactly(10).of(repeating).execute();
-      will(returnValue(true));
-    }});
+    verify(repeating, never()).execute();
     timer.tick(99);
+    verify(repeating, times(10)).execute();
   }
 
   /**
@@ -249,14 +221,12 @@ public class FakeTimerServiceTest extends 
MockObjectTestCase {
    */
   public void testIncrementalTaskIsCanceledIfItReturnsFalse() {
     timer.scheduleRepeating(repeating, 0, 1);
-    checking(new Expectations() {{
-      exactly(10).of(repeating).execute();
-      will(returnValue(true));
-      one(repeating).execute();
-      will(returnValue(false));
-      never(repeating).execute();
-    }});
-    timer.tick(1000);
+    when(repeating.execute()).thenReturn(true, true, true, true, false);
+
+    timer.tick(4);
+    verify(repeating, times(5)).execute();
+    timer.tick(100);
+    verify(repeating, times(5)).execute();
   }
 
   /**
@@ -267,9 +237,7 @@ public class FakeTimerServiceTest extends 
MockObjectTestCase {
    */
   public void testNormalTaskIsRunOnlyOnce() {
     timer.schedule(oneoff);
-    checking(new Expectations() {{
-      one(oneoff).execute();
-    }});
     timer.tick(1000);
+    verify(oneoff, times(1)).execute();
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/client/testing/UndercurrentHarness.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/wave/client/testing/UndercurrentHarness.java
 
b/wave/src/test/java/org/waveprotocol/wave/client/testing/UndercurrentHarness.java
new file mode 100644
index 0000000..48a6753
--- /dev/null
+++ 
b/wave/src/test/java/org/waveprotocol/wave/client/testing/UndercurrentHarness.java
@@ -0,0 +1,380 @@
+/**
+ * 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.client.testing;
+
+import com.google.gwt.core.client.Duration;
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.core.client.Scheduler.RepeatingCommand;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.user.client.Command;
+
+import org.waveprotocol.wave.client.StageOne;
+import org.waveprotocol.wave.client.StageThree;
+import org.waveprotocol.wave.client.StageTwo;
+import org.waveprotocol.wave.client.StageZero;
+import org.waveprotocol.wave.client.Stages;
+import org.waveprotocol.wave.client.common.safehtml.SafeHtmlBuilder;
+import org.waveprotocol.wave.client.common.util.AsyncHolder;
+import org.waveprotocol.wave.client.concurrencycontrol.MuxConnector;
+import org.waveprotocol.wave.client.doodad.DoodadInstallers;
+import 
org.waveprotocol.wave.client.doodad.attachment.AttachmentManagerProvider;
+import 
org.waveprotocol.wave.client.doodad.attachment.testing.FakeAttachmentsManager;
+import org.waveprotocol.wave.client.util.ClientFlags;
+import org.waveprotocol.wave.client.util.NullTypedSource;
+import org.waveprotocol.wave.client.util.OverridingTypedSource;
+import 
org.waveprotocol.wave.client.wavepanel.impl.toolbar.color.ComplexColorPicker;
+import 
org.waveprotocol.wave.client.wavepanel.impl.toolbar.color.SampleCustomColorPicker;
+import org.waveprotocol.wave.common.bootstrap.FlagConstants;
+import org.waveprotocol.wave.concurrencycontrol.channel.WaveViewService;
+import org.waveprotocol.wave.model.conversation.Conversation;
+import org.waveprotocol.wave.model.conversation.ConversationBlip;
+import org.waveprotocol.wave.model.conversation.ConversationThread;
+import org.waveprotocol.wave.model.conversation.ConversationView;
+import org.waveprotocol.wave.model.conversation.WaveBasedConversationView;
+import org.waveprotocol.wave.model.document.util.XmlStringBuilder;
+import org.waveprotocol.wave.model.id.IdGenerator;
+import org.waveprotocol.wave.model.schema.SchemaProvider;
+import org.waveprotocol.wave.model.schema.conversation.ConversationSchemas;
+import org.waveprotocol.wave.model.testing.BasicFactories;
+import org.waveprotocol.wave.model.testing.FakeIdGenerator;
+import org.waveprotocol.wave.model.util.CollectionUtils;
+import org.waveprotocol.wave.model.util.ReadableStringMap.ProcV;
+import org.waveprotocol.wave.model.util.StringMap;
+import org.waveprotocol.wave.model.wave.ParticipantId;
+import org.waveprotocol.wave.model.wave.data.DocumentFactory;
+import org.waveprotocol.wave.model.wave.data.ObservableWaveletData;
+import org.waveprotocol.wave.model.wave.data.ReadableWaveletData;
+import org.waveprotocol.wave.model.wave.data.WaveViewData;
+import org.waveprotocol.wave.model.wave.data.impl.WaveViewDataImpl;
+import org.waveprotocol.wave.model.wave.data.impl.WaveletDataImpl;
+import org.waveprotocol.wave.model.wave.opbased.OpBasedWavelet;
+import org.waveprotocol.wave.model.wave.opbased.WaveViewImpl;
+import 
org.waveprotocol.wave.model.wave.opbased.WaveViewImpl.WaveletConfigurator;
+import org.waveprotocol.wave.model.wave.opbased.WaveViewImpl.WaveletFactory;
+
+/**
+ * Kicks off some initial actions for development purposes.
+ *
+ */
+public class UndercurrentHarness implements EntryPoint {
+
+  private UndercurrentHarness() {
+  }
+
+  private static boolean loaded;
+
+  /**
+   * Runs the harness script.
+   */
+  @Override
+  public void onModuleLoad() {
+    AttachmentManagerProvider.init(new FakeAttachmentsManager());
+    if (loaded) {
+      return;
+    }
+    loaded = true;
+
+    final Timeline timeline = new Timeline();
+    new Stages() {
+
+      @Override
+      protected AsyncHolder<StageZero> createStageZeroLoader() {
+        return new StageZero.DefaultProvider() {
+
+          @Override
+          protected void onStageInit() {
+            timeline.add("stage0_start");
+          }
+
+          @Override
+          protected void onStageLoaded() {
+            timeline.add("stage0_end");
+          }
+
+          @Override
+          protected UncaughtExceptionHandler createUncaughtExceptionHandler() {
+            return GWT.getUncaughtExceptionHandler();
+          }
+        };
+      }
+
+      @Override
+      protected AsyncHolder<StageOne> createStageOneLoader(StageZero zero) {
+        return new StageOne.DefaultProvider(zero) {
+          @Override
+          protected void onStageInit() {
+            timeline.add("stage1_start");
+          }
+
+          @Override
+          protected void onStageLoaded() {
+            timeline.add("stage1_end");
+          }
+
+          @Override
+          protected Element createWaveHolder() {
+            return Document.get().getElementById("initialHtml");
+          }
+        };
+      }
+
+      @Override
+      protected AsyncHolder<StageTwo> createStageTwoLoader(StageOne one) {
+        return new StageTwo.DefaultProvider(one, null) {
+
+          @Override
+          protected void onStageInit() {
+            timeline.add("stage2_start");
+          }
+
+          @Override
+          protected void onStageLoaded() {
+            timeline.add("stage2_end");
+          }
+
+          @Override
+          protected void fetchWave(Accessor<WaveViewData> whenReady) {
+            timeline.add("fakewave_start");
+            WaveViewData fake = WaveFactory.create(getDocumentRegistry());
+            timeline.add("fakewave_end");
+            whenReady.use(fake);
+          }
+
+          @Override
+          protected ParticipantId createSignedInUser() {
+            return ParticipantId.ofUnsafe("nob...@example.com");
+          }
+
+          @Override
+          protected String createSessionId() {
+            return "session";
+          }
+
+          @Override
+          protected MuxConnector createConnector() {
+            return new MuxConnector() {
+              @Override
+              public void connect(Command whenOpened) {
+                if (whenOpened != null) {
+                  whenOpened.execute();
+                }
+              }
+
+              @Override
+              public void close() {
+                // Ignore
+              }
+            };
+          }
+
+          @Override
+          protected WaveViewService createWaveViewService() {
+            // The vacuous MuxConnector should avoid the need for a
+            // communication layer.
+            throw new UnsupportedOperationException();
+          }
+
+          @Override
+          protected SchemaProvider createSchemas() {
+            return new ConversationSchemas();
+          }
+        };
+      }
+
+      @Override
+      protected AsyncHolder<StageThree> createStageThreeLoader(StageTwo two) {
+        ClientFlags.resetWithSourceForTesting(OverridingTypedSource.of(new 
NullTypedSource())
+            .withBoolean(FlagConstants.ENABLE_UNDERCURRENT_EDITING, true)
+            .build());
+
+        // Only for test additional color pickers
+        new SampleCustomColorPicker(ComplexColorPicker.getInstance());
+
+        return new StageThree.DefaultProvider(two) {
+          @Override
+          protected void onStageInit() {
+            timeline.add("stage3_start");
+          }
+
+          @Override
+          protected void onStageLoaded() {
+            timeline.add("stage3_end");
+          }
+        };
+      }
+    }.load(new Command() {
+      @Override
+      public void execute() {
+        showInfo(timeline);
+      }
+    });
+  }
+
+  /**
+   * Populates the info box. Continuously reports which element has browser
+   * focus, and reports timing information for the stage loading.
+   *
+   * @param timeline timeline to report
+   */
+  private static void showInfo(Timeline timeline) {
+    Element timeBox = Document.get().getElementById("timeline");
+    timeline.dump(timeBox);
+
+    Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
+      private final Element activeBox = 
Document.get().getElementById("active");
+
+      @Override
+      public boolean execute() {
+        Element e = getActiveElement();
+        String text = (e != null ? e.getTagName() + " id:" + e.getId() : 
"none");
+        activeBox.setInnerText(text);
+        return true;
+      }
+
+      private native Element getActiveElement() /*-{
+        return $doc.activeElement;
+      }-*/;
+    }, 1000);
+  }
+
+  /**
+   * Creates a sample wave with a conversation in it.
+   */
+  private final static class WaveFactory {
+
+    /**
+     * Creates a sample wave.
+     *
+     * @param docFactory factory/registry for documents in the wave
+     * @return the wave state of the sample wave.
+     */
+    public static WaveViewDataImpl create(DocumentFactory<?> docFactory) {
+      // Create a sample wave.
+      WaveViewData sampleData = createSampleWave();
+
+      // Now build one that has the same setup state as that required by
+      // undercurrent (complex issue with the per-document output sinks).
+      WaveViewDataImpl newData = 
WaveViewDataImpl.create(sampleData.getWaveId());
+      WaveletDataImpl.Factory copier = 
WaveletDataImpl.Factory.create(docFactory);
+      for (ReadableWaveletData src : sampleData.getWavelets()) {
+        WaveletDataImpl copied = copier.create(src);
+        for (ParticipantId p : src.getParticipants()) {
+          copied.addParticipant(p);
+        }
+        copied.setVersion(copied.getVersion());
+        copied.setHashedVersion(src.getHashedVersion());
+        copied.setLastModifiedTime(src.getLastModifiedTime());
+        newData.addWavelet(copied);
+      }
+      return newData;
+    }
+
+    /** @return a sample wave with a conversation in it. */
+    private static WaveViewData createSampleWave() {
+      final ParticipantId sampleAuthor = 
ParticipantId.ofUnsafe("nob...@example.com");
+      IdGenerator gen = FakeIdGenerator.create();
+      final WaveViewDataImpl waveData = 
WaveViewDataImpl.create(gen.newWaveId());
+      final DocumentFactory<?> docFactory = 
BasicFactories.fakeDocumentFactory();
+      final ObservableWaveletData.Factory<?> waveletDataFactory =
+          new ObservableWaveletData.Factory<WaveletDataImpl>() {
+            private final ObservableWaveletData.Factory<WaveletDataImpl> inner 
=
+                WaveletDataImpl.Factory.create(docFactory);
+
+            @Override
+            public WaveletDataImpl create(ReadableWaveletData data) {
+              WaveletDataImpl wavelet = inner.create(data);
+              waveData.addWavelet(wavelet);
+              return wavelet;
+            }
+          };
+      WaveletFactory<OpBasedWavelet> waveletFactory = BasicFactories
+            .opBasedWaveletFactoryBuilder()
+            .with(waveletDataFactory)
+            .with(sampleAuthor)
+            .build();
+
+      WaveViewImpl<?> wave = WaveViewImpl.create(
+          waveletFactory, waveData.getWaveId(), gen, sampleAuthor, 
WaveletConfigurator.ADD_CREATOR);
+
+      // Build a conversation in that wave.
+      ConversationView v = WaveBasedConversationView.create(wave, gen);
+      Conversation c = v.createRoot();
+      ConversationThread root = c.getRootThread();
+      sampleReply(root.appendBlip());
+      write(root.appendBlip());
+      write(root.appendBlip());
+      write(root.appendBlip());
+
+      return waveData;
+    }
+
+    private static void write(ConversationBlip blip) {
+      org.waveprotocol.wave.model.document.Document d = blip.getContent();
+      d.emptyElement(d.getDocumentElement());
+      
d.appendXml(XmlStringBuilder.createFromXmlString("<body><line></line>Hello 
World</body>"));
+    }
+
+    private static void sampleReply(ConversationBlip blip) {
+      write(blip);
+      ConversationThread thread = blip.addReplyThread(8);
+      write(thread.appendBlip());
+    }
+
+    private static void biggerSampleReply(ConversationBlip blip) {
+      write(blip);
+      ConversationThread thread = blip.addReplyThread();
+      sampleReply(thread.appendBlip());
+      sampleReply(thread.appendBlip());
+      write(thread.appendBlip());
+    }
+  }
+
+  private static class Timeline {
+    private final StringMap<Integer> events = 
CollectionUtils.createStringMap();
+    private final Duration duration = new Duration();
+
+    void add(String name) {
+      events.put(name, duration.elapsedMillis());
+    }
+
+    void dump(Element timeBox) {
+      final SafeHtmlBuilder timeHtml = new SafeHtmlBuilder();
+      timeHtml.appendHtmlConstant("<table cellpadding='0' cellspacing='0'>");
+      events.each(new ProcV<Integer>() {
+        @Override
+        public void apply(String key, Integer value) {
+          timeHtml.appendHtmlConstant("<tr><td>");
+          timeHtml.appendEscaped(key);
+          timeHtml.appendHtmlConstant(":</td><td>");
+          timeHtml.appendEscaped("" + value);
+          timeHtml.appendHtmlConstant("</td></tr>");
+        }
+      });
+      timeHtml.appendHtmlConstant("</table>");
+      timeBox.setInnerHTML(timeHtml.toSafeHtml().asString());
+
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/client/testing/public/UndercurrentHarness.html
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/wave/client/testing/public/UndercurrentHarness.html
 
b/wave/src/test/java/org/waveprotocol/wave/client/testing/public/UndercurrentHarness.html
new file mode 100644
index 0000000..a7ac1a3
--- /dev/null
+++ 
b/wave/src/test/java/org/waveprotocol/wave/client/testing/public/UndercurrentHarness.html
@@ -0,0 +1,86 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd";>
+<!--
+  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.
+-->
+<html>
+  <head>
+    <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+    <meta name="gwt:property" content="locale=en">
+    <title>Undercurrent Harness</title>
+    <style type='text/css'>
+      /* Full screen box. */
+      body {
+        position: absolute;
+        margin: 0;
+        padding: 0;
+        left: 0;
+        right: 0;
+        top: 0;
+        bottom: 0;
+
+        /*
+         * Arial seems to be the only non-ugly sans-serif font that renders 
roughly the same on both
+         * Chrome and Firefox.  e.g., 'sans-serif' on Firefox is a nicer 
mystery font, because it is
+         * wider, but Chrome binds 'sans-serif' to 'arial'.
+         */
+        font-family: arial;
+        font-size: small;
+      }
+
+      /* Fixed-width and horizontally centered, in a way that works on IE. */
+      #initialHtml {
+        position: absolute;
+        left: 50%;
+        right: 50%;
+        top: 4em;  /* Enough space for the header. */
+        bottom: 4em;
+        /* Expand out of the center line, aiming for ~70-75 character blip 
widths. */
+        margin-left: -250px;
+        margin-right: -250px;
+        border: 1px solid lightGray;
+      }
+
+      #info {
+        position: absolute;
+        z-index: 100;
+        right: 20px;
+        top: 20px;
+        padding: 0.2em;
+        width: 10em;
+        border: 1px solid black;
+        background-color: lightGray;
+      }
+
+      #timeline {
+        margin-left: 1em;
+      }
+
+    </style>
+  </head>
+  <body>
+    <h2>Undercurrent Test Harness</h2>
+    <div id='initialHtml'></div>
+    <div id='info'>
+      Active: <span id='active'></span><br/>
+      Timing:
+      <div id='timeline'></div>
+    </div>
+    <script type='text/javascript' src='waveharness.nocache.js'>
+    </script>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/client/wavepanel/tests.gwt.xml
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/wave/client/wavepanel/tests.gwt.xml 
b/wave/src/test/java/org/waveprotocol/wave/client/wavepanel/tests.gwt.xml
index 866f890..a69575b 100644
--- a/wave/src/test/java/org/waveprotocol/wave/client/wavepanel/tests.gwt.xml
+++ b/wave/src/test/java/org/waveprotocol/wave/client/wavepanel/tests.gwt.xml
@@ -22,7 +22,13 @@
 
 <module>
   <inherits name="com.google.gwt.user.User"/>
-   
+    <inherits name="org.waveprotocol.wave.model.util.Util"/>
+  <inherits name="org.waveprotocol.wave.client.common.safehtml.SafeHtml"/>
+  <inherits name="org.waveprotocol.wave.client.common.util.Util"/>
+  <inherits name="org.waveprotocol.wave.client.uibuilder.UiBuilder"/>
+  <inherits name="org.waveprotocol.wave.client.uibuilder.UiBuilder"/>
+  <inherits name="org.waveprotocol.box.stat.Stat"/>
+
   <source path=""/>
     
 </module>

http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/d35211be/wave/src/test/java/org/waveprotocol/wave/model/conversation/testing/BlipTestUtils.java
----------------------------------------------------------------------
diff --git 
a/wave/src/test/java/org/waveprotocol/wave/model/conversation/testing/BlipTestUtils.java
 
b/wave/src/test/java/org/waveprotocol/wave/model/conversation/testing/BlipTestUtils.java
new file mode 100644
index 0000000..4bdf453
--- /dev/null
+++ 
b/wave/src/test/java/org/waveprotocol/wave/model/conversation/testing/BlipTestUtils.java
@@ -0,0 +1,94 @@
+/**
+ * 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.Blips;
+import org.waveprotocol.wave.model.conversation.ConversationBlip;
+import org.waveprotocol.wave.model.conversation.TitleHelper;
+
+import org.waveprotocol.wave.model.document.ReadableWDocument;
+import org.waveprotocol.wave.model.document.indexed.IndexedDocument;
+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.DocHelper;
+import org.waveprotocol.wave.model.document.util.DocProviders;
+import org.waveprotocol.wave.model.document.util.LineContainers;
+import org.waveprotocol.wave.model.document.util.XmlStringBuilder;
+
+/**
+ * Utility functions used by conversation tests.
+ *
+ */
+public final class BlipTestUtils {
+
+  // Non-instantiatable class
+  private BlipTestUtils() {
+  }
+
+  /**
+   * Returns the position of the body element in an initial, empty, blip.
+   */
+  public static int getInitialBlipBodyPosition() {
+    IndexedDocument<Node, Element, Text> d = DocProviders.POJO.build(
+        TitleHelper.emptyDocumentWithTitle(),
+        DocumentSchema.NO_SCHEMA_CONSTRAINTS);
+    return getBodyPosition(d);
+  }
+
+
+  /**
+   * Returns the position of the body element of the given blip.
+   */
+  public static int getBodyPosition(ConversationBlip blip) {
+    return getBodyPosition(blip.getContent());
+  }
+
+  /**
+   * Returns the position of the body element of the given document or 0 if the
+   * document has no body.
+   */
+  public static <N, E extends N> int getBodyPosition(ReadableWDocument<N, E, 
?> doc) {
+    N body = DocHelper.getElementWithTagName(doc, Blips.BODY_TAGNAME);
+    if (body == null) return 0;
+    return doc.getLocation(body);
+  }
+
+  /**
+   * Wrap the given xml string containing a blip body in an XmlStringBuilder
+   * and prepend an empty head.
+   */
+  public static XmlStringBuilder prependHead(String body) {
+    return XmlStringBuilder.createEmpty()
+      .append(Blips.INITIAL_HEAD)
+      .append(XmlStringBuilder.createFromXmlString(body));
+  }
+
+  /**
+   * Returns a blip whose contents are the given lines.
+   */
+  public static String debugBlipWrap(String ... lines) {
+    return XmlStringBuilder.createEmpty()
+        .append(Blips.INITIAL_HEAD)
+        
.append(XmlStringBuilder.createFromXmlString(LineContainers.debugContainerWrap(lines)))
+        .toString();
+  }
+}

Reply via email to