KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > rice > cs > drjava > ui > JUnitPanel


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.ui;
35
36 import edu.rice.cs.drjava.model.DJDocument;
37 import edu.rice.cs.drjava.model.OpenDefinitionsDocument;
38 import edu.rice.cs.drjava.model.SingleDisplayModel;
39 import edu.rice.cs.drjava.model.compiler.CompilerError;
40 import edu.rice.cs.drjava.model.junit.JUnitError;
41 import edu.rice.cs.drjava.model.junit.JUnitErrorModel;
42 import edu.rice.cs.util.UnexpectedException;
43 import edu.rice.cs.util.swing.BorderlessScrollPane;
44 import edu.rice.cs.util.text.SwingDocument;
45 import edu.rice.cs.util.swing.RightClickMouseAdapter;
46
47 import javax.swing.*;
48 import javax.swing.border.EmptyBorder JavaDoc;
49 import javax.swing.text.*;
50 import java.awt.*;
51 import java.awt.event.ActionEvent JavaDoc;
52 import java.awt.event.ActionListener JavaDoc;
53 import java.awt.event.MouseEvent JavaDoc;
54 import java.io.File JavaDoc;
55 import java.util.List JavaDoc;
56 import java.util.ArrayList JavaDoc;
57 import java.util.HashMap JavaDoc;
58
59 /** The panel that displays all the testing errors.
60  * @version $Id: JUnitPanel.java 4043 2006-11-22 23:04:59Z rcartwright $
61  */

