KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > rice > cs > drjava > model > junit > JUnitTestManager


1 /*BEGIN_COPYRIGHT_BLOCK
2  *
3  * This file is part of DrJava. Download the current version of this project from http://www.drjava.org/
4  * or http://sourceforge.net/projects/drjava/
5  *
6  * DrJava Open Source License
7  *
8  * Copyright (C) 2001-2005 JavaPLT group at Rice University (javaplt@rice.edu). All rights reserved.
9  *
10  * Developed by: Java Programming Languages Team, Rice University, http://www.cs.rice.edu/~javaplt/
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
13  * documentation files (the "Software"), to deal with the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
15  * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
16  *
17  * - Redistributions of source code must retain the above copyright notice, this list of conditions and the
18  * following disclaimers.
19  * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
20  * following disclaimers in the documentation and/or other materials provided with the distribution.
21  * - Neither the names of DrJava, the JavaPLT, Rice University, nor the names of its contributors may be used to
22  * endorse or promote products derived from this Software without specific prior written permission.
23  * - Products derived from this software may not be called "DrJava" nor use the term "DrJava" as part of their
24  * names without prior written permission from the JavaPLT group. For permission, write to javaplt@rice.edu.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
27  * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28  * CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
29  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30  * WITH THE SOFTWARE.
31  *
32  *END_COPYRIGHT_BLOCK*/

33
34 package edu.rice.cs.drjava.model.junit;
35
36 import junit.framework.*;
37
38 import java.io.File JavaDoc;
39 import java.io.IOException JavaDoc;
40 import java.util.Enumeration JavaDoc;
41 import java.util.List JavaDoc;
42 import java.util.ArrayList JavaDoc;
43
44 import edu.rice.cs.util.UnexpectedException;
45 import edu.rice.cs.util.StringOps;
46 import edu.rice.cs.util.classloader.ClassFileError;
47 import edu.rice.cs.util.swing.Utilities;
48 import edu.rice.cs.util.swing.ScrollableDialog;
49
50 import java.lang.reflect.Modifier JavaDoc;
51
52 /** Runs in the InterpreterJVM. Runs tests given a classname and formats the results into a (serializable) array of
53  * JUnitError that can be passed back to the MainJVM.
54  * @version $Id: JUnitTestManager.java 4003 2006-09-15 18:02:05Z rcartwright $
55  */

