// Copyright 2015 The Project Buendia 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 distrib- // uted 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 // specific language governing permissions and limitations under the License. package org.projectbuendia.client.ui; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import org.projectbuendia.client.events.CleanupSubscriber; import org.projectbuendia.client.events.CrudEventBus; import org.projectbuendia.client.utils.EventBusInterface; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashSet; import java.util.List; import java.util.Set; import de.greenrobot.event.EventBus; import static junit.framework.Assert.fail; /** * Fake event bus implementation. The real {@link EventBus} is not suitable for unit tests * because it calls methods on a separate thread to the test thread. This is because the * 'main thread' is not the same as the test thread. */ public final class FakeEventBus implements EventBusInterface, CrudEventBus { private static final String METHOD_NAME_EVENT_RECEIVER_MAIN_THREAD = "onEventMainThread"; private static final Set<String> IGNORED_METHOD_NAMES = ImmutableSet.of( "equals", "hashCode", "toString", "getClass", "notify", "notifyAll", "wait"); private final Set<Object> mRegisteredReceivers = new HashSet<>(); private final List<Object> mEventLog = Lists.newArrayList(); @Override public void register(Object receiver) { for (Method method : receiver.getClass().getMethods()) { // We only support a subset of the event bus functionality, so we check methods on the // receiver match a whitelist of supported methods. This should ensure the tests fail // in an explicit noisy way if the code under test is using event bus functionality that // we haven't implemented in this class. if (!IGNORED_METHOD_NAMES.contains((method.getName()))) { Preconditions.checkArgument( method.getName().equals(METHOD_NAME_EVENT_RECEIVER_MAIN_THREAD), "Method was called " + method.getName() + ". Fake event bus only supports " + "methods called " + METHOD_NAME_EVENT_RECEIVER_MAIN_THREAD); Preconditions.checkArgument( method.getParameterTypes().length == 1, "The fake event bus only supports methods with a single parameter"); } } mRegisteredReceivers.add(receiver); } @Override public void unregister(Object receiver) { mRegisteredReceivers.remove(receiver); } public int countRegisteredReceivers() { return mRegisteredReceivers.size(); } /** * Fails the current test unless the event log contains a particular event. * @param event the event to search for */ public void assertEventLogContains(Object event) { if (!mEventLog.contains(event)) { fail("Expected event not present. Actual events: " + mEventLog); } } public List<Object> getEventLog() { return ImmutableList.copyOf(mEventLog); } @Override public void post(Object event) { mEventLog.add(event); // Clone the receivers set so receivers can unregister themselves after responding to an // event. Set<Object> receivers = new HashSet<Object>(mRegisteredReceivers); for (Object receiver : receivers) { for (Method method : receiver.getClass().getMethods()) { if (method.getName().equals(METHOD_NAME_EVENT_RECEIVER_MAIN_THREAD)) { Class<?> parameter = method.getParameterTypes()[0]; if (parameter.isInstance(event)) { try { method.invoke(receiver, event); } catch (InvocationTargetException e) { throw new RuntimeException(e.getCause()); } catch (IllegalAccessException | IllegalArgumentException e) { throw new RuntimeException(e); } } } } } } @Override public void registerCleanupSubscriber(CleanupSubscriber subscriber) { } @Override public void unregisterCleanupSubscriber(CleanupSubscriber subscriber) { } }