All Downloads are FREE. Search and download functionalities are using the official Maven repository.

holmos.webtest.junitextentions.HolmosRunner Maven / Gradle / Ivy

There is a newer version: 1.0.2u10
Show newest version
package holmos.webtest.junitextentions;

import holmos.webtest.basetools.HolmosAnnotationTool;
import holmos.webtest.basetools.HolmosBaseTools;
import holmos.webtest.exceptions.HolmosFailedError;
import holmos.webtest.junitextentions.annotations.DataBase;
import holmos.webtest.junitextentions.annotations.MultiThread;
import holmos.webtest.junitextentions.annotations.Parameter;
import holmos.webtest.junitextentions.annotations.RunAfter;
import holmos.webtest.junitextentions.annotations.Source;
import holmos.webtest.junitextentions.annotations.Timesout;
import holmos.webtest.junitextentions.parameters.HolmosFrameWorkMethod;
import holmos.webtest.junitextentions.parameters.ParameterDataInfo;
import holmos.webtest.junitextentions.parameters.ParameterFrameWorkMethod;
import holmos.webtest.junitextentions.parameters.database.ConnectionInfo;
import holmos.webtest.junitextentions.parameters.getter.DataGetter;
import holmos.webtest.junitextentions.parameters.getter.DataGetterFactory;
import holmos.webtest.junitextentions.parameters.getter.DatabaseDataGetter;

import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.junit.Test;
import org.junit.runner.Description;
import org.junit.runner.manipulation.Filter;
import org.junit.runner.manipulation.NoTestsRemainException;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.ParentRunner;
import org.junit.runners.model.FrameworkField;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.RunnerScheduler;
import org.junit.runners.model.Statement;

/**
 * junit的case扩展顺序执行器
 * 
 * @author 吴银龙([email protected])
 * */
public class HolmosRunner extends ParentRunner{
	/*所有待测的测试方法*/
	private List children;
	/**此测试类中所有的{@link HolmosFrameWorkMethod}类型方法,这种类型将以执行为单位的方法组织起来*/
	private ArrayListholmosMethods;
	/*类级别的数据库连接信息*/
	private HashMapconnections=new HashMap();
	/**本测试类所有的测试参数数据读取器*/
	private ArrayList parameterDataGetters;
	/**设置类所有测试方法的超时时间*/
	private int testClassTimesout;
	/**测试类实例*/
	private Object testTarget;
	/*数据源信息缓冲池*/
	private HashMapsourceInfo=new HashMap();
	public static HashMapsources=new HashMap();
	private static ClasstestClassforGetter;
	public static Class getTestClassForGetter(){
		return testClassforGetter;
	}
	public HolmosRunner(Class klass) throws InitializationError {
		super(klass);
		//检查哪些带有@Source注解或者带有@Parameter注解的方法,如果有不是public类型的,将抛出异常,不予执行
		try {getTestTarget();} catch (Exception e) {}
		validatePublicModiferWithAnnotation(Source.class);
		validatePublicModiferWithAnnotation(Parameter.class);
		volidateDeadLockExcuteOrder();
		initDataGetters();
		initExecuteOrderInfo();
		initTestClassTimesout();
	}
	
	private void initTestClassTimesout() {
		Timesout timesout=HolmosAnnotationTool.getClassLevelAnnotation(getTestClass().getClass(), Timesout.class);
		if(timesout!=null){
			testClassTimesout=timesout.value();
			if(testClassTimesout<=0)testClassTimesout=1;
		}else{
			testClassTimesout=-1;//知道所有用例执行完毕
		}
	}

	@Override
	protected List getChildren() {
		return computeTestMethods();
	}

	@Override
	protected Description describeChild(ParameterFrameWorkMethod method) {
		Description description= Description.createTestDescription(getTestClass().getJavaClass(),
				testName(method), method.getAnnotations());
		return description;
	}
	
