Adding some extra capabilities to CountingEventBus so that we can reduce the
boilerplate involved in testing events fired on the EventBus.

Review at http://gwt-code-reviews.appspot.com/1526803

Review by: skybrian@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10594 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/web/bindery/event/shared/testing/CountingEventBus.java b/user/src/com/google/web/bindery/event/shared/testing/CountingEventBus.java
index 4b657a4..b9dc703 100644
--- a/user/src/com/google/web/bindery/event/shared/testing/CountingEventBus.java
+++ b/user/src/com/google/web/bindery/event/shared/testing/CountingEventBus.java
@@ -25,11 +25,13 @@
 import java.util.Map;
 
 /**
- * Wraps an {@link EventBus} to keep a count of registered handlers. Handy for
- * tests.
+ * Wraps an {@link EventBus} to keep a count of registered handlers and how many times events have
+ * fired. Handy for tests.
  */
 public class CountingEventBus extends EventBus {
-  private final Map<Type<?>, Integer> counts = new HashMap<Event.Type<?>, Integer>();
+  private final KeyedCounter<Type<?>> handlerCounts = new KeyedCounter<Event.Type<?>>();
+  private final KeyedCounter<Type<?>> firedCounts = new KeyedCounter<Event.Type<?>> ();
+  private final KeyedCounter<TypeSourcePair> sourceCounts = new KeyedCounter<TypeSourcePair>();
   private final EventBus wrapped;
 
   public CountingEventBus() {
@@ -42,47 +44,125 @@
 
   @Override
   public <H> HandlerRegistration addHandler(Type<H> type, H handler) {
-    increment(type);
     final HandlerRegistration superReg = wrapped.addHandler(type, handler);
+    handlerCounts.increment(type);
     return makeReg(type, superReg);
   }
 
   @Override
   public <H> HandlerRegistration addHandlerToSource(final Type<H> type, Object source, H handler) {
-    increment(type);
     final HandlerRegistration superReg = wrapped.addHandlerToSource(type, source, handler);
+    handlerCounts.increment(type);
     return makeReg(type, superReg);
   }
 
   @Override
   public void fireEvent(Event<?> event) {
     wrapped.fireEvent(event);
+    firedCounts.increment(event.getAssociatedType());
+    sourceCounts.increment(new TypeSourcePair(event.getAssociatedType(), null));
   }
 
   @Override
   public void fireEventFromSource(Event<?> event, Object source) {
     wrapped.fireEventFromSource(event, source);
+    firedCounts.increment(event.getAssociatedType());
+    sourceCounts.increment(new TypeSourcePair(event.getAssociatedType(), source));
   }
 
+  /**
+   * How many handlers are registered for the given {@code type}.
+   *
+   * @deprecated Please use {@code getHandlerCount}.
+   */
   public int getCount(Type<?> type) {
-    Integer count = counts.get(type);
-    return count == null ? 0 : count;
+    return getHandlerCount(type);
   }
 
-  private void decrement(Type<?> type) {
-    counts.put(type, getCount(type) - 1);
+  /**
+   * How many events have fired for the given {@code type}. These events may not have been
+   * passed to any handlers.
+   */
+  public int getFiredCount(Type<?> type) {
+    return firedCounts.getCount(type);
   }
 
-  private <H> void increment(final Type<H> type) {
-    counts.put(type, getCount(type) + 1);
+  /**
+   * How many events have fired for the given pairing  of {@code type} and {@code source}. These
+   * events may not have been passed to any handlers.
+   */
+  public int getFiredCountFromSource(Type<?> type, Object source) {
+    return sourceCounts.getCount(new TypeSourcePair(type, source));
+  }
+
+  /**
+   * How many handlers are registered for the given {@code type}.
+   */
+  public int getHandlerCount(Type<?> type) {
+    return handlerCounts.getCount(type);
   }
 
   private <H> HandlerRegistration makeReg(final Type<H> type, final HandlerRegistration superReg) {
     return new HandlerRegistration() {
       public void removeHandler() {
-        decrement(type);
+        handlerCounts.decrement(type);
         superReg.removeHandler();
       }
     };
   }
+
+  private class TypeSourcePair {
+    final Type<?> type;
+    final Object source;
+
+    TypeSourcePair(Type<?> type, Object source) {
+      this.type = type;
+      this.source = source;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (!(o instanceof TypeSourcePair)) {
+        return false;
+      }
+
+      TypeSourcePair pair = (TypeSourcePair) o;      
+      return doNullEquals(type, pair.type) && doNullEquals(source, pair.source);
+    }
+
+    @Override
+    public int hashCode() {
+      int hash = 7;
+      hash = (hash * 31) + (type == null ? 0 : type.hashCode());
+      hash = (hash * 31) + (source == null ? 0 : source.hashCode());
+      return hash;
+    }
+
+    private boolean doNullEquals(Object a, Object b) {
+      if ((a == null) ^ (b == null)) {
+        return false;
+      }
+      return ((a == null) && (b == null)) || a.equals(b);
+    }
+  }
+  
+  private class KeyedCounter<K> {
+    private Map<K, Integer> counts = new HashMap<K, Integer>();
+
+    int getCount(K key) {
+      Integer count = counts.get(key);
+      return count == null ? 0 : count;
+    }
+
+    void decrement(K key) {
+      counts.put(key, getCount(key) - 1);
+    }
+
+    void increment(K key) {
+      counts.put(key, getCount(key) + 1);
+    }
+  }
 }
diff --git a/user/test/com/google/web/bindery/event/shared/BarEvent.java b/user/test/com/google/web/bindery/event/shared/BarEvent.java
index 35b21f9..ab366ff 100644
--- a/user/test/com/google/web/bindery/event/shared/BarEvent.java
+++ b/user/test/com/google/web/bindery/event/shared/BarEvent.java
@@ -19,11 +19,14 @@
  * For {@link EventBus} tests.
  */
 public class BarEvent extends Event<BarEvent.Handler> {
-  interface Handler {
+  /**
+   * The handler for the event.
+   */
+  public interface Handler {
     void onBar(BarEvent e);
   }
   
-  static final Type<Handler> TYPE = new Type<Handler>();
+  public static final Type<Handler> TYPE = new Type<Handler>();
 
   public static HandlerRegistration register(EventBus bus, Handler handler) {
     return bus.addHandler(TYPE, handler);
diff --git a/user/test/com/google/web/bindery/event/shared/EventBusTestBase.java b/user/test/com/google/web/bindery/event/shared/EventBusTestBase.java
index 129b182..0dd4d16 100644
--- a/user/test/com/google/web/bindery/event/shared/EventBusTestBase.java
+++ b/user/test/com/google/web/bindery/event/shared/EventBusTestBase.java
@@ -25,7 +25,10 @@
  */
 public abstract class EventBusTestBase extends TestCase {
 
-  class Adaptor implements FooEvent.Handler, BarEvent.Handler {
+  /**
+   * Handler implementation to allow for easy testing of whether the handler is being called.
+   */
+  protected class Adaptor implements FooEvent.Handler, BarEvent.Handler {
 
     public void onFoo(FooEvent event) {
       add(this);
@@ -41,11 +44,11 @@
     }
   }
 
-  Adaptor adaptor1 = new Adaptor();
+  protected Adaptor adaptor1 = new Adaptor();
 
   private HashSet<Object> active = new HashSet<Object>();
 
-  FooEvent.Handler fooHandler1 = new FooEvent.Handler() {
+  protected FooEvent.Handler fooHandler1 = new FooEvent.Handler() {
     public void onFoo(FooEvent event) {
       add(fooHandler1);
     }
@@ -56,7 +59,7 @@
     }
   };
 
-  FooEvent.Handler fooHandler2 = new FooEvent.Handler() {
+  protected FooEvent.Handler fooHandler2 = new FooEvent.Handler() {
     public void onFoo(FooEvent event) {
       add(fooHandler2);
     }
@@ -67,7 +70,7 @@
     }
   };
 
-  FooEvent.Handler fooHandler3 = new FooEvent.Handler() {
+  protected FooEvent.Handler fooHandler3 = new FooEvent.Handler() {
     public void onFoo(FooEvent event) {
       add(fooHandler3);
     }
@@ -78,7 +81,7 @@
     }
   };
 
-  BarEvent.Handler barHandler1 = new BarEvent.Handler() {
+  protected BarEvent.Handler barHandler1 = new BarEvent.Handler() {
 
     public void onBar(BarEvent event) {
       add(barHandler1);
@@ -90,7 +93,7 @@
     }
   };
 
-  BarEvent.Handler barHandler2 = new BarEvent.Handler() {
+  protected BarEvent.Handler barHandler2 = new BarEvent.Handler() {
 
     public void onBar(BarEvent event) {
       add(barHandler2);
@@ -102,7 +105,7 @@
     }
   };
 
-  BarEvent.Handler barHandler3 = new BarEvent.Handler() {
+  protected BarEvent.Handler barHandler3 = new BarEvent.Handler() {
 
     public void onBar(BarEvent event) {
       add(barHandler3);
@@ -114,24 +117,24 @@
     }
   };
 
-  void add(Object handler) {
+  protected void add(Object handler) {
     active.add(handler);
   }
 
-  void assertFired(Object... handler) {
+  protected void assertFired(Object... handler) {
     for (int i = 0; i < handler.length; i++) {
       assertTrue(handler[i] + " should have fired", active.contains(handler[i]));
     }
   }
 
-  void assertNotFired(Object... handler) {
+  protected void assertNotFired(Object... handler) {
     for (int i = 0; i < handler.length; i++) {
       assertFalse(handler[i] + " should not have fired",
           active.contains(handler[i]));
     }
   }
 
-  void reset() {
+  protected void reset() {
     active.clear();
   }
 
diff --git a/user/test/com/google/web/bindery/event/shared/FooEvent.java b/user/test/com/google/web/bindery/event/shared/FooEvent.java
index a63f53a..ee746b2 100644
--- a/user/test/com/google/web/bindery/event/shared/FooEvent.java
+++ b/user/test/com/google/web/bindery/event/shared/FooEvent.java
@@ -19,11 +19,14 @@
  * For {@link EventBus} tests.
  */
 public class FooEvent extends Event<FooEvent.Handler> {
-  interface Handler {
+  /**
+   * The handler for the event.
+   */
+  public interface Handler {
     void onFoo(FooEvent e);
   }
   
-  static final Type<Handler> TYPE = new Type<Handler>();
+  public static final Type<Handler> TYPE = new Type<Handler>();
 
   public static HandlerRegistration register(EventBus bus, Handler handler) {
     return bus.addHandler(TYPE, handler);
diff --git a/user/test/com/google/web/bindery/event/shared/testing/CountingEventBusTest.java b/user/test/com/google/web/bindery/event/shared/testing/CountingEventBusTest.java
new file mode 100644
index 0000000..6ab4f1d
--- /dev/null
+++ b/user/test/com/google/web/bindery/event/shared/testing/CountingEventBusTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2011 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.web.bindery.event.shared.testing;
+
+import com.google.web.bindery.event.shared.BarEvent;
+import com.google.web.bindery.event.shared.Event.Type;
+import com.google.web.bindery.event.shared.EventBusTestBase;
+import com.google.web.bindery.event.shared.FooEvent;
+import com.google.web.bindery.event.shared.HandlerRegistration;
+import com.google.web.bindery.event.shared.SimpleEventBus;
+
+/**
+ * Eponymous unit tests.
+ */
+public class CountingEventBusTest extends EventBusTestBase {
+  private CountingEventBus eventBus;
+
+  @Override
+  protected void setUp() throws Exception {
+    super.setUp();
+    eventBus = new CountingEventBus(new SimpleEventBus());
+  }
+
+  public void testAddAndRemoveMultipleHandlers() {
+    HandlerRegistration fooReg = eventBus.addHandler(FooEvent.TYPE, fooHandler1);
+    checkHandlerCount(1, FooEvent.TYPE);
+
+    HandlerRegistration barReg1 = eventBus.addHandler(BarEvent.TYPE, barHandler1);
+    HandlerRegistration barReg2 = eventBus.addHandler(BarEvent.TYPE, barHandler2);
+    checkHandlerCount(2, BarEvent.TYPE);
+
+    fooReg.removeHandler();
+    checkHandlerCount(0, FooEvent.TYPE);
+
+    barReg2.removeHandler();
+    checkHandlerCount(1, BarEvent.TYPE);
+
+    barReg1.removeHandler();
+    checkHandlerCount(0, BarEvent.TYPE);
+  }
+
+  public void testAddAndRemoveSourcedHandlers() {
+    Object source1 = new Object();
+    Object source2 = new Object();
+    
+    HandlerRegistration fooReg1 = eventBus.addHandlerToSource(FooEvent.TYPE, source1, fooHandler1);
+    checkHandlerCount(1, FooEvent.TYPE);
+
+    HandlerRegistration fooReg2 = eventBus.addHandlerToSource(FooEvent.TYPE, source2, fooHandler2);
+    checkHandlerCount(2, FooEvent.TYPE);
+
+    fooReg2.removeHandler();
+    checkHandlerCount(1, FooEvent.TYPE);
+
+    fooReg1.removeHandler();
+    checkHandlerCount(0, FooEvent.TYPE);
+  }
+
+  public void testFireEvent() {
+    checkTotalEvents(0, FooEvent.TYPE);
+    checkTotalEvents(0, BarEvent.TYPE);
+
+    for (int i = 0; i < 5; i++) {
+      eventBus.fireEvent(new FooEvent());
+      checkTotalEvents(i + 1, FooEvent.TYPE);
+      checkTotalEvents(i, BarEvent.TYPE);
+
+      eventBus.fireEvent(new BarEvent());
+      checkTotalEvents(i + 1, FooEvent.TYPE);
+      checkTotalEvents(i + 1, BarEvent.TYPE);
+    }
+  }
+
+  public void testFireEventFromSource() {
+    Object source1 = new Object();
+    Object source2 = new Object();
+    
+    eventBus.fireEvent(new FooEvent());
+    checkSourceEvents(0, FooEvent.TYPE, source1);
+    checkSourceEvents(0, FooEvent.TYPE, source2);
+    checkTotalEvents(1, FooEvent.TYPE);
+
+    eventBus.fireEventFromSource(new FooEvent(), source1);
+    checkSourceEvents(1, FooEvent.TYPE, source1);
+    checkSourceEvents(0, FooEvent.TYPE, source2);
+    checkSourceEvents(1, FooEvent.TYPE, null);
+    assertEquals(2, eventBus.getFiredCount(FooEvent.TYPE));
+
+    eventBus.fireEventFromSource(new FooEvent(), source1);
+    checkSourceEvents(2, FooEvent.TYPE, source1);
+    checkSourceEvents(0, FooEvent.TYPE, source2);
+    checkSourceEvents(1, FooEvent.TYPE, null);
+    assertEquals(3, eventBus.getFiredCount(FooEvent.TYPE));
+
+    eventBus.fireEventFromSource(new FooEvent(), source2);
+    checkSourceEvents(2, FooEvent.TYPE, source1);
+    checkSourceEvents(1, FooEvent.TYPE, source2);
+    checkSourceEvents(1, FooEvent.TYPE, null);
+    assertEquals(4, eventBus.getFiredCount(FooEvent.TYPE));
+
+    eventBus.fireEventFromSource(new BarEvent(), source2);
+    checkSourceEvents(2, FooEvent.TYPE, source1);
+    checkSourceEvents(1, FooEvent.TYPE, source2);
+    checkSourceEvents(1, FooEvent.TYPE, null);
+    assertEquals(4, eventBus.getFiredCount(FooEvent.TYPE));
+    checkSourceEvents(1, BarEvent.TYPE, source2);
+    assertEquals(1, eventBus.getFiredCount(BarEvent.TYPE));
+  }
+
+  public void testFireEventFromSource_LotsOfEvents() {
+    Object source = new Object();
+
+    for (int i = 0; i < Integer.MAX_VALUE; i++) {
+      eventBus.fireEventFromSource(new FooEvent(), source);
+      assertEquals(i + 1, eventBus.getFiredCount(FooEvent.TYPE));
+      assertEquals(i + 1, eventBus.getFiredCountFromSource(FooEvent.TYPE, source));
+    }
+  }
+
+  private void checkHandlerCount(int expected, Type<?> type) {
+    assertEquals(expected, eventBus.getHandlerCount(type));
+    assertEquals(expected, eventBus.getCount(type));
+  }
+
+  private void checkSourceEvents(int expectedCount, Type<?> type, Object source) {
+    assertEquals(expectedCount, eventBus.getFiredCountFromSource(type, source));
+  }
+
+  private void checkTotalEvents(int expectedCount, Type<?> type) {
+    assertEquals(expectedCount, eventBus.getFiredCount(type));
+    assertEquals(expectedCount, eventBus.getFiredCountFromSource(type, null));
+  }
+}
+