/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.plugin.ij.suite;
import gw.lang.reflect.Modifier;
import gw.test.TestClassHelper;
import gw.test.TestSpec;
import junit.framework.TestCase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.Test;
import org.junit.internal.TextListener;
import org.junit.internal.runners.JUnit38ClassRunner;
import org.junit.runner.Computer;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.Runner;
import org.junit.runners.model.RunnerBuilder;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public abstract class AbstractIntelliJTestScanner {
private static final Set<String> EMPTY_FILTER = Collections.emptySet();
@NotNull
private final Set<String> _filter;
private final File[] _dirsToScan;
public AbstractIntelliJTestScanner(File... dirsToScan) {
this(null, dirsToScan);
}
public AbstractIntelliJTestScanner(@Nullable String filterDesc, File... dirsToScan) {
_filter = filterDesc != null ? new HashSet<>(Arrays.asList(filterDesc.split(","))) : EMPTY_FILTER;
_dirsToScan = dirsToScan;
}
public Result runTests() throws IOException {
Class[] classes = findTests(_filter, _dirsToScan);
JUnitCore core = new JUnitCore();
core.addListener(new TextListener(System.out));
File resultsDir = new File(System.getProperty("junit.results.dir"));
resultsDir.mkdirs();
File outputFile = new File(resultsDir, "TEST-IntelliJTestScanner.xml");
log("Output file : " + outputFile.getAbsolutePath());
core.addListener(new ThinXmlListener(outputFile,
System.getProperty("changelist"),
"gw.plugin.ij.suite.IntelliJTestScanner",
java.net.InetAddress.getLocalHost().getHostName()
));
return core.run(new Computer() {
@Override
protected Runner getRunner(RunnerBuilder builder, Class<?> testClass) throws Throwable {
Class<? extends TestCase> testCaseClass = (Class<? extends TestCase>) testClass;
List<String> methodNames = TestSpec.extractTestMethods(testCaseClass);
log("Running tests " + methodNames + " from " + testCaseClass);
return new JUnit38ClassRunner(TestClassHelper.createTestSuite(testCaseClass, methodNames));
}
}, classes);
}
public Class[] findTests(@NotNull Set<String> filter, @NotNull File... dirsToScan) throws IOException {
ArrayList<Class> tests = new ArrayList<>();
for (File root : dirsToScan) {
addTests(filter, tests, root, root);
}
Collections.sort(tests, new Comparator<Class>() {
@Override
public int compare(@NotNull Class o1, @NotNull Class o2) {
return o1.getName().compareTo(o2.getName());
}
});
return tests.toArray(new Class[tests.size()]);
}
private void addTests(@NotNull Set<String> filter, @NotNull List<Class> tests, @NotNull File testDir, @NotNull File possibleTest) throws IOException {
log("Possible Test Root : " + possibleTest);
log("Possible Test Root Exists : " + possibleTest.exists());
if (possibleTest.exists()) {
if (possibleTest.getName().endsWith(".jar")) {
JarFile jarFile = new JarFile(testDir);
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
String s = jarEntry.toString();
log("Looking at jar entry " + s);
if (s.endsWith(".class")) {
String typeName = s.substring(0, s.lastIndexOf('.'));
typeName = typeName.replace('/', '.');
maybeAddTest(filter, tests, typeName);
}
}
} else if (possibleTest.isDirectory()) {
for (File child : possibleTest.listFiles()) {
addTests(filter, tests, testDir, child);
}
} else {
String absolutePath = possibleTest.getAbsolutePath();
if (absolutePath.endsWith(".class")) {
String relativeName = absolutePath.substring(testDir.getAbsolutePath().length() + 1);
int lastDot = relativeName.lastIndexOf(".");
if (lastDot > 0) {
relativeName = relativeName.substring(0, lastDot);
String typeName = relativeName.replace(File.separator, ".");
maybeAddTest(filter, tests, typeName);
}
}
}
}
}
private void maybeAddTest(@NotNull Set<String> filter, @NotNull List<Class> tests, @NotNull String typeName) {
if (typeName.endsWith("Test")) {
log("Looking at type name " + typeName);
if(!shouldExclude(filter, typeName)) {
try {
Class backingClass = Class.forName(typeName);
if (backingClass.getAnnotation(gw.testharness.Disabled.class) == null) {
if (isValidJUnit3Test(backingClass)) {
tests.add(backingClass);
} else if (isJUnit4Test(backingClass)) {
tests.add(backingClass);
}
}
} catch (ClassNotFoundException e) {
System.out.println("Could not load class " + typeName + " : " + e.getMessage());
}
} else {
log("Filtering " + typeName);
}
}
}
private boolean shouldExclude(@NotNull Set<String> filter, @NotNull String typeName) {
return shouldExcludeByFilter(filter, typeName) || shouldExclude(typeName);
}
protected abstract boolean shouldExclude(String typeName);
private static boolean shouldExcludeByFilter(Set<String> filter, String typeName) {
// totally cheesy way to allow test or package filtering
if(filter.size() > 0) {
for(String filterName : filter) {
if(typeName.startsWith(filterName)) {
return false;
}
}
return true;
} else {
return false;
}
}
private static boolean isValidJUnit3Test(@NotNull Class backingClass) {
return
!Modifier.isAbstract(backingClass.getModifiers()) &&
junit.framework.Test.class.isAssignableFrom(backingClass) &&
!IntelliJSuite.class.isAssignableFrom(backingClass) &&
!IntelliJScratchSuite.class.isAssignableFrom(backingClass);
}
private static void log(String x) {
System.out.println(x);
}
private static boolean isJUnit4Test(@NotNull Class clazz) {
for (Method m : clazz.getMethods()) {
if (m.getAnnotation(Test.class) != null) {
return true;
}
}
return false;
}
}