001: package junit.extensions.abbot;
002:
003: import java.io.File;
004: import java.lang.reflect.Constructor;
005: import java.util.*;
006:
007: import junit.framework.*;
008: import abbot.Log;
009: import abbot.script.Script;
010:
011: //import junit.ui.TestRunner;
012: //import junit.swingui;
013:
014: /** Similar to TestSuite, except that it auto-generates a suite based on test
015: * scripts matching certain criteria.
016: * <p>
017: * By default, generate a suite of all scripts found in a given directory for
018: * which the accept method returns true. Note that there is no guarantee of
019: * the order of the scripts.
020: * <p>
021: * The ScriptTestSuite constructors which require a class argument provide a
022: * means for using custom fixtures derived from
023: * <a href="ScriptFixture.html">ScriptFixture</a>. The default fixture
024: * preserves existing environment windows (e.g. the JUnit Swing UI TestRunner)
025: * and disposes of all windows generated by the code under test. Derived
026: * fixtures may provide arbitrary code in their setUp/tearDown methods (such
027: * as install/uninstall a custom security manager, set system properties,
028: * etc), the same as you would do in any other derivation of
029: * junit.framework.TestCase.
030: * <p>
031: * <h3>Example 1</h3>
032: * Following is a ScriptTestSuite which will aggregate all tests in the
033: * directory "src/example", whose filenames begin with "MyCode-" and end with
034: * ".xml":<br>
035: * <pre><code>
036: * public class MyCodeTest extends ScriptFixture {
037: * public MyCodeTest(String name) { super(name); }
038: * public static Test suite() {
039: * return new ScriptTestSuite(MyCodeTest.class, "src/example") {
040: * public boolean accept(File file) {
041: * String name = file.getName();
042: * return name.startsWith("MyCode-") && name.endsWith(".xml");
043: * }
044: * };
045: * }
046: * }
047: * </code></pre>
048: */
049: public class ScriptTestSuite extends TestSuite {
050:
051: private File primaryDirectory;
052:
053: /** Constructs a suite of tests from all the scripts found in the
054: directory specified by the system property "abbot.testsuite.path".
055: The most common use for this constructor would be from an Ant 'junit'
056: task, where the system property is defined for a given run.
057: The suite will recurse directories if "abbot.testsuite.path.recurse" is
058: set to true.
059: */
060: public ScriptTestSuite() {
061: this (ScriptFixture.class,
062: System.getProperty("abbot.testsuite.path", System
063: .getProperty("user.dir")), Boolean
064: .getBoolean("abbot.testsuite.path.recurse"));
065: }
066:
067: /** Constructs a suite of tests from all the scripts found in the current
068: * directory. Does not recurse to subdirectories. The Class argument
069: * must be a subclass of junit.extensions.abbot.ScriptFixture.
070: */
071: public ScriptTestSuite(Class fixtureClass) {
072: this (fixtureClass, System.getProperty("user.dir"), false);
073: }
074:
075: /** Constructs a suite of tests from all the scripts found in the given
076: * directory. Does not recurse to subdirectories. The Class argument
077: * must be a subclass of junit.extensions.abbot.ScriptFixture.
078: */
079: public ScriptTestSuite(Class fixtureClass, String dirname) {
080: this (fixtureClass, dirname, false);
081: }
082:
083: /** Constructs an ScriptTestSuite from all the scripts in the given
084: * directory, recursing if recurse is true. The Class argument
085: * must be a class derived from junit.extensions.abbot.ScriptFixture.
086: */
087: public ScriptTestSuite(Class fixtureClass, String dirname,
088: boolean recurse) {
089: this (fixtureClass, findFilenames(dirname, recurse));
090: primaryDirectory = new File(dirname);
091: if (!primaryDirectory.exists()
092: || !primaryDirectory.isDirectory()) {
093: String msg = "Directory '" + dirname + "' did not exist"
094: + " when scanning for test scripts";
095: addTest(warningTest(msg));
096: }
097: }
098:
099: /** Constructs a suite of tests for each script given in the argument
100: * list.
101: */
102: public ScriptTestSuite(String[] filenames) {
103: this (ScriptFixture.class, filenames);
104: }
105:
106: /** Constructs a suite of tests for each script given in the argument
107: * list, using the given class derived from ScriptFixture to wrap each
108: * script.
109: */
110: public ScriptTestSuite(Class fixtureClass, String[] filenames) {
111: super (fixtureClass.getName());
112: primaryDirectory = new File(System.getProperty("user.dir"));
113: Log.debug("Loading " + fixtureClass + ", with "
114: + filenames.length + " files");
115: for (int i = 0; i < filenames.length; i++) {
116: File file = new File(filenames[i]);
117: // Filter for desired files only
118: if (!accept(file))
119: continue;
120: try {
121: Log.debug("Attempting to create " + fixtureClass);
122: Constructor ctor = fixtureClass
123: .getConstructor(new Class[] { String.class });
124: Test test = (Test) ctor.newInstance(new Object[] { file
125: .getAbsolutePath() });
126: Log.debug("Created an instance of " + fixtureClass);
127: addTest(test);
128: } catch (Throwable thr) {
129: Log.warn(thr);
130: addTest(warningTest("Could not construct an instance of "
131: + fixtureClass));
132: break;
133: }
134: }
135: if (testCount() == 0) {
136: addTest(warningTest("No scripts found"));
137: }
138: }
139:
140: public File getDirectory() {
141: return primaryDirectory;
142: }
143:
144: /** Return whether to accept the given file. The default implementation
145: * omits common backup files.
146: */
147: public boolean accept(File file) {
148: String name = file.getName();
149: return !name.startsWith(".#") && !name.endsWith("~")
150: && !name.endsWith(".bak");
151: }
152:
153: /**
154: * Returns a test which will fail and log a warning message.
155: */
156: private Test warningTest(final String message) {
157: return new TestCase("warning") {
158: protected void runTest() {
159: fail(message);
160: }
161: };
162: }
163:
164: /** Add all test scripts in the given directory, optionally recursing to
165: * subdirectories. Returns a list of absolute paths.
166: */
167: protected static List findTestScripts(File dir, List files,
168: boolean recurse) {
169: File[] flist = dir.listFiles();
170: for (int i = 0; flist != null && i < flist.length; i++) {
171: //Log.debug("Examining " + flist[i]);
172: if (flist[i].isDirectory()) {
173: if (recurse)
174: findTestScripts(flist[i], files, recurse);
175: } else if (Script.isScript(flist[i])) {
176: String filename = flist[i].getAbsolutePath();
177: if (!files.contains(filename)) {
178: Log.debug("Adding " + filename);
179: files.add(filename);
180: }
181: }
182: }
183: return files;
184: }
185:
186: /** Scan for test scripts and return an array of filenames for all scripts
187: found.
188: */
189: static String[] findFilenames(String dirname, boolean recurse) {
190: File dir = new File(dirname);
191: ArrayList list = new ArrayList();
192: if (dir.exists() && dir.isDirectory()) {
193: findTestScripts(dir, list, recurse);
194: }
195: return (String[]) list.toArray(new String[list.size()]);
196: }
197:
198: /** Run all scripts on the command line as a single suite. */
199: public static void main(String[] args) {
200: args = Log.init(args);
201: ScriptTestSuite suite = new ScriptTestSuite(args);
202: junit.textui.TestRunner runner = new junit.textui.TestRunner();
203: try {
204: TestResult r = runner.doRun(suite, false);
205: if (!r.wasSuccessful())
206: System.exit(-1);
207: System.exit(0);
208: } catch (Throwable thr) {
209: System.err.println(thr.getMessage());
210: System.exit(-2);
211: }
212: }
213: }
|