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
17package com.google.common.eventbus;
18
19import java.lang.reflect.InvocationTargetException;
20import java.lang.reflect.Method;
21import junit.framework.TestCase;
22
23/**
24 * Test case for {@link EventHandler}.
25 *
26 * @author Cliff Biffle
27 */
28public class EventHandlerTest extends TestCase {
29
30  private static final Object FIXTURE_ARGUMENT = new Object();
31
32  private boolean methodCalled;
33  private Object methodArgument;
34
35  @Override protected void setUp() throws Exception {
36    super.setUp();
37
38    methodCalled = false;
39    methodArgument = null;
40  }
41
42  /**
43   * Checks that a no-frills, no-issues method call is properly executed.
44   *
45   * @throws Exception  if the aforementioned proper execution is not to be had.
46   */
47  public void testBasicMethodCall() throws Exception {
48    Method method = getRecordingMethod();
49
50    EventHandler handler = new EventHandler(this, method);
51
52    handler.handleEvent(FIXTURE_ARGUMENT);
53
54    assertTrue("Handler must call provided method.", methodCalled);
55    assertTrue("Handler argument must be *exactly* the provided object.",
56        methodArgument == FIXTURE_ARGUMENT);
57  }
58
59  /**
60   * Checks that EventHandler's constructor disallows null methods.
61   */
62  public void testRejectionOfNullMethods() {
63    try {
64      new EventHandler(this, null);
65      fail("EventHandler must immediately reject null methods.");
66    } catch (NullPointerException e) {
67      // Hooray!
68    }
69  }
70
71  /**
72   * Checks that EventHandler's constructor disallows null targets.
73   */
74  public void testRejectionOfNullTargets() {
75    Method method = getRecordingMethod();
76    try {
77      new EventHandler(null, method);
78      fail("EventHandler must immediately reject null targets.");
79    } catch (NullPointerException e) {
80      // Huzzah!
81    }
82  }
83
84  public void testExceptionWrapping() {
85    Method method = getExceptionThrowingMethod();
86    EventHandler handler = new EventHandler(this, method);
87
88    try {
89      handler.handleEvent(new Object());
90      fail("Handlers whose methods throw must throw InvocationTargetException");
91    } catch (InvocationTargetException e) {
92      assertTrue("Expected exception must be wrapped.",
93          e.getCause() instanceof IntentionalException);
94    }
95  }
96
97  public void testErrorPassthrough() throws InvocationTargetException {
98    Method method = getErrorThrowingMethod();
99    EventHandler handler = new EventHandler(this, method);
100
101    try {
102      handler.handleEvent(new Object());
103      fail("Handlers whose methods throw Errors must rethrow them");
104    } catch (JudgmentError e) {
105      // Expected.
106    }
107  }
108
109  /**
110   * Gets a reference to {@link #recordingMethod(Object)}.
111   *
112   * @return a Method wrapping {@link #recordingMethod(Object)}.
113   * @throws IllegalStateException if executed in a context where reflection is
114   *         unavailable.
115   * @throws AssertionError if something odd has happened to
116   *         {@link #recordingMethod(Object)}.
117   */
118  private Method getRecordingMethod() {
119    Method method;
120    try {
121      method = getClass().getMethod("recordingMethod", Object.class);
122    } catch (SecurityException e) {
123      throw new IllegalStateException("This test needs access to reflection.");
124    } catch (NoSuchMethodException e) {
125      throw new AssertionError(
126          "Someone changed EventHandlerTest#recordingMethod's visibility, " +
127          "signature, or removed it entirely.  (Must be public.)");
128    }
129    return method;
130  }
131
132  /**
133   * Gets a reference to {@link #exceptionThrowingMethod(Object)}.
134   *
135   * @return a Method wrapping {@link #exceptionThrowingMethod(Object)}.
136   * @throws IllegalStateException if executed in a context where reflection is
137   *         unavailable.
138   * @throws AssertionError if something odd has happened to
139   *         {@link #exceptionThrowingMethod(Object)}.
140   */
141  private Method getExceptionThrowingMethod() {
142    Method method;
143    try {
144      method = getClass().getMethod("exceptionThrowingMethod", Object.class);
145    } catch (SecurityException e) {
146      throw new IllegalStateException("This test needs access to reflection.");
147    } catch (NoSuchMethodException e) {
148      throw new AssertionError(
149          "Someone changed EventHandlerTest#exceptionThrowingMethod's " +
150          "visibility, signature, or removed it entirely.  (Must be public.)");
151    }
152    return method;
153  }
154
155  /**
156   * Gets a reference to {@link #errorThrowingMethod(Object)}.
157   *
158   * @return a Method wrapping {@link #errorThrowingMethod(Object)}.
159   * @throws IllegalStateException if executed in a context where reflection is
160   *         unavailable.
161   * @throws AssertionError if something odd has happened to
162   *         {@link #errorThrowingMethod(Object)}.
163   */
164  private Method getErrorThrowingMethod() {
165    Method method;
166    try {
167      method = getClass().getMethod("errorThrowingMethod", Object.class);
168    } catch (SecurityException e) {
169      throw new IllegalStateException("This test needs access to reflection.");
170    } catch (NoSuchMethodException e) {
171      throw new AssertionError(
172          "Someone changed EventHandlerTest#errorThrowingMethod's " +
173          "visibility, signature, or removed it entirely.  (Must be public.)");
174    }
175    return method;
176  }
177
178  /**
179   * Records the provided object in {@link #methodArgument} and sets
180   * {@link #methodCalled}.  This method is called reflectively by EventHandler
181   * during tests, and must remain public.
182   *
183   * @param arg  argument to record.
184   */
185  public void recordingMethod(Object arg) {
186    if (methodCalled == true) {
187      throw new IllegalStateException("Method called more than once.");
188    }
189    methodCalled = true;
190    methodArgument = arg;
191  }
192
193  public void exceptionThrowingMethod(Object arg) throws Exception {
194    throw new IntentionalException();
195  }
196  /** Local exception subclass to check variety of exception thrown. */
197  class IntentionalException extends Exception {
198    private static final long serialVersionUID = -2500191180248181379L;
199  }
200
201  public void errorThrowingMethod(Object arg) {
202    throw new JudgmentError();
203  }
204  /** Local Error subclass to check variety of error thrown. */
205  class JudgmentError extends Error {
206    private static final long serialVersionUID = 634248373797713373L;
207  }
208}
209