1 /*
2  * Copyright (C) 2007 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.testing.EqualsTester;
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24 import junit.framework.TestCase;
25 
26 /**
27  * Tests for {@link Subscriber}.
28  *
29  * @author Cliff Biffle
30  * @author Colin Decker
31  */
32 public class SubscriberTest extends TestCase {
33 
34   private static final Object FIXTURE_ARGUMENT = new Object();
35 
36   private EventBus bus;
37   private boolean methodCalled;
38   private Object methodArgument;
39 
40   @Override
setUp()41   protected void setUp() throws Exception {
42     bus = new EventBus();
43     methodCalled = false;
44     methodArgument = null;
45   }
46 
testCreate()47   public void testCreate() {
48     Subscriber s1 = Subscriber.create(bus, this, getTestSubscriberMethod("recordingMethod"));
49     assertThat(s1).isInstanceOf(Subscriber.SynchronizedSubscriber.class);
50 
51     // a thread-safe method should not create a synchronized subscriber
52     Subscriber s2 = Subscriber.create(bus, this, getTestSubscriberMethod("threadSafeMethod"));
53     assertThat(s2).isNotInstanceOf(Subscriber.SynchronizedSubscriber.class);
54   }
55 
testInvokeSubscriberMethod_basicMethodCall()56   public void testInvokeSubscriberMethod_basicMethodCall() throws Throwable {
57     Method method = getTestSubscriberMethod("recordingMethod");
58     Subscriber subscriber = Subscriber.create(bus, this, method);
59 
60     subscriber.invokeSubscriberMethod(FIXTURE_ARGUMENT);
61 
62     assertTrue("Subscriber must call provided method", methodCalled);
63     assertTrue(
64         "Subscriber argument must be exactly the provided object.",
65         methodArgument == FIXTURE_ARGUMENT);
66   }
67 
testInvokeSubscriberMethod_exceptionWrapping()68   public void testInvokeSubscriberMethod_exceptionWrapping() throws Throwable {
69     Method method = getTestSubscriberMethod("exceptionThrowingMethod");
70     Subscriber subscriber = Subscriber.create(bus, this, method);
71 
72     try {
73       subscriber.invokeSubscriberMethod(FIXTURE_ARGUMENT);
74       fail("Subscribers whose methods throw must throw InvocationTargetException");
75     } catch (InvocationTargetException expected) {
76       assertThat(expected).hasCauseThat().isInstanceOf(IntentionalException.class);
77     }
78   }
79 
testInvokeSubscriberMethod_errorPassthrough()80   public void testInvokeSubscriberMethod_errorPassthrough() throws Throwable {
81     Method method = getTestSubscriberMethod("errorThrowingMethod");
82     Subscriber subscriber = Subscriber.create(bus, this, method);
83 
84     try {
85       subscriber.invokeSubscriberMethod(FIXTURE_ARGUMENT);
86       fail("Subscribers whose methods throw Errors must rethrow them");
87     } catch (JudgmentError expected) {
88     }
89   }
90 
testEquals()91   public void testEquals() throws Exception {
92     Method charAt = String.class.getMethod("charAt", int.class);
93     Method concat = String.class.getMethod("concat", String.class);
94     new EqualsTester()
95         .addEqualityGroup(
96             Subscriber.create(bus, "foo", charAt), Subscriber.create(bus, "foo", charAt))
97         .addEqualityGroup(Subscriber.create(bus, "bar", charAt))
98         .addEqualityGroup(Subscriber.create(bus, "foo", concat))
99         .testEquals();
100   }
101 
getTestSubscriberMethod(String name)102   private Method getTestSubscriberMethod(String name) {
103     try {
104       return getClass().getDeclaredMethod(name, Object.class);
105     } catch (NoSuchMethodException e) {
106       throw new AssertionError();
107     }
108   }
109 
110   /**
111    * Records the provided object in {@link #methodArgument} and sets {@link #methodCalled}. This
112    * method is called reflectively by Subscriber during tests, and must remain public.
113    *
114    * @param arg argument to record.
115    */
116   @Subscribe
recordingMethod(Object arg)117   public void recordingMethod(Object arg) {
118     assertFalse(methodCalled);
119     methodCalled = true;
120     methodArgument = arg;
121   }
122 
123   @Subscribe
exceptionThrowingMethod(Object arg)124   public void exceptionThrowingMethod(Object arg) throws Exception {
125     throw new IntentionalException();
126   }
127 
128   /** Local exception subclass to check variety of exception thrown. */
129   class IntentionalException extends Exception {
130 
131     private static final long serialVersionUID = -2500191180248181379L;
132   }
133 
134   @Subscribe
errorThrowingMethod(Object arg)135   public void errorThrowingMethod(Object arg) {
136     throw new JudgmentError();
137   }
138 
139   @Subscribe
140   @AllowConcurrentEvents
threadSafeMethod(Object arg)141   public void threadSafeMethod(Object arg) {}
142 
143   /** Local Error subclass to check variety of error thrown. */
144   class JudgmentError extends Error {
145 
146     private static final long serialVersionUID = 634248373797713373L;
147   }
148 }
149