1 /*
2  * Copyright (C) 2014 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.eventbus;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import com.google.common.collect.ImmutableList;
22 import com.google.common.collect.Queues;
23 import com.google.common.util.concurrent.Uninterruptibles;
24 import java.util.concurrent.ConcurrentLinkedQueue;
25 import java.util.concurrent.CountDownLatch;
26 import java.util.concurrent.CyclicBarrier;
27 import junit.framework.TestCase;
28 
29 /**
30  * Tests for {@link Dispatcher} implementations.
31  *
32  * @author Colin Decker
33  */
34 
35 public class DispatcherTest extends TestCase {
36 
37   private final EventBus bus = new EventBus();
38 
39   private final IntegerSubscriber i1 = new IntegerSubscriber("i1");
40   private final IntegerSubscriber i2 = new IntegerSubscriber("i2");
41   private final IntegerSubscriber i3 = new IntegerSubscriber("i3");
42   private final ImmutableList<Subscriber> integerSubscribers =
43       ImmutableList.of(
44           subscriber(bus, i1, "handleInteger", Integer.class),
45           subscriber(bus, i2, "handleInteger", Integer.class),
46           subscriber(bus, i3, "handleInteger", Integer.class));
47 
48   private final StringSubscriber s1 = new StringSubscriber("s1");
49   private final StringSubscriber s2 = new StringSubscriber("s2");
50   private final ImmutableList<Subscriber> stringSubscribers =
51       ImmutableList.of(
52           subscriber(bus, s1, "handleString", String.class),
53           subscriber(bus, s2, "handleString", String.class));
54 
55   private final ConcurrentLinkedQueue<Object> dispatchedSubscribers =
56       Queues.newConcurrentLinkedQueue();
57 
58   private Dispatcher dispatcher;
59 
testPerThreadQueuedDispatcher()60   public void testPerThreadQueuedDispatcher() {
61     dispatcher = Dispatcher.perThreadDispatchQueue();
62     dispatcher.dispatch(1, integerSubscribers.iterator());
63 
64     assertThat(dispatchedSubscribers)
65         .containsExactly(
66             i1,
67             i2,
68             i3, // Integer subscribers are dispatched to first.
69             s1,
70             s2, // Though each integer subscriber dispatches to all string subscribers,
71             s1,
72             s2, // those string subscribers aren't actually dispatched to until all integer
73             s1,
74             s2 // subscribers have finished.
75             )
76         .inOrder();
77   }
78 
testLegacyAsyncDispatcher()79   public void testLegacyAsyncDispatcher() {
80     dispatcher = Dispatcher.legacyAsync();
81 
82     final CyclicBarrier barrier = new CyclicBarrier(2);
83     final CountDownLatch latch = new CountDownLatch(2);
84 
85     new Thread(
86             new Runnable() {
87               @Override
88               public void run() {
89                 try {
90                   barrier.await();
91                 } catch (Exception e) {
92                   throw new AssertionError(e);
93                 }
94 
95                 dispatcher.dispatch(2, integerSubscribers.iterator());
96                 latch.countDown();
97               }
98             })
99         .start();
100 
101     new Thread(
102             new Runnable() {
103               @Override
104               public void run() {
105                 try {
106                   barrier.await();
107                 } catch (Exception e) {
108                   throw new AssertionError(e);
109                 }
110 
111                 dispatcher.dispatch("foo", stringSubscribers.iterator());
112                 latch.countDown();
113               }
114             })
115         .start();
116 
117     Uninterruptibles.awaitUninterruptibly(latch);
118 
119     // See Dispatcher.LegacyAsyncDispatcher for an explanation of why there aren't really any
120     // useful testable guarantees about the behavior of that dispatcher in a multithreaded
121     // environment. Here we simply test that all the expected dispatches happened in some order.
122     assertThat(dispatchedSubscribers).containsExactly(i1, i2, i3, s1, s1, s1, s1, s2, s2, s2, s2);
123   }
124 
testImmediateDispatcher()125   public void testImmediateDispatcher() {
126     dispatcher = Dispatcher.immediate();
127     dispatcher.dispatch(1, integerSubscribers.iterator());
128 
129     assertThat(dispatchedSubscribers)
130         .containsExactly(
131             i1, s1, s2, // Each integer subscriber immediately dispatches to 2 string subscribers.
132             i2, s1, s2, i3, s1, s2)
133         .inOrder();
134   }
135 
subscriber( EventBus bus, Object target, String methodName, Class<?> eventType)136   private static Subscriber subscriber(
137       EventBus bus, Object target, String methodName, Class<?> eventType) {
138     try {
139       return Subscriber.create(bus, target, target.getClass().getMethod(methodName, eventType));
140     } catch (NoSuchMethodException e) {
141       throw new AssertionError(e);
142     }
143   }
144 
145   public final class IntegerSubscriber {
146     private final String name;
147 
IntegerSubscriber(String name)148     public IntegerSubscriber(String name) {
149       this.name = name;
150     }
151 
152     @Subscribe
handleInteger(Integer integer)153     public void handleInteger(Integer integer) {
154       dispatchedSubscribers.add(this);
155       dispatcher.dispatch("hello", stringSubscribers.iterator());
156     }
157 
158     @Override
toString()159     public String toString() {
160       return name;
161     }
162   }
163 
164   public final class StringSubscriber {
165     private final String name;
166 
StringSubscriber(String name)167     public StringSubscriber(String name) {
168       this.name = name;
169     }
170 
171     @Subscribe
handleString(String string)172     public void handleString(String string) {
173       dispatchedSubscribers.add(this);
174     }
175 
176     @Override
toString()177     public String toString() {
178       return name;
179     }
180   }
181 }
182