SimpleTester.java
package eu.javaexperience.test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import eu.javaexperience.log.JavaExperienceLoggingFacility;
import eu.javaexperience.log.LogLevel;
import eu.javaexperience.log.Loggable;
import eu.javaexperience.log.Logger;
import eu.javaexperience.log.LoggingDetailLevel;
import eu.javaexperience.semantic.TesterFunction;
import static eu.javaexperience.log.LoggingTools.*;
public class SimpleTester
{
protected static final Logger LOG = JavaExperienceLoggingFacility.getLogger(new Loggable("Tester"));
public static class TestStatistic
{
protected Class<?> testSubjectClass;
protected int ignored = 0;
protected int tests = 0;
protected int errors = 0;
protected int fails = 0;
protected int success = 0;
public int getIgnoredCount()
{
return ignored;
}
public int getTestsCount()
{
return tests;
}
public int getErrorsCount()
{
return errors;
}
public int getFailsCount()
{
return fails;
}
public int getSuccessCount()
{
return success;
}
public Class<?> getTestSubjectClass()
{
return testSubjectClass;
}
public boolean isAllPassed()
{
return 0 == ignored && tests == success;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
//best case
if(0 == ignored && tests == success)
{
sb.append("all of ");
sb.append(tests);
sb.append(" tests are passed.");
}
else
{
boolean needComma = false;
if(0 != ignored)
{
sb.append(ignored);
sb.append(" test ignored");
needComma = true;
}
if(0 != errors)
{
if(needComma)
{
sb.append(", ");
}
sb.append(errors);
sb.append(" test caused internal error");
needComma = true;
}
if(0 != fails)
{
if(needComma)
{
sb.append(", ");
}
sb.append(fails);
sb.append(" test failed");
needComma = true;
}
{
if(needComma)
{
sb.append(", ");
}
sb.append(success);
sb.append(" test succeed");
needComma = true;
}
}
return sb.toString();
}
public LoggingDetailLevel getRecommendedLoggingPriorityForResult()
{
if(0 == ignored && tests == success)
{
return LogLevel.INFO;
}
else if(0 != errors)
{
return LogLevel.FATAL;
}
else
{
return LogLevel.WARNING;
}
}
}
public static TestStatistic testClass(Class<?> cls)
{
tryLogFormat(LOG, LogLevel.INFO, "Discovering class `%s` for @TesterFunction-s", cls);
TestStatistic ts = new TestStatistic();
Method[] ms = cls.getDeclaredMethods();
for(Method m:ms)
{
if(null != m.getAnnotation(TesterFunction.class))
{
//if not static we warn, and not execute
int mods = m.getModifiers();
if(!Modifier.isStatic(mods))
{
tryLogFormat(LOG, LogLevel.WARNING, "Method `%s` has @TesterFunction annotation, but not declared as static, 'skip' testing this function.", m);
++ts.ignored;
continue;
}
Class<?>[] params = m.getParameterTypes();
if(0 != params.length)
{
tryLogFormat(LOG, LogLevel.WARNING, "Method `%s` requires parameters of `%s`, 'skip' testing this function.", m, Arrays.toString(params));
++ts.ignored;
continue;
}
m.setAccessible(true);
++ts.tests;
Object ret = null;
try
{
ret = m.invoke(null);
}
catch (IllegalAccessException e)
{
tryLogFormatException(LOG, LogLevel.ERROR, e, "Internal error raised at calling test function `%s`\n", m);
++ts.errors;
continue;
}
catch (IllegalArgumentException e)
{
tryLogFormatException(LOG, LogLevel.ERROR, e, "'strange, this should'nt to be happened' at calling test function `%s`\n", m);
++ts.errors;
continue;
}
catch (InvocationTargetException e)
{
Throwable t = e;
if(null != e.getCause())
{
t = e.getCause();
}
tryLogFormatException(LOG, LogLevel.FATAL, t, "Exception raised in the `%s` test function, so this test failed!\n", m);
++ts.fails;
continue;
}
if(!Void.class.equals(m.getReturnType()))
{
tryLogFormat(LOG, LogLevel.INFO, "Test function `%s` has return value of type `%s` and resulted: `%s` test passed", m, m.getReturnType(), ret);
}
++ts.success;
}
}
tryLogFormat(LOG, ts.getRecommendedLoggingPriorityForResult() , "Class testing `%s` finished. Result: %s", cls, ts);
return ts;
}
}