/** * Copyright 2010 Wealthfront Inc. Licensed under the Apache License, * Version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law * or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package com.kaching.platform.testing; import static java.io.File.separator; import java.io.File; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import junit.framework.JUnit4TestAdapter; import junit.framework.TestCase; import junit.framework.TestSuite; import org.junit.Ignore; import org.junit.runner.RunWith; /** * A {@link TestSuite} builder. */ public class TestSuiteBuilder { private final TestSuite testSuite = new TestSuite(); private final File root; private TestSuiteBuilder(File root) { this.root = root; } /** * Builds and returns a test suite by traversing the root directory * recursively and finding all the tests. */ public static TestSuite buildFromRoot(File root) { final String testCaseBuildingSuite = builderCaller(); return buildFromRoot(root, new TestFilter() { public boolean shouldIncludeTest(Class<?> test) { return !test.getName().equals(testCaseBuildingSuite); } }); } /** * Builds and returns a test suite by traversing the root directory * recursively and finding all the tests matching the {@code filter}. */ public static TestSuite buildFromRoot(File root, TestFilter filter) { TestSuiteBuilder builder = new TestSuiteBuilder(root); builder.addTestsInDirectory(root, filter); return builder.testSuite; } static String builderCaller() { String builderClassName = TestSuiteBuilder.class.getName(); StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); boolean found = false; for (int i = 0; i < stackTrace.length; i++) { String className = stackTrace[i].getClassName(); found = found || className.equals(builderClassName); if (found && !className.equals(builderClassName)) { return className; } } throw new IllegalStateException("unreachable code"); } private void addTestsInDirectory(File directory, TestFilter filter) { for (File file : directory.listFiles()) { if (file.isDirectory()) { addTestsInDirectory(file, filter); } else if (file.getName().endsWith(".java")) { Class<?> clazz = forName(file); if (filter.shouldIncludeTest(clazz)) { if (TestCase.class.isAssignableFrom(clazz) && clazz.getAnnotation(Ignore.class) == null) { testSuite.addTestSuite(castToTestCase(clazz)); } else if (hasTests(clazz) && filter.shouldIncludeTest(clazz)) { JUnit4TestAdapter test = new JUnit4TestAdapter(clazz); testSuite.addTest(test); } } } } } private boolean hasTests(Class<?> clazz) { for (Method method : clazz.getDeclaredMethods()) { if (method.getAnnotation(org.junit.Test.class) != null) { return true; } } for (Annotation a : clazz.getAnnotations()) { if (a.annotationType().equals(RunWith.class)) { return true; } } return false; } @SuppressWarnings("unchecked") private static Class<? extends TestCase> castToTestCase(Class<?> clazz) { return (Class<? extends TestCase>) clazz; } private Class<?> forName(File file) { String name = file.toString(); name = name.replace(root.toString() + separator, ""); name = name.replace(".java", ""); name = name.replace(separator, "."); try { return Class.forName(name); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } }