/*
* Copyright 2008-2009 the original author or authors.
*
* 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 net.hasor.plugins.junit;
import net.hasor.core.*;
import net.hasor.core.context.TemplateAppContext;
import net.hasor.core.utils.BeanUtils;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
*
* @version : 2014年7月8日
* @author 赵永春(zyc@hasor.net)
*/
public class HasorUnitRunner extends BlockJUnit4ClassRunner {
protected static Logger logger = LoggerFactory.getLogger(HasorUnitRunner.class);
private AppContext appContext = null;
private BindInfo<?> typeRegister = null;
//
public HasorUnitRunner(final Class<?> klass) throws InitializationError {
super(klass);
try {
String configResource = TemplateAppContext.DefaultSettings;
//1.获取配置信息
ContextConfiguration config = klass.getAnnotation(ContextConfiguration.class);
List<Module> loadModule = new ArrayList<Module>();
if (config != null) {
configResource = config.value();
for (Class<? extends Module> mod : config.loadModules())
loadModule.add(mod.newInstance());
}
//2.初始化绑定Test
loadModule.add(new Module() {
@Override
public void loadModule(final ApiBinder apiBinder) throws Throwable {
HasorUnitRunner.this.typeRegister = apiBinder.bindType(klass).uniqueName().toInfo();
}
});
this.appContext = Hasor.createAppContext(configResource, loadModule.toArray(new Module[loadModule.size()]));
//3.
if (this.appContext == null)
throw new NullPointerException("HasorFactory.createAppContext return null.");
} catch (Exception e) {
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new InitializationError(e);
}
}
//
@Override
protected List<FrameworkMethod> computeTestMethods() {
//1.获取带有 @Test 注解的方法
List<FrameworkMethod> toRunMethodList = super.computeTestMethods();
//2.检查是否Test方法中同时带有DaemonThread注解的方法。
for (FrameworkMethod method : toRunMethodList) {
if (method.getAnnotation(DaemonThread.class) != null) {
throw new IllegalStateException("test method cannot be used at the same time, @Test, @DaemonThread");
}
}
//3.获取测试方法上的 @Order 注解,并对所有的测试方法重新排序
toRunMethodList = new ArrayList<FrameworkMethod>(toRunMethodList);
Collections.sort(toRunMethodList, new Comparator<FrameworkMethod>() {
@Override
public int compare(final FrameworkMethod m1, final FrameworkMethod m2) {
TestOrder o1 = m1.getAnnotation(TestOrder.class);
TestOrder o2 = m2.getAnnotation(TestOrder.class);
if (o1 == null || o2 == null) {
return 0;
}
return o1.value() - o2.value();
}
});
return toRunMethodList;
}
//
@Override
protected Statement methodInvoker(final FrameworkMethod method, final Object test) {
//1.准备要执行的线程
List<FrameworkMethod> methodList = this.getTestClass().getAnnotatedMethods(DaemonThread.class);//有单例问题,每个Test都会调用该方法。
final List<Thread> daemonThreads = new ArrayList<Thread>();
for (FrameworkMethod threadMethod : methodList) {
Thread daemonThread = new TestThread(test, threadMethod);
daemonThread.setDaemon(true);
daemonThreads.add(daemonThread);
}
//2.Return Statement
final Statement invokerStatement = super.methodInvoker(method, test);
return new Statement() {
@Override
public void evaluate() throws Throwable {
try {
/*A.启动监控线程*/
for (Thread thread : daemonThreads) {
thread.start();
}
invokerStatement.evaluate();
} finally {
/*b.终止监控线程*/
for (Thread thread : daemonThreads) {
thread.suspend();
thread.interrupt();
}
}
}
};
}
//
@Override
protected Object createTest() throws Exception {
Object testUnit = this.appContext.getInstance(this.typeRegister);
return testUnit;
}
//
private static class TestThread extends Thread {
private Object targetObject = null;
private FrameworkMethod method = null;
public TestThread(final Object targetObject, final FrameworkMethod method) {
super("daemonThread:" + method.getName());
this.targetObject = targetObject;
this.method = method;
}
@Override
public void run() {
List<Object> args = new ArrayList<Object>();
Class<?>[] params = this.method.getMethod().getParameterTypes();
if (params != null) {
for (Class<?> param : params) {
args.add(BeanUtils.getDefaultValue(param));
}
}
try {
this.method.invokeExplosively(this.targetObject, args.toArray());
} catch (Throwable e) {
logger.error("call invokeExplosively exception = {}.", e);
}
}
}
}