	@Override
	protected void runChild(ParameterFrameWorkMethod child, RunNotifier notifier) {
		try {
			HolmosFrameWorkMethod holmosFrameWorkMethod=getMethodByName(child.getName());
			holmosFrameWorkMethod.setTarget(getTestTarget());
			holmosFrameWorkMethod.setNotifier(notifier);
			if(!holmosFrameWorkMethod.hasPrecursorMethod())
				holmosFrameWorkMethod.invokeExplosively(getTestTarget(), notifier);
		} catch (Exception e) {
			e.printStackTrace();
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}
	private List fFilteredChildren= null;
	private RunnerScheduler fScheduler= new RunnerScheduler() {	
		public void schedule(Runnable childStatement) {
			childStatement.run();
		}
	
		public void finished() {
			if(HolmosRunner.this.testClassTimesout==-1){
				while(true){
					if(isAllexecuted())
						return;
					HolmosBaseTools.sleep(30);
				}
			}else{
				int i=0,milliseconds=1000*testClassTimesout;
				while(true){
					if(i>=milliseconds){
						throw new HolmosFailedError(getTestClass().getName()+"执行超时,设置的超时时间为"+testClassTimesout+"秒!");
					}
					if(isAllexecuted())
						return;
				}
			}
		}
		
		private boolean isAllexecuted(){
			for(ParameterFrameWorkMethod method:getFilteredChildren()){
				if(!getMethodByName(method.getName()).isExecuted())
					return false;
			}
			return true;
		}
	};
	
	protected Statement childrenInvoker(final RunNotifier notifier) {
		return new Statement() {
			@Override
			public void evaluate() {
				runChildren(notifier);
			}
		};
	}

	private boolean shouldRun(Filter filter, ParameterFrameWorkMethod each) {
		return filter.shouldRun(describeChild(each));
	}
	public void filter(Filter filter) throws NoTestsRemainException {
		for (Iterator iter = getFilteredChildren().iterator(); iter.hasNext(); ) {
			ParameterFrameWorkMethod each = iter.next();
			if (shouldRun(filter, each))
				try {
					filter.apply(each);
				} catch (NoTestsRemainException e) {
					iter.remove();
				}
			else
				iter.remove();
		}
	    if (getFilteredChildren().isEmpty()) {
	        throw new NoTestsRemainException();
	    }
	}
	
	private void runChildren(final RunNotifier notifier) {
		for (final ParameterFrameWorkMethod each : getFilteredChildren())
 			fScheduler.schedule(new Runnable() {			
				public void run() {
					HolmosRunner.this.runChild(each, notifier);
				}
			});
		fScheduler.finished();
	}
	private List getFilteredChildren() {
		if (fFilteredChildren == null)
			fFilteredChildren = new ArrayList(getChildren());
		return fFilteredChildren;
	}
	
	protected String testName(FrameworkMethod method) {
		if(method instanceof ParameterFrameWorkMethod){
			String name = method.toString();
			((ParameterFrameWorkMethod) method).setMethodName(null);
			return name;
		}
		return method.getName();
	}
	
	/**
	 * 实例化待测的测试类,此类里面包含了所有带有@Test注解的测试方法,并加上了带有@Source的方法,如果此方法是在测试类级别的注解
	 * 那么这个时候便会查看ID变量如果没有的话并且有参数信息,那么参数将会被按成默认值处理(对象为null,数值为0/0.0)
	 * */
	protected Object getTestTarget() throws Exception {
		//实例化测试类,生成测试对象
		if(null==testTarget)
		  testTarget = getTestClass().getOnlyConstructor().newInstance();
		HolmosRunner.testClassforGetter=HolmosRunner.this.testTarget.getClass();
		return testTarget;
	}
	
	/**
	 * 初始化执行顺序前驱和后继信息以及确立执行顺序
	 * 
  • 带有多线程异步执行注解{@link MultiThread}的放到最前面
  • * */ private void initExecuteOrderInfo() { if(null==holmosMethods) computeTestMethods(); for(HolmosFrameWorkMethod method:holmosMethods){ RunAfter runAfter=method.getMethod().getAnnotation(RunAfter.class); if(runAfter!=null){ HolmosFrameWorkMethod precursorMethod=getMethodByName(runAfter.methodName()); if(precursorMethod!=null) precursorMethod.addSuccessorMethods(method); else throw new HolmosFailedError("方法"+method.getMethod().getName()+"的@RunAfter注解里面的方法名字在此测试类里面不存在,请您检查!"); } } } private HolmosFrameWorkMethod getMethodByName(String methodName){ for(HolmosFrameWorkMethod method:getHolmosMethods()){ if(method.getMethod().getName().equalsIgnoreCase(methodName)) return method; }return null; } /** * 得到此testClass里面所有的测试方法,以执行为单位 * */ protected List computeTestMethods() { if(null==children){ //children只会获取一次,获取一次之后就会缓存在这里面 children=new ArrayList(); holmosMethods=new ArrayList(); //得到所有的测试方法 for(FrameworkMethod method:getTestClass().getAnnotatedMethods(Test.class)){ wrapperMethod(method); } } return children; } /** * 获取HolmosFrameWorkMethod列表,以组织为单位 * */ private ArrayListgetHolmosMethods(){ if(null==holmosMethods){ computeTestMethods(); }return holmosMethods; } /**装配方法*/ private void wrapperMethod(FrameworkMethod method){ HolmosFrameWorkMethod holmosMethod=new HolmosFrameWorkMethod(method); if(holmosMethod.isMultiThread()){ holmosMethods.add(0, holmosMethod); children.addAll(0, holmosMethod.getExcuteFrameWorkMethods()); }else{ holmosMethods.add(holmosMethod); children.addAll(holmosMethod.getExcuteFrameWorkMethods()); } wrapperDataGetter(holmosMethod); } private void wrapperDataGetter(HolmosFrameWorkMethod method){ Source source=method.getMethod().getAnnotation(Source.class); if(source!=null){ method.setGetter(sources.get(source.sourceID())); } } /** * 初始化参数数据源配置信息,在类和方法的@Source注解里面指定 * */ private void initDataGetters() { parameterDataGetters=new ArrayList(); initDataBaseConnections(); for(Source source:getSourceAnnotations()){ volidateValueOfSourceAnnotation(source); String mark=getValueOfSourceAnnotation(source); if(sourceInfo.get(mark)!=null) throw new HolmosFailedError("配置了相同的数据源信息:"+mark); DataGetter getter=DataGetterFactory.getDataGetter(source); if((!source.selector().equalsIgnoreCase(""))&&(source.selector()!=null)){ if(connections.get(source.connectionName())==null) throw new HolmosFailedError(source.connectionName()+"没有为数据源配置数据库连接!"); ((DatabaseDataGetter)getter).setConnection(connections.get(source.connectionName())); } ParameterDataInfo parameterDataInfo=new ParameterDataInfo(getter); parameterDataGetters.add(parameterDataInfo); sourceInfo.put(mark, parameterDataInfo); if(sources.get(source.sourceID())!=null) throw new HolmosFailedError("不同的参数源信息的id配置重复!请查看,重复的id为:"+source.sourceID()); sources.put(source.sourceID(), sourceInfo.get(mark)); } } /** * 检查在配置case执行顺序的时候,是否出现了死锁现象,比如1->2->3->1这个时候就出现了死锁,如果出现了死锁,将会抛出异常 * */ private void volidateDeadLockExcuteOrder() { volidateRunAfterAnnotationMethodName(); Listmethods=getTestClass().getAnnotatedMethods(RunAfter.class); HashMap>orderInfo=new HashMap>(); ArrayListafterMethods=new ArrayList(); for(FrameworkMethod method:methods){ String priorMethodName=method.getAnnotation(RunAfter.class).methodName(); if(orderInfo.get(priorMethodName)==null) afterMethods=new ArrayList(); else afterMethods=orderInfo.get(priorMethodName); afterMethods.add(method.getName()); orderInfo.put(priorMethodName, afterMethods); } volidateDeadLock(orderInfo); } @SuppressWarnings("unchecked") private void volidateDeadLock(HashMap> orderInfo) { Iterator>>iterator=orderInfo.entrySet().iterator(); while(iterator.hasNext()){ @SuppressWarnings("rawtypes") Map.Entry entry = (Map.Entry) iterator.next(); for(String next:(ArrayList)entry.getValue()){ String deadLockPath=getDeadLockPath((String) entry.getKey(),next,orderInfo); if(deadLockPath!=null){ throw new HolmosFailedError("方法"+entry.getKey()+"()和方法"+next+"()存在了运行顺序死锁,死锁" + "情况如下:\n"+deadLockPath); } } } } private String getDeadLockPath(String start, String end, HashMap> orderInfo) { if(start.equalsIgnoreCase(end))return start+"->"+start+"->"+start; String startToEnd=getReachablePath(start,end,orderInfo); String endToStart=getReachablePath(end,start,orderInfo); if(startToEnd!=null&&endToStart!=null){ return startToEnd+endToStart.substring(endToStart.indexOf('-')); } return null; } private String getReachablePath(String start, String end, HashMap> orderInfo) { if(start.equalsIgnoreCase(end))return start; ArrayListnodes=orderInfo.get(start); if(null==nodes)return null; for(String node:nodes){ String pathTemp=getReachablePath(node, end, orderInfo); if(null!=pathTemp) return start+"->"+pathTemp; } return null; } /** * 校验在{@link RunAfter}注解里面的方法名称必须是此测试类中的方法 * */ private void volidateRunAfterAnnotationMethodName() { Listmethods=getTestClass().getAnnotatedMethods(RunAfter.class); for(FrameworkMethod method:methods){ volidateMethodInTestClass(method); } } private void volidateMethodInTestClass(FrameworkMethod method){ ListallTestMethods=getTestClass().getAnnotatedMethods(Test.class); for(FrameworkMethod methodTemp:allTestMethods){ if(methodTemp.getName().equalsIgnoreCase(method.getName())) return; } throw new HolmosFailedError(method.getName()+"方法里面的@RunAfer注解里面的方法名字不在此测试类测试方法列表里面!"); } private String getValueOfSourceAnnotation(Source source){ if(source.file()==""||source.file().equalsIgnoreCase("")) return source.selector(); return source.file(); } /** * 校验该测试类的所有的Source注解的数据源信息是否符合要求 *
  • 不能都有值
  • *
  • 不能是都为null或空窜
  • * */ private void volidateValueOfSourceAnnotation(Source source){ int sourceCount=0; if(source.file()!=null&&!source.file().equalsIgnoreCase("")) sourceCount++; if(source.selector()!=null&&!source.selector().equalsIgnoreCase("")) sourceCount++; if(source.className()!=null&&!source.className().equalsIgnoreCase("")) sourceCount++; if(sourceCount>=2){ throw new HolmosFailedError("在一个数据源配置里面不能同时配置数据库和文件两种类型!"); }if(0==sourceCount){ throw new HolmosFailedError("@Source没有配置数据源!"); }if(source.sourceID()<=0) throw new HolmosFailedError("Holmos框架不允许参数源的id值小于等于0!请设置参数源的id值为一个正数!"); } /** * 获取此测试类中的所有的@Source注解对象;如果有类级别的注解,那么第一个就是这个类级别的,方法级别的按照定义的顺序依次排列 * * */ private ArrayList getSourceAnnotations(){ ArrayListsources=new ArrayList(); try { Source sourceOfClass=HolmosAnnotationTool.getClassLevelAnnotation(getTestTarget().getClass(), Source.class); if(null!=sourceOfClass) sources.add(sourceOfClass); sources.addAll(HolmosAnnotationTool.getMethodLevelAnnotations(getTestTarget().getClass(), Source.class)); } catch (Exception e) { e.printStackTrace(); } return sources; } /** * 初始化类级别的数据库连接信息,在@Database注解里面指定,必须制定数据库连接的名字,如果不指定,抛出异常 * */ private void initDataBaseConnections() { try{ DataBase databaseOfClass=HolmosAnnotationTool.getClassLevelAnnotation(getTestTarget().getClass(), DataBase.class); if(databaseOfClass!=null){ connections.put(databaseOfClass.connectionName(),new ConnectionInfo(databaseOfClass)); }for(DataBase database:HolmosAnnotationTool.getMethodLevelAnnotations(getTestTarget().getClass(), DataBase.class)){ if(connections.get(database.connectionName())!=null) throw new HolmosFailedError("数据库连接的名字重复了,这个重复的名字为:"+database.connectionName()); connections.put(database.connectionName(),new ConnectionInfo(database)); } }catch(Exception e){} } private void validatePublicModiferWithAnnotation(Class annotation){ Listfields=getTestClass().getAnnotatedFields(annotation); for(FrameworkField field:fields){ if((Modifier.PUBLIC & field.getField().getModifiers())==0){ throw new HolmosFailedError(field.getName()+"的修饰符不是public类型,程序已经退出!Holmos框架不允许不是public类型的测试方法运行!"); } } } }




    © 2015 - 2024 Weber Informatics LLC | Privacy Policy