62 public class JUnitPanel extends ErrorPanel {
63   private static final String JavaDoc START_JUNIT_MSG = "Testing in progress. Please wait ...\n";
64   private static final String JavaDoc JUNIT_FINISHED_MSG = "All tests completed successfully.\n";
65   private static final String JavaDoc NO_TESTS_MSG = "";
66
67   private static final SimpleAttributeSet OUT_OF_SYNC_ATTRIBUTES = _getOutOfSyncAttributes();
68   private static final SimpleAttributeSet _getOutOfSyncAttributes() {
69     SimpleAttributeSet s = new SimpleAttributeSet();
70     s.addAttribute(StyleConstants.Foreground, Color.red.darker());
71     s.addAttribute(StyleConstants.Bold, Boolean.TRUE);
72     return s;
73   }
74
75   private static final SimpleAttributeSet TEST_PASS_ATTRIBUTES = _getTestPassAttributes();
76   private static final SimpleAttributeSet _getTestPassAttributes() {
77     SimpleAttributeSet s = new SimpleAttributeSet();
78     s.addAttribute(StyleConstants.Foreground, Color.green.darker());
79     return s;
80   }
81
82   private static final SimpleAttributeSet TEST_FAIL_ATTRIBUTES = _getTestFailAttributes();
83   private static final SimpleAttributeSet _getTestFailAttributes() {
84     SimpleAttributeSet s = new SimpleAttributeSet();
85     s.addAttribute(StyleConstants.Foreground, Color.red);
86     return s;
87   }
88
89   private static final String JavaDoc TEST_OUT_OF_SYNC =
90     "The documents being tested have been modified and should be recompiled!\n";
91
92   protected JUnitErrorListPane _errorListPane;
93   private final MainFrame _mainFrame; // only used in assert statements
94
private int _testCount;
95   private boolean _testsSuccessful;
96
97   private JUnitProgressBar _progressBar;
98   private List JavaDoc<OpenDefinitionsDocument> _odds = new ArrayList JavaDoc<OpenDefinitionsDocument>();
99
100   private Action _showStackTraceAction = new AbstractAction("Show Stack Trace") {
101         public void actionPerformed(ActionEvent JavaDoc ae) {
102           if (_error != null) {
103             _displayStackTrace(_error);
104           }
105         }
106       };
107
108   private JButton _showStackTraceButton;
109
110   /** The currently selected error. */
111   private JUnitError _error = null;
112   private Window _stackFrame = null;
113   private JTextArea _stackTextArea;
114   private final JLabel _errorLabel = new JLabel();
115   private final JLabel _testLabel = new JLabel();
116   private final JLabel _fileLabel = new JLabel();
117
118   /** Constructor.
119     * @param model SingleDisplayModel in which we are running
120     * @param frame MainFrame in which we are displayed
121     */

122   public JUnitPanel(SingleDisplayModel model, MainFrame frame) {
123     super(model, frame, "Test Output", "Test Progress");
124     _mainFrame = frame;
125     _testCount = 0;
126     _testsSuccessful = true;
127
128     _progressBar = new JUnitProgressBar();
129     _progressBar.setUI(new javax.swing.plaf.basic.BasicProgressBarUI JavaDoc());
130     _showStackTraceButton = new JButton(_showStackTraceAction);
131     customPanel.add(_progressBar, BorderLayout.NORTH);
132     customPanel.add(_showStackTraceButton, BorderLayout.SOUTH);
133
134     _errorListPane = new JUnitErrorListPane();
135     setErrorListPane(_errorListPane);
136   }
137
138   /** Returns the JUnitErrorListPane that this panel manages. */
139   public JUnitErrorListPane getErrorListPane() { return _errorListPane; }
140
141   protected JUnitErrorModel getErrorModel() { return getModel().getJUnitModel().getJUnitErrorModel(); }
142
143   /** Updates all document styles with the attributes contained in newSet.
144     * @param newSet Style containing new attributes to use.
145     */

146   protected void _updateStyles(AttributeSet newSet) {
147     super._updateStyles(newSet);
148     OUT_OF_SYNC_ATTRIBUTES.addAttributes(newSet);
149     StyleConstants.setBold(OUT_OF_SYNC_ATTRIBUTES, true); // should always be bold
150
TEST_PASS_ATTRIBUTES.addAttributes(newSet);
151     TEST_FAIL_ATTRIBUTES.addAttributes(newSet);
152   }
153
154   /** called when work begins */
155   public void setJUnitInProgress() {
156     _errorListPane.setJUnitInProgress();
157   }
158
159   /** Clean up when the tab is closed. */
160   protected void _close() {
161     super._close();
162     getModel().getJUnitModel().resetJUnitErrors();
163     reset();
164   }
165
166   /** Reset the errors to the current error information. */
167   public void reset() {
168     JUnitErrorModel juem = getModel().getJUnitModel().getJUnitErrorModel();
169     boolean testsHaveRun = false;
170     if (juem != null) {
171       _numErrors = juem.getNumErrors();
172       testsHaveRun = juem.haveTestsRun();
173     }
174     else _numErrors = 0;
175     _errorListPane.updateListPane(testsHaveRun); //changed!!
176
}
177
178   /** Resets the progress bar to start counting the given number of tests. */
179   public void progressReset(int numTests) {
180     _progressBar.reset();
181     _progressBar.start(numTests);
182     _testsSuccessful = true;
183     _testCount = 0;
184   }
185
186   /** Steps the progress bar forward by one test.
187    * @param successful Whether the last test was successful or not.
188    */

189   public void progressStep(boolean successful) {
190     _testCount++;
191     _testsSuccessful &= successful;
192     _progressBar.step(_testCount, _testsSuccessful);
193   }
194
195   public void testStarted(String JavaDoc className, String JavaDoc testName) { }
196
197   private void _displayStackTrace (JUnitError e) {
198     _errorLabel.setText((e.isWarning() ? "Error: " : "Failure: ") +
199                         e.message());
200     _fileLabel.setText("File: "+(new File JavaDoc(e.fileName())).getName());
201     if (!e.testName().equals("")) {
202       _testLabel.setText("Test: "+e.testName());
203     }
204     else {
205       _testLabel.setText("");
206     }
207     _stackTextArea.setText(e.stackTrace());
208     _stackTextArea.setCaretPosition(0);
209     _stackFrame.setVisible(true);
210   }
211
212   /** A pane to show JUnit errors. It acts like a listbox (clicking selects an item) but items can each wrap, etc. */
213   public class JUnitErrorListPane extends ErrorPanel.ErrorListPane {
214     private JPopupMenu _popMenu;
215     private String JavaDoc _runningTestName;
216     private boolean _warnedOutOfSync;
217     private static final String JavaDoc JUNIT_WARNING = "junit.framework.TestSuite$1.warning";
218
219     /** Maps any test names in the currently running suite to the position that they appear in the list pane. */
220     private final HashMap JavaDoc<String JavaDoc, Position> _runningTestNamePositions;
221
222     /** Constructs the JUnitErrorListPane. */
223     public JUnitErrorListPane() {
224       removeMouseListener(defaultMouseListener);
225       _popMenu = new JPopupMenu();
226       _popMenu.add(_showStackTraceAction);
227       _error = null;
228       _setupStackTraceFrame();
229       addMouseListener(new PopupAdapter());
230       _runningTestName = null;
231       _runningTestNamePositions = new HashMap JavaDoc<String JavaDoc, Position>();
232       _showStackTraceButton.setEnabled(false);
233     }
234
235     private String JavaDoc _getTestFromName(String JavaDoc name) {
236       int paren = name.indexOf('(');
237       
238       if ((paren > -1) && (paren < name.length())) return name.substring(0, paren);
239       
240       else throw new IllegalArgumentException JavaDoc("Name does not contain any parens: " + name);
241     }
242
243     private String JavaDoc _getClassFromName(String JavaDoc name) {
244       int paren = name.indexOf('(');
245       
246       if ((paren > -1) && (paren < name.length())) return name.substring(paren + 1, name.length() - 1);
247       else throw new IllegalArgumentException JavaDoc("Name does not contain any parens: " + name);
248     }
249
250     /** Provides the ability to display the name of the test being run. */
251     public void testStarted(String JavaDoc name) {
252       String JavaDoc testName = _getTestFromName(name);
253       String JavaDoc className = _getClassFromName(name);
254       String JavaDoc fullName = className + "." + testName;
255       if (fullName.equals(JUNIT_WARNING)) return;
256       SwingDocument doc = getSwingDocument();
257       int index = doc.getLength();
258
259       try {
260         // Insert the classname if it has changed
261
if (! className.equals(_runningTestName)) {
262           _runningTestName = className;
263           doc.insertString(index, " " + className + "\n", NORMAL_ATTRIBUTES);
264           index = doc.getLength();
265         }
266
267         // Insert the test name, remembering its position
268
doc.insertString(index, " ", NORMAL_ATTRIBUTES);
269         index = doc.getLength();
270         doc.insertString(index, testName + "\n", NORMAL_ATTRIBUTES);
271         Position pos = doc.createPosition(index);
272         _runningTestNamePositions.put(fullName, pos);
273         setCaretPosition(index);
274       }
275       catch (BadLocationException ble) {
276         // Inserting at end, shouldn't happen
277
throw new UnexpectedException(ble);
278       }
279     }
280
281     /** Displays the results of a test that has finished. */
282     public void testEnded(String JavaDoc name, boolean wasSuccessful, boolean causedError) {
283       String JavaDoc testName = _getTestFromName(name);
284       String JavaDoc fullName = _getClassFromName(name) + "." + testName;
285       if (fullName.equals(JUNIT_WARNING)) return;
286       
287       SwingDocument doc = getSwingDocument();
288       Position namePos = _runningTestNamePositions.get(fullName);
289       AttributeSet set;
290       if (! wasSuccessful || causedError) set = TEST_FAIL_ATTRIBUTES;
291       else set = TEST_PASS_ATTRIBUTES;
292       if (namePos != null) {
293         int index = namePos.getOffset();
294         int length = testName.length();
295         doc.setCharacterAttributes(index, length, set, false);
296       }
297     }
298
299     /** Puts the error pane into "junit in progress" state. Only runs in event thread. */
300     public void setJUnitInProgress() {
301       assert EventQueue.isDispatchThread();
302       _errorListPositions = new Position[0];
303       progressReset(0);
304       _runningTestNamePositions.clear();
305       _runningTestName = null;
306       _warnedOutOfSync = false;
307
308       SwingDocument doc = new SwingDocument();
309 // _checkSync(doc);
310

311       doc.append(START_JUNIT_MSG, BOLD_ATTRIBUTES);
312       setDocument(doc);
313       selectNothing();
314     }
315
316     /** Used to show that testing was unsuccessful. */
317     protected void _updateWithErrors() throws BadLocationException {
318       //DefaultStyledDocument doc = new DefaultStyledDocument();
319
SwingDocument doc = getSwingDocument();
320 // _checkSync(doc);
321
_updateWithErrors("test", "failed", doc);
322     }
323     
324     /** Gets the message indicating the number of errors and warnings.*/
325     protected String JavaDoc _getNumErrorsMessage(String JavaDoc failureName, String JavaDoc failureMeaning) {
326       StringBuilder JavaDoc numErrMsg;
327       
328       /** Used for display purposes only */
329       int numCompErrs = getErrorModel().getNumCompErrors();
330       int numWarnings = getErrorModel().getNumWarnings();
331       
332       if (! getErrorModel().hasOnlyWarnings()) {
333         numErrMsg = new StringBuilder JavaDoc(numCompErrs + " " + failureName); //failureName = error or test (for compilation and JUnit testing respectively)
334
if (numCompErrs > 1) numErrMsg.append("s");
335         numErrMsg.append(" " + failureMeaning);
336         if (numWarnings > 0) numErrMsg.append(" and " + numWarnings + " warning");
337       }
338       else numErrMsg = new StringBuilder JavaDoc(numWarnings + " warning");
339       
340       if (numWarnings > 1) numErrMsg.append("s");
341       if (numWarnings > 0) numErrMsg.append(" found");
342       
343       numErrMsg.append(":\n");
344       
345       return numErrMsg.toString();
346     }
347
348     protected void _updateWithErrors(String JavaDoc failureName, String JavaDoc failureMeaning, SwingDocument doc)
349       throws BadLocationException {
350       // Print how many errors
351
_replaceInProgressText(_getNumErrorsMessage(failureName, failureMeaning));
352
353       _insertErrors(doc);
354
355       // Select the first error
356
switchToError(0);
357     }
358     
359     /** Replaces the "Testing in progress..." text with the given message. Only runs in event thread.
360       * @param msg the text to insert
361       */

362     public void _replaceInProgressText(String JavaDoc msg) throws BadLocationException {
363       assert ! _mainFrame.isVisible() || EventQueue.isDispatchThread();
364       int start = 0;
365       if (_warnedOutOfSync) { start = TEST_OUT_OF_SYNC.length(); }
366       int len = START_JUNIT_MSG.length();
367       SwingDocument doc = getSwingDocument();
368       if (doc.getLength() >= len + start) {
369         doc.remove(start, len);
370         doc.insertString(start, msg, BOLD_ATTRIBUTES);
371       }
372     }
373
374     /** Returns the string to identify a warning. In JUnit, warnings (the odd case) indicate errors/exceptions.
375      */

376     protected String JavaDoc _getWarningText() { return "Error: "; }
377
378     /** Returns the string to identify an error. In JUnit, errors (the normal case) indicate TestFailures. */
379     protected String JavaDoc _getErrorText() { return "Failure: "; }
380
381     /** Updates the list pane with no errors. */
382     protected void _updateNoErrors(boolean haveTestsRun) throws BadLocationException {
383       //DefaultStyledDocument doc = new DefaultStyledDocument();
384
// _checkSync(getDocument());
385
_replaceInProgressText(haveTestsRun ? JUNIT_FINISHED_MSG : NO_TESTS_MSG);
386
387       selectNothing();
388       setCaretPosition(0);
389     }
390
391 // /** Checks the document being tested to see if it's in sync. If not,
392
// * displays a message in the document in the test output pane.
393
// */
394
// private void _checkSync(Document doc) {
395
// if (_warnedOutOfSync) return;
396
// List<OpenDefinitionsDocument> odds = _odds; // grab current _odds
397
// for (OpenDefinitionsDocument odoc: odds) {
398
// if (! odoc.checkIfClassFileInSync()) {
399
// try {
400
// doc.insertString(0, TEST_OUT_OF_SYNC, OUT_OF_SYNC_ATTRIBUTES);
401
// _warnedOutOfSync = true;
402
// return;
403
// }
404
// catch (BadLocationException ble) {
405
// throw new UnexpectedException(ble);
406
// }
407
// }
408
// }
409
// }
410

411     private void _setupStackTraceFrame() {
412       //DrJava.consoleOut().println("Stack Trace for Error: \n"+ e.stackTrace());
413
JDialog _dialog = new JDialog(_frame,"JUnit Error Stack Trace",false);
414       _stackFrame = _dialog;
415       _stackTextArea = new JTextArea();
416       _stackTextArea.setEditable(false);
417       _stackTextArea.setLineWrap(false);
418       JScrollPane scroll = new
419         BorderlessScrollPane(_stackTextArea,
420                              JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
421                              JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
422
423       ActionListener JavaDoc closeListener = new ActionListener JavaDoc() {
424         public void actionPerformed(ActionEvent JavaDoc e) {
425           _frame.setPopupLoc(_stackFrame);
426           _stackFrame.setVisible(true);
427         }
428       };
429       JButton closeButton = new JButton("Close");
430       closeButton.addActionListener(closeListener);
431       JPanel closePanel = new JPanel(new BorderLayout());
432       closePanel.setBorder(new EmptyBorder JavaDoc(5,5,0,0));
433       closePanel.add(closeButton, BorderLayout.EAST);
434       JPanel cp = new JPanel(new BorderLayout());
435       _dialog.setContentPane(cp);
436       cp.setBorder(new EmptyBorder JavaDoc(5,5,5,5));
437       cp.add(scroll, BorderLayout.CENTER);
438       cp.add(closePanel, BorderLayout.SOUTH);
439       JPanel topPanel = new JPanel(new GridLayout(0,1,0,5));
440       topPanel.setBorder(new EmptyBorder JavaDoc(0,0,5,0));
441       topPanel.add(_fileLabel);
442       topPanel.add(_testLabel);
443       topPanel.add(_errorLabel);
444       cp.add(topPanel, BorderLayout.NORTH);
445       _dialog.setSize(600, 500);
446       // initial location is relative to parent (MainFrame)
447
}
448
449     /** Overrides selectItem in ErrorListPane to update the current _error selected
450      * and enabling the _showStackTraceButton.
451      */

452     public void selectItem(CompilerError error) {
453       super.selectItem(error);
454       _error = (JUnitError) error;
455       _showStackTraceButton.setEnabled(true);
456     }
457
458
459     /**
460      * Overrides _removeListHighlight in ErrorListPane to disable the _showStackTraceButton.
461      */

462     protected void _removeListHighlight() {
463       super._removeListHighlight();
464       _showStackTraceButton.setEnabled(false);
465     }
466
467     /**
468      * Updates the UI to a new look and feel.
469      * Need to update the contained popup menu as well.
470      *
471      * Currently, we don't support changing the look and feel
472      * on the fly, so this is disabled.
473      *
474     public void updateUI() {
475       super.updateUI();
476       if (_popMenu != null) {
477         SwingUtilities.updateComponentTreeUI(_popMenu);
478       }
479     }*/

480
481     private class PopupAdapter extends RightClickMouseAdapter {
482       /**
483        * Show popup if the click is on an error.
484        * @param e the MouseEvent correponding to this click
485        */

486       public void mousePressed(MouseEvent JavaDoc e) {
487         if (_selectError(e)) {
488           super.mousePressed(e);
489         }
490       }
491
492       /**
493        * Show popup if the click is on an error.
494        * @param e the MouseEvent correponding to this click
495        */

496       public void mouseReleased(MouseEvent JavaDoc e) {
497         if (_selectError(e)) {
498           super.mouseReleased(e);
499         }
500       }
501
502       /**
503        * Select the error at the given mouse event.
504        * @param e the MouseEvent correponding to this click
505        * @return true iff the mouse click is over an error
506        */

507       private boolean _selectError(MouseEvent JavaDoc e) {
508         //TODO: get rid of cast in the next line, if possible
509
_error = (JUnitError)_errorAtPoint(e.getPoint());
510
511         if (_isEmptySelection() && _error != null) {
512           _errorListPane.switchToError(_error);
513           return true;
514         }
515         else {
516           selectNothing();
517           return false;
518         }
519       }
520
521       /**
522        * Shows the popup menu for this mouse adapter.
523        * @param e the MouseEvent correponding to this click
524        */

525       protected void _popupAction(MouseEvent JavaDoc e) {
526         _popMenu.show(e.getComponent(), e.getX(), e.getY());
527       }
528
529 // public JUnitError getError() {
530
// return _error;
531
// }
532
}
533   }
534   
535   
536   /**
537    * A progress bar showing the status of JUnit tests.
538    * Green until a test fails, then red.
539    * Adapted from JUnit code.
540    */

541   static class JUnitProgressBar extends JProgressBar {
542     private boolean _hasError = false;
543
544     public JUnitProgressBar() {
545       super();
546       setForeground(getStatusColor());
547     }
548
549     private Color getStatusColor() {
550       if (_hasError) {
551         return Color.red;
552       }
553       else {
554         return Color.green;
555       }
556     }
557
558     public void reset() {
559       _hasError = false;
560       setForeground(getStatusColor());
561       setValue(0);
562     }
563
564     public void start(int total) {
565       setMaximum(total);
566       reset();
567     }
568
569     public void step(int value, boolean successful) {
570       setValue(value);
571       if (!_hasError && !successful) {
572         _hasError= true;
573         setForeground(getStatusColor());
574       }
575     }
576   }
577 }
578
Popular Tags