001/*
002 * Copyright 2012 Atteo.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.atteo.moonshine.tests;
017
018import java.lang.reflect.InvocationTargetException;
019import java.util.ArrayList;
020import java.util.List;
021import java.util.Set;
022
023import org.atteo.moonshine.Moonshine;
024import org.junit.rules.MethodRule;
025import org.junit.rules.TestRule;
026import org.junit.runners.BlockJUnit4ClassRunner;
027import org.junit.runners.model.InitializationError;
028
029import com.google.common.collect.Lists;
030import com.google.common.reflect.TypeToken;
031
032/**
033 * Runs the tests inside {@link Moonshine} container.
034 *
035 * <p>
036 * You can configure the container by annotating the class with {@link MoonshineConfiguration}.
037 * </p>
038 * <p>
039 * The test class will be instantiated using global Guice injector of the Moonshine container.
040 * </p>
041 */
042public class MoonshineRunner extends BlockJUnit4ClassRunner {
043    private MoonshineRule moonshineRule = null;
044
045    public MoonshineRunner(Class<?> klass) throws InitializationError {
046        super(klass);
047    }
048
049    @Override
050    protected Object createTest() throws Exception {
051        return moonshineRule.getGlobalInjector().getInstance(getTestClass().getJavaClass());
052    }
053
054    @Override
055    protected List<TestRule> classRules() {
056        @SuppressWarnings("unchecked")
057        Set<Class<?>> ancestorSet = (Set<Class<?>>) TypeToken.of(getTestClass().getJavaClass()).getTypes().rawTypes();
058        List<Class<?>> ancestors = Lists.reverse(new ArrayList<>(ancestorSet));
059
060        List<String> configs = new ArrayList<>();
061        List<MoonshineConfigurator> configurators = new ArrayList<>();
062
063        for (Class<?> ancestor : ancestors) {
064            final MoonshineConfiguration annotation = ancestor.getAnnotation(MoonshineConfiguration.class);
065            if (annotation == null) {
066                continue;
067            }
068
069            for (String config : annotation.value()) {
070                if (!config.startsWith("/")) {
071                    config = "/" + ancestor.getPackage() .getName().replace(".", "/") + "/" + config;
072                }
073                configs.add(config);
074            }
075
076            Class<? extends MoonshineConfigurator> configuratorKlass = annotation.configurator();
077            if (configuratorKlass != null && configuratorKlass != MoonshineConfigurator.class) {
078                try {
079                    MoonshineConfigurator configurator = configuratorKlass.getConstructor().newInstance();
080                    configurators.add(configurator);
081                } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException
082                        | IllegalArgumentException | InvocationTargetException e) {
083                    throw new RuntimeException(e);
084                }
085            }
086
087            if (annotation.skipDefault() || !annotation.fromString().isEmpty()
088                    || annotation.arguments().length != 0) {
089                MoonshineConfigurator configurator = new MoonshineConfigurator() {
090                    @Override
091                    public void configureMoonshine(Moonshine.Builder builder) {
092                        if (annotation.skipDefault()) {
093                            builder.skipDefaultConfigurationFiles();
094                        }
095                        if (!annotation.fromString().isEmpty()) {
096                            builder.addConfigurationFromString(annotation.fromString());
097                        }
098
099                        builder.arguments(annotation.arguments());
100                    }
101                };
102                configurators.add(configurator);
103            }
104        }
105
106        moonshineRule = new MoonshineRule(configurators, configs.toArray(new String[configs.size()]));
107
108        List<TestRule> rules = super.classRules();
109        rules.add(moonshineRule);
110        return rules;
111    }
112
113    @Override
114    protected List<TestRule> getTestRules(Object target) {
115        List<TestRule> rules = super.getTestRules(target);
116        rules.add(new RequestRule());
117        return rules;
118    }
119
120    @Override
121    protected List<MethodRule> rules(Object target) {
122        List<MethodRule> rules = super.rules(target);
123        rules.add(moonshineRule.injectMembers(target));
124        rules.add(new MockitoRule());
125        return rules;
126    }
127}