/* * JBoss, Home of Professional Open Source * Copyright 2010 Red Hat Inc. and/or its affiliates and other contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * 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 org.jboss.arquillian.core.test; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import org.jboss.arquillian.core.api.Injector; import org.jboss.arquillian.core.api.InstanceProducer; import org.jboss.arquillian.core.api.annotation.ApplicationScoped; import org.jboss.arquillian.core.api.annotation.Inject; import org.jboss.arquillian.core.api.annotation.Observes; import org.jboss.arquillian.core.impl.ManagerImpl; import org.jboss.arquillian.core.impl.UncheckedThrow; import org.jboss.arquillian.core.impl.context.ApplicationContextImpl; import org.jboss.arquillian.core.spi.Manager; import org.jboss.arquillian.core.spi.ManagerBuilder; import org.jboss.arquillian.core.spi.context.ApplicationContext; import org.jboss.arquillian.core.spi.context.Context; import org.junit.After; import org.junit.Assert; import org.junit.Before; /** * AbstractManagerTestBase * * @author <a href="mailto:aslak@redhat.com">Aslak Knutsen</a> * @version $Revision: $ */ public abstract class AbstractManagerTestBase { private static ManagerImpl manager; private static List<Class<? extends Context>> contexts; @Before public final void create() throws Exception { contexts = new ArrayList<Class<? extends Context>>(); ManagerBuilder builder = ManagerBuilder.from(); addContexts(contexts); for (Class<? extends Context> context : contexts) { builder.context(context); } // Add ApplicationContext, it's internal to Manager, but needs to be registered as a context so EventRecorder will pick up on it contexts.add(0, ApplicationContextImpl.class); builder.extension(EventRegisterObserver.class); List<Class<?>> extensions = new ArrayList<Class<?>>(); addExtensions(extensions); for (Class<?> extension : extensions) { builder.extension(extension); } manager = (ManagerImpl) builder.create(); manager.start(); executeInApplicationScope(new Callable<Void>() { @Override public Void call() throws Exception { manager.resolve(Injector.class).inject(AbstractManagerTestBase.this); return null; } }); startContexts(manager); } @After public final void destory() { manager.shutdown(); manager = null; } public ManagerImpl getManager() { return manager; } //-------------------------------------------------------------------------------------|| // Assertions and Helper operations ----------------------------------------------------|| //-------------------------------------------------------------------------------------|| public final void fire(Object event) { manager.fire(event); } public final <T> void bind(Class<? extends Annotation> scope, Class<T> type, T instance) { manager.bind(scope, type, instance); } public final void assertEventFired(Class<?> type) { Assert.assertNotNull( "Event " + type.getName() + " should have been fired", getRegister().getCount(type)); } public final void assertEventFired(Class<?> type, Integer count) { Assert.assertEquals( "The event of exact type " + type.getName() + " should have been fired", count, getRegister().getCount(type)); } public final void assertEventFiredTyped(Class<?> type, Integer count) { Assert.assertEquals( "The event of assiganble type to " + type.getName() + " should have been fired", count, getRegister().getCountTyped(type)); } public final void assertEventFiredInContext(Class<?> type, Class<? extends Context> activeContext) { assertEventInContext(type, activeContext, true); } public final void assertEventNotFiredInContext(Class<?> type, Class<? extends Context> activeContext) { assertEventInContext(type, activeContext, false); } private void assertEventInContext(Class<?> type, Class<? extends Context> activeContext, Boolean active) { Assert.assertEquals( "Event " + type.getName() + " should" + (active ? " " : " not") + " have been fired within context " + activeContext.getName(), active, getRegister().wasActive(type, activeContext)); } public final void assertEventFiredOnOtherThread(Class<?> type) { Assert.assertTrue( "The event of type to " + type.getName() + " should have been fired on a different thread", getRegister().wasExecutedOnNonMainThread(type)); } private EventRegister getRegister() { try { return executeInApplicationScope(new Callable<EventRegister>() { @Override public EventRegister call() throws Exception { return manager.resolve(EventRegister.class); } }); } catch (Exception e) { throw new RuntimeException(e); } } //-------------------------------------------------------------------------------------|| // Extendables ------------------------------------------------------------------------|| //-------------------------------------------------------------------------------------|| protected void addExtensions(List<Class<?>> extensions) { } protected void addContexts(List<Class<? extends Context>> contexts) { } protected void startContexts(Manager manager) { manager.getContext(ApplicationContext.class).activate(); } //-------------------------------------------------------------------------------------|| // Internal Helpers - Track events ----------------------------------------------------|| //-------------------------------------------------------------------------------------|| public <T> T executeInApplicationScope(Callable<T> op) throws Exception { return manager.executeInApplicationContext(op); } public static class EventRegisterObserver { @Inject @ApplicationScoped private InstanceProducer<EventRegister> register; @SuppressWarnings("unchecked") public synchronized void register(@Observes Object event) { if (register.get() == null) { register.set(new EventRegister()); } // TODO: looking up the static manager is a hack get to the Contexts dynamically. Manager can be null since events are fired during Manager creation if (manager == null) { return; } EventRegister reg = register.get(); EventRecording rec = new EventRecording(); for (Class<? extends Context> context : contexts) { Class<? extends Context> contextInterface = (Class<? extends Context>) context.getInterfaces()[0]; rec.add(contextInterface, manager.getContext(contextInterface).isActive()); } reg.add(event.getClass(), rec); if (event instanceof Throwable) { // we are listening to Object which is not really a good thing, so throw exceptions if found. UncheckedThrow.throwUnchecked((Throwable) event); } } } public static class EventRegister { private Map<Class<?>, List<EventRecording>> events; public EventRegister() { events = new HashMap<Class<?>, List<EventRecording>>(); } public void add(Class<?> type, EventRecording recording) { if (events.get(type) == null) { List<EventRecording> recordings = new ArrayList<EventRecording>(); recordings.add(recording); events.put(type, recordings); } else { events.get(type).add(recording); } } /** * Get the count of a assignable count. * * @param type * The assignable event type * * @return Number of times fired */ public Integer getCountTyped(Class<?> type) { int count = 0; for (Map.Entry<Class<?>, List<EventRecording>> recordingEntry : events.entrySet()) { if (type.isAssignableFrom(recordingEntry.getKey())) { count += recordingEntry.getValue().size(); } } return count; } /** * Get the count of a specific type. * * @param type * The exact event type * * @return Number of times fired */ public Integer getCount(Class<?> type) { return events.containsKey(type) ? events.get(type).size() : 0; } public Boolean wasExecutedOnNonMainThread(Class<?> type) { if (getCount(type) == 0) { return false; } for (EventRecording recording : events.get(type)) { if ("main".equalsIgnoreCase(recording.getThreadName())) { return false; } } return true; } public Boolean wasActive(Class<?> type, Class<? extends Context> context) { if (getCount(type) == 0) { return false; } for (EventRecording recording : events.get(type)) { if (!recording.wasActive(context)) { return false; } } return true; } } private static class EventRecording { private Map<Class<? extends Context>, Boolean> activeContexts; private String threadName; public EventRecording() { threadName = Thread.currentThread().getName(); activeContexts = new HashMap<Class<? extends Context>, Boolean>(); } public String getThreadName() { return threadName; } public EventRecording add(Class<? extends Context> context, Boolean isActive) { activeContexts.put(context, isActive); return this; } public Boolean wasActive(Class<? extends Context> context) { if (activeContexts.get(context) != null) { return activeContexts.get(context); } return false; } } }