56 public class JUnitTestManager {
57   
58   /** The interface to the master JVM via RMI. */
59   private final JUnitModelCallback _jmc;
60   
61   /** The current testRunner; initially null. Each test suite requires a new runner. */
62   private JUnitTestRunner _testRunner;
63   
64   /** The accumulated test suite; null if no test is pending. */
65   private TestSuite _suite = null;
66   
67   /** The accumulated list of names of TestCase classes; null if no test is pending. */
68   private List JavaDoc<String JavaDoc> _testClassNames = null;
69   
70   /** The list of files corresponding to testClassNames; null if no test is pending. */
71   private List JavaDoc<File JavaDoc> _testFiles = null;
72   
73   /** Standard constructor */
74   public JUnitTestManager(JUnitModelCallback jmc) { _jmc = jmc; }
75
76   public JUnitTestRunner getTestRunner() { return _testRunner; }
77   
78   /** Find the test classes among the given classNames and accumulate them in
79     * TestSuite for junit. Returns null if a test suite is already pending.
80     * @param classNames the class names that are test class candidates
81     * @param files the files corresponding to classNames
82     */

83   public List JavaDoc<String JavaDoc> findTestClasses(final List JavaDoc<String JavaDoc> classNames, final List JavaDoc<File JavaDoc> files) {
84
85     //Utilities.showDebug("InterpreterJVM.findTestClasses(" + classNames + ", " + files + ") called");
86

87     if (_testClassNames != null && ! _testClassNames.isEmpty())
88       throw new IllegalStateException JavaDoc("Test suite is still pending!");
89     
90     _testRunner = new JUnitTestRunner(_jmc);
91     
92     _testClassNames = new ArrayList JavaDoc<String JavaDoc>();
93     _testFiles = new ArrayList JavaDoc<File JavaDoc>();
94     _suite = new TestSuite();
95
96    //new ScrollableDialog(null, "JUnitManager.findTestClasses invoked", "Candidate classes are = " + classNames, "files = " + files).show();
97

98     int i = 0;
99     try {
100       for (i = 0; i < classNames.size(); i++) {
101         String JavaDoc cName = classNames.get(i);
102        //new ScrollableDialog(null, "Class to be checked in JUnitManager: " + cName, "", "").show();
103
try {
104           if (_isTestCase(cName)) {
105             //new ScrollableDialog(null, "Test class " + cName + " found!", "", "").show();
106
_testClassNames.add(cName);
107             _testFiles.add(files.get(i));
108             _suite.addTest(_testRunner.getTest(cName));
109           }
110         }
111         catch(LinkageError JavaDoc e) {
112           //new ScrollableDialog(null, "LinkageError(" + e + ") encountered in JUnitTestManager", "", "").show();
113
_jmc.classFileError(new ClassFileError(cName, files.get(i).getCanonicalPath(), e));
114         }
115       }
116     }
117     catch(IOException JavaDoc e) { throw new UnexpectedException(e); }
118     //new ScrollableDialog(null, "TestClassNames are: " + _testClassNames, "", "").show();
119

120     return _testClassNames;
121   }
122     
123   /** Runs the pending test suite set up by the preceding call to findTestClasses
124    * @return false if no test suite (even an empty one) has been set up
125    */

126   public /* synchronized */ boolean runTestSuite() {
127     
128     if (_testClassNames == null || _testClassNames.isEmpty()) return false;
129     
130 // new ScrollableDialog(null, "runTestSuite() in SlaveJVM called", "", "").show();
131

132     try {
133       TestResult result = _testRunner.doRun(_suite);
134     
135       JUnitError[] errors = new JUnitError[result.errorCount() + result.failureCount()];
136       
137       Enumeration JavaDoc failures = result.failures();
138       Enumeration JavaDoc errEnum = result.errors();
139       
140       int i = 0;
141       
142       while (errEnum.hasMoreElements()) {
143         TestFailure tErr = (TestFailure) errEnum.nextElement();
144         errors[i] = _makeJUnitError(tErr, _testClassNames, true, _testFiles);
145         i++;
146       }
147       
148       while (failures.hasMoreElements()) {
149         TestFailure tFail = (TestFailure) failures.nextElement();
150         errors[i] = _makeJUnitError(tFail, _testClassNames, false, _testFiles);
151         i++;
152       }
153 // new ScrollableDialog(null, "Slave JVM: testSuite ended with errors", "", Arrays.toString(errors)).show();
154

155       _jmc.testSuiteEnded(errors);
156     }
157     catch(Throwable JavaDoc t) {
158       JUnitError[] errors = new JUnitError[1];
159       errors[0] = new JUnitError(null, -1, -1, t.getMessage(),
160                                  false, "", "", StringOps.getStackTrace(t));
161       _jmc.testSuiteEnded(errors);
162 // new ScrollableDialog(null, "Slave JVM: testSuite ended with errors", "", Arrays.toString(errors)).show();
163

164     }
165     finally {
166       _suite = null;
167       _testClassNames = null;
168       _testFiles = null;
169     }
170     return true;
171   }
172
173   /** Determines if the given class is a junit Test.
174    * @param c the class to check
175    * @return true iff the given class is an instance of junit.framework.Test
176    */

177   private boolean _isJUnitTest(Class JavaDoc c) {
178     //new ScrollableDialog(null, "_isJUnitTestCase called on " + c, "", "").show();
179

180     return Test.class.isAssignableFrom(c) && !Modifier.isAbstract(c.getModifiers()) &&
181       !Modifier.isInterface(c.getModifiers());
182   }
183
184   /** Checks whether the given file name corresponds to a valid JUnit Test. */
185   private boolean _isTestCase(String JavaDoc className) {
186     try { return _isJUnitTest(_testRunner.getLoader().load(className)); }
187     catch (ClassNotFoundException JavaDoc cnfe) {
188               //new ScrollableDialog(null, "ClassNotFoundException: "+ className, cnfe.getStackTrace().toString(), "").show();
189
return false; }
190   }
191   
192   /** Constructs a new JUnitError from a TestFailure
193    * @param failure A given TestFailure
194    * @param classNames The classes that were used for this test suite
195    * @param isError The passed TestFailure may signify either an error or a failure
196    * @param files The files that were used for this test suite
197    * @return JUnitError
198    */

199   private JUnitError _makeJUnitError(TestFailure failure, List JavaDoc<String JavaDoc> classNames, boolean isError, List JavaDoc<File JavaDoc> files) {
200
201     Test failedTest = failure.failedTest();
202     String JavaDoc testName;
203     if (failedTest instanceof TestCase) testName = ((TestCase)failedTest).getName();
204     else testName = failedTest.getClass().getName();
205     
206     String JavaDoc testString = failure.toString();
207     int firstIndex = testString.indexOf('(') + 1;
208     int secondIndex = testString.indexOf(')');
209     
210     /** junit can return a string in two different formats; we parse both formats, and then decide which one to use. */
211     
212     String JavaDoc className;
213     String JavaDoc className1 = testString.substring(firstIndex, secondIndex);
214     String JavaDoc className2 = testString.substring(0, firstIndex-1);
215     if (firstIndex == secondIndex) className = className2;
216     else className = className1;
217     
218     String JavaDoc classNameAndTest = className + "." + testName;
219     String JavaDoc stackTrace = StringOps.getStackTrace(failure.thrownException());
220     
221     /** If the classname is not in the stacktrace, then the test that failed was inherited from a superclass. let's look
222       * for the classname.
223       */

224     if (stackTrace.indexOf(className) == -1) {
225       /* get the stack trace of the junit error */
226       String JavaDoc trace = failure.trace();
227       /* knock off the first line of the stack trace.
228        * now the string will look like
229        * at my.package.class(file.java:line)
230        * at other.package.class(anotherfile.java:line)
231        * etc...
232        */

233       trace = trace.substring(trace.indexOf('\n')+1);
234       while (trace.indexOf("junit.framework.Assert") != -1 &&
235             trace.indexOf("junit.framework.Assert") < trace.indexOf("(")) {
236         /* the format of the trace will have "at junit.framework.Assert..."
237          * on each line until the line of the actual source file.
238          * if the exception was thrown from the test case (so the test failed
239          * without going through assert), then the source file will be on
240          * the first line of the stack trace
241          */

242         trace = trace.substring(trace.indexOf('\n') + 1);
243       }
244       trace = trace.substring(trace.indexOf('(')+1);
245       trace = trace.substring(0, trace.indexOf(')'));
246       className = trace.substring(0,trace.indexOf(':'));
247       className = trace.substring(0,trace.lastIndexOf('.'));
248       classNameAndTest = className + "." + testName;
249     }
250     
251     
252     
253     int lineNum = _lineNumber(stackTrace, classNameAndTest);
254     
255 // if (lineNum > -1) _errorsWithPos++;
256

257     String JavaDoc exception = (isError) ? failure.thrownException().toString():
258                                     failure.thrownException().getMessage();
259     boolean isFailure = (failure.thrownException() instanceof AssertionFailedError) &&
260       !classNameAndTest.equals("junit.framework.TestSuite$1.warning");
261
262 // for dubugging
263
// try{
264
// File temp = File.createTempFile("asdf", "java", new File("/home/awulf"));
265
// FileWriter writer = new FileWriter(temp);
266
// writer.write("testString: " + testString + "\n");
267
// writer.write("old className: " + className1 + "\n");
268
// writer.write("new className: " + className2 + "\n");
269
// writer.write("file: " + file + "\n");
270
// writer.write("lineNum: " + lineNum + "\n");
271
// writer.write("exception: " + exception + "\n");
272
// writer.write("!isFailure: " + !isFailure + "\n");
273
// writer.write("testName: " + testName + "\n");
274
// writer.write("className: " + className + "\n");
275
// writer.write("stackTrace: " + stackTrace + "\n");
276
// writer.close();
277
// } catch(IOException e) {
278
//
279
// }
280

281     int indexOfClass = classNames.indexOf(className);
282     File JavaDoc file;
283     if (indexOfClass != -1) file = files.get(indexOfClass);
284     else file = _jmc.getFileForClassName(className);
285     
286     // if testClass contains no
287

288     // a test didn't fail, we couldn't even open the test.
289
if (file == null) {
290       return new JUnitError(new File JavaDoc("nofile"), 0, 0, exception, !isFailure, testName, className, stackTrace);
291     }
292     
293     // The code augmentation for elementary and intermediate level files causes the error to be highlighted on
294
// the wrong line. The following code adjusts for this discrepancy.
295
String JavaDoc name = file.getName();
296     int adjLineNum;
297     if (name.endsWith(".dj0") || name.endsWith(".dj0")) adjLineNum = lineNum - 1;
298     else adjLineNum = lineNum;
299     
300     return new JUnitError(file, adjLineNum, 0, exception, !isFailure, testName, className, stackTrace);
301   }
302
303   /** Parses the line number out of the stack trace in the given class name. */
304   private int _lineNumber(String JavaDoc sw, String JavaDoc classname) {
305     int lineNum;
306     int idxClassname = sw.indexOf(classname);
307     if (idxClassname == -1) return -1;
308
309     String JavaDoc theLine = sw.substring(idxClassname, sw.length());
310     
311     theLine = theLine.substring(theLine.indexOf(classname), theLine.length());
312     theLine = theLine.substring(theLine.indexOf("(") + 1, theLine.length());
313     theLine = theLine.substring(0, theLine.indexOf(")"));
314
315     try {
316       int i = theLine.indexOf(":") + 1;
317       lineNum = Integer.parseInt(theLine.substring(i, theLine.length())) - 1;
318     }
319     catch (NumberFormatException JavaDoc e) { throw new UnexpectedException(e); }
320     
321     return lineNum;
322   }
323 }
324
Popular Tags