/* * Copyright (C) 2014 The Guava Authors * * 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.common.eventbus; import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableList; import com.google.common.collect.Queues; import com.google.common.util.concurrent.Uninterruptibles; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import junit.framework.TestCase; /** * Tests for {@link Dispatcher} implementations. * * @author Colin Decker */ public class DispatcherTest extends TestCase { private final EventBus bus = new EventBus(); private final IntegerSubscriber i1 = new IntegerSubscriber("i1"); private final IntegerSubscriber i2 = new IntegerSubscriber("i2"); private final IntegerSubscriber i3 = new IntegerSubscriber("i3"); private final ImmutableList<Subscriber> integerSubscribers = ImmutableList.of( subscriber(bus, i1, "handleInteger", Integer.class), subscriber(bus, i2, "handleInteger", Integer.class), subscriber(bus, i3, "handleInteger", Integer.class)); private final StringSubscriber s1 = new StringSubscriber("s1"); private final StringSubscriber s2 = new StringSubscriber("s2"); private final ImmutableList<Subscriber> stringSubscribers = ImmutableList.of( subscriber(bus, s1, "handleString", String.class), subscriber(bus, s2, "handleString", String.class)); private final ConcurrentLinkedQueue<Object> dispatchedSubscribers = Queues.newConcurrentLinkedQueue(); private Dispatcher dispatcher; public void testPerThreadQueuedDispatcher() { dispatcher = Dispatcher.perThreadDispatchQueue(); dispatcher.dispatch(1, integerSubscribers.iterator()); assertThat(dispatchedSubscribers) .containsExactly( i1, i2, i3, // Integer subscribers are dispatched to first. s1, s2, // Though each integer subscriber dispatches to all string subscribers, s1, s2, // those string subscribers aren't actually dispatched to until all integer s1, s2 // subscribers have finished. ).inOrder(); } public void testLegacyAsyncDispatcher() { dispatcher = Dispatcher.legacyAsync(); final CyclicBarrier barrier = new CyclicBarrier(2); final CountDownLatch latch = new CountDownLatch(2); new Thread(new Runnable() { @Override public void run() { try { barrier.await(); } catch (Exception e) { throw new AssertionError(e); } dispatcher.dispatch(2, integerSubscribers.iterator()); latch.countDown(); } }).start(); new Thread(new Runnable() { @Override public void run() { try { barrier.await(); } catch (Exception e) { throw new AssertionError(e); } dispatcher.dispatch("foo", stringSubscribers.iterator()); latch.countDown(); } }).start(); Uninterruptibles.awaitUninterruptibly(latch); // See Dispatcher.LegacyAsyncDispatcher for an explanation of why there aren't really any // useful testable guarantees about the behavior of that dispatcher in a multithreaded // environment. Here we simply test that all the expected dispatches happened in some order. assertThat(dispatchedSubscribers) .containsExactly( i1, i2, i3, s1, s1, s1, s1, s2, s2, s2, s2); } public void testImmediateDispatcher() { dispatcher = Dispatcher.immediate(); dispatcher.dispatch(1, integerSubscribers.iterator()); assertThat(dispatchedSubscribers) .containsExactly( i1, s1, s2, // Each integer subscriber immediately dispatches to 2 string subscribers. i2, s1, s2, i3, s1, s2 ).inOrder(); } private static Subscriber subscriber( EventBus bus, Object target, String methodName, Class<?> eventType) { try { return Subscriber.create(bus, target, target.getClass().getMethod(methodName, eventType)); } catch (NoSuchMethodException e) { throw new AssertionError(e); } } public final class IntegerSubscriber { private final String name; public IntegerSubscriber(String name) { this.name = name; } @Subscribe public void handleInteger(Integer integer) { dispatchedSubscribers.add(this); dispatcher.dispatch("hello", stringSubscribers.iterator()); } @Override public String toString() { return name; } } public final class StringSubscriber { private final String name; public StringSubscriber(String name) { this.name = name; } @Subscribe public void handleString(String string) { dispatchedSubscribers.add(this); } @Override public String toString() { return name; } } }