/*
* This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivs 2.0 Belgium License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/2.0/be/deed.en_US.
*/
package be.pw999.jape;
import be.pw999.jape.annotations.DirtiesObject;
import be.pw999.jape.annotations.NotInfinite;
import be.pw999.jape.annotations.Test;
import be.pw999.jape.annotations.TestSuite;
import be.pw999.jape.constants.Settings;
import be.pw999.jape.exceptions.InitializationException;
import be.pw999.jape.exporters.ExcelExporter;
import be.pw999.jape.models.Result;
import be.pw999.jape.tests.ITest;
import be.pw999.jape.tests.utils.TestScraper;
import be.pw999.jape.utils.GCManager;
import be.pw999.jape.utils.ReflectionUtils;
import be.pw999.jape.utils.SysinfoUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.sigar.Sigar;
import org.hyperic.sigar.SigarException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
/**
* @author P_W999
*/
public class TestRunner {
private Map<String, List<Result>> results = new HashMap<String, List<Result>>(10);
private static Log log = LogFactory.getLog(TestRunner.class);
private Sigar sigar = new Sigar();
public void run() {
log.info("Run classloader is " + this.getClass().getClassLoader().getClass().getName());
Set<Map.Entry<String, String>> sysInfo;
Class<? extends ITest>[] classes = TestScraper.getClasses();
log.info("Found " + classes.length + " test classes");
preLoadClassesAndMethods(classes);
//Getting system information
try {
sysInfo = SysinfoUtils.getAllInfo(sigar).entrySet();
} catch (SigarException e) {
log.error("Error while getting system info", e);
Map<String, String> errorMap = new HashMap<String, String>();
errorMap.put("ERROR", e.getMessage());
sysInfo = errorMap.entrySet();
}
prettyPrintSysInfo(sysInfo);
//Preparation
results.clear();
//The actual tests
for (Class<? extends ITest> clazz : classes) {
log.info("Starting test class " + clazz.getSimpleName());
try {
ITest test = (ITest) (this.getClass().getClassLoader().loadClass(clazz.getName())).newInstance();
log.trace("Initializing test class for first time (" + test.getClass().getSimpleName() + ")");
test.init();
//Each test-method inside a test-class
Method[] methods = test.getClass().getMethods();
for (Method method : methods) {
if (ReflectionUtils.getAnnotation(Test.class, method) != null) {
try {
int repeats = 1;
int size = Settings.TEST_LOOPS;
int leftOvers = 0;
long totalDuration = 0L;
boolean noGC = false;
if (ReflectionUtils.getAnnotation(NotInfinite.class, method) != null) {
size = ReflectionUtils.getAnnotation(NotInfinite.class, method).invocations();
repeats = Settings.TEST_LOOPS / size;
leftOvers = Settings.TEST_LOOPS % size;
noGC = ReflectionUtils.getAnnotation(NotInfinite.class, method).noGC();
log.trace("NotInfinite annotation found on " + test.getClass().getSimpleName() + "#" + method.getName() + ". {size=" + size + ",repeats=" + repeats + ",leftOvers=" + leftOvers + "}");
}
long start;
for (int i = 0; i < repeats; ++i) {
start = System.currentTimeMillis();
for (int j = 0; j < size; ++j) {
method.invoke(test);
}
totalDuration += (System.currentTimeMillis() - start);
test.init();
if (!noGC) {
GCManager.gc();
}
}
if (leftOvers != 0) { //TODO: remove repeats
start = System.currentTimeMillis();
for (int j = 0; j < leftOvers; ++j) {
method.invoke(test);
}
totalDuration += (System.currentTimeMillis() - start);
}
log.trace("Test finished (" + test.getClass().getSimpleName() + "#" + method.getName() + ")");
addResult(new Result(test.getClass(), method, totalDuration), test.getClass().getAnnotation(TestSuite.class));
if (ReflectionUtils.getAnnotation(DirtiesObject.class, method) != null) {
log.trace("DirtiesObject annotation found on " + test.getClass().getSimpleName() + "#" + method.getName() + ": reinitializing");
test.init();
}
} catch (InvocationTargetException e) {
log.error("Failed to invoke testmethod " + test.getClass().getSimpleName() + "#" + method.getName() + ": " + e.getMessage(), e);
addResult(new Result(test.getClass(), method, Long.MIN_VALUE), test.getClass().getAnnotation(TestSuite.class));
}
} else {
if (method.getName().startsWith("test")) {
log.warn("Method " + method.getName() + " skipped");
} else {
log.trace("Method " + method.getName() + " skipped");
}
}
}
//Test-class finished, clear resources and go to next test-class
log.trace("Test finished");
test.destroy();
test = null;
GCManager.forceGC();
} catch (InitializationException e) {
log.error("Failed to initialize test " + clazz.getSimpleName() + ": " + e.getMessage(), e);
} catch (InstantiationException e) {
log.error("Failed to instantiate test " + clazz.getSimpleName() + ": " + e.getMessage(), e);
} catch (IllegalAccessException e) {
log.error("IllegalAccessException on test " + clazz.getSimpleName() + ": " + e.getMessage(), e);
} catch (ClassNotFoundException e) {
log.error("ClassNotFoundException on test " + clazz.getSimpleName() + ": " + e.getMessage(), e);
}
}
//Loop finished, export to excel.
ExcelExporter exporter = new ExcelExporter();
exporter.export(results, sysInfo, "workbook_" + Settings.LIST_SIZE + ".xls");
//Print result if log.trace is enabled
printResults();
}
//http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6440250
private void preLoadClassesAndMethods(Class<? extends ITest>[] classes) {
int k = -1;
//The actual tests
for (Class<? extends ITest> clazz : classes) {
log.info("Pre-Loading test class " + clazz.getSimpleName() + " iteration " + k);
try {
ITest test = (ITest) (this.getClass().getClassLoader().loadClass(clazz.getName())).newInstance();
log.trace("Initializing test class for first time (" + test.getClass().getSimpleName() + ")");
test.init();
//Each test-method inside a test-class
Method[] methods = test.getClass().getMethods();
for (Method method : methods) {
if (ReflectionUtils.getAnnotation(Test.class, method) != null) {
try {
int repeats = 100;
int size = 10;
int leftOvers = 0;
long totalDuration = 0L;
boolean noGC = false;
if (ReflectionUtils.getAnnotation(NotInfinite.class, method) != null) {
size = 10;
repeats = 100;
leftOvers = 0;
noGC = ReflectionUtils.getAnnotation(NotInfinite.class, method).noGC();
log.trace("NotInfinite annotation found on " + test.getClass().getSimpleName() + "#" + method.getName() + ". {size=" + size + ",repeats=" + repeats + ",leftOvers=" + leftOvers + "}");
}
long start;
for (int i = 0; i < repeats; ++i) {
start = System.currentTimeMillis();
for (int j = 0; j < size; ++j) {
method.invoke(test);
}
totalDuration += (System.currentTimeMillis() - start);
test.init();
if (!noGC) {
GCManager.gc();
}
}
if (leftOvers != 0) { //TODO: remove repeats
start = System.currentTimeMillis();
for (int j = 0; j < leftOvers; ++j) {
method.invoke(test);
}
totalDuration += (System.currentTimeMillis() - start);
}
log.trace("Test finished (" + test.getClass().getSimpleName() + "#" + method.getName() + ")");
if (ReflectionUtils.getAnnotation(DirtiesObject.class, method) != null) {
log.trace("DirtiesObject annotation found on " + test.getClass().getSimpleName() + "#" + method.getName() + ": reinitializing");
test.init();
}
} catch (InvocationTargetException e) {
log.error("Failed to invoke testmethod " + test.getClass().getSimpleName() + "#" + method.getName() + ": " + e.getMessage(), e);
}
}
}
//Test-class finished, clear resources and go to next test-class
log.trace("Test finished");
test.destroy();
test = null;
GCManager.forceGC();
} catch (InitializationException e) {
log.error("Failed to initialize test " + clazz.getSimpleName() + ": " + e.getMessage(), e);
} catch (InstantiationException e) {
log.error("Failed to instantiate test " + clazz.getSimpleName() + ": " + e.getMessage(), e);
} catch (IllegalAccessException e) {
log.error("IllegalAccessException on test " + clazz.getSimpleName() + ": " + e.getMessage(), e);
} catch (ClassNotFoundException e) {
log.error("ClassNotFoundException on test " + clazz.getSimpleName() + ": " + e.getMessage(), e);
}
}
ExcelExporter e = new ExcelExporter();
try {
Result r = new Result(ITest.class, Object.class.getMethod("wait"), Long.MIN_VALUE);
} catch (NoSuchMethodException z) {
;
}
}
private void addResult(Result result, TestSuite suite) {
String name = "defaultSuite";
if (suite != null) {
name = suite.name();
}
if (results.get(name) == null) {
results.put(name, new ArrayList<Result>());
}
results.get(name).remove(result);
results.get(name).add(result);
}
private void printResults() {
if (log.isTraceEnabled()) {
Set<Map.Entry<String, List<Result>>> set = results.entrySet();
Iterator<Map.Entry<String, List<Result>>> iterator = set.iterator();
while (iterator.hasNext()) {
Map.Entry<String, List<Result>> entry = iterator.next();
log.trace(entry.getKey());
int l = entry.getValue().size();
for (int i = 0; i < l; ++i) {
log.trace("-- " + entry.getValue().get(i));
}
}
}
}
private void prettyPrintSysInfo(Set<Map.Entry<String, String>> sysInfo) {
log.info("************************************************************************");
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : sysInfo) {
sb.setLength(0);
sb.ensureCapacity(77);
sb.append("* ");
sb.append(entry.getKey()).append(":");
while (sb.length() < 30) {
sb.append(" ");
}
sb.append(entry.getValue());
while (sb.length() < 71) {
sb.append(" ");
}
sb.append("*");
log.info(sb.toString());
}
log.info("************************************************************************");
}
}