1   package junit.swingui;
2   
3   import java.awt.*;
4   import java.awt.event.*;
5   import java.io.*;
6   import java.lang.reflect.Constructor;
7   import java.net.URL;
8   import java.util.*;
9   
10  import javax.swing.*;
11  import javax.swing.event.*;
12  import junit.framework.*;
13  import junit.runner.*;
14  
15  /***
16   * A Swing based user interface to run tests.
17   * Enter the name of a class which either provides a static
18   * suite method or is a subclass of TestCase.
19   * <pre>
20   * Synopsis: java junit.swingui.TestRunner [-noloading] [TestCase]
21   * </pre>
22   * TestRunner takes as an optional argument the name of the testcase class to be run.
23   */
24  public class TestRunner extends BaseTestRunner implements TestRunContext {
25  	private static final int GAP= 4;
26  	private static final int HISTORY_LENGTH= 5;
27  
28  	protected JFrame fFrame;
29  	private Thread fRunner;
30  	private TestResult fTestResult;
31  
32  	private JComboBox fSuiteCombo;
33  	private ProgressBar fProgressIndicator;
34  	private DefaultListModel fFailures;
35  	private JLabel fLogo;
36  	private CounterPanel fCounterPanel;
37  	private JButton fRun;
38  	private JButton fQuitButton;
39  	private JButton fRerunButton;
40  	private StatusLine fStatusLine;
41  	private FailureDetailView fFailureView;
42  	private JTabbedPane fTestViewTab;
43  	private JCheckBox fUseLoadingRunner;
44  	private Vector fTestRunViews= new Vector(); // view associated with tab in tabbed pane
45  
46  	private static final String TESTCOLLECTOR_KEY= "TestCollectorClass";
47  	private static final String FAILUREDETAILVIEW_KEY= "FailureViewClass";
48  
49  	public TestRunner() {
50  	}
51  
52  	public static void main(String[] args) {
53  		new TestRunner().start(args);
54  	}
55  
56  	public static void run(Class test) {
57  		String args[]= { test.getName() };
58  		main(args);
59  	}
60  
61  	public void testFailed(final int status, final Test test, final Throwable t) {
62  		SwingUtilities.invokeLater(
63  			new Runnable() {
64  				public void run() {
65  					switch (status) {
66  						case TestRunListener.STATUS_ERROR:
67  							fCounterPanel.setErrorValue(fTestResult.errorCount());
68  							appendFailure(test, t);
69  							break;
70  						case TestRunListener.STATUS_FAILURE:
71  							fCounterPanel.setFailureValue(fTestResult.failureCount());
72  							appendFailure(test, t);
73  							break;
74  					}
75  				}
76  			}
77  		);
78  	}
79  
80  	public void testStarted(String testName) {
81  		postInfo("Running: "+testName);
82  	}
83  
84  	public void testEnded(String stringName) {
85  		synchUI();
86  		SwingUtilities.invokeLater(
87  			new Runnable() {
88  				public void run() {
89  					if (fTestResult != null) {
90  						fCounterPanel.setRunValue(fTestResult.runCount());
91  						fProgressIndicator.step(fTestResult.runCount(), fTestResult.wasSuccessful());
92  					}
93  				}
94  			}
95  		);
96  	}
97  
98  	public void setSuite(String suiteName) {
99  		fSuiteCombo.getEditor().setItem(suiteName);
100 	}
101 
102 	private void addToHistory(final String suite) {
103 		for (int i= 0; i < fSuiteCombo.getItemCount(); i++) {
104 			if (suite.equals(fSuiteCombo.getItemAt(i))) {
105 				fSuiteCombo.removeItemAt(i);
106 				fSuiteCombo.insertItemAt(suite, 0);
107 				fSuiteCombo.setSelectedIndex(0);
108 				return;
109 			}
110 		}
111 		fSuiteCombo.insertItemAt(suite, 0);
112 		fSuiteCombo.setSelectedIndex(0);
113 		pruneHistory();
114 	}
115 
116 	private void pruneHistory() {
117 		int historyLength= getPreference("maxhistory", HISTORY_LENGTH);
118 		if (historyLength < 1)
119 			historyLength= 1;
120 		for (int i= fSuiteCombo.getItemCount()-1; i > historyLength-1; i--)
121 			fSuiteCombo.removeItemAt(i);
122 	}
123 
124 	private void appendFailure(Test test, Throwable t) {
125 		fFailures.addElement(new TestFailure(test, t));
126 		if (fFailures.size() == 1)
127 			revealFailure(test);
128 	}
129 
130 	private void revealFailure(Test test) {
131 		for (Enumeration e= fTestRunViews.elements(); e.hasMoreElements(); ) {
132 			TestRunView v= (TestRunView) e.nextElement();
133 			v.revealFailure(test);
134 		}
135 	}
136 
137 	protected void aboutToStart(final Test testSuite) {
138 		for (Enumeration e= fTestRunViews.elements(); e.hasMoreElements(); ) {
139 			TestRunView v= (TestRunView) e.nextElement();
140 			v.aboutToStart(testSuite, fTestResult);
141 		}
142 	}
143 
144 	protected void runFinished(final Test testSuite) {
145 		SwingUtilities.invokeLater(
146 			new Runnable() {
147 				public void run() {
148 					for (Enumeration e= fTestRunViews.elements(); e.hasMoreElements(); ) {
149 						TestRunView v= (TestRunView) e.nextElement();
150 						v.runFinished(testSuite, fTestResult);
151 					}
152 				}
153 			}
154 		);
155 	}
156 
157 	protected CounterPanel createCounterPanel() {
158 		return new CounterPanel();
159 	}
160 
161 	protected JPanel createFailedPanel() {
162 		JPanel failedPanel= new JPanel(new GridLayout(0, 1, 0, 2));
163 		fRerunButton= new JButton("Run");
164 		fRerunButton.setEnabled(false);
165 		fRerunButton.addActionListener(
166 			new ActionListener() {
167 				public void actionPerformed(ActionEvent e) {
168 					rerun();
169 				}
170 			}
171 		);
172 		failedPanel.add(fRerunButton);
173 		return failedPanel;
174 	}
175 
176 	protected FailureDetailView createFailureDetailView() {
177 		String className= BaseTestRunner.getPreference(FAILUREDETAILVIEW_KEY);
178 		if (className != null) {
179 			Class viewClass= null;
180 			try {
181 				viewClass= Class.forName(className);
182 				return (FailureDetailView)viewClass.newInstance();
183 			} catch(Exception e) {
184 				JOptionPane.showMessageDialog(fFrame, "Could not create Failure DetailView - using default view");
185 			}
186 		}
187 		return new DefaultFailureDetailView();
188 	}
189 
190 	/***
191 	 * Creates the JUnit menu. Clients override this
192 	 * method to add additional menu items.
193 	 */
194 	protected JMenu createJUnitMenu() {
195 		JMenu menu= new JMenu("JUnit");
196 		menu.setMnemonic('J');
197 		JMenuItem mi1= new JMenuItem("About...");
198 		mi1.addActionListener(
199 		    new ActionListener() {
200 		        public void actionPerformed(ActionEvent event) {
201 		            about();
202 		        }
203 		    }
204 		);
205 		mi1.setMnemonic('A');
206 		menu.add(mi1);
207 
208 		menu.addSeparator();
209 		JMenuItem mi2= new JMenuItem(" Exit ");
210 		mi2.addActionListener(
211 		    new ActionListener() {
212 		        public void actionPerformed(ActionEvent event) {
213 		            terminate();
214 		        }
215 		    }
216 		);
217 		mi2.setMnemonic('x');
218 		menu.add(mi2);
219 
220 		return menu;
221 	}
222 
223 	protected JFrame createFrame() {
224 		JFrame frame= new JFrame("JUnit");
225 		Image icon= loadFrameIcon();
226 		if (icon != null)
227 			frame.setIconImage(icon);
228 		frame.getContentPane().setLayout(new BorderLayout(0, 0));
229 
230 		frame.addWindowListener(
231 			new WindowAdapter() {
232 				public void windowClosing(WindowEvent e) {
233 					terminate();
234 				}
235 			}
236 		);
237 		return frame;
238 	}
239 
240 	protected JLabel createLogo() {
241 		JLabel label;
242 		Icon icon= getIconResource(BaseTestRunner.class, "logo.gif");
243 		if (icon != null)
244 			label= new JLabel(icon);
245 		else
246 			label= new JLabel("JV");
247 		label.setToolTipText("JUnit Version "+Version.id());
248 		return label;
249 	}
250 
251 	protected void createMenus(JMenuBar mb) {
252 		mb.add(createJUnitMenu());
253 	}
254 
255 	protected JCheckBox createUseLoaderCheckBox() {
256 		boolean useLoader= useReloadingTestSuiteLoader();
257 		JCheckBox box= new JCheckBox("Reload classes every run", useLoader);
258 		box.setToolTipText("Use a custom class loader to reload the classes for every run");
259 		if (inVAJava())
260 			box.setVisible(false);
261 		return box;
262 	}
263 
264 	protected JButton createQuitButton() {
265 		 // spaces required to avoid layout flicker
266 		 // Exit is shorter than Stop that shows in the same column
267 		JButton quit= new JButton(" Exit ");
268 		quit.addActionListener(
269 			new ActionListener() {
270 				public void actionPerformed(ActionEvent e) {
271 					terminate();
272 				}
273 			}
274 		);
275 		return quit;
276 	}
277 
278 	protected JButton createRunButton() {
279 		JButton run= new JButton("Run");
280 		run.setEnabled(true);
281 		run.addActionListener(
282 			new ActionListener() {
283 				public void actionPerformed(ActionEvent e) {
284 					runSuite();
285 				}
286 			}
287 		);
288 		return run;
289 	}
290 
291 	protected Component createBrowseButton() {
292 		JButton browse= new JButton("...");
293 		browse.setToolTipText("Select a Test class");
294 		browse.addActionListener(
295 			new ActionListener() {
296 				public void actionPerformed(ActionEvent e) {
297 					browseTestClasses();
298 				}
299 			}
300 		);
301 		return browse;
302 	}
303 
304 	protected StatusLine createStatusLine() {
305 		return new StatusLine(380);
306 	}
307 
308 	protected JComboBox createSuiteCombo() {
309 		JComboBox combo= new JComboBox();
310 		combo.setEditable(true);
311 		combo.setLightWeightPopupEnabled(false);
312 
313 		combo.getEditor().getEditorComponent().addKeyListener(
314 			new KeyAdapter() {
315 				public void keyTyped(KeyEvent e) {
316 					textChanged();
317 					if (e.getKeyChar() == KeyEvent.VK_ENTER)
318 						runSuite();
319 				}
320 			}
321 		);
322 		try {
323 			loadHistory(combo);
324 		} catch (IOException e) {
325 			// fails the first time
326 		}
327 		combo.addItemListener(
328 			new ItemListener() {
329 				public void itemStateChanged(ItemEvent event) {
330 					if (event.getStateChange() == ItemEvent.SELECTED) {
331 						textChanged();
332 					}
333 				}
334 			}
335 		);
336 		return combo;
337 	}
338 
339 	protected JTabbedPane createTestRunViews() {
340 		JTabbedPane pane= new JTabbedPane(JTabbedPane.BOTTOM);
341 
342 		FailureRunView lv= new FailureRunView(this);
343 		fTestRunViews.addElement(lv);
344 		lv.addTab(pane);
345 
346 		TestHierarchyRunView tv= new TestHierarchyRunView(this);
347 		fTestRunViews.addElement(tv);
348 		tv.addTab(pane);
349 
350 		pane.addChangeListener(
351 			new ChangeListener() {
352 				public void stateChanged(ChangeEvent e) {
353 					testViewChanged();
354 				}
355 			}
356 		);
357 		return pane;
358 	}
359 
360 	public void testViewChanged() {
361 		TestRunView view= (TestRunView)fTestRunViews.elementAt(fTestViewTab.getSelectedIndex());
362 		view.activate();
363 	}
364 
365 	protected TestResult createTestResult() {
366 		return new TestResult();
367 	}
368 
369 	protected JFrame createUI(String suiteName) {
370 		JFrame frame= createFrame();
371 		JMenuBar mb= new JMenuBar();
372 		createMenus(mb);
373 		frame.setJMenuBar(mb);
374 
375 		JLabel suiteLabel= new JLabel("Test class name:");
376 		fSuiteCombo= createSuiteCombo();
377 		fRun= createRunButton();
378 		frame.getRootPane().setDefaultButton(fRun);
379 		Component browseButton= createBrowseButton();
380 
381 		fUseLoadingRunner= createUseLoaderCheckBox();
382 
383 		fStatusLine= createStatusLine();
384 		if (inMac()) 
385 			fProgressIndicator= new MacProgressBar(fStatusLine); 
386 		else                                                            
387 		fProgressIndicator= new ProgressBar();
388 		fCounterPanel= createCounterPanel();
389 
390 		fFailures= new DefaultListModel();
391 
392 		fTestViewTab= createTestRunViews();
393 		JPanel failedPanel= createFailedPanel();
394 
395 		fFailureView= createFailureDetailView();
396 		JScrollPane tracePane= new JScrollPane(fFailureView.getComponent(), JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
397 
398 		
399 				
400 		fQuitButton= createQuitButton();
401 		fLogo= createLogo();
402 
403 		JPanel panel= new JPanel(new GridBagLayout());
404 
405 		addGrid(panel, suiteLabel,	0, 0, 2, GridBagConstraints.HORIZONTAL, 	1.0, GridBagConstraints.WEST);
406 		addGrid(panel, fSuiteCombo, 	0, 1, 1, GridBagConstraints.HORIZONTAL, 	1.0, GridBagConstraints.WEST);
407 		addGrid(panel, browseButton, 	1, 1, 1, GridBagConstraints.NONE, 			0.0, GridBagConstraints.WEST);
408 		addGrid(panel, fRun, 		2, 1, 1, GridBagConstraints.HORIZONTAL, 	0.0, GridBagConstraints.CENTER);
409 
410 		addGrid(panel, fUseLoadingRunner,  	0, 2, 3, GridBagConstraints.NONE, 1.0, GridBagConstraints.WEST);
411 		//addGrid(panel, new JSeparator(), 	0, 3, 3, GridBagConstraints.HORIZONTAL, 1.0, GridBagConstraints.WEST);
412 
413 
414 		addGrid(panel, fProgressIndicator, 	0, 3, 2, GridBagConstraints.HORIZONTAL, 	1.0, GridBagConstraints.WEST);
415 		addGrid(panel, fLogo, 			2, 3, 1, GridBagConstraints.NONE, 			0.0, GridBagConstraints.NORTH);
416 
417 		addGrid(panel, fCounterPanel,	 0, 4, 2, GridBagConstraints.NONE, 			0.0, GridBagConstraints.WEST);
418 		addGrid(panel, new JSeparator(), 	0, 5, 2, GridBagConstraints.HORIZONTAL, 1.0, GridBagConstraints.WEST);
419 		addGrid(panel, new JLabel("Results:"),	0, 6, 2, GridBagConstraints.HORIZONTAL, 	1.0, GridBagConstraints.WEST);
420 
421 		JSplitPane splitter= new JSplitPane(JSplitPane.VERTICAL_SPLIT, fTestViewTab, tracePane);
422 		addGrid(panel, splitter, 	 0, 7, 2, GridBagConstraints.BOTH, 			1.0, GridBagConstraints.WEST);
423 
424 		addGrid(panel, failedPanel, 	 2, 7, 1, GridBagConstraints.HORIZONTAL, 	0.0, GridBagConstraints.NORTH/*CENTER*/);
425 
426 		addGrid(panel, fStatusLine, 	 0, 9, 2, GridBagConstraints.HORIZONTAL, 	1.0, GridBagConstraints.CENTER);
427 		addGrid(panel, fQuitButton, 	 2, 9, 1, GridBagConstraints.HORIZONTAL, 	0.0, GridBagConstraints.CENTER);
428 
429 		frame.setContentPane(panel);
430 		frame.pack();
431 		frame.setLocation(200, 200);
432 		return frame;
433 	}
434 
435 	private void addGrid(JPanel p, Component co, int x, int y, int w, int fill, double wx, int anchor) {
436 		GridBagConstraints c= new GridBagConstraints();
437 		c.gridx= x; c.gridy= y;
438 		c.gridwidth= w;
439 		c.anchor= anchor;
440 		c.weightx= wx;
441 		c.fill= fill;
442 		if (fill == GridBagConstraints.BOTH || fill == GridBagConstraints.VERTICAL)
443 			c.weighty= 1.0;
444 		c.insets= new Insets(y == 0 ? 10 : 0, x == 0 ? 10 : GAP, GAP, GAP); 
445 		p.add(co, c);
446 	}
447 
448 	protected String getSuiteText() {
449 		if (fSuiteCombo == null)
450 			return "";
451 		return (String)fSuiteCombo.getEditor().getItem();
452 	}
453 
454 	public ListModel getFailures() {
455 		return fFailures;
456 	}
457 
458 	public void insertUpdate(DocumentEvent event) {
459 		textChanged();
460 	}
461 
462 	protected Object instanciateClass(String fullClassName, Object param) {
463 		try {
464 			Class clazz= Class.forName(fullClassName);                                               
465 			if (param == null) {
466 				return clazz.newInstance();
467 			} else {
468 				Class[] clazzParam= {param.getClass()};
469 				Constructor clazzConstructor= clazz.getConstructor(clazzParam);
470 				Object[] objectParam= {param};	
471 				return clazzConstructor.newInstance(objectParam);
472 			}
473 		} catch (Exception e) {
474 			e.printStackTrace();
475 		}
476 		return null;
477 	}
478 
479 	public void browseTestClasses() {
480 		TestCollector collector= createTestCollector();
481 		TestSelector selector= new TestSelector(fFrame, collector);
482 		if (selector.isEmpty()) {
483 			JOptionPane.showMessageDialog(fFrame, "No Test Cases found.\nCheck that the configured \'TestCollector\' is supported on this platform.");
484 			return;
485 		}
486 		selector.show();
487 		String className= selector.getSelectedItem();
488 		if (className != null)
489 			setSuite(className);
490 	}
491 
492 	TestCollector createTestCollector() {
493 		String className= BaseTestRunner.getPreference(TESTCOLLECTOR_KEY);
494 		if (className != null) {
495 			Class collectorClass= null;
496 			try {
497 				collectorClass= Class.forName(className);
498 				return (TestCollector)collectorClass.newInstance();
499 			} catch(Exception e) {
500 				JOptionPane.showMessageDialog(fFrame, "Could not create TestCollector - using default collector");
501 			}
502 		}
503 		return new SimpleTestCollector();
504 	}
505 
506 	private Image loadFrameIcon() {
507 		ImageIcon icon= (ImageIcon)getIconResource(BaseTestRunner.class, "smalllogo.gif");
508 		if (icon != null)
509 			return icon.getImage();
510 		return null;
511 	}
512 
513 	private void loadHistory(JComboBox combo) throws IOException {
514 		BufferedReader br= new BufferedReader(new FileReader(getSettingsFile()));
515 		int itemCount= 0;
516 		try {
517 			String line;
518 			while ((line= br.readLine()) != null) {
519 				combo.addItem(line);
520 				itemCount++;
521 			}
522 			if (itemCount > 0)
523 				combo.setSelectedIndex(0);
524 
525 		} finally {
526 			br.close();
527 		}
528 	}
529 
530 	private File getSettingsFile() {
531 	 	String home= System.getProperty("user.home");
532  		return new File(home,".junitsession");
533  	}
534 
535 	private void postInfo(final String message) {
536 		SwingUtilities.invokeLater(
537 			new Runnable() {
538 				public void run() {
539 					showInfo(message);
540 				}
541 			}
542 		);
543 	}
544 
545 	private void postStatus(final String status) {
546 		SwingUtilities.invokeLater(
547 			new Runnable() {
548 				public void run() {
549 					showStatus(status);
550 				}
551 			}
552 		);
553 	}
554 
555 	public void removeUpdate(DocumentEvent event) {
556 		textChanged();
557 	}
558 
559 	private void rerun() {
560 		TestRunView view= (TestRunView)fTestRunViews.elementAt(fTestViewTab.getSelectedIndex());
561 		Test rerunTest= view.getSelectedTest();
562 		if (rerunTest != null)
563 			rerunTest(rerunTest);
564 	}
565 
566 	private void rerunTest(Test test) {
567 		if (!(test instanceof TestCase)) {
568 			showInfo("Could not reload "+ test.toString());
569 			return;
570 		}
571 		Test reloadedTest= null;
572 		TestCase rerunTest= (TestCase)test;
573 
574 		try {
575 			Class reloadedTestClass= getLoader().reload(test.getClass()); 
576 			reloadedTest= TestSuite.createTest(reloadedTestClass, rerunTest.getName());
577 		} catch(Exception e) {
578 			showInfo("Could not reload "+ test.toString());
579 			return;
580 		}
581 		TestResult result= new TestResult();
582 		reloadedTest.run(result);
583 
584 		String message= reloadedTest.toString();
585 		if(result.wasSuccessful())
586 			showInfo(message+" was successful");
587 		else if (result.errorCount() == 1)
588 			showStatus(message+" had an error");
589 		else
590 			showStatus(message+" had a failure");
591 	}
592 
593 	protected void reset() {
594 		fCounterPanel.reset();
595 		fProgressIndicator.reset();
596 		fRerunButton.setEnabled(false);
597 		fFailureView.clear();
598 		fFailures.clear();
599 	}
600 
601 	protected void runFailed(String message) {
602 		showStatus(message);
603 		fRun.setText("Run");
604 		fRunner= null;
605 	}
606 
607 	synchronized public void runSuite() {
608 		if (fRunner != null) {
609 			fTestResult.stop();
610 		} else {
611 			setLoading(shouldReload());
612 			reset();
613 			showInfo("Load Test Case...");
614 			final String suiteName= getSuiteText();
615 			final Test testSuite= getTest(suiteName);
616 			if (testSuite != null) {
617 				addToHistory(suiteName);
618 				doRunTest(testSuite);
619 			}
620 		}
621 	}
622 
623 	private boolean shouldReload() {
624 		return !inVAJava() && fUseLoadingRunner.isSelected();
625 	}
626 
627 
628 	synchronized protected void runTest(final Test testSuite) {
629 		if (fRunner != null) {
630 			fTestResult.stop();
631 		} else {
632 			reset();
633 			if (testSuite != null) {
634 				doRunTest(testSuite);
635 			}
636 		}
637 	}
638 
639 	private void doRunTest(final Test testSuite) {
640 		setButtonLabel(fRun, "Stop");
641 		fRunner= new Thread("TestRunner-Thread") {
642 			public void run() {
643 				TestRunner.this.start(testSuite);
644 				postInfo("Running...");
645 
646 				long startTime= System.currentTimeMillis();
647 				testSuite.run(fTestResult);
648 
649 				if (fTestResult.shouldStop()) {
650 					postStatus("Stopped");
651 				} else {
652 					long endTime= System.currentTimeMillis();
653 					long runTime= endTime-startTime;
654 					postInfo("Finished: " + elapsedTimeAsString(runTime) + " seconds");
655 				}
656 				runFinished(testSuite);
657 				setButtonLabel(fRun, "Run");
658 				fRunner= null;
659 				System.gc();
660 			}
661 		};
662 		// make sure that the test result is created before we start the
663 		// test runner thread so that listeners can register for it.
664 		fTestResult= createTestResult();
665 		fTestResult.addListener(TestRunner.this);
666 		aboutToStart(testSuite);
667 
668 		fRunner.start();
669 	}
670 
671 	private void saveHistory() throws IOException {
672 		BufferedWriter bw= new BufferedWriter(new FileWriter(getSettingsFile()));
673 		try {
674 			for (int i= 0; i < fSuiteCombo.getItemCount(); i++) {
675 				String testsuite= fSuiteCombo.getItemAt(i).toString();
676 				bw.write(testsuite, 0, testsuite.length());
677 				bw.newLine();
678 			}
679 		} finally {
680 			bw.close();
681 		}
682 	}
683 
684 	private void setButtonLabel(final JButton button, final String label) {
685 		SwingUtilities.invokeLater(
686 			new Runnable() {
687 				public void run() {
688 					button.setText(label);
689 				}
690 			}
691 		);
692 	}
693 
694 	public void handleTestSelected(Test test) {
695 		fRerunButton.setEnabled(test != null && (test instanceof TestCase));
696 		showFailureDetail(test);
697 	}
698 
699 	private void showFailureDetail(Test test) {
700 		if (test != null) {
701 			ListModel failures= getFailures();
702 			for (int i= 0; i < failures.getSize(); i++) {
703 				TestFailure failure= (TestFailure)failures.getElementAt(i);
704 				if (failure.failedTest() == test) {
705 					fFailureView.showFailure(failure);
706 					return;
707 				}
708 			}
709 		}
710 		fFailureView.clear();
711 	}
712 
713 	private void showInfo(String message) {
714 		fStatusLine.showInfo(message);
715 	}
716 
717 	private void showStatus(String status) {
718 		fStatusLine.showError(status);
719 	}
720 
721 	/***
722 	 * Starts the TestRunner
723 	 */
724 	public void start(String[] args) {
725 		String suiteName= processArguments(args);
726 		fFrame= createUI(suiteName);
727 		fFrame.pack();
728 		fFrame.setVisible(true);
729 
730 		if (suiteName != null) {
731 			setSuite(suiteName);
732 			runSuite();
733 		}
734 	}
735 
736 	private void start(final Test test) {
737 		SwingUtilities.invokeLater(
738 			new Runnable() {
739 				public void run() {
740 					int total= test.countTestCases();
741 					fProgressIndicator.start(total);
742 					fCounterPanel.setTotal(total);
743 				}
744 			}
745 		);
746 	}
747 
748 	/***
749 	 * Wait until all the events are processed in the event thread
750 	 */
751 	private void synchUI() {
752 		try {
753 			SwingUtilities.invokeAndWait(
754 				new Runnable() {
755 					public void run() {}
756 				}
757 			);
758 		}
759 		catch (Exception e) {
760 		}
761 	}
762 
763 	/***
764 	 * Terminates the TestRunner
765 	 */
766 	public void terminate() {
767 		fFrame.dispose();
768 		try {
769 			saveHistory();
770 		} catch (IOException e) {
771 			System.out.println("Couldn't save test run history");
772 		}
773 		System.exit(0);
774 	}
775 
776 	public void textChanged() {
777 		fRun.setEnabled(getSuiteText().length() > 0);
778 		clearStatus();
779 	}
780 
781 	protected void clearStatus() {
782 		fStatusLine.clear();
783 	}
784 
785 	public static Icon getIconResource(Class clazz, String name) {
786 		URL url= clazz.getResource(name);
787 		if (url == null) {
788 			System.err.println("Warning: could not load \""+name+"\" icon");
789 			return null;
790 		}
791 		return new ImageIcon(url);
792 	}
793 
794 	private void about() {
795 		AboutDialog about= new AboutDialog(fFrame);
796 		about.show();
797 	}
798 }