//: net/mindview/atunit/AtUnit.java
// An annotation-based unit-test framework.
// {RunByHand}
package net.mindview.atunit;
import net.mindview.util.BinaryFile;
import net.mindview.util.ProcessFiles;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import static net.mindview.util.Print.print;
import static net.mindview.util.Print.printnb;
public class AtUnit implements ProcessFiles.Strategy {
static Class<?> testClass;
static List<String> failedTests = new ArrayList<String>();
static long testsRun = 0;
static long failures = 0;
/**
* Unit测试框架
*
* @param args
*/
public static void unit(String[] args) {
ClassLoader.getSystemClassLoader().setDefaultAssertionStatus(true); // Enable
// asserts
new ProcessFiles(new AtUnit(), "class").start(args);
if (failures == 0)
print("OK (" + testsRun + " tests)");
else {
print("(" + testsRun + " tests)");
print("\n>>> " + failures + " FAILURE" + (failures > 1 ? "S" : "")
+ " <<<");
for (String failed : failedTests)
print(" " + failed);
}
}
public void process(File cFile) {
try {
String cName = ClassNameFinder.thisClass(BinaryFile.read(cFile));
if (!cName.contains("."))
return; // Ignore unpackaged classes
testClass = Class.forName(cName);
} catch (Exception e) {
throw new RuntimeException(e);
}
TestMethods testMethods = new TestMethods();
Method creator = null;
Method cleanup = null;
for (Method m : testClass.getDeclaredMethods()) {
testMethods.addIfTestMethod(m);
if (creator == null)
creator = checkForCreatorMethod(m);
if (cleanup == null)
cleanup = checkForCleanupMethod(m);
}
if (testMethods.size() > 0) {
if (creator == null)
try {
if (!Modifier.isPublic(testClass.getDeclaredConstructor()
.getModifiers())) {
print("Error: " + testClass
+ " default constructor must be public");
System.exit(1);
}
} catch (NoSuchMethodException e) {
// Synthesized default constructor; OK
}
print(testClass.getName());
}
for (Method m : testMethods) {
printnb(" . " + m.getName() + " ");
try {
Object testObject = createTestObject(creator);
boolean success = false;
try {
if (m.getReturnType().equals(boolean.class))
success = (Boolean) m.invoke(testObject);
else {
m.invoke(testObject);
success = true; // If no assert fails
}
} catch (InvocationTargetException e) {
// Actual exception is inside e:
print(e.getCause());
}
print(success ? "" : "(failed)");
testsRun++;
if (!success) {
failures++;
failedTests.add(testClass.getName() + ": " + m.getName());
}
if (cleanup != null)
cleanup.invoke(testObject, testObject);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
static class TestMethods extends ArrayList<Method> {
/**
*
*/
private static final long serialVersionUID = -2725127849187372104L;
void addIfTestMethod(Method m) {
if (m.getAnnotation(Test.class) == null)
return;
if (!(m.getReturnType().equals(boolean.class) || m.getReturnType()
.equals(void.class)))
throw new RuntimeException("@Test method"
+ " must return boolean or void");
m.setAccessible(true); // In case it's private, etc.
add(m);
}
}
private static Method checkForCreatorMethod(Method m) {
if (m.getAnnotation(TestObjectCreate.class) == null)
return null;
if (!m.getReturnType().equals(testClass))
throw new RuntimeException("@TestObjectCreate "
+ "must return instance of Class to be tested");
if ((m.getModifiers() & Modifier.STATIC) < 1)
throw new RuntimeException("@TestObjectCreate " + "must be static.");
m.setAccessible(true);
return m;
}
private static Method checkForCleanupMethod(Method m) {
if (m.getAnnotation(TestObjectCleanup.class) == null)
return null;
if (!m.getReturnType().equals(void.class))
throw new RuntimeException("@TestObjectCleanup "
+ "must return void");
if ((m.getModifiers() & Modifier.STATIC) < 1)
throw new RuntimeException("@TestObjectCleanup "
+ "must be static.");
if (m.getParameterTypes().length == 0
|| m.getParameterTypes()[0] != testClass)
throw new RuntimeException("@TestObjectCleanup "
+ "must take an argument of the tested type.");
m.setAccessible(true);
return m;
}
private static Object createTestObject(Method creator) {
if (creator != null) {
try {
return creator.invoke(testClass);
} catch (Exception e) {
throw new RuntimeException("Couldn't run "
+ "@TestObject (creator) method.");
}
} else { // Use the default constructor:
try {
return testClass.newInstance();
} catch (Exception e) {
throw new RuntimeException("Couldn't create a "
+ "test object. Try using a @TestObject method.");
}
}
}
} // /:~