package org.activityinfo.fixtures; /* * #%L * ActivityInfo Server * %% * Copyright (C) 2009 - 2013 UNICEF * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Module; import com.google.inject.name.Names; import org.activityinfo.server.database.LoadDataSet; import org.activityinfo.server.database.OnDataSet; import org.junit.AfterClass; import org.junit.internal.runners.statements.RunAfters; import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; import org.junit.runners.model.Statement; import java.lang.reflect.Method; import java.sql.Connection; import java.util.ArrayList; import java.util.List; public class InjectionSupport extends BlockJUnit4ClassRunner { private Injector injector; private TestScopeModule scopeModule; private List<Module> modules; public InjectionSupport(Class<?> klass) throws InitializationError { super(klass); modules = new ArrayList<Module>(); scopeModule = new TestScopeModule(); modules.add(scopeModule); addModulesFromClass(klass); System.err.println("Creating injector for " + klass.getName()); injector = Guice.createInjector(modules); } private void addModulesFromClass(Class<?> klass) { while (klass != null) { Modules moduleClasses = klass.getAnnotation(Modules.class); if (moduleClasses != null) { addModulesFromAnnotation(klass, moduleClasses); } klass = klass.getSuperclass(); } } private void addModulesFromAnnotation(Class<?> klass, Modules moduleClasses) { for (Class moduleClass : moduleClasses.value()) { try { modules .add((Module) moduleClass.getConstructor().newInstance()); } catch (Exception e) { throw new RuntimeException( "Exception thrown while creating modules for test " + klass.getName() + ":\n could not instantiate module class " + moduleClass.getName(), e); } } } @Override protected void validateConstructor(List<Throwable> errors) { // We'll just have to wait for Guice to throw errors if there is a // problem } @Override protected Statement classBlock(RunNotifier notifier) { Statement statement = super.classBlock(notifier); for (Module module : modules) { statement = withModuleAfterClasses(statement, module); } return statement; } @Override protected Statement withAfters(FrameworkMethod method, Object target, Statement statement) { // withAfters() is the last called, so it give us a chance to // add InjectDependencies as the outermost wrapper and ensure // that class members are injected return new InjectDependencies(super.withAfters(method, target, statement), injector, scopeModule.getTestScope(), target); } @Override protected Statement methodInvoker(FrameworkMethod method, Object test) { Statement statement = super.methodInvoker(method, test); statement = withLoadDatasets(method, statement, test); return statement; } @Override protected Statement withBefores(FrameworkMethod method, Object target, Statement statement) { return withLoadDatasets(method, super.withBefores(method, target, statement), target); } private Statement withLoadDatasets(FrameworkMethod method, Statement statement, Object target) { OnDataSet ods = method.getAnnotation(OnDataSet.class); if (ods == null) { ods = target.getClass().getAnnotation(OnDataSet.class); } return ods == null ? statement : new LoadDataSet(injector.getProvider(Connection.class), statement, ods.value(), target); } /** * Returns a {@link Statement}: run all non-overridden {@code @AfterClass} * methods on this class and superclasses before executing {@code statement} * ; all AfterClass methods are always executed: exceptions thrown by * previous steps are combined, if necessary, with exceptions from * AfterClass methods into a * {@link org.junit.internal.runners.model.MultipleFailureException}. */ protected Statement withModuleAfterClasses(Statement statement, Module module) { List<FrameworkMethod> afters = getAnnotatedModuleMethods(module, AfterClass.class); return afters.isEmpty() ? statement : new RunAfters(statement, afters, module); } protected List<FrameworkMethod> getAnnotatedModuleMethods(Module module, Class annotationClass) { List<FrameworkMethod> annotated = new ArrayList<FrameworkMethod>(); for (Method method : module.getClass().getMethods()) { if (method.getAnnotation(annotationClass) != null) { annotated.add(new FrameworkMethod(method)); } } return annotated; } public static class TestScopeModule extends AbstractModule { private SimpleScope testScope; public TestScopeModule() { } @Override protected void configure() { testScope = new SimpleScope(); // tell Guice about the scope bindScope(TestScoped.class, testScope); // make our scope instance injectable bind(SimpleScope.class) .annotatedWith(Names.named("tests")) .toInstance(testScope); } public SimpleScope getTestScope() { return testScope; } } }