1   package junit.awtui;
2   
3   import java.awt.*;
4   import java.awt.event.*;
5   import java.awt.image.ImageProducer;
6   import java.util.Vector;
7   
8   import junit.framework.*;
9   import junit.runner.*;
10  
11  /***
12   * An AWT based user interface to run tests.
13   * Enter the name of a class which either provides a static
14   * suite method or is a subclass of TestCase.
15   * <pre>
16   * Synopsis: java junit.awtui.TestRunner [-noloading] [TestCase]
17   * </pre>
18   * TestRunner takes as an optional argument the name of the testcase class to be run.
19   */
20   public class TestRunner extends BaseTestRunner {
21  	protected Frame fFrame;
22  	protected Vector fExceptions;
23  	protected Vector fFailedTests;
24  	protected Thread fRunner;
25  	protected TestResult fTestResult;
26  
27  	protected TextArea fTraceArea;
28  	protected TextField fSuiteField;
29  	protected Button fRun;
30  	protected ProgressBar fProgressIndicator;
31  	protected List fFailureList;
32  	protected Logo fLogo;
33  	protected Label fNumberOfErrors;
34  	protected Label fNumberOfFailures;
35  	protected Label fNumberOfRuns;
36  	protected Button fQuitButton;
37  	protected Button fRerunButton;
38  	protected TextField fStatusLine;
39  	protected Checkbox fUseLoadingRunner;
40  
41  	protected static final Font PLAIN_FONT= new Font("dialog", Font.PLAIN, 12);
42  	private static final int GAP= 4;
43  
44  	public TestRunner() {
45  	}
46  
47  	private void about() {
48  		AboutDialog about= new AboutDialog(fFrame);
49  		about.setModal(true);
50  		about.setLocation(300, 300);
51  		about.setVisible(true);
52  	}
53  
54  	public void testStarted(String testName) {
55  		showInfo("Running: "+testName);
56  	}
57  
58  	public void testEnded(String testName) {
59  		setLabelValue(fNumberOfRuns, fTestResult.runCount());
60  		synchronized(this) {
61  			fProgressIndicator.step(fTestResult.wasSuccessful());
62  		}
63  	}
64  
65  	public void testFailed(int status, Test test, Throwable t) {
66  		switch (status) {
67  			case TestRunListener.STATUS_ERROR:
68  				fNumberOfErrors.setText(Integer.toString(fTestResult.errorCount()));
69  				appendFailure("Error", test, t);
70  				break;
71  			case TestRunListener.STATUS_FAILURE:
72  				fNumberOfFailures.setText(Integer.toString(fTestResult.failureCount()));
73  				appendFailure("Failure", test, t);
74  				break;
75  		}
76  	}
77  
78  	protected void addGrid(Panel p, Component co, int x, int y, int w, int fill, double wx, int anchor) {
79  		GridBagConstraints c= new GridBagConstraints();
80  		c.gridx= x; c.gridy= y;
81  		c.gridwidth= w;
82  		c.anchor= anchor;
83  		c.weightx= wx;
84  		c.fill= fill;
85  		if (fill == GridBagConstraints.BOTH || fill == GridBagConstraints.VERTICAL)
86  			c.weighty= 1.0;
87  		c.insets= new Insets(y == 0 ? GAP : 0, x == 0 ? GAP : 0, GAP, GAP);
88  		p.add(co, c);
89  	}
90  
91  	private void appendFailure(String kind, Test test, Throwable t) {
92  		kind+= ": " + test;
93  		String msg= t.getMessage();
94  		if (msg != null) {
95  			kind+= ":" + truncate(msg);
96  		}
97  		fFailureList.add(kind);
98  		fExceptions.addElement(t);
99  		fFailedTests.addElement(test);
100 		if (fFailureList.getItemCount() == 1) {
101 			fFailureList.select(0);
102 			failureSelected();
103 		}
104 	}
105 	/***
106 	 * Creates the JUnit menu. Clients override this
107 	 * method to add additional menu items.
108 	 */
109 	protected Menu createJUnitMenu() {
110 		Menu menu= new Menu("JUnit");
111 		MenuItem mi= new MenuItem("About...");
112 		mi.addActionListener(
113 		    new ActionListener() {
114 		        public void actionPerformed(ActionEvent event) {
115 		            about();
116 		        }
117 		    }
118 		);
119 		menu.add(mi);
120 
121 		menu.addSeparator();
122 		mi= new MenuItem("Exit");
123 		mi.addActionListener(
124 		    new ActionListener() {
125 		        public void actionPerformed(ActionEvent event) {
126 		            System.exit(0);
127 		        }
128 		    }
129 		);
130 		menu.add(mi);
131 		return menu;
132 	}
133 
134 	protected void createMenus(MenuBar mb) {
135 		mb.add(createJUnitMenu());
136 	}
137 	protected TestResult createTestResult() {
138 		return new TestResult();
139 	}
140 
141 	protected Frame createUI(String suiteName) {
142 		Frame frame= new Frame("JUnit");
143 		Image icon= loadFrameIcon();
144 		if (icon != null)
145 			frame.setIconImage(icon);
146 
147 		frame.setLayout(new BorderLayout(0, 0));
148 		frame.setBackground(SystemColor.control);
149 		final Frame finalFrame= frame;
150 
151 		frame.addWindowListener(
152 			new WindowAdapter() {
153 				public void windowClosing(WindowEvent e) {
154 					finalFrame.dispose();
155 					System.exit(0);
156 				}
157 			}
158 		);
159 
160 		MenuBar mb = new MenuBar();
161 		createMenus(mb);
162 		frame.setMenuBar(mb);
163 
164 		//---- first section
165 		Label suiteLabel= new Label("Test class name:");
166 
167 		fSuiteField= new TextField(suiteName != null ? suiteName : "");
168 		fSuiteField.selectAll();
169 		fSuiteField.requestFocus();
170 		fSuiteField.setFont(PLAIN_FONT);
171 		fSuiteField.setColumns(40);
172 		fSuiteField.addActionListener(
173 			new ActionListener() {
174 				public void actionPerformed(ActionEvent e) {
175 					runSuite();
176 				}
177 			}
178 		);
179 		fSuiteField.addTextListener(
180 			new TextListener() {
181 				public void textValueChanged(TextEvent e) {
182 					fRun.setEnabled(fSuiteField.getText().length() > 0);
183 					fStatusLine.setText("");
184 				}
185 			}
186 		);
187 		fRun= new Button("Run");
188 		fRun.setEnabled(false);
189 		fRun.addActionListener(
190 			new ActionListener() {
191 				public void actionPerformed(ActionEvent e) {
192 					runSuite();
193 				}
194 			}
195 		);
196 		boolean useLoader= useReloadingTestSuiteLoader();
197 		fUseLoadingRunner= new Checkbox("Reload classes every run", useLoader);
198 		if (inVAJava())
199 			fUseLoadingRunner.setVisible(false);
200 
201 		//---- second section
202 		fProgressIndicator= new ProgressBar();
203 
204 		//---- third section
205 		fNumberOfErrors= new Label("0000", Label.RIGHT);
206 		fNumberOfErrors.setText("0");
207 		fNumberOfErrors.setFont(PLAIN_FONT);
208 
209 		fNumberOfFailures= new Label("0000", Label.RIGHT);
210 		fNumberOfFailures.setText("0");
211 		fNumberOfFailures.setFont(PLAIN_FONT);
212 
213 		fNumberOfRuns= new Label("0000", Label.RIGHT);
214 		fNumberOfRuns.setText("0");
215 		fNumberOfRuns.setFont(PLAIN_FONT);
216 
217 		Panel numbersPanel= createCounterPanel();
218 
219 		//---- fourth section
220 		Label failureLabel= new Label("Errors and Failures:");
221 
222 		fFailureList= new List(5);
223 		fFailureList.addItemListener(
224 			new ItemListener() {
225 				public void itemStateChanged(ItemEvent e) {
226 					failureSelected();
227 				}
228 			}
229 		);
230 		fRerunButton= new Button("Run");
231 		fRerunButton.setEnabled(false);
232 		fRerunButton.addActionListener(
233 			new ActionListener() {
234 				public void actionPerformed(ActionEvent e) {
235 					rerun();
236 				}
237 			}
238 		);
239 
240 		Panel failedPanel= new Panel(new GridLayout(0, 1, 0, 2));
241 		failedPanel.add(fRerunButton);
242 
243 		fTraceArea= new TextArea();
244 		fTraceArea.setRows(5);
245 		fTraceArea.setColumns(60);
246 
247 		//---- fifth section
248 		fStatusLine= new TextField();
249 		fStatusLine.setFont(PLAIN_FONT);
250 		fStatusLine.setEditable(false);
251 		fStatusLine.setForeground(Color.red);
252 
253 		fQuitButton= new Button("Exit");
254 		fQuitButton.addActionListener(
255 			new ActionListener() {
256 				public void actionPerformed(ActionEvent e) {
257 					System.exit(0);
258 				}
259 			}
260 		);
261 
262 		// ---------
263 		fLogo= new Logo();
264 
265 		//---- overall layout
266 		Panel panel= new Panel(new GridBagLayout());
267 
268 		addGrid(panel, suiteLabel,		 0, 0, 2, GridBagConstraints.HORIZONTAL, 	1.0, GridBagConstraints.WEST);
269 
270 		addGrid(panel, fSuiteField, 	 0, 1, 2, GridBagConstraints.HORIZONTAL, 	1.0, GridBagConstraints.WEST);
271 		addGrid(panel, fRun, 			 2, 1, 1, GridBagConstraints.HORIZONTAL, 	0.0, GridBagConstraints.CENTER);
272 		addGrid(panel, fUseLoadingRunner, 0, 2, 2, GridBagConstraints.NONE, 	1.0, GridBagConstraints.WEST);
273 		addGrid(panel, fProgressIndicator, 0, 3, 2, GridBagConstraints.HORIZONTAL, 	1.0, GridBagConstraints.WEST);
274 		addGrid(panel, fLogo, 			 2, 3, 1, GridBagConstraints.NONE, 			0.0, GridBagConstraints.NORTH);
275 
276 		addGrid(panel, numbersPanel,	 0, 4, 2, GridBagConstraints.NONE, 			0.0, GridBagConstraints.WEST);
277 
278 		addGrid(panel, failureLabel, 	 0, 5, 2, GridBagConstraints.HORIZONTAL, 	1.0, GridBagConstraints.WEST);
279 		addGrid(panel, fFailureList, 	 0, 6, 2, GridBagConstraints.BOTH, 			1.0, GridBagConstraints.WEST);
280 		addGrid(panel, failedPanel, 	 2, 6, 1, GridBagConstraints.HORIZONTAL, 	0.0, GridBagConstraints.CENTER);
281 		addGrid(panel, fTraceArea, 	     0, 7, 2, GridBagConstraints.BOTH, 			1.0, GridBagConstraints.WEST);
282 
283 		addGrid(panel, fStatusLine, 	 0, 8, 2, GridBagConstraints.HORIZONTAL, 	1.0, GridBagConstraints.CENTER);
284 		addGrid(panel, fQuitButton, 	 2, 8, 1, GridBagConstraints.HORIZONTAL, 	0.0, GridBagConstraints.CENTER);
285 
286 		frame.add(panel, BorderLayout.CENTER);
287 		frame.pack();
288 		return frame;
289 	}
290 
291 	protected Panel createCounterPanel() {
292 		Panel numbersPanel= new Panel(new GridBagLayout());
293 		addToCounterPanel(
294 			numbersPanel,
295 			new Label("Runs:"),
296 			0, 0, 1, 1, 0.0, 0.0,
297           	GridBagConstraints.CENTER, GridBagConstraints.NONE,
298           	new Insets(0, 0, 0, 0) 
299 		);	
300 		addToCounterPanel(
301 			numbersPanel,
302 			fNumberOfRuns, 
303           	1, 0, 1, 1, 0.33, 0.0,
304           	GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
305           	new Insets(0, 8, 0, 40)
306 		);
307 		addToCounterPanel(
308 			numbersPanel,
309 			new Label("Errors:"),
310           	2, 0, 1, 1, 0.0, 0.0,
311           	GridBagConstraints.CENTER, GridBagConstraints.NONE,
312           	new Insets(0, 8, 0, 0)
313 		);
314 		addToCounterPanel(
315 			numbersPanel,
316 			fNumberOfErrors,
317           	3, 0, 1, 1, 0.33, 0.0,
318           	GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
319           	new Insets(0, 8, 0, 40)
320 		);
321 		addToCounterPanel(
322 			numbersPanel,
323 			new Label("Failures:"),
324           	4, 0, 1, 1, 0.0, 0.0,
325           	GridBagConstraints.CENTER, GridBagConstraints.NONE,
326           	new Insets(0, 8, 0, 0)
327 		);	
328 		addToCounterPanel(
329 			numbersPanel,
330 			fNumberOfFailures,
331           	5, 0, 1, 1, 0.33, 0.0,
332           	GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
333           	new Insets(0, 8, 0, 0) 
334 		);
335 		return numbersPanel;
336 	}
337 
338 	private void addToCounterPanel(Panel counter, Component comp,
339 	    	int gridx, int gridy, int gridwidth, int gridheight,
340 			double weightx, double weighty,
341 			int anchor, int fill,
342 			Insets insets) {
343 		
344 		GridBagConstraints constraints= new GridBagConstraints();
345 		constraints.gridx= gridx;
346 		constraints.gridy= gridy;
347 		constraints.gridwidth= gridwidth;
348 		constraints.gridheight= gridheight;
349 		constraints.weightx= weightx;
350 		constraints.weighty= weighty;
351 		constraints.anchor= anchor;
352 		constraints.fill= fill;
353 		constraints.insets= insets;
354 		counter.add(comp, constraints);
355 	}
356 
357 
358 	public void failureSelected() {
359 		fRerunButton.setEnabled(isErrorSelected());
360 		showErrorTrace();
361 	}
362 
363 	private boolean isErrorSelected() {
364 		return fFailureList.getSelectedIndex() != -1;
365 	}
366 
367 	private Image loadFrameIcon() {
368 		Toolkit toolkit= Toolkit.getDefaultToolkit();
369 		try {
370 			java.net.URL url= BaseTestRunner.class.getResource("smalllogo.gif");
371 			return toolkit.createImage((ImageProducer) url.getContent());
372 		} catch (Exception ex) {
373 		}
374 		return null;
375 	}
376 
377 	public Thread getRunner() {
378 		return fRunner;
379 	}
380 
381 	public static void main(String[] args) {
382 		new TestRunner().start(args);
383 	}
384 
385 	public static void run(Class test) {
386 		String args[]= { test.getName() };
387 		main(args);
388 	}
389 
390 	public void rerun() {
391 		int index= fFailureList.getSelectedIndex();
392 		if (index == -1)
393 			return;
394 
395 		Test test= (Test)fFailedTests.elementAt(index);
396 		rerunTest(test);
397 	}
398 
399 	private void rerunTest(Test test) {
400 		if (!(test instanceof TestCase)) {
401 			showInfo("Could not reload "+ test.toString());
402 			return;
403 		}
404 		Test reloadedTest= null;
405 		TestCase rerunTest= (TestCase)test;
406 		try {
407 			Class reloadedTestClass= getLoader().reload(test.getClass()); 
408 			reloadedTest= TestSuite.createTest(reloadedTestClass, rerunTest.getName());
409 		} catch(Exception e) {
410 			showInfo("Could not reload "+ test.toString());
411 			return;
412 		}
413 		TestResult result= new TestResult();
414 		reloadedTest.run(result);
415 
416 		String message= reloadedTest.toString();
417 		if(result.wasSuccessful())
418 			showInfo(message+" was successful");
419 		else if (result.errorCount() == 1)
420 			showStatus(message+" had an error");
421 		else
422 			showStatus(message+" had a failure");
423 	}
424 
425 	protected void reset() {
426 		setLabelValue(fNumberOfErrors, 0);
427 		setLabelValue(fNumberOfFailures, 0);
428 		setLabelValue(fNumberOfRuns, 0);
429 		fProgressIndicator.reset();
430 		fRerunButton.setEnabled(false);
431 		fFailureList.removeAll();
432 		fExceptions= new Vector(10);
433 		fFailedTests= new Vector(10);
434 		fTraceArea.setText("");
435 
436 	}
437 
438 	protected void runFailed(String message) {
439 		showStatus(message);
440 		fRun.setLabel("Run");
441 		fRunner= null;
442 	}
443 
444 	synchronized public void runSuite() {
445 		if (fRunner != null && fTestResult != null) {
446 			fTestResult.stop();
447 		} else {
448 			setLoading(shouldReload());
449 			fRun.setLabel("Stop");
450 			showInfo("Initializing...");
451 			reset();
452 
453 			showInfo("Load Test Case...");
454 
455 			final Test testSuite= getTest(fSuiteField.getText());
456 			if (testSuite != null) {
457 				fRunner= new Thread() {
458 					public void run() {
459 						fTestResult= createTestResult();
460 						fTestResult.addListener(TestRunner.this);
461 						fProgressIndicator.start(testSuite.countTestCases());
462 						showInfo("Running...");
463 
464 						long startTime= System.currentTimeMillis();
465 						testSuite.run(fTestResult);
466 
467 						if (fTestResult.shouldStop()) {
468 							showStatus("Stopped");
469 						} else {
470 							long endTime= System.currentTimeMillis();
471 							long runTime= endTime-startTime;
472 							showInfo("Finished: " + elapsedTimeAsString(runTime) + " seconds");
473 						}
474 						fTestResult= null;
475 						fRun.setLabel("Run");
476 						fRunner= null;
477 						System.gc();
478 					}
479 				};
480 				fRunner.start();
481 			}
482 		}
483 	}
484 
485 	private boolean shouldReload() {
486 		return !inVAJava() && fUseLoadingRunner.getState();
487 	}
488 
489 	private void setLabelValue(Label label, int value) {
490 		label.setText(Integer.toString(value));
491 		label.invalidate();
492 		label.getParent().validate();
493 
494 	}
495 
496 	public void setSuiteName(String suite) {
497 		fSuiteField.setText(suite);
498 	}
499 
500 	private void showErrorTrace() {
501 		int index= fFailureList.getSelectedIndex();
502 		if (index == -1)
503 			return;
504 
505 		Throwable t= (Throwable) fExceptions.elementAt(index);
506 		fTraceArea.setText(getFilteredTrace(t));
507 	}
508 
509 
510 	private void showInfo(String message) {
511 		fStatusLine.setFont(PLAIN_FONT);
512 		fStatusLine.setForeground(Color.black);
513 		fStatusLine.setText(message);
514 	}
515 
516 	protected void clearStatus() {
517 		showStatus("");
518 	}
519 
520 	private void showStatus(String status) {
521 		fStatusLine.setFont(PLAIN_FONT);
522 		fStatusLine.setForeground(Color.red);
523 		fStatusLine.setText(status);
524 	}
525 	/***
526 	 * Starts the TestRunner
527 	 */
528 	public void start(String[] args) {
529 		String suiteName= processArguments(args);
530 		fFrame= createUI(suiteName);
531 		fFrame.setLocation(200, 200);
532 		fFrame.setVisible(true);
533 
534 		if (suiteName != null) {
535 			setSuiteName(suiteName);
536 			runSuite();
537 		}
538 	}
539 }