001: package org.drools.eclipse;
002:
003: //Copyright (c) 2006 Alex Blewitt
004: //All rights reserved. This program and the accompanying materials
005: //are made available under the terms of the Eclipse Public License v1.0
006: //which accompanies this distribution, and is available at
007: //http://www.eclipse.org/legal/epl-v10.html
008: //
009: //Contributors:
010: //Alex Blewitt - Initial API and implementation
011: //
012: import java.lang.reflect.Modifier;
013: import java.net.URL;
014: import java.util.Enumeration;
015: import java.util.Vector;
016: import junit.framework.Test;
017: import junit.framework.TestSuite;
018: import junit.runner.ClassPathTestCollector;
019: import junit.runner.TestCollector;
020: import org.eclipse.core.runtime.Platform;
021:
022: /**
023: * Run all the tests in this project, either computed from the classpath or from
024: * the bundlepath. To use this as-is, drop it into a non-default package that
025: * has the same name as the plugin. For example, if the plugin is called
026: * <code>org.example.foo</code>, this should be placed in a package
027: * <code>org.example.foo</code>, and all tests should live under the
028: * <code>org.example.foo</code> package structure (either directly, or in any
029: * subpackage). By default this will include all non-abstract classes named
030: * <code>XxxTest</code>, excluding <code>XxxPlatformTest</code> if running
031: * outside of the platform.
032: */
033:
034: public class AllTestsUtil {
035: /**
036: * Detects classes from the bundle PLUGIN_NAME's entries. Uses
037: * <code>bundle.findEntries</code> to obtain a list of classes that live
038: * in the specified PACKAGE_NAME, and adds those to the test path, providing
039: * that they are {@link AllTests#isValidTest(String, boolean) valid}.
040: */
041: private static class BundleTestDetector implements TestCollector {
042: /*
043: * @see junit.runner.TestCollector#collectTests()
044: */
045: public Enumeration collectTests() {
046: final Vector tests = new Vector();
047: try {
048: Enumeration entries = Platform
049: .getBundle(PLUGIN_NAME)
050: .findEntries("/", "*" + SUFFIX + ".class", true);
051: while (entries.hasMoreElements()) {
052: URL entry = (URL) entries.nextElement();
053: // Change the URLs to have Java class names
054: String path = entry.getPath().replace('/', '.');
055: int start = path.indexOf(PACKAGE_NAME);
056: String name = path.substring(start, path.length()
057: - ".class".length());
058: if (isValidTest(name, true)) {
059: tests.add(name);
060: }
061: }
062: } catch (Exception e) {
063: // If we get here, the Platform isn't installed and so we fail
064: // quietly. This isn't a problem; we might be outside of the
065: // Platform framework and just running tests locally. It's not
066: // even worth printing anything out to the error log as it would
067: // just confuse people investigating stack traces etc.
068: }
069: return tests.elements();
070: }
071: }
072:
073: /**
074: * Searches the current classpath for tests, which are those ending with
075: * SUFFIX, excluding those which end in IN_CONTAINER_SUFFIX, providing that
076: * they are {@link AllTests#isValidTest(String, boolean) valid}.
077: */
078: private static class ClassFileDetector extends
079: ClassPathTestCollector {
080: /*
081: * @see junit.runner.ClassPathTestCollector#isTestClass(java.lang.String)
082: */
083: protected boolean isTestClass(String classFileName) {
084: return classFileName.endsWith(SUFFIX + ".class")
085: && isValidTest(classNameFromFile(classFileName),
086: false);
087: }
088: }
089:
090: /**
091: * All tests should end in XxxTest
092: */
093: public static final String SUFFIX = "Test";
094: /**
095: * All in-container tests should end in XxxPlatformTest
096: */
097: public static final String IN_CONTAINER_SUFFIX = "Platform"
098: + SUFFIX;
099: /**
100: * The base package name of the tests to run. This defaults to the name of
101: * the package that the AllTests class is in for ease of management but may
102: * be trivially changed if required. Note that at least some identifiable
103: * part must be provided here (so default package names are not allowed)
104: * since the URL that comes up in the bundle entries have a prefix that is
105: * not detectable automatically. Even if this is "org" or "com" that should
106: * be enough.
107: */
108: public static final String PACKAGE_NAME = AllTests.class
109: .getPackage().getName();
110: /**
111: * The name of the plugin to search if the platform is loaded. This defaults
112: * to the name of the package that the AllTests class is in for ease of
113: * management but may be trivially changed if required.
114: */
115:
116: //PO: this is wrong. we need to use the PLUGIN_ID of the host, not that of the
117: // fragment
118: // public static final String PLUGIN_NAME = AllTests.class.getPackage()
119: // .getName();
120: public static final String PLUGIN_NAME = "org.drools.eclipse";
121:
122: /**
123: * Add the tests reported by collector to the list of tests to run
124: * @param collector the test collector to run
125: * @param suite the suite to add the tests to
126: */
127: private static void addTestsToSuite(TestCollector collector,
128: TestSuite suite) {
129: Enumeration e = collector.collectTests();
130: while (e.hasMoreElements()) {
131: String name = (String) e.nextElement();
132: try {
133: suite.addTestSuite(Class.forName(name));
134: } catch (ClassNotFoundException e1) {
135: System.err.println("Cannot load test: " + e1);
136: }
137: }
138: }
139:
140: /**
141: * Is the test a valid test?
142: * @param name the name of the test
143: * @param inContainer true if we want to include the inContainer tests
144: * @return true if the name is a valid class (can be loaded), that it is not
145: * abstract, and that it ends with SUFFIX, and that either
146: * inContainer tests are to be included or the name does not end
147: * with IN_CONTAINER_SUFFIX
148: */
149: private static boolean isValidTest(String name, boolean inContainer) {
150: try {
151: return name.endsWith(SUFFIX)
152: && (inContainer || !name
153: .endsWith(IN_CONTAINER_SUFFIX))
154: && ((Class.forName(name).getModifiers() & Modifier.ABSTRACT) == 0);
155: } catch (ClassNotFoundException e) {
156: System.err.println(e.toString());
157: return false;
158: }
159: }
160:
161: /**
162: * Return all the tests. If we're in a platform, return everything. If not,
163: * we return those tests that end in SUFFIX but excluding those ending in
164: * IN_CONTAINER_SUFFIX.
165: * @return a suite of tests for JUnit to run
166: * @throws Error if there are no tests to run.
167: */
168: public static Test suite() {
169: TestSuite suite = new TestSuite(AllTests.class.getName());
170: addTestsToSuite(new ClassFileDetector(), suite);
171: addTestsToSuite(new BundleTestDetector(), suite);
172: if (suite.countTestCases() == 0) {
173: throw new Error("There are no test cases to run");
174: } else {
175: return suite;
176: }
177: }
178: }
|