/*
* Copyright 2004-2015 the Seasar Foundation and the Others.
*
* 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.seasar.framework.unit;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.manipulation.Filter;
import org.junit.runner.manipulation.Filterable;
import org.junit.runner.manipulation.NoTestsRemainException;
import org.junit.runner.manipulation.Sortable;
import org.junit.runner.manipulation.Sorter;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.Parameterized.Parameters;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;
import org.seasar.framework.env.Env;
import org.seasar.framework.util.ResourceUtil;
/**
* S2JUnit4を実行するための{@link org.junit.runner.Runner}です。
* <p>
* {@link org.junit.runner.RunWith}に指定して次のように利用します。
*
* <pre>
* @RunWith(Seasar2.class)
* public class HogeTest {
* ...
* }
* </pre>
*
* </p>
*
* @author taedium
*
*/
public class Seasar2 extends Runner implements Filterable, Sortable {
/** S2JUnit4の振る舞いを設定するためのコンフィグレーションファイルのキー */
public static final String S2JUNIT4_CONFIG_KEY = "org.seasar.framework.unit.s2junit4.config";
/** S2JUnit4の振る舞いを設定するためのコンフィグレーションファイルのパス */
public static final String S2JUNIT4_CONFIG_PATH = "s2junit4config.dicon";
/** このランナーで使用する環境名設定ファイルのパス */
public static final String ENV_PATH = "env_ut.txt";
/** 環境名設定ファイルのパスにファイルが存在しない場合の環境名 */
public static final String ENV_VALUE = "ut";
/** コンフィグレーションファイルから構築されたコンフィグレーションS2コンテナ */
protected static S2Container configurationContainer;
/** {@link Seasar2}の振る舞いを提供するプロバイダ */
protected static Provider provider;
static {
configure();
}
private final Runner delegate;
/**
* インスタンスを構築します。
*
* @param clazz
* テストクラス
* @throws Exception
* 何らかの例外が発生した場合
*/
public Seasar2(final Class<?> clazz) throws Exception {
delegate = createTestClassRunner(clazz);
}
/**
* テストクラスランナーを作成します。
*
* @param clazz
* テストクラス
* @return テストクラスランナー
* @throws Exception
* 何らかの例外が発生した場合
*/
protected Runner createTestClassRunner(Class<?> clazz) throws Exception {
return getProvider().createTestClassRunner(clazz);
}
/**
* {@link Seasar2}の振る舞いを提供するプロバイダを返します。
*
* @return 振る舞いを提供するプロバイダ
*/
protected static Provider getProvider() {
return provider;
}
/**
* {@link Seasar2}の振る舞いを提供するプロバイダを設定します。
*
* @param p
* 振る舞いを提供するプロバイダ
*/
protected static void setProvider(final Provider p) {
provider = p;
}
/**
* このクラスを設定します。
*/
public static void configure() {
final String configFile = System.getProperty(S2JUNIT4_CONFIG_KEY,
S2JUNIT4_CONFIG_PATH);
configure(configFile);
}
/**
* このクラスを設定します。
*
* @param configFile
* 設定ファイルのパス
*/
public static void configure(final String configFile) {
Env.setFilePath(ENV_PATH);
Env.setValueIfAbsent(ENV_VALUE);
if (provider == null) {
provider = new DefaultProvider();
}
if (ResourceUtil.isExist(configFile)) {
configurationContainer = S2ContainerFactory.create(configFile);
Configurator configurator;
if (configurationContainer.hasComponentDef(Configurator.class)) {
configurator = (Configurator) configurationContainer
.getComponent(Configurator.class);
} else {
configurator = new DefaultConfigurator();
}
configurator.configure(configurationContainer);
}
}
/**
* このクラスを破棄します。
*/
public static void dispose() {
S2TestClassMethodsRunner.dispose();
provider = null;
if (configurationContainer != null) {
configurationContainer.destroy();
}
configurationContainer = null;
Env.initialize();
}
@Override
public Description getDescription() {
return delegate.getDescription();
}
@Override
public void run(final RunNotifier notifier) {
delegate.run(notifier);
}
public void filter(final Filter filter) throws NoTestsRemainException {
FilterCompatibility.apply(filter, delegate);
}
public void sort(final Sorter sorter) {
SorterCompatibility.apply(sorter, delegate);
}
/**
* {@link Seasar2}の振る舞いを構成します。
*
* @author taedium
*/
public interface Configurator {
/**
* Seasar2ランナーの振る舞いを構成します。
*
* @param configurationContainer
* コンフィグレーションS2コンテナ
*/
void configure(S2Container configurationContainer);
}
/**
* {@link Seasar2}の振る舞いを構成するデフォルトの実装クラスです。
*
* @author taedium
*/
public static class DefaultConfigurator implements Configurator {
public void configure(final S2Container configurationContainer) {
if (configurationContainer.hasComponentDef(Provider.class)) {
provider = (Provider) configurationContainer
.getComponent(Provider.class);
}
if (configurationContainer
.hasComponentDef(S2TestClassMethodsRunner.Provider.class)) {
S2TestClassMethodsRunner
.setProvider((S2TestClassMethodsRunner.Provider) configurationContainer
.getComponent(S2TestClassMethodsRunner.Provider.class));
}
}
}
/**
* {@link Seasar2}の振る舞いを提供します。
*
* @author taedium
*/
public interface Provider {
/**
* テストクラスランナーを返します。
*
* @param clazz
* テストクラス
* @return テストクラスランナー
* @throws Exception
* 何らかの例外が発生した場合
*/
Runner createTestClassRunner(Class<?> clazz) throws Exception;
}
/**
* {@link Seasar2}の振る舞いを提供するデフォルトの実装クラスです。
*
* @author taedium
*/
public static class DefaultProvider implements Provider {
public Runner createTestClassRunner(final Class<?> clazz)
throws Exception {
if (hasParameterAnnotation(clazz)) {
return new S2Parameterized(clazz);
}
return new S2TestClassRunner(clazz, new S2TestClassMethodsRunner(
clazz));
}
/**
* {@link Parameters}が注釈されたメソッドが存在する場合<code>true</code>
*
* @param clazz
* テストクラス
* @return <code>Parameters</code>が注釈されたメソッドが存在する場合<code>true</code>
* 、そうでない場合<code>false</code>
*/
protected boolean hasParameterAnnotation(final Class<?> clazz) {
for (Method each : clazz.getMethods()) {
if (each.isBridge() || each.isSynthetic()) {
continue;
}
if (Modifier.isStatic(each.getModifiers())) {
Annotation[] annotations = each.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation.annotationType() == Parameters.class)
return true;
}
}
}
return false;
}
}
}