/* * Copyright 2012 Atteo. * * 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.atteo.moonshine.tests; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import org.atteo.moonshine.Moonshine; import org.atteo.moonshine.tests.MoonshineConfiguration.Config; import org.junit.rules.MethodRule; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; import com.google.common.base.Joiner; import com.google.common.collect.Lists; import com.google.common.reflect.TypeToken; import com.google.inject.AbstractModule; import com.google.inject.TypeLiteral; /** * Runs the tests inside {@link Moonshine} container. * * <p> * You can configure the container by annotating the class with * {@link MoonshineConfiguration}. * </p> * <p> * The test class will be instantiated using global Guice injector of the * Moonshine container. * </p> */ public class MoonshineRunner extends BlockJUnit4ClassRunner { private MoonshineRule moonshineRule = null; private boolean requestPerClass = false; private final List<Config> iterationConfigs; private final List<String> iterationIds; private final Class<?> klass; public MoonshineRunner(Class<?> klass) throws InitializationError { super(klass); this.klass = klass; iterationConfigs = Collections.emptyList(); iterationIds = Collections.emptyList(); } /** * Used by {@link MoonshineMultiRunner}. */ MoonshineRunner(Class<?> klass, List<Config> iterationConfigs) throws InitializationError { super(klass); this.klass = klass; this.iterationConfigs = iterationConfigs; iterationIds = getIterationIds(iterationConfigs); } @Override protected Object createTest() throws Exception { return moonshineRule.getGlobalInjector().getInstance(getTestClass().getJavaClass()); } @Override protected List<TestRule> classRules() { @SuppressWarnings("unchecked") Set<Class<?>> ancestorSet = (Set<Class<?>>) TypeToken.of(getTestClass().getJavaClass()).getTypes().rawTypes(); List<Class<?>> ancestors = Lists.reverse(new ArrayList<>(ancestorSet)); final List<String> configPaths = new ArrayList<>(); List<MoonshineConfigurator> configurators = new ArrayList<>(); AtomicBoolean loadTestConfigXml = new AtomicBoolean(true); for (Class<?> ancestor : ancestors) { analyseAncestor(ancestor, configPaths, configurators, loadTestConfigXml); } analyseIterationConfigs(configPaths, configurators); moonshineRule = new MoonshineRule(configurators, configPaths.toArray(new String[configPaths.size()])); moonshineRule.setLoadTestConfigXml(loadTestConfigXml.get()); List<TestRule> rules = super.classRules(); if (requestPerClass) { rules.add(new RequestRule()); } rules.add(moonshineRule); return rules; } @Override protected String getName() { String name = super.getName(); if (!iterationConfigs.isEmpty()) { return name + " with config [" + Joiner.on(",").join(iterationIds) + "]"; } return name; } @Override public Description getDescription() { Description description = Description.createTestDescription(klass, getName(), getRunnerAnnotations()); for (FrameworkMethod child : getChildren()) { description.addChild(describeChild(child)); } return description; } private void analyseIterationConfigs(final List<String> configs, List<MoonshineConfigurator> configurators) { if (iterationConfigs.isEmpty()) { return; } for (Config config : iterationConfigs) { if (config.value().length != 0) { configs.addAll(Arrays.asList(config.value())); } } configurators.add((MoonshineConfigurator) (Moonshine.Builder builder) -> { for (Config config : iterationConfigs) { if (!config.fromString().isEmpty()) { builder.addConfigurationFromString(config.fromString()); } } builder.addModule(new AbstractModule() { @Override protected void configure() { bind(new TypeLiteral<List<String>>() {}).annotatedWith(EnabledConfigs.class) .toInstance(iterationIds); } }); builder.applicationName(klass.getSimpleName() + "[" + Joiner.on(",").join(iterationIds) + "]"); }); } private static List<String> getIterationIds(List<Config> iterationConfigs) { final List<String> iterationIds = new ArrayList<>(); for (Config config : iterationConfigs) { iterationIds.add(config.id()); } return iterationIds; } private static String getPathToResource(Class<?> klass, String annotationValue) { if (annotationValue.startsWith("/")) { return annotationValue; } else { return "/" + klass.getPackage().getName().replace(".", "/") + "/" + annotationValue; } } private void analyseAncestor(Class<?> ancestor, final List<String> configs, List<MoonshineConfigurator> configurators, final AtomicBoolean loadTestConfigXml) { final MoonshineConfiguration annotation = ancestor.getAnnotation(MoonshineConfiguration.class); if (annotation == null) { return; } loadTestConfigXml.set(false); for (String config : annotation.value()) { configs.add(getPathToResource(ancestor, config)); } requestPerClass = annotation.oneRequestPerClass(); if ((annotation.forEach().length != 0 || annotation.forCartesianProductOf().length != 0) && iterationConfigs.isEmpty()) { throw new RuntimeException("Error on class " + ancestor.getName() + ": @" + MoonshineConfiguration.class.getSimpleName() + " forEach and forCartesianProductOf can be used only with " + MoonshineMultiRunner.class.getSimpleName()); } Class<? extends MoonshineConfigurator> configuratorKlass = annotation.configurator(); if (configuratorKlass != null && configuratorKlass != MoonshineConfigurator.class) { try { MoonshineConfigurator configurator = configuratorKlass.getConstructor().newInstance(); configurators.add(configurator); } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException(e); } } if (annotation.autoConfiguration() || annotation.skipDefault() || !annotation.fromString().isEmpty() || annotation.arguments().length != 0) { MoonshineConfigurator configurator = (Moonshine.Builder builder) -> { if (annotation.autoConfiguration()) { builder.autoConfiguration(); } if (annotation.skipDefault()) { builder.skipDefaultConfigurationFiles(); } if (!annotation.fromString().isEmpty()) { builder.addConfigurationFromString(annotation.fromString()); } builder.arguments(annotation.arguments()); }; configurators.add(configurator); } } @Override protected List<TestRule> getTestRules(Object target) { List<TestRule> rules = super.getTestRules(target); if (!requestPerClass) { rules.add(new RequestRule()); } return rules; } @Override protected List<MethodRule> rules(Object target) { List<MethodRule> rules = super.rules(target); rules.add(moonshineRule.injectMembers(target)); rules.add(new MockitoRule()); return rules; } }