aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBrett Chabot <brettchabot@google.com>2009-06-17 17:21:33 -0700
committerBrett Chabot <brettchabot@google.com>2009-06-17 17:21:33 -0700
commit58a8b0aba2dec5695628a2bf25a3fae42c2c3533 (patch)
tree7c3e88d9efbf280f4849710858b6a67c740920be /src
parentd550fde3ba6ac3e9b5abb87b7d722a4d30f8d57b (diff)
downloadjunit-58a8b0aba2dec5695628a2bf25a3fae42c2c3533.tar.gz
Add junit 3.8.2 source to external/junit.
Diffstat (limited to 'src')
-rw-r--r--src/META-INF/MANIFEST.MF4
-rw-r--r--src/junit/awtui/AboutDialog.java77
-rw-r--r--src/junit/awtui/Logo.java58
-rw-r--r--src/junit/awtui/ProgressBar.java88
-rw-r--r--src/junit/awtui/TestRunner.java571
-rw-r--r--src/junit/extensions/ActiveTestSuite.java66
-rw-r--r--src/junit/extensions/ExceptionTestCase.java46
-rw-r--r--src/junit/extensions/RepeatedTest.java32
-rw-r--r--src/junit/extensions/TestDecorator.java40
-rw-r--r--src/junit/extensions/TestSetup.java39
-rw-r--r--src/junit/framework/Assert.java289
-rw-r--r--src/junit/framework/AssertionFailedError.java15
-rw-r--r--src/junit/framework/ComparisonCompactor.java72
-rw-r--r--src/junit/framework/ComparisonFailure.java51
-rw-r--r--src/junit/framework/Protectable.java14
-rw-r--r--src/junit/framework/Test.java17
-rw-r--r--src/junit/framework/TestCase.java207
-rw-r--r--src/junit/framework/TestFailure.java57
-rw-r--r--src/junit/framework/TestListener.java23
-rw-r--r--src/junit/framework/TestResult.java166
-rw-r--r--src/junit/framework/TestSuite.java293
-rw-r--r--src/junit/runner/BaseTestRunner.java343
-rw-r--r--src/junit/runner/ClassPathTestCollector.java83
-rw-r--r--src/junit/runner/FailureDetailView.java23
-rw-r--r--src/junit/runner/LoadingTestCollector.java70
-rw-r--r--src/junit/runner/ReloadingTestSuiteLoader.java19
-rw-r--r--src/junit/runner/SimpleTestCollector.java20
-rw-r--r--src/junit/runner/Sorter.java36
-rw-r--r--src/junit/runner/StandardTestSuiteLoader.java19
-rw-r--r--src/junit/runner/TestCaseClassLoader.java240
-rw-r--r--src/junit/runner/TestCollector.java17
-rw-r--r--src/junit/runner/TestRunListener.java19
-rw-r--r--src/junit/runner/TestSuiteLoader.java9
-rw-r--r--src/junit/runner/Version.java18
-rw-r--r--src/junit/runner/excluded.properties13
-rw-r--r--src/junit/runner/logo.gifbin0 -> 964 bytes
-rw-r--r--src/junit/runner/smalllogo.gifbin0 -> 883 bytes
-rw-r--r--src/junit/swingui/AboutDialog.java93
-rw-r--r--src/junit/swingui/CounterPanel.java118
-rw-r--r--src/junit/swingui/DefaultFailureDetailView.java101
-rw-r--r--src/junit/swingui/FailureRunView.java122
-rw-r--r--src/junit/swingui/MacProgressBar.java20
-rw-r--r--src/junit/swingui/ProgressBar.java46
-rw-r--r--src/junit/swingui/StatusLine.java45
-rw-r--r--src/junit/swingui/TestHierarchyRunView.java77
-rw-r--r--src/junit/swingui/TestRunContext.java21
-rw-r--r--src/junit/swingui/TestRunView.java39
-rw-r--r--src/junit/swingui/TestRunner.java849
-rw-r--r--src/junit/swingui/TestSelector.java285
-rw-r--r--src/junit/swingui/TestSuitePanel.java172
-rw-r--r--src/junit/swingui/TestTreeModel.java190
-rw-r--r--src/junit/swingui/icons/error.gifbin0 -> 868 bytes
-rw-r--r--src/junit/swingui/icons/failure.gifbin0 -> 862 bytes
-rw-r--r--src/junit/swingui/icons/hierarchy.gifbin0 -> 891 bytes
-rw-r--r--src/junit/swingui/icons/ok.gifbin0 -> 85 bytes
-rw-r--r--src/junit/textui/ResultPrinter.java139
-rw-r--r--src/junit/textui/TestRunner.java207
57 files changed, 5678 insertions, 0 deletions
diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..e171a01
--- /dev/null
+++ b/src/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Ant-Version: Apache Ant 1.6.5
+Created-By: 1.4.2_10-b03 (Sun Microsystems Inc.)
+
diff --git a/src/junit/awtui/AboutDialog.java b/src/junit/awtui/AboutDialog.java
new file mode 100644
index 0000000..38f57bb
--- /dev/null
+++ b/src/junit/awtui/AboutDialog.java
@@ -0,0 +1,77 @@
+package junit.awtui;
+
+import java.awt.Button;
+import java.awt.Dialog;
+import java.awt.Font;
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Label;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+
+import junit.runner.Version;
+
+class AboutDialog extends Dialog {
+ public AboutDialog(Frame parent) {
+ super(parent);
+
+ setResizable(false);
+ setLayout(new GridBagLayout());
+ setSize(330, 138);
+ setTitle("About");
+
+ Button button= new Button("Close");
+ button.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ dispose();
+ }
+ }
+ );
+
+ Label label1= new Label("JUnit");
+ label1.setFont(new Font("dialog", Font.PLAIN, 36));
+
+ Label label2= new Label("JUnit "+Version.id()+ " by Kent Beck and Erich Gamma");
+ label2.setFont(new Font("dialog", Font.PLAIN, 14));
+
+ Logo logo= new Logo();
+
+ GridBagConstraints constraintsLabel1= new GridBagConstraints();
+ constraintsLabel1.gridx = 3; constraintsLabel1.gridy = 0;
+ constraintsLabel1.gridwidth = 1; constraintsLabel1.gridheight = 1;
+ constraintsLabel1.anchor = GridBagConstraints.CENTER;
+ add(label1, constraintsLabel1);
+
+ GridBagConstraints constraintsLabel2= new GridBagConstraints();
+ constraintsLabel2.gridx = 2; constraintsLabel2.gridy = 1;
+ constraintsLabel2.gridwidth = 2; constraintsLabel2.gridheight = 1;
+ constraintsLabel2.anchor = GridBagConstraints.CENTER;
+ add(label2, constraintsLabel2);
+
+ GridBagConstraints constraintsButton1= new GridBagConstraints();
+ constraintsButton1.gridx = 2; constraintsButton1.gridy = 2;
+ constraintsButton1.gridwidth = 2; constraintsButton1.gridheight = 1;
+ constraintsButton1.anchor = GridBagConstraints.CENTER;
+ constraintsButton1.insets= new Insets(8, 0, 8, 0);
+ add(button, constraintsButton1);
+
+ GridBagConstraints constraintsLogo1= new GridBagConstraints();
+ constraintsLogo1.gridx = 2; constraintsLogo1.gridy = 0;
+ constraintsLogo1.gridwidth = 1; constraintsLogo1.gridheight = 1;
+ constraintsLogo1.anchor = GridBagConstraints.CENTER;
+ add(logo, constraintsLogo1);
+
+ addWindowListener(
+ new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ dispose();
+ }
+ }
+ );
+ }
+} \ No newline at end of file
diff --git a/src/junit/awtui/Logo.java b/src/junit/awtui/Logo.java
new file mode 100644
index 0000000..4cc3b4f
--- /dev/null
+++ b/src/junit/awtui/Logo.java
@@ -0,0 +1,58 @@
+package junit.awtui;
+
+import java.awt.Canvas;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.MediaTracker;
+import java.awt.SystemColor;
+import java.awt.Toolkit;
+import java.awt.image.ImageProducer;
+import java.net.URL;
+
+import junit.runner.BaseTestRunner;
+
+public class Logo extends Canvas {
+ private Image fImage;
+ private int fWidth;
+ private int fHeight;
+
+ public Logo() {
+ fImage= loadImage("logo.gif");
+ MediaTracker tracker= new MediaTracker(this);
+ tracker.addImage(fImage, 0);
+ try {
+ tracker.waitForAll();
+ } catch (Exception e) {
+ }
+
+ if (fImage != null) {
+ fWidth= fImage.getWidth(this);
+ fHeight= fImage.getHeight(this);
+ } else {
+ fWidth= 20;
+ fHeight= 20;
+ }
+ setSize(fWidth, fHeight);
+ }
+
+ public Image loadImage(String name) {
+ Toolkit toolkit= Toolkit.getDefaultToolkit();
+ try {
+ URL url= BaseTestRunner.class.getResource(name);
+ return toolkit.createImage((ImageProducer) url.getContent());
+ } catch (Exception ex) {
+ }
+ return null;
+ }
+
+ public void paint(Graphics g) {
+ paintBackground(g);
+ if (fImage != null)
+ g.drawImage(fImage, 0, 0, fWidth, fHeight, this);
+ }
+
+ public void paintBackground( java.awt.Graphics g) {
+ g.setColor(SystemColor.control);
+ g.fillRect(0, 0, getBounds().width, getBounds().height);
+ }
+} \ No newline at end of file
diff --git a/src/junit/awtui/ProgressBar.java b/src/junit/awtui/ProgressBar.java
new file mode 100644
index 0000000..03e7ec2
--- /dev/null
+++ b/src/junit/awtui/ProgressBar.java
@@ -0,0 +1,88 @@
+package junit.awtui;
+
+import java.awt.Canvas;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.SystemColor;
+
+public class ProgressBar extends Canvas {
+ public boolean fError= false;
+ public int fTotal= 0;
+ public int fProgress= 0;
+ public int fProgressX= 0;
+
+ public ProgressBar() {
+ super();
+ setSize(20, 30);
+ }
+
+ private Color getStatusColor() {
+ if (fError)
+ return Color.red;
+ return Color.green;
+ }
+
+ public void paint(Graphics g) {
+ paintBackground(g);
+ paintStatus(g);
+ }
+
+ public void paintBackground(Graphics g) {
+ g.setColor(SystemColor.control);
+ Rectangle r= getBounds();
+ g.fillRect(0, 0, r.width, r.height);
+ g.setColor(Color.darkGray);
+ g.drawLine(0, 0, r.width-1, 0);
+ g.drawLine(0, 0, 0, r.height-1);
+ g.setColor(Color.white);
+ g.drawLine(r.width-1, 0, r.width-1, r.height-1);
+ g.drawLine(0, r.height-1, r.width-1, r.height-1);
+ }
+
+ public void paintStatus(Graphics g) {
+ g.setColor(getStatusColor());
+ Rectangle r= new Rectangle(0, 0, fProgressX, getBounds().height);
+ g.fillRect(1, 1, r.width-1, r.height-2);
+ }
+
+ private void paintStep(int startX, int endX) {
+ repaint(startX, 1, endX-startX, getBounds().height-2);
+ }
+
+ public void reset() {
+ fProgressX= 1;
+ fProgress= 0;
+ fError= false;
+ paint(getGraphics());
+ }
+
+ public int scale(int value) {
+ if (fTotal > 0)
+ return Math.max(1, value*(getBounds().width-1)/fTotal);
+ return value;
+ }
+
+ public void setBounds(int x, int y, int w, int h) {
+ super.setBounds(x, y, w, h);
+ fProgressX= scale(fProgress);
+ }
+
+ public void start(int total) {
+ fTotal= total;
+ reset();
+ }
+
+ public void step(boolean successful) {
+ fProgress++;
+ int x= fProgressX;
+
+ fProgressX= scale(fProgress);
+
+ if (!fError && !successful) {
+ fError= true;
+ x= 1;
+ }
+ paintStep(x, fProgressX);
+ }
+} \ No newline at end of file
diff --git a/src/junit/awtui/TestRunner.java b/src/junit/awtui/TestRunner.java
new file mode 100644
index 0000000..cb735d5
--- /dev/null
+++ b/src/junit/awtui/TestRunner.java
@@ -0,0 +1,571 @@
+package junit.awtui;
+
+import java.awt.BorderLayout;
+import java.awt.Button;
+import java.awt.Checkbox;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.GridLayout;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.Label;
+import java.awt.List;
+import java.awt.Menu;
+import java.awt.MenuBar;
+import java.awt.MenuItem;
+import java.awt.Panel;
+import java.awt.SystemColor;
+import java.awt.TextArea;
+import java.awt.TextField;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.TextEvent;
+import java.awt.event.TextListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.image.ImageProducer;
+import java.util.Vector;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+import junit.runner.BaseTestRunner;
+import junit.runner.TestRunListener;
+
+/**
+ * An AWT based user interface to run tests.
+ * Enter the name of a class which either provides a static
+ * suite method or is a subclass of TestCase.
+ * <pre>
+ * Synopsis: java junit.awtui.TestRunner [-noloading] [TestCase]
+ * </pre>
+ * TestRunner takes as an optional argument the name of the testcase class to be run.
+ */
+ public class TestRunner extends BaseTestRunner {
+ protected Frame fFrame;
+ protected Vector fExceptions;
+ protected Vector fFailedTests;
+ protected Thread fRunner;
+ protected TestResult fTestResult;
+
+ protected TextArea fTraceArea;
+ protected TextField fSuiteField;
+ protected Button fRun;
+ protected ProgressBar fProgressIndicator;
+ protected List fFailureList;
+ protected Logo fLogo;
+ protected Label fNumberOfErrors;
+ protected Label fNumberOfFailures;
+ protected Label fNumberOfRuns;
+ protected Button fQuitButton;
+ protected Button fRerunButton;
+ protected TextField fStatusLine;
+ protected Checkbox fUseLoadingRunner;
+
+ protected static final Font PLAIN_FONT= new Font("dialog", Font.PLAIN, 12);
+ private static final int GAP= 4;
+
+ public TestRunner() {
+ }
+
+ private void about() {
+ AboutDialog about= new AboutDialog(fFrame);
+ about.setModal(true);
+ about.setLocation(300, 300);
+ about.setVisible(true);
+ }
+
+ public void testStarted(String testName) {
+ showInfo("Running: "+testName);
+ }
+
+ public void testEnded(String testName) {
+ setLabelValue(fNumberOfRuns, fTestResult.runCount());
+ synchronized(this) {
+ fProgressIndicator.step(fTestResult.wasSuccessful());
+ }
+ }
+
+ public void testFailed(int status, Test test, Throwable t) {
+ switch (status) {
+ case TestRunListener.STATUS_ERROR:
+ fNumberOfErrors.setText(Integer.toString(fTestResult.errorCount()));
+ appendFailure("Error", test, t);
+ break;
+ case TestRunListener.STATUS_FAILURE:
+ fNumberOfFailures.setText(Integer.toString(fTestResult.failureCount()));
+ appendFailure("Failure", test, t);
+ break;
+ }
+ }
+
+ protected void addGrid(Panel p, Component co, int x, int y, int w, int fill, double wx, int anchor) {
+ GridBagConstraints c= new GridBagConstraints();
+ c.gridx= x; c.gridy= y;
+ c.gridwidth= w;
+ c.anchor= anchor;
+ c.weightx= wx;
+ c.fill= fill;
+ if (fill == GridBagConstraints.BOTH || fill == GridBagConstraints.VERTICAL)
+ c.weighty= 1.0;
+ c.insets= new Insets(y == 0 ? GAP : 0, x == 0 ? GAP : 0, GAP, GAP);
+ p.add(co, c);
+ }
+
+ private void appendFailure(String kind, Test test, Throwable t) {
+ kind+= ": " + test;
+ String msg= t.getMessage();
+ if (msg != null) {
+ kind+= ":" + truncate(msg);
+ }
+ fFailureList.add(kind);
+ fExceptions.addElement(t);
+ fFailedTests.addElement(test);
+ if (fFailureList.getItemCount() == 1) {
+ fFailureList.select(0);
+ failureSelected();
+ }
+ }
+ /**
+ * Creates the JUnit menu. Clients override this
+ * method to add additional menu items.
+ */
+ protected Menu createJUnitMenu() {
+ Menu menu= new Menu("JUnit");
+ MenuItem mi= new MenuItem("About...");
+ mi.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ about();
+ }
+ }
+ );
+ menu.add(mi);
+
+ menu.addSeparator();
+ mi= new MenuItem("Exit");
+ mi.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ System.exit(0);
+ }
+ }
+ );
+ menu.add(mi);
+ return menu;
+ }
+
+ protected void createMenus(MenuBar mb) {
+ mb.add(createJUnitMenu());
+ }
+ protected TestResult createTestResult() {
+ return new TestResult();
+ }
+
+ protected Frame createUI(String suiteName) {
+ Frame frame= new Frame("JUnit");
+ Image icon= loadFrameIcon();
+ if (icon != null)
+ frame.setIconImage(icon);
+
+ frame.setLayout(new BorderLayout(0, 0));
+ frame.setBackground(SystemColor.control);
+ final Frame finalFrame= frame;
+
+ frame.addWindowListener(
+ new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ finalFrame.dispose();
+ System.exit(0);
+ }
+ }
+ );
+
+ MenuBar mb = new MenuBar();
+ createMenus(mb);
+ frame.setMenuBar(mb);
+
+ //---- first section
+ Label suiteLabel= new Label("Test class name:");
+
+ fSuiteField= new TextField(suiteName != null ? suiteName : "");
+ fSuiteField.selectAll();
+ fSuiteField.requestFocus();
+ fSuiteField.setFont(PLAIN_FONT);
+ fSuiteField.setColumns(40);
+ fSuiteField.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ runSuite();
+ }
+ }
+ );
+ fSuiteField.addTextListener(
+ new TextListener() {
+ public void textValueChanged(TextEvent e) {
+ fRun.setEnabled(fSuiteField.getText().length() > 0);
+ fStatusLine.setText("");
+ }
+ }
+ );
+ fRun= new Button("Run");
+ fRun.setEnabled(false);
+ fRun.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ runSuite();
+ }
+ }
+ );
+ boolean useLoader= useReloadingTestSuiteLoader();
+ fUseLoadingRunner= new Checkbox("Reload classes every run", useLoader);
+ if (inVAJava())
+ fUseLoadingRunner.setVisible(false);
+
+ //---- second section
+ fProgressIndicator= new ProgressBar();
+
+ //---- third section
+ fNumberOfErrors= new Label("0000", Label.RIGHT);
+ fNumberOfErrors.setText("0");
+ fNumberOfErrors.setFont(PLAIN_FONT);
+
+ fNumberOfFailures= new Label("0000", Label.RIGHT);
+ fNumberOfFailures.setText("0");
+ fNumberOfFailures.setFont(PLAIN_FONT);
+
+ fNumberOfRuns= new Label("0000", Label.RIGHT);
+ fNumberOfRuns.setText("0");
+ fNumberOfRuns.setFont(PLAIN_FONT);
+
+ Panel numbersPanel= createCounterPanel();
+
+ //---- fourth section
+ Label failureLabel= new Label("Errors and Failures:");
+
+ fFailureList= new List(5);
+ fFailureList.addItemListener(
+ new ItemListener() {
+ public void itemStateChanged(ItemEvent e) {
+ failureSelected();
+ }
+ }
+ );
+ fRerunButton= new Button("Run");
+ fRerunButton.setEnabled(false);
+ fRerunButton.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ rerun();
+ }
+ }
+ );
+
+ Panel failedPanel= new Panel(new GridLayout(0, 1, 0, 2));
+ failedPanel.add(fRerunButton);
+
+ fTraceArea= new TextArea();
+ fTraceArea.setRows(5);
+ fTraceArea.setColumns(60);
+
+ //---- fifth section
+ fStatusLine= new TextField();
+ fStatusLine.setFont(PLAIN_FONT);
+ fStatusLine.setEditable(false);
+ fStatusLine.setForeground(Color.red);
+
+ fQuitButton= new Button("Exit");
+ fQuitButton.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ System.exit(0);
+ }
+ }
+ );
+
+ // ---------
+ fLogo= new Logo();
+
+ //---- overall layout
+ Panel panel= new Panel(new GridBagLayout());
+
+ addGrid(panel, suiteLabel, 0, 0, 2, GridBagConstraints.HORIZONTAL, 1.0, GridBagConstraints.WEST);
+
+ addGrid(panel, fSuiteField, 0, 1, 2, GridBagConstraints.HORIZONTAL, 1.0, GridBagConstraints.WEST);
+ addGrid(panel, fRun, 2, 1, 1, GridBagConstraints.HORIZONTAL, 0.0, GridBagConstraints.CENTER);
+ addGrid(panel, fUseLoadingRunner, 0, 2, 2, GridBagConstraints.NONE, 1.0, GridBagConstraints.WEST);
+ addGrid(panel, fProgressIndicator, 0, 3, 2, GridBagConstraints.HORIZONTAL, 1.0, GridBagConstraints.WEST);
+ addGrid(panel, fLogo, 2, 3, 1, GridBagConstraints.NONE, 0.0, GridBagConstraints.NORTH);
+
+ addGrid(panel, numbersPanel, 0, 4, 2, GridBagConstraints.NONE, 0.0, GridBagConstraints.WEST);
+
+ addGrid(panel, failureLabel, 0, 5, 2, GridBagConstraints.HORIZONTAL, 1.0, GridBagConstraints.WEST);
+ addGrid(panel, fFailureList, 0, 6, 2, GridBagConstraints.BOTH, 1.0, GridBagConstraints.WEST);
+ addGrid(panel, failedPanel, 2, 6, 1, GridBagConstraints.HORIZONTAL, 0.0, GridBagConstraints.CENTER);
+ addGrid(panel, fTraceArea, 0, 7, 2, GridBagConstraints.BOTH, 1.0, GridBagConstraints.WEST);
+
+ addGrid(panel, fStatusLine, 0, 8, 2, GridBagConstraints.HORIZONTAL, 1.0, GridBagConstraints.CENTER);
+ addGrid(panel, fQuitButton, 2, 8, 1, GridBagConstraints.HORIZONTAL, 0.0, GridBagConstraints.CENTER);
+
+ frame.add(panel, BorderLayout.CENTER);
+ frame.pack();
+ return frame;
+ }
+
+ protected Panel createCounterPanel() {
+ Panel numbersPanel= new Panel(new GridBagLayout());
+ addToCounterPanel(
+ numbersPanel,
+ new Label("Runs:"),
+ 0, 0, 1, 1, 0.0, 0.0,
+ GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0)
+ );
+ addToCounterPanel(
+ numbersPanel,
+ fNumberOfRuns,
+ 1, 0, 1, 1, 0.33, 0.0,
+ GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 8, 0, 40)
+ );
+ addToCounterPanel(
+ numbersPanel,
+ new Label("Errors:"),
+ 2, 0, 1, 1, 0.0, 0.0,
+ GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 8, 0, 0)
+ );
+ addToCounterPanel(
+ numbersPanel,
+ fNumberOfErrors,
+ 3, 0, 1, 1, 0.33, 0.0,
+ GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 8, 0, 40)
+ );
+ addToCounterPanel(
+ numbersPanel,
+ new Label("Failures:"),
+ 4, 0, 1, 1, 0.0, 0.0,
+ GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 8, 0, 0)
+ );
+ addToCounterPanel(
+ numbersPanel,
+ fNumberOfFailures,
+ 5, 0, 1, 1, 0.33, 0.0,
+ GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 8, 0, 0)
+ );
+ return numbersPanel;
+ }
+
+ private void addToCounterPanel(Panel counter, Component comp,
+ int gridx, int gridy, int gridwidth, int gridheight,
+ double weightx, double weighty,
+ int anchor, int fill,
+ Insets insets) {
+
+ GridBagConstraints constraints= new GridBagConstraints();
+ constraints.gridx= gridx;
+ constraints.gridy= gridy;
+ constraints.gridwidth= gridwidth;
+ constraints.gridheight= gridheight;
+ constraints.weightx= weightx;
+ constraints.weighty= weighty;
+ constraints.anchor= anchor;
+ constraints.fill= fill;
+ constraints.insets= insets;
+ counter.add(comp, constraints);
+ }
+
+
+ public void failureSelected() {
+ fRerunButton.setEnabled(isErrorSelected());
+ showErrorTrace();
+ }
+
+ private boolean isErrorSelected() {
+ return fFailureList.getSelectedIndex() != -1;
+ }
+
+ private Image loadFrameIcon() {
+ Toolkit toolkit= Toolkit.getDefaultToolkit();
+ try {
+ java.net.URL url= BaseTestRunner.class.getResource("smalllogo.gif");
+ return toolkit.createImage((ImageProducer) url.getContent());
+ } catch (Exception ex) {
+ }
+ return null;
+ }
+
+ public Thread getRunner() {
+ return fRunner;
+ }
+
+ public static void main(String[] args) {
+ new TestRunner().start(args);
+ }
+
+ public static void run(Class test) {
+ String args[]= { test.getName() };
+ main(args);
+ }
+
+ public void rerun() {
+ int index= fFailureList.getSelectedIndex();
+ if (index == -1)
+ return;
+
+ Test test= (Test)fFailedTests.elementAt(index);
+ rerunTest(test);
+ }
+
+ private void rerunTest(Test test) {
+ if (!(test instanceof TestCase)) {
+ showInfo("Could not reload "+ test.toString());
+ return;
+ }
+ Test reloadedTest= null;
+ TestCase rerunTest= (TestCase)test;
+ try {
+ Class reloadedTestClass= getLoader().reload(test.getClass());
+ reloadedTest= TestSuite.createTest(reloadedTestClass, rerunTest.getName());
+ } catch(Exception e) {
+ showInfo("Could not reload "+ test.toString());
+ return;
+ }
+ TestResult result= new TestResult();
+ reloadedTest.run(result);
+
+ String message= reloadedTest.toString();
+ if(result.wasSuccessful())
+ showInfo(message+" was successful");
+ else if (result.errorCount() == 1)
+ showStatus(message+" had an error");
+ else
+ showStatus(message+" had a failure");
+ }
+
+ protected void reset() {
+ setLabelValue(fNumberOfErrors, 0);
+ setLabelValue(fNumberOfFailures, 0);
+ setLabelValue(fNumberOfRuns, 0);
+ fProgressIndicator.reset();
+ fRerunButton.setEnabled(false);
+ fFailureList.removeAll();
+ fExceptions= new Vector(10);
+ fFailedTests= new Vector(10);
+ fTraceArea.setText("");
+
+ }
+
+ protected void runFailed(String message) {
+ showStatus(message);
+ fRun.setLabel("Run");
+ fRunner= null;
+ }
+
+ synchronized public void runSuite() {
+ if (fRunner != null && fTestResult != null) {
+ fTestResult.stop();
+ } else {
+ setLoading(shouldReload());
+ fRun.setLabel("Stop");
+ showInfo("Initializing...");
+ reset();
+
+ showInfo("Load Test Case...");
+
+ final Test testSuite= getTest(fSuiteField.getText());
+ if (testSuite != null) {
+ fRunner= new Thread() {
+ public void run() {
+ fTestResult= createTestResult();
+ fTestResult.addListener(TestRunner.this);
+ fProgressIndicator.start(testSuite.countTestCases());
+ showInfo("Running...");
+
+ long startTime= System.currentTimeMillis();
+ testSuite.run(fTestResult);
+
+ if (fTestResult.shouldStop()) {
+ showStatus("Stopped");
+ } else {
+ long endTime= System.currentTimeMillis();
+ long runTime= endTime-startTime;
+ showInfo("Finished: " + elapsedTimeAsString(runTime) + " seconds");
+ }
+ fTestResult= null;
+ fRun.setLabel("Run");
+ fRunner= null;
+ System.gc();
+ }
+ };
+ fRunner.start();
+ }
+ }
+ }
+
+ private boolean shouldReload() {
+ return !inVAJava() && fUseLoadingRunner.getState();
+ }
+
+ private void setLabelValue(Label label, int value) {
+ label.setText(Integer.toString(value));
+ label.invalidate();
+ label.getParent().validate();
+
+ }
+
+ public void setSuiteName(String suite) {
+ fSuiteField.setText(suite);
+ }
+
+ private void showErrorTrace() {
+ int index= fFailureList.getSelectedIndex();
+ if (index == -1)
+ return;
+
+ Throwable t= (Throwable) fExceptions.elementAt(index);
+ fTraceArea.setText(getFilteredTrace(t));
+ }
+
+
+ private void showInfo(String message) {
+ fStatusLine.setFont(PLAIN_FONT);
+ fStatusLine.setForeground(Color.black);
+ fStatusLine.setText(message);
+ }
+
+ protected void clearStatus() {
+ showStatus("");
+ }
+
+ private void showStatus(String status) {
+ fStatusLine.setFont(PLAIN_FONT);
+ fStatusLine.setForeground(Color.red);
+ fStatusLine.setText(status);
+ }
+ /**
+ * Starts the TestRunner
+ */
+ public void start(String[] args) {
+ String suiteName= processArguments(args);
+ fFrame= createUI(suiteName);
+ fFrame.setLocation(200, 200);
+ fFrame.setVisible(true);
+
+ if (suiteName != null) {
+ setSuiteName(suiteName);
+ runSuite();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/junit/extensions/ActiveTestSuite.java b/src/junit/extensions/ActiveTestSuite.java
new file mode 100644
index 0000000..9fc4edd
--- /dev/null
+++ b/src/junit/extensions/ActiveTestSuite.java
@@ -0,0 +1,66 @@
+package junit.extensions;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+/**
+ * A TestSuite for active Tests. It runs each
+ * test in a separate thread and waits until all
+ * threads have terminated.
+ * -- Aarhus Radisson Scandinavian Center 11th floor
+ */
+public class ActiveTestSuite extends TestSuite {
+ private volatile int fActiveTestDeathCount;
+
+ public ActiveTestSuite() {
+ }
+
+ public ActiveTestSuite(Class theClass) {
+ super(theClass);
+ }
+
+ public ActiveTestSuite(String name) {
+ super (name);
+ }
+
+ public ActiveTestSuite(Class theClass, String name) {
+ super(theClass, name);
+ }
+
+ public void run(TestResult result) {
+ fActiveTestDeathCount= 0;
+ super.run(result);
+ waitUntilFinished();
+ }
+
+ public void runTest(final Test test, final TestResult result) {
+ Thread t= new Thread() {
+ public void run() {
+ try {
+ // inlined due to limitation in VA/Java
+ //ActiveTestSuite.super.runTest(test, result);
+ test.run(result);
+ } finally {
+ ActiveTestSuite.this.runFinished();
+ }
+ }
+ };
+ t.start();
+ }
+
+ synchronized void waitUntilFinished() {
+ while (fActiveTestDeathCount < testCount()) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ return; // ignore
+ }
+ }
+ }
+
+ synchronized public void runFinished() {
+ fActiveTestDeathCount++;
+ notifyAll();
+ }
+} \ No newline at end of file
diff --git a/src/junit/extensions/ExceptionTestCase.java b/src/junit/extensions/ExceptionTestCase.java
new file mode 100644
index 0000000..7004085
--- /dev/null
+++ b/src/junit/extensions/ExceptionTestCase.java
@@ -0,0 +1,46 @@
+package junit.extensions;
+
+import junit.framework.TestCase;
+
+/**
+ * A TestCase that expects an Exception of class fExpected to be thrown.
+ * The other way to check that an expected exception is thrown is:
+ * <pre>
+ * try {
+ * shouldThrow();
+ * }
+ * catch (SpecialException e) {
+ * return;
+ * }
+ * fail("Expected SpecialException");
+ * </pre>
+ *
+ * To use ExceptionTestCase, create a TestCase like:
+ * <pre>
+ * new ExceptionTestCase("testShouldThrow", SpecialException.class);
+ * </pre>
+ */
+public class ExceptionTestCase extends TestCase {
+ Class fExpected;
+
+ public ExceptionTestCase(String name, Class exception) {
+ super(name);
+ fExpected= exception;
+ }
+ /**
+ * Execute the test method expecting that an Exception of
+ * class fExpected or one of its subclasses will be thrown
+ */
+ protected void runTest() throws Throwable {
+ try {
+ super.runTest();
+ }
+ catch (Exception e) {
+ if (fExpected.isAssignableFrom(e.getClass()))
+ return;
+ else
+ throw e;
+ }
+ fail("Expected exception " + fExpected);
+ }
+} \ No newline at end of file
diff --git a/src/junit/extensions/RepeatedTest.java b/src/junit/extensions/RepeatedTest.java
new file mode 100644
index 0000000..be5c439
--- /dev/null
+++ b/src/junit/extensions/RepeatedTest.java
@@ -0,0 +1,32 @@
+package junit.extensions;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+/**
+ * A Decorator that runs a test repeatedly.
+ *
+ */
+public class RepeatedTest extends TestDecorator {
+ private int fTimesRepeat;
+
+ public RepeatedTest(Test test, int repeat) {
+ super(test);
+ if (repeat < 0)
+ throw new IllegalArgumentException("Repetition count must be > 0");
+ fTimesRepeat= repeat;
+ }
+ public int countTestCases() {
+ return super.countTestCases()*fTimesRepeat;
+ }
+ public void run(TestResult result) {
+ for (int i= 0; i < fTimesRepeat; i++) {
+ if (result.shouldStop())
+ break;
+ super.run(result);
+ }
+ }
+ public String toString() {
+ return super.toString()+"(repeated)";
+ }
+} \ No newline at end of file
diff --git a/src/junit/extensions/TestDecorator.java b/src/junit/extensions/TestDecorator.java
new file mode 100644
index 0000000..2111e8f
--- /dev/null
+++ b/src/junit/extensions/TestDecorator.java
@@ -0,0 +1,40 @@
+package junit.extensions;
+
+import junit.framework.Assert;
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+/**
+ * A Decorator for Tests. Use TestDecorator as the base class
+ * for defining new test decorators. Test decorator subclasses
+ * can be introduced to add behaviour before or after a test
+ * is run.
+ *
+ */
+public class TestDecorator extends Assert implements Test {
+ protected Test fTest;
+
+ public TestDecorator(Test test) {
+ fTest= test;
+ }
+ /**
+ * The basic run behaviour.
+ */
+ public void basicRun(TestResult result) {
+ fTest.run(result);
+ }
+ public int countTestCases() {
+ return fTest.countTestCases();
+ }
+ public void run(TestResult result) {
+ basicRun(result);
+ }
+
+ public String toString() {
+ return fTest.toString();
+ }
+
+ public Test getTest() {
+ return fTest;
+ }
+} \ No newline at end of file
diff --git a/src/junit/extensions/TestSetup.java b/src/junit/extensions/TestSetup.java
new file mode 100644
index 0000000..9ee8b05
--- /dev/null
+++ b/src/junit/extensions/TestSetup.java
@@ -0,0 +1,39 @@
+package junit.extensions;
+
+import junit.framework.Protectable;
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+/**
+ * A Decorator to set up and tear down additional fixture state.
+ * Subclass TestSetup and insert it into your tests when you want
+ * to set up additional state once before the tests are run.
+ */
+public class TestSetup extends TestDecorator {
+
+ public TestSetup(Test test) {
+ super(test);
+ }
+ public void run(final TestResult result) {
+ Protectable p= new Protectable() {
+ public void protect() throws Exception {
+ setUp();
+ basicRun(result);
+ tearDown();
+ }
+ };
+ result.runProtected(this, p);
+ }
+ /**
+ * Sets up the fixture. Override to set up additional fixture
+ * state.
+ */
+ protected void setUp() throws Exception {
+ }
+ /**
+ * Tears down the fixture. Override to tear down the additional
+ * fixture state.
+ */
+ protected void tearDown() throws Exception {
+ }
+} \ No newline at end of file
diff --git a/src/junit/framework/Assert.java b/src/junit/framework/Assert.java
new file mode 100644
index 0000000..ec6e838
--- /dev/null
+++ b/src/junit/framework/Assert.java
@@ -0,0 +1,289 @@
+package junit.framework;
+
+/**
+ * A set of assert methods. Messages are only displayed when an assert fails.
+ */
+
+public class Assert {
+ /**
+ * Protect constructor since it is a static only class
+ */
+ protected Assert() {
+ }
+
+ /**
+ * Asserts that a condition is true. If it isn't it throws
+ * an AssertionFailedError with the given message.
+ */
+ static public void assertTrue(String message, boolean condition) {
+ if (!condition)
+ fail(message);
+ }
+ /**
+ * Asserts that a condition is true. If it isn't it throws
+ * an AssertionFailedError.
+ */
+ static public void assertTrue(boolean condition) {
+ assertTrue(null, condition);
+ }
+ /**
+ * Asserts that a condition is false. If it isn't it throws
+ * an AssertionFailedError with the given message.
+ */
+ static public void assertFalse(String message, boolean condition) {
+ assertTrue(message, !condition);
+ }
+ /**
+ * Asserts that a condition is false. If it isn't it throws
+ * an AssertionFailedError.
+ */
+ static public void assertFalse(boolean condition) {
+ assertFalse(null, condition);
+ }
+ /**
+ * Fails a test with the given message.
+ */
+ static public void fail(String message) {
+ throw new AssertionFailedError(message);
+ }
+ /**
+ * Fails a test with no message.
+ */
+ static public void fail() {
+ fail(null);
+ }
+ /**
+ * Asserts that two objects are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, Object expected, Object actual) {
+ if (expected == null && actual == null)
+ return;
+ if (expected != null && expected.equals(actual))
+ return;
+ failNotEquals(message, expected, actual);
+ }
+ /**
+ * Asserts that two objects are equal. If they are not
+ * an AssertionFailedError is thrown.
+ */
+ static public void assertEquals(Object expected, Object actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two Strings are equal.
+ */
+ static public void assertEquals(String message, String expected, String actual) {
+ if (expected == null && actual == null)
+ return;
+ if (expected != null && expected.equals(actual))
+ return;
+ throw new ComparisonFailure(message, expected, actual);
+ }
+ /**
+ * Asserts that two Strings are equal.
+ */
+ static public void assertEquals(String expected, String actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two doubles are equal concerning a delta. If they are not
+ * an AssertionFailedError is thrown with the given message. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(String message, double expected, double actual, double delta) {
+ if (Double.compare(expected, actual) == 0)
+ return;
+ if (!(Math.abs(expected-actual) <= delta))
+ failNotEquals(message, new Double(expected), new Double(actual));
+ }
+ /**
+ * Asserts that two doubles are equal concerning a delta. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(double expected, double actual, double delta) {
+ assertEquals(null, expected, actual, delta);
+ }
+ /**
+ * Asserts that two floats are equal concerning a delta. If they are not
+ * an AssertionFailedError is thrown with the given message. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(String message, float expected, float actual, float delta) {
+ // handle infinity specially since subtracting to infinite values gives NaN and the
+ // the following test fails
+ if (Float.isInfinite(expected)) {
+ if (!(expected == actual))
+ failNotEquals(message, new Float(expected), new Float(actual));
+ } else if (!(Math.abs(expected-actual) <= delta))
+ failNotEquals(message, new Float(expected), new Float(actual));
+ }
+ /**
+ * Asserts that two floats are equal concerning a delta. If the expected
+ * value is infinity then the delta value is ignored.
+ */
+ static public void assertEquals(float expected, float actual, float delta) {
+ assertEquals(null, expected, actual, delta);
+ }
+ /**
+ * Asserts that two longs are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, long expected, long actual) {
+ assertEquals(message, new Long(expected), new Long(actual));
+ }
+ /**
+ * Asserts that two longs are equal.
+ */
+ static public void assertEquals(long expected, long actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two booleans are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, boolean expected, boolean actual) {
+ assertEquals(message, Boolean.valueOf(expected), Boolean.valueOf(actual));
+ }
+ /**
+ * Asserts that two booleans are equal.
+ */
+ static public void assertEquals(boolean expected, boolean actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two bytes are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, byte expected, byte actual) {
+ assertEquals(message, new Byte(expected), new Byte(actual));
+ }
+ /**
+ * Asserts that two bytes are equal.
+ */
+ static public void assertEquals(byte expected, byte actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two chars are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, char expected, char actual) {
+ assertEquals(message, new Character(expected), new Character(actual));
+ }
+ /**
+ * Asserts that two chars are equal.
+ */
+ static public void assertEquals(char expected, char actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two shorts are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, short expected, short actual) {
+ assertEquals(message, new Short(expected), new Short(actual));
+ }
+ /**
+ * Asserts that two shorts are equal.
+ */
+ static public void assertEquals(short expected, short actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that two ints are equal. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertEquals(String message, int expected, int actual) {
+ assertEquals(message, new Integer(expected), new Integer(actual));
+ }
+ /**
+ * Asserts that two ints are equal.
+ */
+ static public void assertEquals(int expected, int actual) {
+ assertEquals(null, expected, actual);
+ }
+ /**
+ * Asserts that an object isn't null.
+ */
+ static public void assertNotNull(Object object) {
+ assertNotNull(null, object);
+ }
+ /**
+ * Asserts that an object isn't null. If it is
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertNotNull(String message, Object object) {
+ assertTrue(message, object != null);
+ }
+ /**
+ * Asserts that an object is null.
+ */
+ static public void assertNull(Object object) {
+ assertNull(null, object);
+ }
+ /**
+ * Asserts that an object is null. If it is not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertNull(String message, Object object) {
+ assertTrue(message, object == null);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * an AssertionFailedError is thrown with the given message.
+ */
+ static public void assertSame(String message, Object expected, Object actual) {
+ if (expected == actual)
+ return;
+ failNotSame(message, expected, actual);
+ }
+ /**
+ * Asserts that two objects refer to the same object. If they are not
+ * the same an AssertionFailedError is thrown.
+ */
+ static public void assertSame(Object expected, Object actual) {
+ assertSame(null, expected, actual);
+ }
+ /**
+ * Asserts that two objects do not refer to the same object. If they do
+ * refer to the same object an AssertionFailedError is thrown with the
+ * given message.
+ */
+ static public void assertNotSame(String message, Object expected, Object actual) {
+ if (expected == actual)
+ failSame(message);
+ }
+ /**
+ * Asserts that two objects do not refer to the same object. If they do
+ * refer to the same object an AssertionFailedError is thrown.
+ */
+ static public void assertNotSame(Object expected, Object actual) {
+ assertNotSame(null, expected, actual);
+ }
+
+ static public void failSame(String message) {
+ String formatted= "";
+ if (message != null)
+ formatted= message+" ";
+ fail(formatted+"expected not same");
+ }
+
+ static public void failNotSame(String message, Object expected, Object actual) {
+ String formatted= "";
+ if (message != null)
+ formatted= message+" ";
+ fail(formatted+"expected same:<"+expected+"> was not:<"+actual+">");
+ }
+
+ static public void failNotEquals(String message, Object expected, Object actual) {
+ fail(format(message, expected, actual));
+ }
+
+ static String format(String message, Object expected, Object actual) {
+ String formatted= "";
+ if (message != null)
+ formatted= message+" ";
+ return formatted+"expected:<"+expected+"> but was:<"+actual+">";
+ }
+}
diff --git a/src/junit/framework/AssertionFailedError.java b/src/junit/framework/AssertionFailedError.java
new file mode 100644
index 0000000..b041f06
--- /dev/null
+++ b/src/junit/framework/AssertionFailedError.java
@@ -0,0 +1,15 @@
+package junit.framework;
+
+/**
+ * Thrown when an assertion failed.
+ */
+public class AssertionFailedError extends Error {
+
+ private static final long serialVersionUID= 1L;
+
+ public AssertionFailedError () {
+ }
+ public AssertionFailedError (String message) {
+ super (message);
+ }
+} \ No newline at end of file
diff --git a/src/junit/framework/ComparisonCompactor.java b/src/junit/framework/ComparisonCompactor.java
new file mode 100644
index 0000000..bbc3ba1
--- /dev/null
+++ b/src/junit/framework/ComparisonCompactor.java
@@ -0,0 +1,72 @@
+package junit.framework;
+
+public class ComparisonCompactor {
+
+ private static final String ELLIPSIS= "...";
+ private static final String DELTA_END= "]";
+ private static final String DELTA_START= "[";
+
+ private int fContextLength;
+ private String fExpected;
+ private String fActual;
+ private int fPrefix;
+ private int fSuffix;
+
+ public ComparisonCompactor(int contextLength, String expected, String actual) {
+ fContextLength= contextLength;
+ fExpected= expected;
+ fActual= actual;
+ }
+
+ public String compact(String message) {
+ if (fExpected == null || fActual == null || areStringsEqual())
+ return Assert.format(message, fExpected, fActual);
+
+ findCommonPrefix();
+ findCommonSuffix();
+ String expected= compactString(fExpected);
+ String actual= compactString(fActual);
+ return Assert.format(message, expected, actual);
+ }
+
+ private String compactString(String source) {
+ String result= DELTA_START + source.substring(fPrefix, source.length() - fSuffix + 1) + DELTA_END;
+ if (fPrefix > 0)
+ result= computeCommonPrefix() + result;
+ if (fSuffix > 0)
+ result= result + computeCommonSuffix();
+ return result;
+ }
+
+ private void findCommonPrefix() {
+ fPrefix= 0;
+ int end= Math.min(fExpected.length(), fActual.length());
+ for (; fPrefix < end; fPrefix++) {
+ if (fExpected.charAt(fPrefix) != fActual.charAt(fPrefix))
+ break;
+ }
+ }
+
+ private void findCommonSuffix() {
+ int expectedSuffix= fExpected.length() - 1;
+ int actualSuffix= fActual.length() - 1;
+ for (; actualSuffix >= fPrefix && expectedSuffix >= fPrefix; actualSuffix--, expectedSuffix--) {
+ if (fExpected.charAt(expectedSuffix) != fActual.charAt(actualSuffix))
+ break;
+ }
+ fSuffix= fExpected.length() - expectedSuffix;
+ }
+
+ private String computeCommonPrefix() {
+ return (fPrefix > fContextLength ? ELLIPSIS : "") + fExpected.substring(Math.max(0, fPrefix - fContextLength), fPrefix);
+ }
+
+ private String computeCommonSuffix() {
+ int end= Math.min(fExpected.length() - fSuffix + 1 + fContextLength, fExpected.length());
+ return fExpected.substring(fExpected.length() - fSuffix + 1, end) + (fExpected.length() - fSuffix + 1 < fExpected.length() - fContextLength ? ELLIPSIS : "");
+ }
+
+ private boolean areStringsEqual() {
+ return fExpected.equals(fActual);
+ }
+}
diff --git a/src/junit/framework/ComparisonFailure.java b/src/junit/framework/ComparisonFailure.java
new file mode 100644
index 0000000..c31068f
--- /dev/null
+++ b/src/junit/framework/ComparisonFailure.java
@@ -0,0 +1,51 @@
+package junit.framework;
+
+/**
+ * Thrown when an assert equals for Strings failed.
+ *
+ * Inspired by a patch from Alex Chaffee mailto:alex@purpletech.com
+ */
+public class ComparisonFailure extends AssertionFailedError {
+ private static final int MAX_CONTEXT_LENGTH= 20;
+ private static final long serialVersionUID= 1L;
+
+ private String fExpected;
+ private String fActual;
+
+ /**
+ * Constructs a comparison failure.
+ * @param message the identifying message or null
+ * @param expected the expected string value
+ * @param actual the actual string value
+ */
+ public ComparisonFailure (String message, String expected, String actual) {
+ super (message);
+ fExpected= expected;
+ fActual= actual;
+ }
+
+ /**
+ * Returns "..." in place of common prefix and "..." in
+ * place of common suffix between expected and actual.
+ *
+ * @see java.lang.Throwable#getMessage()
+ */
+ public String getMessage() {
+ return new ComparisonCompactor(MAX_CONTEXT_LENGTH, fExpected, fActual).compact(super.getMessage());
+ }
+
+ /**
+ * Gets the actual string value
+ * @return the actual string value
+ */
+ public String getActual() {
+ return fActual;
+ }
+ /**
+ * Gets the expected string value
+ * @return the expected string value
+ */
+ public String getExpected() {
+ return fExpected;
+ }
+} \ No newline at end of file
diff --git a/src/junit/framework/Protectable.java b/src/junit/framework/Protectable.java
new file mode 100644
index 0000000..e143237
--- /dev/null
+++ b/src/junit/framework/Protectable.java
@@ -0,0 +1,14 @@
+package junit.framework;
+
+/**
+ * A <em>Protectable</em> can be run and can throw a Throwable.
+ *
+ * @see TestResult
+ */
+public interface Protectable {
+
+ /**
+ * Run the the following method protected.
+ */
+ public abstract void protect() throws Throwable;
+} \ No newline at end of file
diff --git a/src/junit/framework/Test.java b/src/junit/framework/Test.java
new file mode 100644
index 0000000..a016ee8
--- /dev/null
+++ b/src/junit/framework/Test.java
@@ -0,0 +1,17 @@
+package junit.framework;
+
+/**
+ * A <em>Test</em> can be run and collect its results.
+ *
+ * @see TestResult
+ */
+public interface Test {
+ /**
+ * Counts the number of test cases that will be run by this test.
+ */
+ public abstract int countTestCases();
+ /**
+ * Runs a test and collects its result in a TestResult instance.
+ */
+ public abstract void run(TestResult result);
+} \ No newline at end of file
diff --git a/src/junit/framework/TestCase.java b/src/junit/framework/TestCase.java
new file mode 100644
index 0000000..e54b16b
--- /dev/null
+++ b/src/junit/framework/TestCase.java
@@ -0,0 +1,207 @@
+package junit.framework;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * A test case defines the fixture to run multiple tests. To define a test case<br>
+ * 1) implement a subclass of TestCase<br>
+ * 2) define instance variables that store the state of the fixture<br>
+ * 3) initialize the fixture state by overriding <code>setUp</code><br>
+ * 4) clean-up after a test by overriding <code>tearDown</code>.<br>
+ * Each test runs in its own fixture so there
+ * can be no side effects among test runs.
+ * Here is an example:
+ * <pre>
+ * public class MathTest extends TestCase {
+ * protected double fValue1;
+ * protected double fValue2;
+ *
+ * protected void setUp() {
+ * fValue1= 2.0;
+ * fValue2= 3.0;
+ * }
+ * }
+ * </pre>
+ *
+ * For each test implement a method which interacts
+ * with the fixture. Verify the expected results with assertions specified
+ * by calling <code>assertTrue</code> with a boolean.
+ * <pre>
+ * public void testAdd() {
+ * double result= fValue1 + fValue2;
+ * assertTrue(result == 5.0);
+ * }
+ * </pre>
+ * Once the methods are defined you can run them. The framework supports
+ * both a static type safe and more dynamic way to run a test.
+ * In the static way you override the runTest method and define the method to
+ * be invoked. A convenient way to do so is with an anonymous inner class.
+ * <pre>
+ * TestCase test= new MathTest("add") {
+ * public void runTest() {
+ * testAdd();
+ * }
+ * };
+ * test.run();
+ * </pre>
+ * The dynamic way uses reflection to implement <code>runTest</code>. It dynamically finds
+ * and invokes a method.
+ * In this case the name of the test case has to correspond to the test method
+ * to be run.
+ * <pre>
+ * TestCase test= new MathTest("testAdd");
+ * test.run();
+ * </pre>
+ * The tests to be run can be collected into a TestSuite. JUnit provides
+ * different <i>test runners</i> which can run a test suite and collect the results.
+ * A test runner either expects a static method <code>suite</code> as the entry
+ * point to get a test to run or it will extract the suite automatically.
+ * <pre>
+ * public static Test suite() {
+ * suite.addTest(new MathTest("testAdd"));
+ * suite.addTest(new MathTest("testDivideByZero"));
+ * return suite;
+ * }
+ * </pre>
+ * @see TestResult
+ * @see TestSuite
+ */
+
+public abstract class TestCase extends Assert implements Test {
+ /**
+ * the name of the test case
+ */
+ private String fName;
+
+ /**
+ * No-arg constructor to enable serialization. This method
+ * is not intended to be used by mere mortals without calling setName().
+ */
+ public TestCase() {
+ fName= null;
+ }
+ /**
+ * Constructs a test case with the given name.
+ */
+ public TestCase(String name) {
+ fName= name;
+ }
+ /**
+ * Counts the number of test cases executed by run(TestResult result).
+ */
+ public int countTestCases() {
+ return 1;
+ }
+ /**
+ * Creates a default TestResult object
+ *
+ * @see TestResult
+ */
+ protected TestResult createResult() {
+ return new TestResult();
+ }
+ /**
+ * A convenience method to run this test, collecting the results with a
+ * default TestResult object.
+ *
+ * @see TestResult
+ */
+ public TestResult run() {
+ TestResult result= createResult();
+ run(result);
+ return result;
+ }
+ /**
+ * Runs the test case and collects the results in TestResult.
+ */
+ public void run(TestResult result) {
+ result.run(this);
+ }
+ /**
+ * Runs the bare test sequence.
+ * @exception Throwable if any exception is thrown
+ */
+ public void runBare() throws Throwable {
+ Throwable exception= null;
+ setUp();
+ try {
+ runTest();
+ } catch (Throwable running) {
+ exception= running;
+ }
+ finally {
+ try {
+ tearDown();
+ } catch (Throwable tearingDown) {
+ if (exception == null) exception= tearingDown;
+ }
+ }
+ if (exception != null) throw exception;
+ }
+ /**
+ * Override to run the test and assert its state.
+ * @exception Throwable if any exception is thrown
+ */
+ protected void runTest() throws Throwable {
+ assertNotNull(fName); // Some VMs crash when calling getMethod(null,null);
+ Method runMethod= null;
+ try {
+ // use getMethod to get all public inherited
+ // methods. getDeclaredMethods returns all
+ // methods of this class but excludes the
+ // inherited ones.
+ runMethod= getClass().getMethod(fName, (Class[])null);
+ } catch (NoSuchMethodException e) {
+ fail("Method \""+fName+"\" not found");
+ }
+ if (!Modifier.isPublic(runMethod.getModifiers())) {
+ fail("Method \""+fName+"\" should be public");
+ }
+
+ try {
+ runMethod.invoke(this, (Object[])new Class[0]);
+ }
+ catch (InvocationTargetException e) {
+ e.fillInStackTrace();
+ throw e.getTargetException();
+ }
+ catch (IllegalAccessException e) {
+ e.fillInStackTrace();
+ throw e;
+ }
+ }
+ /**
+ * Sets up the fixture, for example, open a network connection.
+ * This method is called before a test is executed.
+ */
+ protected void setUp() throws Exception {
+ }
+ /**
+ * Tears down the fixture, for example, close a network connection.
+ * This method is called after a test is executed.
+ */
+ protected void tearDown() throws Exception {
+ }
+ /**
+ * Returns a string representation of the test case
+ */
+ public String toString() {
+ return getName() + "(" + getClass().getName() + ")";
+ }
+ /**
+ * Gets the name of a TestCase
+ * @return returns a String
+ */
+ public String getName() {
+ return fName;
+ }
+ /**
+ * Sets the name of a TestCase
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ fName= name;
+ }
+}
diff --git a/src/junit/framework/TestFailure.java b/src/junit/framework/TestFailure.java
new file mode 100644
index 0000000..aff6a5a
--- /dev/null
+++ b/src/junit/framework/TestFailure.java
@@ -0,0 +1,57 @@
+package junit.framework;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+
+/**
+ * A <code>TestFailure</code> collects a failed test together with
+ * the caught exception.
+ * @see TestResult
+ */
+public class TestFailure extends Object {
+ protected Test fFailedTest;
+ protected Throwable fThrownException;
+
+
+ /**
+ * Constructs a TestFailure with the given test and exception.
+ */
+ public TestFailure(Test failedTest, Throwable thrownException) {
+ fFailedTest= failedTest;
+ fThrownException= thrownException;
+ }
+ /**
+ * Gets the failed test.
+ */
+ public Test failedTest() {
+ return fFailedTest;
+ }
+ /**
+ * Gets the thrown exception.
+ */
+ public Throwable thrownException() {
+ return fThrownException;
+ }
+ /**
+ * Returns a short description of the failure.
+ */
+ public String toString() {
+ StringBuffer buffer= new StringBuffer();
+ buffer.append(fFailedTest+": "+fThrownException.getMessage());
+ return buffer.toString();
+ }
+ public String trace() {
+ StringWriter stringWriter= new StringWriter();
+ PrintWriter writer= new PrintWriter(stringWriter);
+ thrownException().printStackTrace(writer);
+ StringBuffer buffer= stringWriter.getBuffer();
+ return buffer.toString();
+ }
+ public String exceptionMessage() {
+ return thrownException().getMessage();
+ }
+ public boolean isFailure() {
+ return thrownException() instanceof AssertionFailedError;
+ }
+} \ No newline at end of file
diff --git a/src/junit/framework/TestListener.java b/src/junit/framework/TestListener.java
new file mode 100644
index 0000000..9b69443
--- /dev/null
+++ b/src/junit/framework/TestListener.java
@@ -0,0 +1,23 @@
+package junit.framework;
+
+/**
+ * A Listener for test progress
+ */
+public interface TestListener {
+ /**
+ * An error occurred.
+ */
+ public void addError(Test test, Throwable t);
+ /**
+ * A failure occurred.
+ */
+ public void addFailure(Test test, AssertionFailedError t);
+ /**
+ * A test ended.
+ */
+ public void endTest(Test test);
+ /**
+ * A test started.
+ */
+ public void startTest(Test test);
+} \ No newline at end of file
diff --git a/src/junit/framework/TestResult.java b/src/junit/framework/TestResult.java
new file mode 100644
index 0000000..8535ce0
--- /dev/null
+++ b/src/junit/framework/TestResult.java
@@ -0,0 +1,166 @@
+package junit.framework;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * A <code>TestResult</code> collects the results of executing
+ * a test case. It is an instance of the Collecting Parameter pattern.
+ * The test framework distinguishes between <i>failures</i> and <i>errors</i>.
+ * A failure is anticipated and checked for with assertions. Errors are
+ * unanticipated problems like an <code>ArrayIndexOutOfBoundsException</code>.
+ *
+ * @see Test
+ */
+public class TestResult extends Object {
+ protected Vector fFailures;
+ protected Vector fErrors;
+ protected Vector fListeners;
+ protected int fRunTests;
+ private boolean fStop;
+
+ public TestResult() {
+ fFailures= new Vector();
+ fErrors= new Vector();
+ fListeners= new Vector();
+ fRunTests= 0;
+ fStop= false;
+ }
+ /**
+ * Adds an error to the list of errors. The passed in exception
+ * caused the error.
+ */
+ public synchronized void addError(Test test, Throwable t) {
+ fErrors.addElement(new TestFailure(test, t));
+ for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
+ ((TestListener)e.nextElement()).addError(test, t);
+ }
+ }
+ /**
+ * Adds a failure to the list of failures. The passed in exception
+ * caused the failure.
+ */
+ public synchronized void addFailure(Test test, AssertionFailedError t) {
+ fFailures.addElement(new TestFailure(test, t));
+ for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
+ ((TestListener)e.nextElement()).addFailure(test, t);
+ }
+ }
+ /**
+ * Registers a TestListener
+ */
+ public synchronized void addListener(TestListener listener) {
+ fListeners.addElement(listener);
+ }
+ /**
+ * Unregisters a TestListener
+ */
+ public synchronized void removeListener(TestListener listener) {
+ fListeners.removeElement(listener);
+ }
+ /**
+ * Returns a copy of the listeners.
+ */
+ private synchronized Vector cloneListeners() {
+ return (Vector)fListeners.clone();
+ }
+ /**
+ * Informs the result that a test was completed.
+ */
+ public void endTest(Test test) {
+ for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
+ ((TestListener)e.nextElement()).endTest(test);
+ }
+ }
+ /**
+ * Gets the number of detected errors.
+ */
+ public synchronized int errorCount() {
+ return fErrors.size();
+ }
+ /**
+ * Returns an Enumeration for the errors
+ */
+ public synchronized Enumeration errors() {
+ return fErrors.elements();
+ }
+ /**
+ * Gets the number of detected failures.
+ */
+ public synchronized int failureCount() {
+ return fFailures.size();
+ }
+ /**
+ * Returns an Enumeration for the failures
+ */
+ public synchronized Enumeration failures() {
+ return fFailures.elements();
+ }
+ /**
+ * Runs a TestCase.
+ */
+ protected void run(final TestCase test) {
+ startTest(test);
+ Protectable p= new Protectable() {
+ public void protect() throws Throwable {
+ test.runBare();
+ }
+ };
+ runProtected(test, p);
+
+ endTest(test);
+ }
+ /**
+ * Gets the number of run tests.
+ */
+ public synchronized int runCount() {
+ return fRunTests;
+ }
+ /**
+ * Runs a TestCase.
+ */
+ public void runProtected(final Test test, Protectable p) {
+ try {
+ p.protect();
+ }
+ catch (AssertionFailedError e) {
+ addFailure(test, e);
+ }
+ catch (ThreadDeath e) { // don't catch ThreadDeath by accident
+ throw e;
+ }
+ catch (Throwable e) {
+ addError(test, e);
+ }
+ }
+ /**
+ * Checks whether the test run should stop
+ */
+ public synchronized boolean shouldStop() {
+ return fStop;
+ }
+ /**
+ * Informs the result that a test will be started.
+ */
+ public void startTest(Test test) {
+ final int count= test.countTestCases();
+ synchronized(this) {
+ fRunTests+= count;
+ }
+ for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
+ ((TestListener)e.nextElement()).startTest(test);
+ }
+ }
+ /**
+ * Marks that the test run should stop.
+ */
+ public synchronized void stop() {
+ fStop= true;
+ }
+ /**
+ * Returns whether the entire test was successful or not.
+ */
+ public synchronized boolean wasSuccessful() {
+ return failureCount() == 0 && errorCount() == 0;
+ }
+} \ No newline at end of file
diff --git a/src/junit/framework/TestSuite.java b/src/junit/framework/TestSuite.java
new file mode 100644
index 0000000..5a96bc2
--- /dev/null
+++ b/src/junit/framework/TestSuite.java
@@ -0,0 +1,293 @@
+package junit.framework;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * A <code>TestSuite</code> is a <code>Composite</code> of Tests.
+ * It runs a collection of test cases. Here is an example using
+ * the dynamic test definition.
+ * <pre>
+ * TestSuite suite= new TestSuite();
+ * suite.addTest(new MathTest("testAdd"));
+ * suite.addTest(new MathTest("testDivideByZero"));
+ * </pre>
+ * Alternatively, a TestSuite can extract the tests to be run automatically.
+ * To do so you pass the class of your TestCase class to the
+ * TestSuite constructor.
+ * <pre>
+ * TestSuite suite= new TestSuite(MathTest.class);
+ * </pre>
+ * This constructor creates a suite with all the methods
+ * starting with "test" that take no arguments.
+ * <p>
+ * A final option is to do the same for a large array of test classes.
+ * <pre>
+ * Class[] testClasses = { MathTest.class, AnotherTest.class }
+ * TestSuite suite= new TestSuite(testClasses);
+ * </pre>
+ *
+ * @see Test
+ */
+public class TestSuite implements Test {
+
+ /**
+ * ...as the moon sets over the early morning Merlin, Oregon
+ * mountains, our intrepid adventurers type...
+ */
+ static public Test createTest(Class theClass, String name) {
+ Constructor constructor;
+ try {
+ constructor= getTestConstructor(theClass);
+ } catch (NoSuchMethodException e) {
+ return warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()");
+ }
+ Object test;
+ try {
+ if (constructor.getParameterTypes().length == 0) {
+ test= constructor.newInstance(new Object[0]);
+ if (test instanceof TestCase)
+ ((TestCase) test).setName(name);
+ } else {
+ test= constructor.newInstance(new Object[]{name});
+ }
+ } catch (InstantiationException e) {
+ return(warning("Cannot instantiate test case: "+name+" ("+exceptionToString(e)+")"));
+ } catch (InvocationTargetException e) {
+ return(warning("Exception in constructor: "+name+" ("+exceptionToString(e.getTargetException())+")"));
+ } catch (IllegalAccessException e) {
+ return(warning("Cannot access test case: "+name+" ("+exceptionToString(e)+")"));
+ }
+ return (Test) test;
+ }
+
+ /**
+ * Gets a constructor which takes a single String as
+ * its argument or a no arg constructor.
+ */
+ public static Constructor getTestConstructor(Class theClass) throws NoSuchMethodException {
+ Class[] args= { String.class };
+ try {
+ return theClass.getConstructor(args);
+ } catch (NoSuchMethodException e) {
+ // fall through
+ }
+ return theClass.getConstructor(new Class[0]);
+ }
+
+ /**
+ * Returns a test which will fail and log a warning message.
+ */
+ public static Test warning(final String message) {
+ return new TestCase("warning") {
+ protected void runTest() {
+ fail(message);
+ }
+ };
+ }
+
+ /**
+ * Converts the stack trace into a string
+ */
+ private static String exceptionToString(Throwable t) {
+ StringWriter stringWriter= new StringWriter();
+ PrintWriter writer= new PrintWriter(stringWriter);
+ t.printStackTrace(writer);
+ return stringWriter.toString();
+
+ }
+ private String fName;
+
+ private Vector fTests= new Vector(10);
+
+ /**
+ * Constructs an empty TestSuite.
+ */
+ public TestSuite() {
+ }
+
+ /**
+ * Constructs a TestSuite from the given class. Adds all the methods
+ * starting with "test" as test cases to the suite.
+ * Parts of this method was written at 2337 meters in the Hueffihuette,
+ * Kanton Uri
+ */
+ public TestSuite(final Class theClass) {
+ fName= theClass.getName();
+ try {
+ getTestConstructor(theClass); // Avoid generating multiple error messages
+ } catch (NoSuchMethodException e) {
+ addTest(warning("Class "+theClass.getName()+" has no public constructor TestCase(String name) or TestCase()"));
+ return;
+ }
+
+ if (!Modifier.isPublic(theClass.getModifiers())) {
+ addTest(warning("Class "+theClass.getName()+" is not public"));
+ return;
+ }
+
+ Class superClass= theClass;
+ Vector names= new Vector();
+ while (Test.class.isAssignableFrom(superClass)) {
+ Method[] methods= superClass.getDeclaredMethods();
+ for (int i= 0; i < methods.length; i++) {
+ addTestMethod(methods[i], names, theClass);
+ }
+ superClass= superClass.getSuperclass();
+ }
+ if (fTests.size() == 0)
+ addTest(warning("No tests found in "+theClass.getName()));
+ }
+
+ /**
+ * Constructs a TestSuite from the given class with the given name.
+ * @see TestSuite#TestSuite(Class)
+ */
+ public TestSuite(Class theClass, String name) {
+ this(theClass);
+ setName(name);
+ }
+
+ /**
+ * Constructs an empty TestSuite.
+ */
+ public TestSuite(String name) {
+ setName(name);
+ }
+
+ /**
+ * Constructs a TestSuite from the given array of classes.
+ * @param classes
+ */
+ public TestSuite (Class[] classes) {
+ for (int i= 0; i < classes.length; i++)
+ addTest(new TestSuite(classes[i]));
+ }
+
+ /**
+ * Constructs a TestSuite from the given array of classes with the given name.
+ * @see TestSuite#TestSuite(Class[])
+ */
+ public TestSuite(Class[] classes, String name) {
+ this(classes);
+ setName(name);
+ }
+
+ /**
+ * Adds a test to the suite.
+ */
+ public void addTest(Test test) {
+ fTests.addElement(test);
+ }
+
+ /**
+ * Adds the tests from the given class to the suite
+ */
+ public void addTestSuite(Class testClass) {
+ addTest(new TestSuite(testClass));
+ }
+
+ /**
+ * Counts the number of test cases that will be run by this test.
+ */
+ public int countTestCases() {
+ int count= 0;
+ for (Enumeration e= tests(); e.hasMoreElements(); ) {
+ Test test= (Test)e.nextElement();
+ count= count + test.countTestCases();
+ }
+ return count;
+ }
+
+ /**
+ * Returns the name of the suite. Not all
+ * test suites have a name and this method
+ * can return null.
+ */
+ public String getName() {
+ return fName;
+ }
+
+ /**
+ * Runs the tests and collects their result in a TestResult.
+ */
+ public void run(TestResult result) {
+ for (Enumeration e= tests(); e.hasMoreElements(); ) {
+ if (result.shouldStop() )
+ break;
+ Test test= (Test)e.nextElement();
+ runTest(test, result);
+ }
+ }
+
+ public void runTest(Test test, TestResult result) {
+ test.run(result);
+ }
+
+ /**
+ * Sets the name of the suite.
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ fName= name;
+ }
+
+ /**
+ * Returns the test at the given index
+ */
+ public Test testAt(int index) {
+ return (Test)fTests.elementAt(index);
+ }
+
+ /**
+ * Returns the number of tests in this suite
+ */
+ public int testCount() {
+ return fTests.size();
+ }
+
+ /**
+ * Returns the tests as an enumeration
+ */
+ public Enumeration tests() {
+ return fTests.elements();
+ }
+
+ /**
+ */
+ public String toString() {
+ if (getName() != null)
+ return getName();
+ return super.toString();
+ }
+
+ private void addTestMethod(Method m, Vector names, Class theClass) {
+ String name= m.getName();
+ if (names.contains(name))
+ return;
+ if (! isPublicTestMethod(m)) {
+ if (isTestMethod(m))
+ addTest(warning("Test method isn't public: "+m.getName()));
+ return;
+ }
+ names.addElement(name);
+ addTest(createTest(theClass, name));
+ }
+
+ private boolean isPublicTestMethod(Method m) {
+ return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
+ }
+
+ private boolean isTestMethod(Method m) {
+ String name= m.getName();
+ Class[] parameters= m.getParameterTypes();
+ Class returnType= m.getReturnType();
+ return parameters.length == 0 && name.startsWith("test") && returnType.equals(Void.TYPE);
+ }
+} \ No newline at end of file
diff --git a/src/junit/runner/BaseTestRunner.java b/src/junit/runner/BaseTestRunner.java
new file mode 100644
index 0000000..1518f7c
--- /dev/null
+++ b/src/junit/runner/BaseTestRunner.java
@@ -0,0 +1,343 @@
+package junit.runner;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.text.NumberFormat;
+import java.util.Properties;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestListener;
+import junit.framework.TestSuite;
+
+/**
+ * Base class for all test runners.
+ * This class was born live on stage in Sardinia during XP2000.
+ */
+public abstract class BaseTestRunner implements TestListener {
+ public static final String SUITE_METHODNAME= "suite";
+
+ private static Properties fPreferences;
+ static int fgMaxMessageLength= 500;
+ static boolean fgFilterStack= true;
+ boolean fLoading= true;
+
+ /*
+ * Implementation of TestListener
+ */
+ public synchronized void startTest(Test test) {
+ testStarted(test.toString());
+ }
+
+ protected static void setPreferences(Properties preferences) {
+ fPreferences= preferences;
+ }
+
+ protected static Properties getPreferences() {
+ if (fPreferences == null) {
+ fPreferences= new Properties();
+ fPreferences.put("loading", "true");
+ fPreferences.put("filterstack", "true");
+ readPreferences();
+ }
+ return fPreferences;
+ }
+
+ public static void savePreferences() throws IOException {
+ FileOutputStream fos= new FileOutputStream(getPreferencesFile());
+ try {
+ // calling of the deprecated save method to enable compiling under 1.1.7
+ getPreferences().save(fos, "");
+ } finally {
+ fos.close();
+ }
+ }
+
+ public static void setPreference(String key, String value) {
+ getPreferences().put(key, value);
+ }
+
+ public synchronized void endTest(Test test) {
+ testEnded(test.toString());
+ }
+
+ public synchronized void addError(final Test test, final Throwable t) {
+ testFailed(TestRunListener.STATUS_ERROR, test, t);
+ }
+
+ public synchronized void addFailure(final Test test, final AssertionFailedError t) {
+ testFailed(TestRunListener.STATUS_FAILURE, test, t);
+ }
+
+ // TestRunListener implementation
+
+ public abstract void testStarted(String testName);
+
+ public abstract void testEnded(String testName);
+
+ public abstract void testFailed(int status, Test test, Throwable t);
+
+ /**
+ * Returns the Test corresponding to the given suite. This is
+ * a template method, subclasses override runFailed(), clearStatus().
+ */
+ public Test getTest(String suiteClassName) {
+ if (suiteClassName.length() <= 0) {
+ clearStatus();
+ return null;
+ }
+ Class testClass= null;
+ try {
+ testClass= loadSuiteClass(suiteClassName);
+ } catch (ClassNotFoundException e) {
+ String clazz= e.getMessage();
+ if (clazz == null)
+ clazz= suiteClassName;
+ runFailed("Class not found \""+clazz+"\"");
+ return null;
+ } catch(Exception e) {
+ runFailed("Error: "+e.toString());
+ return null;
+ }
+ Method suiteMethod= null;
+ try {
+ suiteMethod= testClass.getMethod(SUITE_METHODNAME, new Class[0]);
+ } catch(Exception e) {
+ // try to extract a test suite automatically
+ clearStatus();
+ return new TestSuite(testClass);
+ }
+ if (! Modifier.isStatic(suiteMethod.getModifiers())) {
+ runFailed("Suite() method must be static");
+ return null;
+ }
+ Test test= null;
+ try {
+ test= (Test)suiteMethod.invoke(null, (Object[])new Class[0]); // static method
+ if (test == null)
+ return test;
+ }
+ catch (InvocationTargetException e) {
+ runFailed("Failed to invoke suite():" + e.getTargetException().toString());
+ return null;
+ }
+ catch (IllegalAccessException e) {
+ runFailed("Failed to invoke suite():" + e.toString());
+ return null;
+ }
+
+ clearStatus();
+ return test;
+ }
+
+ /**
+ * Returns the formatted string of the elapsed time.
+ */
+ public String elapsedTimeAsString(long runTime) {
+ return NumberFormat.getInstance().format((double)runTime/1000);
+ }
+
+ /**
+ * Processes the command line arguments and
+ * returns the name of the suite class to run or null
+ */
+ protected String processArguments(String[] args) {
+ String suiteName= null;
+ for (int i= 0; i < args.length; i++) {
+ if (args[i].equals("-noloading")) {
+ setLoading(false);
+ } else if (args[i].equals("-nofilterstack")) {
+ fgFilterStack= false;
+ } else if (args[i].equals("-c")) {
+ if (args.length > i+1)
+ suiteName= extractClassName(args[i+1]);
+ else
+ System.out.println("Missing Test class name");
+ i++;
+ } else {
+ suiteName= args[i];
+ }
+ }
+ return suiteName;
+ }
+
+ /**
+ * Sets the loading behaviour of the test runner
+ */
+ public void setLoading(boolean enable) {
+ fLoading= enable;
+ }
+ /**
+ * Extract the class name from a String in VA/Java style
+ */
+ public String extractClassName(String className) {
+ if(className.startsWith("Default package for"))
+ return className.substring(className.lastIndexOf(".")+1);
+ return className;
+ }
+
+ /**
+ * Truncates a String to the maximum length.
+ */
+ public static String truncate(String s) {
+ if (fgMaxMessageLength != -1 && s.length() > fgMaxMessageLength)
+ s= s.substring(0, fgMaxMessageLength)+"...";
+ return s;
+ }
+
+ /**
+ * Override to define how to handle a failed loading of
+ * a test suite.
+ */
+ protected abstract void runFailed(String message);
+
+ /**
+ * Returns the loaded Class for a suite name.
+ */
+ protected Class loadSuiteClass(String suiteClassName) throws ClassNotFoundException {
+ return getLoader().load(suiteClassName);
+ }
+
+ /**
+ * Clears the status message.
+ */
+ protected void clearStatus() { // Belongs in the GUI TestRunner class
+ }
+
+ /**
+ * Returns the loader to be used.
+ */
+ public TestSuiteLoader getLoader() {
+ if (useReloadingTestSuiteLoader())
+ return new ReloadingTestSuiteLoader();
+ return new StandardTestSuiteLoader();
+ }
+
+ protected boolean useReloadingTestSuiteLoader() {
+ return getPreference("loading").equals("true") && !inVAJava() && fLoading;
+ }
+
+ private static File getPreferencesFile() {
+ String home= System.getProperty("user.home");
+ return new File(home, "junit.properties");
+ }
+
+ private static void readPreferences() {
+ InputStream is= null;
+ try {
+ is= new FileInputStream(getPreferencesFile());
+ setPreferences(new Properties(getPreferences()));
+ getPreferences().load(is);
+ } catch (IOException e) {
+ try {
+ if (is != null)
+ is.close();
+ } catch (IOException e1) {
+ }
+ }
+ }
+
+ public static String getPreference(String key) {
+ return getPreferences().getProperty(key);
+ }
+
+ public static int getPreference(String key, int dflt) {
+ String value= getPreference(key);
+ int intValue= dflt;
+ if (value == null)
+ return intValue;
+ try {
+ intValue= Integer.parseInt(value);
+ } catch (NumberFormatException ne) {
+ }
+ return intValue;
+ }
+
+ public static boolean inVAJava() {
+ try {
+ Class.forName("com.ibm.uvm.tools.DebugSupport");
+ }
+ catch (Exception e) {
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean inMac() {
+ return System.getProperty("mrj.version") != null;
+ }
+
+
+ /**
+ * Returns a filtered stack trace
+ */
+ public static String getFilteredTrace(Throwable t) {
+ StringWriter stringWriter= new StringWriter();
+ PrintWriter writer= new PrintWriter(stringWriter);
+ t.printStackTrace(writer);
+ StringBuffer buffer= stringWriter.getBuffer();
+ String trace= buffer.toString();
+ return BaseTestRunner.getFilteredTrace(trace);
+ }
+
+ /**
+ * Filters stack frames from internal JUnit classes
+ */
+ public static String getFilteredTrace(String stack) {
+ if (showStackRaw())
+ return stack;
+
+ StringWriter sw= new StringWriter();
+ PrintWriter pw= new PrintWriter(sw);
+ StringReader sr= new StringReader(stack);
+ BufferedReader br= new BufferedReader(sr);
+
+ String line;
+ try {
+ while ((line= br.readLine()) != null) {
+ if (!filterLine(line))
+ pw.println(line);
+ }
+ } catch (Exception IOException) {
+ return stack; // return the stack unfiltered
+ }
+ return sw.toString();
+ }
+
+ protected static boolean showStackRaw() {
+ return !getPreference("filterstack").equals("true") || fgFilterStack == false;
+ }
+
+ static boolean filterLine(String line) {
+ String[] patterns= new String[] {
+ "junit.framework.TestCase",
+ "junit.framework.TestResult",
+ "junit.framework.TestSuite",
+ "junit.framework.Assert.", // don't filter AssertionFailure
+ "junit.swingui.TestRunner",
+ "junit.awtui.TestRunner",
+ "junit.textui.TestRunner",
+ "java.lang.reflect.Method.invoke("
+ };
+ for (int i= 0; i < patterns.length; i++) {
+ if (line.indexOf(patterns[i]) > 0)
+ return true;
+ }
+ return false;
+ }
+
+ static {
+ fgMaxMessageLength= getPreference("maxmessage", fgMaxMessageLength);
+ }
+
+}
diff --git a/src/junit/runner/ClassPathTestCollector.java b/src/junit/runner/ClassPathTestCollector.java
new file mode 100644
index 0000000..0dbb98e
--- /dev/null
+++ b/src/junit/runner/ClassPathTestCollector.java
@@ -0,0 +1,83 @@
+package junit.runner;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+/**
+ * An implementation of a TestCollector that consults the
+ * class path. It considers all classes on the class path
+ * excluding classes in JARs. It leaves it up to subclasses
+ * to decide whether a class is a runnable Test.
+ *
+ * @see TestCollector
+ */
+public abstract class ClassPathTestCollector implements TestCollector {
+
+ static final int SUFFIX_LENGTH= ".class".length();
+
+ public ClassPathTestCollector() {
+ }
+
+ public Enumeration collectTests() {
+ String classPath= System.getProperty("java.class.path");
+ Hashtable result = collectFilesInPath(classPath);
+ return result.elements();
+ }
+
+ public Hashtable collectFilesInPath(String classPath) {
+ Hashtable result= collectFilesInRoots(splitClassPath(classPath));
+ return result;
+ }
+
+ Hashtable collectFilesInRoots(Vector roots) {
+ Hashtable result= new Hashtable(100);
+ Enumeration e= roots.elements();
+ while (e.hasMoreElements())
+ gatherFiles(new File((String)e.nextElement()), "", result);
+ return result;
+ }
+
+ void gatherFiles(File classRoot, String classFileName, Hashtable result) {
+ File thisRoot= new File(classRoot, classFileName);
+ if (thisRoot.isFile()) {
+ if (isTestClass(classFileName)) {
+ String className= classNameFromFile(classFileName);
+ result.put(className, className);
+ }
+ return;
+ }
+ String[] contents= thisRoot.list();
+ if (contents != null) {
+ for (int i= 0; i < contents.length; i++)
+ gatherFiles(classRoot, classFileName+File.separatorChar+contents[i], result);
+ }
+ }
+
+ Vector splitClassPath(String classPath) {
+ Vector result= new Vector();
+ String separator= System.getProperty("path.separator");
+ StringTokenizer tokenizer= new StringTokenizer(classPath, separator);
+ while (tokenizer.hasMoreTokens())
+ result.addElement(tokenizer.nextToken());
+ return result;
+ }
+
+ protected boolean isTestClass(String classFileName) {
+ return
+ classFileName.endsWith(".class") &&
+ classFileName.indexOf('$') < 0 &&
+ classFileName.indexOf("Test") > 0;
+ }
+
+ protected String classNameFromFile(String classFileName) {
+ // convert /a/b.class to a.b
+ String s= classFileName.substring(0, classFileName.length()-SUFFIX_LENGTH);
+ String s2= s.replace(File.separatorChar, '.');
+ if (s2.startsWith("."))
+ return s2.substring(1);
+ return s2;
+ }
+}
diff --git a/src/junit/runner/FailureDetailView.java b/src/junit/runner/FailureDetailView.java
new file mode 100644
index 0000000..fc9aaf4
--- /dev/null
+++ b/src/junit/runner/FailureDetailView.java
@@ -0,0 +1,23 @@
+package junit.runner;
+
+import java.awt.Component;
+
+import junit.framework.TestFailure;
+
+/**
+ * A view to show a details about a failure
+ */
+public interface FailureDetailView {
+ /**
+ * Returns the component used to present the TraceView
+ */
+ public Component getComponent();
+ /**
+ * Shows details of a TestFailure
+ */
+ public void showFailure(TestFailure failure);
+ /**
+ * Clears the view
+ */
+ public void clear();
+} \ No newline at end of file
diff --git a/src/junit/runner/LoadingTestCollector.java b/src/junit/runner/LoadingTestCollector.java
new file mode 100644
index 0000000..21a3144
--- /dev/null
+++ b/src/junit/runner/LoadingTestCollector.java
@@ -0,0 +1,70 @@
+package junit.runner;
+
+import java.lang.reflect.Modifier;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * An implementation of a TestCollector that loads
+ * all classes on the class path and tests whether
+ * it is assignable from Test or provides a static suite method.
+ * @see TestCollector
+ */
+public class LoadingTestCollector extends ClassPathTestCollector {
+
+ TestCaseClassLoader fLoader;
+
+ public LoadingTestCollector() {
+ fLoader= new TestCaseClassLoader();
+ }
+
+ protected boolean isTestClass(String classFileName) {
+ try {
+ if (classFileName.endsWith(".class")) {
+ Class testClass= classFromFile(classFileName);
+ return (testClass != null) && isTestClass(testClass);
+ }
+ }
+ catch (ClassNotFoundException expected) {
+ }
+ catch (NoClassDefFoundError notFatal) {
+ }
+ return false;
+ }
+
+ Class classFromFile(String classFileName) throws ClassNotFoundException {
+ String className= classNameFromFile(classFileName);
+ if (!fLoader.isExcluded(className))
+ return fLoader.loadClass(className, false);
+ return null;
+ }
+
+ boolean isTestClass(Class testClass) {
+ if (hasSuiteMethod(testClass))
+ return true;
+ if (Test.class.isAssignableFrom(testClass) &&
+ Modifier.isPublic(testClass.getModifiers()) &&
+ hasPublicConstructor(testClass))
+ return true;
+ return false;
+ }
+
+ boolean hasSuiteMethod(Class testClass) {
+ try {
+ testClass.getMethod(BaseTestRunner.SUITE_METHODNAME, new Class[0]);
+ } catch(Exception e) {
+ return false;
+ }
+ return true;
+ }
+
+ boolean hasPublicConstructor(Class testClass) {
+ try {
+ TestSuite.getTestConstructor(testClass);
+ } catch(NoSuchMethodException e) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/junit/runner/ReloadingTestSuiteLoader.java b/src/junit/runner/ReloadingTestSuiteLoader.java
new file mode 100644
index 0000000..537504b
--- /dev/null
+++ b/src/junit/runner/ReloadingTestSuiteLoader.java
@@ -0,0 +1,19 @@
+package junit.runner;
+
+/**
+ * A TestSuite loader that can reload classes.
+ */
+public class ReloadingTestSuiteLoader implements TestSuiteLoader {
+
+ public Class load(String suiteClassName) throws ClassNotFoundException {
+ return createLoader().loadClass(suiteClassName, true);
+ }
+
+ public Class reload(Class aClass) throws ClassNotFoundException {
+ return createLoader().loadClass(aClass.getName(), true);
+ }
+
+ protected TestCaseClassLoader createLoader() {
+ return new TestCaseClassLoader();
+ }
+} \ No newline at end of file
diff --git a/src/junit/runner/SimpleTestCollector.java b/src/junit/runner/SimpleTestCollector.java
new file mode 100644
index 0000000..9d1956a
--- /dev/null
+++ b/src/junit/runner/SimpleTestCollector.java
@@ -0,0 +1,20 @@
+package junit.runner;
+
+/**
+ * An implementation of a TestCollector that considers
+ * a class to be a test class when it contains the
+ * pattern "Test" in its name
+ * @see TestCollector
+ */
+public class SimpleTestCollector extends ClassPathTestCollector {
+
+ public SimpleTestCollector() {
+ }
+
+ protected boolean isTestClass(String classFileName) {
+ return
+ classFileName.endsWith(".class") &&
+ classFileName.indexOf('$') < 0 &&
+ classFileName.indexOf("Test") > 0;
+ }
+}
diff --git a/src/junit/runner/Sorter.java b/src/junit/runner/Sorter.java
new file mode 100644
index 0000000..e868992
--- /dev/null
+++ b/src/junit/runner/Sorter.java
@@ -0,0 +1,36 @@
+package junit.runner;
+
+import java.util.Vector;
+
+/**
+ * A custom quick sort with support to customize the swap behaviour.
+ * NOTICE: We can't use the the sorting support from the JDK 1.2 collection
+ * classes because of the JDK 1.1.7 compatibility.
+ */
+public class Sorter {
+ public static interface Swapper {
+ public void swap(Vector values, int left, int right);
+ }
+
+ public static void sortStrings(Vector values , int left, int right, Swapper swapper) {
+ int oleft= left;
+ int oright= right;
+ String mid= (String)values.elementAt((left + right) / 2);
+ do {
+ while (((String)(values.elementAt(left))).compareTo(mid) < 0)
+ left++;
+ while (mid.compareTo((String)(values.elementAt(right))) < 0)
+ right--;
+ if (left <= right) {
+ swapper.swap(values, left, right);
+ left++;
+ right--;
+ }
+ } while (left <= right);
+
+ if (oleft < right)
+ sortStrings(values, oleft, right, swapper);
+ if (left < oright)
+ sortStrings(values, left, oright, swapper);
+ }
+} \ No newline at end of file
diff --git a/src/junit/runner/StandardTestSuiteLoader.java b/src/junit/runner/StandardTestSuiteLoader.java
new file mode 100644
index 0000000..54f29c1
--- /dev/null
+++ b/src/junit/runner/StandardTestSuiteLoader.java
@@ -0,0 +1,19 @@
+package junit.runner;
+
+/**
+ * The standard test suite loader. It can only load the same class once.
+ */
+public class StandardTestSuiteLoader implements TestSuiteLoader {
+ /**
+ * Uses the system class loader to load the test class
+ */
+ public Class load(String suiteClassName) throws ClassNotFoundException {
+ return Class.forName(suiteClassName);
+ }
+ /**
+ * Uses the system class loader to load the test class
+ */
+ public Class reload(Class aClass) throws ClassNotFoundException {
+ return aClass;
+ }
+} \ No newline at end of file
diff --git a/src/junit/runner/TestCaseClassLoader.java b/src/junit/runner/TestCaseClassLoader.java
new file mode 100644
index 0000000..b4bbc24
--- /dev/null
+++ b/src/junit/runner/TestCaseClassLoader.java
@@ -0,0 +1,240 @@
+package junit.runner;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * A custom class loader which enables the reloading
+ * of classes for each test run. The class loader
+ * can be configured with a list of package paths that
+ * should be excluded from loading. The loading
+ * of these packages is delegated to the system class
+ * loader. They will be shared across test runs.
+ * <p>
+ * The list of excluded package paths is specified in
+ * a properties file "excluded.properties" that is located in
+ * the same place as the TestCaseClassLoader class.
+ * <p>
+ * <b>Known limitation:</b> the TestCaseClassLoader cannot load classes
+ * from jar files.
+ */
+
+
+public class TestCaseClassLoader extends ClassLoader {
+ /** scanned class path */
+ private Vector fPathItems;
+ /** default excluded paths */
+ private String[] defaultExclusions= {
+ "junit.framework.",
+ "junit.extensions.",
+ "junit.runner."
+ };
+ /** name of excluded properties file */
+ static final String EXCLUDED_FILE= "excluded.properties";
+ /** excluded paths */
+ private Vector fExcluded;
+
+ /**
+ * Constructs a TestCaseLoader. It scans the class path
+ * and the excluded package paths
+ */
+ public TestCaseClassLoader() {
+ this(System.getProperty("java.class.path"));
+ }
+
+ /**
+ * Constructs a TestCaseLoader. It scans the class path
+ * and the excluded package paths
+ */
+ public TestCaseClassLoader(String classPath) {
+ scanPath(classPath);
+ readExcludedPackages();
+ }
+
+ private void scanPath(String classPath) {
+ String separator= System.getProperty("path.separator");
+ fPathItems= new Vector(10);
+ StringTokenizer st= new StringTokenizer(classPath, separator);
+ while (st.hasMoreTokens()) {
+ fPathItems.addElement(st.nextToken());
+ }
+ }
+
+ public URL getResource(String name) {
+ return ClassLoader.getSystemResource(name);
+ }
+
+ public InputStream getResourceAsStream(String name) {
+ return ClassLoader.getSystemResourceAsStream(name);
+ }
+
+ public boolean isExcluded(String name) {
+ for (int i= 0; i < fExcluded.size(); i++) {
+ if (name.startsWith((String) fExcluded.elementAt(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public synchronized Class loadClass(String name, boolean resolve)
+ throws ClassNotFoundException {
+
+ Class c= findLoadedClass(name);
+ if (c != null)
+ return c;
+ //
+ // Delegate the loading of excluded classes to the
+ // standard class loader.
+ //
+ if (isExcluded(name)) {
+ try {
+ c= findSystemClass(name);
+ return c;
+ } catch (ClassNotFoundException e) {
+ // keep searching
+ }
+ }
+ if (c == null) {
+ byte[] data= lookupClassData(name);
+ if (data == null)
+ throw new ClassNotFoundException();
+ c= defineClass(name, data, 0, data.length);
+ }
+ if (resolve)
+ resolveClass(c);
+ return c;
+ }
+
+ private byte[] lookupClassData(String className) throws ClassNotFoundException {
+ byte[] data= null;
+ for (int i= 0; i < fPathItems.size(); i++) {
+ String path= (String) fPathItems.elementAt(i);
+ String fileName= className.replace('.', '/')+".class";
+ if (isJar(path)) {
+ data= loadJarData(path, fileName);
+ } else {
+ data= loadFileData(path, fileName);
+ }
+ if (data != null)
+ return data;
+ }
+ throw new ClassNotFoundException(className);
+ }
+
+ boolean isJar(String pathEntry) {
+ return pathEntry.endsWith(".jar") || pathEntry.endsWith(".zip");
+ }
+
+ private byte[] loadFileData(String path, String fileName) {
+ File file= new File(path, fileName);
+ if (file.exists()) {
+ return getClassData(file);
+ }
+ return null;
+ }
+
+ private byte[] getClassData(File f) {
+ FileInputStream stream= null;
+ try {
+ stream= new FileInputStream(f);
+ ByteArrayOutputStream out= new ByteArrayOutputStream(1000);
+ byte[] b= new byte[1000];
+ int n;
+ while ((n= stream.read(b)) != -1)
+ out.write(b, 0, n);
+ stream.close();
+ out.close();
+ return out.toByteArray();
+
+ } catch (IOException e) {
+ }
+ finally {
+ if (stream != null)
+ try {
+ stream.close();
+ } catch (IOException e1) {
+ }
+ }
+ return null;
+ }
+
+ private byte[] loadJarData(String path, String fileName) {
+ ZipFile zipFile= null;
+ InputStream stream= null;
+ File archive= new File(path);
+ if (!archive.exists())
+ return null;
+ try {
+ zipFile= new ZipFile(archive);
+ } catch(IOException io) {
+ return null;
+ }
+ ZipEntry entry= zipFile.getEntry(fileName);
+ if (entry == null)
+ return null;
+ int size= (int) entry.getSize();
+ try {
+ stream= zipFile.getInputStream(entry);
+ byte[] data= new byte[size];
+ int pos= 0;
+ while (pos < size) {
+ int n= stream.read(data, pos, data.length - pos);
+ pos += n;
+ }
+ zipFile.close();
+ return data;
+ } catch (IOException e) {
+ } finally {
+ try {
+ if (stream != null)
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ return null;
+ }
+
+ private void readExcludedPackages() {
+ fExcluded= new Vector(10);
+ for (int i= 0; i < defaultExclusions.length; i++)
+ fExcluded.addElement(defaultExclusions[i]);
+
+ InputStream is= getClass().getResourceAsStream(EXCLUDED_FILE);
+ if (is == null)
+ return;
+ Properties p= new Properties();
+ try {
+ p.load(is);
+ }
+ catch (IOException e) {
+ return;
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+ }
+ for (Enumeration e= p.propertyNames(); e.hasMoreElements(); ) {
+ String key= (String)e.nextElement();
+ if (key.startsWith("excluded.")) {
+ String path= p.getProperty(key);
+ path= path.trim();
+ if (path.endsWith("*"))
+ path= path.substring(0, path.length()-1);
+ if (path.length() > 0)
+ fExcluded.addElement(path);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/junit/runner/TestCollector.java b/src/junit/runner/TestCollector.java
new file mode 100644
index 0000000..276e7fa
--- /dev/null
+++ b/src/junit/runner/TestCollector.java
@@ -0,0 +1,17 @@
+package junit.runner;
+
+import java.util.Enumeration;
+import junit.swingui.TestSelector;
+
+
+/**
+ * Collects Test class names to be presented
+ * by the TestSelector.
+ * @see TestSelector
+ */
+public interface TestCollector {
+ /**
+ * Returns an enumeration of Strings with qualified class names
+ */
+ public Enumeration collectTests();
+}
diff --git a/src/junit/runner/TestRunListener.java b/src/junit/runner/TestRunListener.java
new file mode 100644
index 0000000..b11ef07
--- /dev/null
+++ b/src/junit/runner/TestRunListener.java
@@ -0,0 +1,19 @@
+package junit.runner;
+/**
+ * A listener interface for observing the
+ * execution of a test run. Unlike TestListener,
+ * this interface using only primitive objects,
+ * making it suitable for remote test execution.
+ */
+ public interface TestRunListener {
+ /* test status constants*/
+ public static final int STATUS_ERROR= 1;
+ public static final int STATUS_FAILURE= 2;
+
+ public void testRunStarted(String testSuiteName, int testCount);
+ public void testRunEnded(long elapsedTime);
+ public void testRunStopped(long elapsedTime);
+ public void testStarted(String testName);
+ public void testEnded(String testName);
+ public void testFailed(int status, String testName, String trace);
+}
diff --git a/src/junit/runner/TestSuiteLoader.java b/src/junit/runner/TestSuiteLoader.java
new file mode 100644
index 0000000..2db589e
--- /dev/null
+++ b/src/junit/runner/TestSuiteLoader.java
@@ -0,0 +1,9 @@
+package junit.runner;
+
+/**
+ * An interface to define how a test suite should be loaded.
+ */
+public interface TestSuiteLoader {
+ abstract public Class load(String suiteClassName) throws ClassNotFoundException;
+ abstract public Class reload(Class aClass) throws ClassNotFoundException;
+} \ No newline at end of file
diff --git a/src/junit/runner/Version.java b/src/junit/runner/Version.java
new file mode 100644
index 0000000..7fd76aa
--- /dev/null
+++ b/src/junit/runner/Version.java
@@ -0,0 +1,18 @@
+package junit.runner;
+
+/**
+ * This class defines the current version of JUnit
+ */
+public class Version {
+ private Version() {
+ // don't instantiate
+ }
+
+ public static String id() {
+ return "3.8.2";
+ }
+
+ public static void main(String[] args) {
+ System.out.println(id());
+ }
+}
diff --git a/src/junit/runner/excluded.properties b/src/junit/runner/excluded.properties
new file mode 100644
index 0000000..011ae3c
--- /dev/null
+++ b/src/junit/runner/excluded.properties
@@ -0,0 +1,13 @@
+#
+# The list of excluded package paths for the TestCaseClassLoader
+#
+excluded.0=sun.*
+excluded.1=com.sun.*
+excluded.2=org.omg.*
+excluded.3=javax.*
+excluded.4=sunw.*
+excluded.5=java.*
+excluded.6=org.w3c.dom.*
+excluded.7=org.xml.sax.*
+excluded.8=net.jini.*
+excluded.9=org.apache.commons.logging.* \ No newline at end of file
diff --git a/src/junit/runner/logo.gif b/src/junit/runner/logo.gif
new file mode 100644
index 0000000..d0e1547
--- /dev/null
+++ b/src/junit/runner/logo.gif
Binary files differ
diff --git a/src/junit/runner/smalllogo.gif b/src/junit/runner/smalllogo.gif
new file mode 100644
index 0000000..7b25eaf
--- /dev/null
+++ b/src/junit/runner/smalllogo.gif
Binary files differ
diff --git a/src/junit/swingui/AboutDialog.java b/src/junit/swingui/AboutDialog.java
new file mode 100644
index 0000000..c55b420
--- /dev/null
+++ b/src/junit/swingui/AboutDialog.java
@@ -0,0 +1,93 @@
+package junit.swingui;
+
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+
+import junit.runner.BaseTestRunner;
+import junit.runner.Version;
+
+/**
+ * The AboutDialog.
+ */
+class AboutDialog extends JDialog {
+ public AboutDialog(JFrame parent) {
+ super(parent, true);
+
+ setResizable(false);
+ getContentPane().setLayout(new GridBagLayout());
+ setSize(330, 138);
+ setTitle("About");
+ // setLocationRelativeTo is only available in JDK 1.4
+ try {
+ setLocationRelativeTo(parent);
+ } catch (NoSuchMethodError e) {
+ TestSelector.centerWindow(this);
+ }
+
+ JButton close= new JButton("Close");
+ close.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ dispose();
+ }
+ }
+ );
+ getRootPane().setDefaultButton(close);
+ JLabel label1= new JLabel("JUnit");
+ label1.setFont(new Font("dialog", Font.PLAIN, 36));
+
+ JLabel label2= new JLabel("JUnit "+Version.id()+" by Kent Beck and Erich Gamma");
+ label2.setFont(new Font("dialog", Font.PLAIN, 14));
+
+ JLabel logo= createLogo();
+
+ GridBagConstraints constraintsLabel1= new GridBagConstraints();
+ constraintsLabel1.gridx = 3; constraintsLabel1.gridy = 0;
+ constraintsLabel1.gridwidth = 1; constraintsLabel1.gridheight = 1;
+ constraintsLabel1.anchor = GridBagConstraints.CENTER;
+ getContentPane().add(label1, constraintsLabel1);
+
+ GridBagConstraints constraintsLabel2= new GridBagConstraints();
+ constraintsLabel2.gridx = 2; constraintsLabel2.gridy = 1;
+ constraintsLabel2.gridwidth = 2; constraintsLabel2.gridheight = 1;
+ constraintsLabel2.anchor = GridBagConstraints.CENTER;
+ getContentPane().add(label2, constraintsLabel2);
+
+ GridBagConstraints constraintsButton1= new GridBagConstraints();
+ constraintsButton1.gridx = 2; constraintsButton1.gridy = 2;
+ constraintsButton1.gridwidth = 2; constraintsButton1.gridheight = 1;
+ constraintsButton1.anchor = GridBagConstraints.CENTER;
+ constraintsButton1.insets= new Insets(8, 0, 8, 0);
+ getContentPane().add(close, constraintsButton1);
+
+ GridBagConstraints constraintsLogo1= new GridBagConstraints();
+ constraintsLogo1.gridx = 2; constraintsLogo1.gridy = 0;
+ constraintsLogo1.gridwidth = 1; constraintsLogo1.gridheight = 1;
+ constraintsLogo1.anchor = GridBagConstraints.CENTER;
+ getContentPane().add(logo, constraintsLogo1);
+
+ addWindowListener(
+ new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ dispose();
+ }
+ }
+ );
+ }
+ protected JLabel createLogo() {
+ Icon icon= TestRunner.getIconResource(BaseTestRunner.class, "logo.gif");
+ return new JLabel(icon);
+ }
+} \ No newline at end of file
diff --git a/src/junit/swingui/CounterPanel.java b/src/junit/swingui/CounterPanel.java
new file mode 100644
index 0000000..cac4427
--- /dev/null
+++ b/src/junit/swingui/CounterPanel.java
@@ -0,0 +1,118 @@
+package junit.swingui;
+
+import java.awt.Component;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+
+import javax.swing.BorderFactory;
+import javax.swing.Icon;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.SwingConstants;
+
+/**
+ * A panel with test run counters
+ */
+public class CounterPanel extends JPanel {
+ private JTextField fNumberOfErrors;
+ private JTextField fNumberOfFailures;
+ private JTextField fNumberOfRuns;
+ private Icon fFailureIcon= TestRunner.getIconResource(getClass(), "icons/failure.gif");
+ private Icon fErrorIcon= TestRunner.getIconResource(getClass(), "icons/error.gif");
+
+ private int fTotal;
+
+ public CounterPanel() {
+ super(new GridBagLayout());
+ fNumberOfErrors= createOutputField(5);
+ fNumberOfFailures= createOutputField(5);
+ fNumberOfRuns= createOutputField(9);
+
+ addToGrid(new JLabel("Runs:", SwingConstants.CENTER),
+ 0, 0, 1, 1, 0.0, 0.0,
+ GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0));
+ addToGrid(fNumberOfRuns,
+ 1, 0, 1, 1, 0.33, 0.0,
+ GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 8, 0, 0));
+
+ addToGrid(new JLabel("Errors:", fErrorIcon, SwingConstants.LEFT),
+ 2, 0, 1, 1, 0.0, 0.0,
+ GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 8, 0, 0));
+ addToGrid(fNumberOfErrors,
+ 3, 0, 1, 1, 0.33, 0.0,
+ GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 8, 0, 0));
+
+ addToGrid(new JLabel("Failures:", fFailureIcon, SwingConstants.LEFT),
+ 4, 0, 1, 1, 0.0, 0.0,
+ GridBagConstraints.CENTER, GridBagConstraints.NONE,
+ new Insets(0, 8, 0, 0));
+ addToGrid(fNumberOfFailures,
+ 5, 0, 1, 1, 0.33, 0.0,
+ GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
+ new Insets(0, 8, 0, 0));
+ }
+
+ private JTextField createOutputField(int width) {
+ JTextField field= new JTextField("0", width);
+ // force a fixed layout to avoid accidental hiding on relayout
+ field.setMinimumSize(field.getPreferredSize());
+ field.setMaximumSize(field.getPreferredSize());
+ field.setHorizontalAlignment(SwingConstants.LEFT);
+ field.setFont(StatusLine.BOLD_FONT);
+ field.setEditable(false);
+ field.setBorder(BorderFactory.createEmptyBorder());
+ return field;
+ }
+
+ public void addToGrid(Component comp,
+ int gridx, int gridy, int gridwidth, int gridheight,
+ double weightx, double weighty,
+ int anchor, int fill,
+ Insets insets) {
+
+ GridBagConstraints constraints= new GridBagConstraints();
+ constraints.gridx= gridx;
+ constraints.gridy= gridy;
+ constraints.gridwidth= gridwidth;
+ constraints.gridheight= gridheight;
+ constraints.weightx= weightx;
+ constraints.weighty= weighty;
+ constraints.anchor= anchor;
+ constraints.fill= fill;
+ constraints.insets= insets;
+ add(comp, constraints);
+ }
+
+ public void reset() {
+ setLabelValue(fNumberOfErrors, 0);
+ setLabelValue(fNumberOfFailures, 0);
+ setLabelValue(fNumberOfRuns, 0);
+ fTotal= 0;
+ }
+
+ public void setTotal(int value) {
+ fTotal= value;
+ }
+
+ public void setRunValue(int value) {
+ fNumberOfRuns.setText(Integer.toString(value) + "/" + fTotal);
+ }
+
+ public void setErrorValue(int value) {
+ setLabelValue(fNumberOfErrors, value);
+ }
+
+ public void setFailureValue(int value) {
+ setLabelValue(fNumberOfFailures, value);
+ }
+
+ private void setLabelValue(JTextField label, int value) {
+ label.setText(Integer.toString(value));
+ }
+} \ No newline at end of file
diff --git a/src/junit/swingui/DefaultFailureDetailView.java b/src/junit/swingui/DefaultFailureDetailView.java
new file mode 100644
index 0000000..51e79c7
--- /dev/null
+++ b/src/junit/swingui/DefaultFailureDetailView.java
@@ -0,0 +1,101 @@
+package junit.swingui;
+
+import java.awt.Component;
+import java.awt.Font;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.swing.AbstractListModel;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.JList;
+import javax.swing.ListSelectionModel;
+
+import junit.framework.TestFailure;
+import junit.runner.BaseTestRunner;
+import junit.runner.FailureDetailView;
+
+/**
+ * A view that shows a stack trace of a failure
+ */
+public class DefaultFailureDetailView implements FailureDetailView {
+ JList fList;
+
+ /**
+ * A ListModel representing the scanned failure stack trace.
+ */
+ static class StackTraceListModel extends AbstractListModel {
+ private Vector fLines= new Vector(20);
+
+ public Object getElementAt(int index) {
+ return fLines.elementAt(index);
+ }
+
+ public int getSize() {
+ return fLines.size();
+ }
+
+ public void setTrace(String trace) {
+ scan(trace);
+ fireContentsChanged(this, 0, fLines.size());
+ }
+
+ public void clear() {
+ fLines.removeAllElements();
+ fireContentsChanged(this, 0, fLines.size());
+ }
+
+ private void scan(String trace) {
+ fLines.removeAllElements();
+ StringTokenizer st= new StringTokenizer(trace, "\n\r", false);
+ while (st.hasMoreTokens())
+ fLines.addElement(st.nextToken());
+ }
+ }
+
+ /**
+ * Renderer for stack entries
+ */
+ static class StackEntryRenderer extends DefaultListCellRenderer {
+
+ public Component getListCellRendererComponent(
+ JList list, Object value, int modelIndex,
+ boolean isSelected, boolean cellHasFocus) {
+ String text= ((String)value).replace('\t', ' ');
+ Component c= super.getListCellRendererComponent(list, text, modelIndex, isSelected, cellHasFocus);
+ setText(text);
+ setToolTipText(text);
+ return c;
+ }
+ }
+
+ /**
+ * Returns the component used to present the trace
+ */
+ public Component getComponent() {
+ if (fList == null) {
+ fList= new JList(new StackTraceListModel());
+ fList.setFont(new Font("Dialog", Font.PLAIN, 12));
+ fList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ fList.setVisibleRowCount(5);
+ fList.setCellRenderer(new StackEntryRenderer());
+ }
+ return fList;
+ }
+
+ /**
+ * Shows a TestFailure
+ */
+ public void showFailure(TestFailure failure) {
+ getModel().setTrace(BaseTestRunner.getFilteredTrace(failure.trace()));
+ }
+ /**
+ * Clears the output.
+ */
+ public void clear() {
+ getModel().clear();
+ }
+
+ private StackTraceListModel getModel() {
+ return (StackTraceListModel)fList.getModel();
+ }
+} \ No newline at end of file
diff --git a/src/junit/swingui/FailureRunView.java b/src/junit/swingui/FailureRunView.java
new file mode 100644
index 0000000..3ec6126
--- /dev/null
+++ b/src/junit/swingui/FailureRunView.java
@@ -0,0 +1,122 @@
+package junit.swingui;
+
+import java.awt.Component;
+import java.awt.Font;
+
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.Icon;
+import javax.swing.JList;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.ListModel;
+import javax.swing.ListSelectionModel;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import junit.framework.Test;
+import junit.framework.TestFailure;
+import junit.framework.TestResult;
+import junit.runner.BaseTestRunner;
+
+
+/**
+ * A view presenting the test failures as a list.
+ */
+public class FailureRunView implements TestRunView {
+ JList fFailureList;
+ TestRunContext fRunContext;
+
+ /**
+ * Renders TestFailures in a JList
+ */
+ static class FailureListCellRenderer extends DefaultListCellRenderer {
+ private Icon fFailureIcon;
+ private Icon fErrorIcon;
+
+ FailureListCellRenderer() {
+ super();
+ loadIcons();
+ }
+
+ void loadIcons() {
+ fFailureIcon= TestRunner.getIconResource(getClass(), "icons/failure.gif");
+ fErrorIcon= TestRunner.getIconResource(getClass(), "icons/error.gif");
+ }
+
+ public Component getListCellRendererComponent(
+ JList list, Object value, int modelIndex,
+ boolean isSelected, boolean cellHasFocus) {
+
+ Component c= super.getListCellRendererComponent(list, value, modelIndex, isSelected, cellHasFocus);
+ TestFailure failure= (TestFailure)value;
+ String text= failure.failedTest().toString();
+ String msg= failure.exceptionMessage();
+ if (msg != null)
+ text+= ":" + BaseTestRunner.truncate(msg);
+
+ if (failure.isFailure()) {
+ if (fFailureIcon != null)
+ setIcon(fFailureIcon);
+ } else {
+ if (fErrorIcon != null)
+ setIcon(fErrorIcon);
+ }
+ setText(text);
+ setToolTipText(text);
+ return c;
+ }
+ }
+
+ public FailureRunView(TestRunContext context) {
+ fRunContext= context;
+ fFailureList= new JList(fRunContext.getFailures());
+ fFailureList.setFont(new Font("Dialog", Font.PLAIN, 12));
+
+ fFailureList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ fFailureList.setCellRenderer(new FailureListCellRenderer());
+ fFailureList.setVisibleRowCount(5);
+
+ fFailureList.addListSelectionListener(
+ new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ testSelected();
+ }
+ }
+ );
+ }
+
+ public Test getSelectedTest() {
+ int index= fFailureList.getSelectedIndex();
+ if (index == -1)
+ return null;
+
+ ListModel model= fFailureList.getModel();
+ TestFailure failure= (TestFailure)model.getElementAt(index);
+ return failure.failedTest();
+ }
+
+ public void activate() {
+ testSelected();
+ }
+
+ public void addTab(JTabbedPane pane) {
+ JScrollPane scrollPane= new JScrollPane(fFailureList, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
+ Icon errorIcon= TestRunner.getIconResource(getClass(), "icons/error.gif");
+ pane.addTab("Failures", errorIcon, scrollPane, "The list of failed tests");
+ }
+
+ public void revealFailure(Test failure) {
+ fFailureList.setSelectedIndex(0);
+ }
+
+ public void aboutToStart(Test suite, TestResult result) {
+ }
+
+ public void runFinished(Test suite, TestResult result) {
+ }
+
+ protected void testSelected() {
+ fRunContext.handleTestSelected(getSelectedTest());
+ }
+}
diff --git a/src/junit/swingui/MacProgressBar.java b/src/junit/swingui/MacProgressBar.java
new file mode 100644
index 0000000..1de6cfd
--- /dev/null
+++ b/src/junit/swingui/MacProgressBar.java
@@ -0,0 +1,20 @@
+package junit.swingui;
+
+import javax.swing.JTextField;
+
+/**
+ http://java.sun.com/developer/technicalArticles/JavaLP/JavaToMac2/
+*/
+public class MacProgressBar extends ProgressBar {
+
+ private JTextField component;
+
+ public MacProgressBar(JTextField component) {
+ super();
+ this.component= component;
+ }
+
+ protected void updateBarColor() {
+ component.setBackground(getStatusColor());
+ }
+}
diff --git a/src/junit/swingui/ProgressBar.java b/src/junit/swingui/ProgressBar.java
new file mode 100644
index 0000000..d5de71e
--- /dev/null
+++ b/src/junit/swingui/ProgressBar.java
@@ -0,0 +1,46 @@
+package junit.swingui;
+
+import java.awt.Color;
+
+import javax.swing.JProgressBar;
+
+/**
+ * A progress bar showing the green/red status
+ */
+class ProgressBar extends JProgressBar {
+ boolean fError= false;
+
+ public ProgressBar() {
+ super();
+ setForeground(getStatusColor());
+ }
+
+ protected Color getStatusColor() {
+ if (fError)
+ return Color.red;
+ return Color.green;
+ }
+
+ public void reset() {
+ fError= false;
+ updateBarColor();
+ setValue(0);
+ }
+
+ public void start(int total) {
+ setMaximum(total);
+ reset();
+ }
+
+ public void step(int value, boolean successful) {
+ setValue(value);
+ if (!fError && !successful) {
+ fError= true;
+ updateBarColor();
+ }
+ }
+
+ protected void updateBarColor() {
+ setForeground(getStatusColor());
+ }
+}
diff --git a/src/junit/swingui/StatusLine.java b/src/junit/swingui/StatusLine.java
new file mode 100644
index 0000000..e18fda2
--- /dev/null
+++ b/src/junit/swingui/StatusLine.java
@@ -0,0 +1,45 @@
+package junit.swingui;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+
+import javax.swing.BorderFactory;
+import javax.swing.JTextField;
+import javax.swing.border.BevelBorder;
+
+/**
+ * A status line component.
+ */
+public class StatusLine extends JTextField {
+ public static final Font PLAIN_FONT= new Font("dialog", Font.PLAIN, 12);
+ public static final Font BOLD_FONT= new Font("dialog", Font.BOLD, 12);
+
+ public StatusLine(int preferredWidth) {
+ super();
+ setFont(BOLD_FONT);
+ setEditable(false);
+ setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
+ Dimension d= getPreferredSize();
+ d.width= preferredWidth;
+ setPreferredSize(d);
+ }
+
+ public void showInfo(String message) {
+ setFont(PLAIN_FONT);
+ setForeground(Color.black);
+ setText(message);
+ }
+
+ public void showError(String status) {
+ setFont(BOLD_FONT);
+ setForeground(Color.red);
+ setText(status);
+ setToolTipText(status);
+ }
+
+ public void clear() {
+ setText("");
+ setToolTipText(null);
+ }
+} \ No newline at end of file
diff --git a/src/junit/swingui/TestHierarchyRunView.java b/src/junit/swingui/TestHierarchyRunView.java
new file mode 100644
index 0000000..89bd297
--- /dev/null
+++ b/src/junit/swingui/TestHierarchyRunView.java
@@ -0,0 +1,77 @@
+package junit.swingui;
+
+import java.util.Vector;
+
+import javax.swing.Icon;
+import javax.swing.JTabbedPane;
+import javax.swing.JTree;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.TreePath;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+/**
+ * A hierarchical view of a test run.
+ * The contents of a test suite is shown
+ * as a tree.
+ */
+public class TestHierarchyRunView implements TestRunView {
+ TestSuitePanel fTreeBrowser;
+ TestRunContext fTestContext;
+
+ public TestHierarchyRunView(TestRunContext context) {
+ fTestContext= context;
+ fTreeBrowser= new TestSuitePanel();
+ fTreeBrowser.getTree().addTreeSelectionListener(
+ new TreeSelectionListener() {
+ public void valueChanged(TreeSelectionEvent e) {
+ testSelected();
+ }
+ }
+ );
+ }
+
+ public void addTab(JTabbedPane pane) {
+ Icon treeIcon= TestRunner.getIconResource(getClass(), "icons/hierarchy.gif");
+ pane.addTab("Test Hierarchy", treeIcon, fTreeBrowser, "The test hierarchy");
+ }
+
+ public Test getSelectedTest() {
+ return fTreeBrowser.getSelectedTest();
+ }
+
+ public void activate() {
+ testSelected();
+ }
+
+ public void revealFailure(Test failure) {
+ JTree tree= fTreeBrowser.getTree();
+ TestTreeModel model= (TestTreeModel)tree.getModel();
+ Vector vpath= new Vector();
+ int index= model.findTest(failure, (Test)model.getRoot(), vpath);
+ if (index >= 0) {
+ Object[] path= new Object[vpath.size()+1];
+ vpath.copyInto(path);
+ Object last= path[vpath.size()-1];
+ path[vpath.size()]= model.getChild(last, index);
+ TreePath selectionPath= new TreePath(path);
+ tree.setSelectionPath(selectionPath);
+ tree.makeVisible(selectionPath);
+ }
+ }
+
+ public void aboutToStart(Test suite, TestResult result) {
+ fTreeBrowser.showTestTree(suite);
+ result.addListener(fTreeBrowser);
+ }
+
+ public void runFinished(Test suite, TestResult result) {
+ result.removeListener(fTreeBrowser);
+ }
+
+ protected void testSelected() {
+ fTestContext.handleTestSelected(getSelectedTest());
+ }
+}
diff --git a/src/junit/swingui/TestRunContext.java b/src/junit/swingui/TestRunContext.java
new file mode 100644
index 0000000..038e3c4
--- /dev/null
+++ b/src/junit/swingui/TestRunContext.java
@@ -0,0 +1,21 @@
+package junit.swingui;
+
+import javax.swing.ListModel;
+
+import junit.framework.Test;
+
+/**
+ * The interface for accessing the Test run context. Test run views
+ * should use this interface rather than accessing the TestRunner
+ * directly.
+ */
+public interface TestRunContext {
+ /**
+ * Handles the selection of a Test.
+ */
+ public void handleTestSelected(Test test);
+ /**
+ * Returns the failure model
+ */
+ public ListModel getFailures();
+} \ No newline at end of file
diff --git a/src/junit/swingui/TestRunView.java b/src/junit/swingui/TestRunView.java
new file mode 100644
index 0000000..1eb5491
--- /dev/null
+++ b/src/junit/swingui/TestRunView.java
@@ -0,0 +1,39 @@
+package junit.swingui;
+
+import javax.swing.JTabbedPane;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+/**
+ * A TestRunView is shown as a page in a tabbed folder.
+ * It contributes the page contents and can return
+ * the currently selected tests. A TestRunView is
+ * notified about the start and finish of a run.
+ */
+interface TestRunView {
+ /**
+ * Returns the currently selected Test in the View
+ */
+ public Test getSelectedTest();
+ /**
+ * Activates the TestRunView
+ */
+ public void activate();
+ /**
+ * Reveals the given failure
+ */
+ public void revealFailure(Test failure);
+ /**
+ * Adds the TestRunView to the test run views tab
+ */
+ public void addTab(JTabbedPane pane);
+ /**
+ * Informs that the suite is about to start
+ */
+ public void aboutToStart(Test suite, TestResult result);
+ /**
+ * Informs that the run of the test suite has finished
+ */
+ public void runFinished(Test suite, TestResult result);
+} \ No newline at end of file
diff --git a/src/junit/swingui/TestRunner.java b/src/junit/swingui/TestRunner.java
new file mode 100644
index 0000000..44aa7a7
--- /dev/null
+++ b/src/junit/swingui/TestRunner.java
@@ -0,0 +1,849 @@
+package junit.swingui;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.GridLayout;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.swing.DefaultListModel;
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSeparator;
+import javax.swing.JSplitPane;
+import javax.swing.JTabbedPane;
+import javax.swing.ListModel;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestFailure;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+import junit.runner.BaseTestRunner;
+import junit.runner.FailureDetailView;
+import junit.runner.SimpleTestCollector;
+import junit.runner.TestCollector;
+import junit.runner.TestRunListener;
+import junit.runner.Version;
+
+/**
+ * A Swing based user interface to run tests.
+ * Enter the name of a class which either provides a static
+ * suite method or is a subclass of TestCase.
+ * <pre>
+ * Synopsis: java junit.swingui.TestRunner [-noloading] [TestCase]
+ * </pre>
+ * TestRunner takes as an optional argument the name of the testcase class to be run.
+ */
+public class TestRunner extends BaseTestRunner implements TestRunContext {
+ private static final int GAP= 4;
+ private static final int HISTORY_LENGTH= 5;
+
+ protected JFrame fFrame;
+ private Thread fRunner;
+ private TestResult fTestResult;
+
+ private JComboBox fSuiteCombo;
+ private ProgressBar fProgressIndicator;
+ private DefaultListModel fFailures;
+ private JLabel fLogo;
+ private CounterPanel fCounterPanel;
+ private JButton fRun;
+ private JButton fQuitButton;
+ private JButton fRerunButton;
+ private StatusLine fStatusLine;
+ private FailureDetailView fFailureView;
+ private JTabbedPane fTestViewTab;
+ private JCheckBox fUseLoadingRunner;
+ private Vector fTestRunViews= new Vector(); // view associated with tab in tabbed pane
+
+ private static final String TESTCOLLECTOR_KEY= "TestCollectorClass";
+ private static final String FAILUREDETAILVIEW_KEY= "FailureViewClass";
+
+ public TestRunner() {
+ }
+
+ public static void main(String[] args) {
+ new TestRunner().start(args);
+ }
+
+ public static void run(Class test) {
+ String args[]= { test.getName() };
+ main(args);
+ }
+
+ public void testFailed(final int status, final Test test, final Throwable t) {
+ SwingUtilities.invokeLater(
+ new Runnable() {
+ public void run() {
+ switch (status) {
+ case TestRunListener.STATUS_ERROR:
+ fCounterPanel.setErrorValue(fTestResult.errorCount());
+ appendFailure(test, t);
+ break;
+ case TestRunListener.STATUS_FAILURE:
+ fCounterPanel.setFailureValue(fTestResult.failureCount());
+ appendFailure(test, t);
+ break;
+ }
+ }
+ }
+ );
+ }
+
+ public void testStarted(String testName) {
+ postInfo("Running: "+testName);
+ }
+
+ public void testEnded(String stringName) {
+ synchUI();
+ SwingUtilities.invokeLater(
+ new Runnable() {
+ public void run() {
+ if (fTestResult != null) {
+ fCounterPanel.setRunValue(fTestResult.runCount());
+ fProgressIndicator.step(fTestResult.runCount(), fTestResult.wasSuccessful());
+ }
+ }
+ }
+ );
+ }
+
+ public void setSuite(String suiteName) {
+ fSuiteCombo.getEditor().setItem(suiteName);
+ }
+
+ private void addToHistory(final String suite) {
+ for (int i= 0; i < fSuiteCombo.getItemCount(); i++) {
+ if (suite.equals(fSuiteCombo.getItemAt(i))) {
+ fSuiteCombo.removeItemAt(i);
+ fSuiteCombo.insertItemAt(suite, 0);
+ fSuiteCombo.setSelectedIndex(0);
+ return;
+ }
+ }
+ fSuiteCombo.insertItemAt(suite, 0);
+ fSuiteCombo.setSelectedIndex(0);
+ pruneHistory();
+ }
+
+ private void pruneHistory() {
+ int historyLength= getPreference("maxhistory", HISTORY_LENGTH);
+ if (historyLength < 1)
+ historyLength= 1;
+ for (int i= fSuiteCombo.getItemCount()-1; i > historyLength-1; i--)
+ fSuiteCombo.removeItemAt(i);
+ }
+
+ private void appendFailure(Test test, Throwable t) {
+ fFailures.addElement(new TestFailure(test, t));
+ if (fFailures.size() == 1)
+ revealFailure(test);
+ }
+
+ private void revealFailure(Test test) {
+ for (Enumeration e= fTestRunViews.elements(); e.hasMoreElements(); ) {
+ TestRunView v= (TestRunView) e.nextElement();
+ v.revealFailure(test);
+ }
+ }
+
+ protected void aboutToStart(final Test testSuite) {
+ for (Enumeration e= fTestRunViews.elements(); e.hasMoreElements(); ) {
+ TestRunView v= (TestRunView) e.nextElement();
+ v.aboutToStart(testSuite, fTestResult);
+ }
+ }
+
+ protected void runFinished(final Test testSuite) {
+ SwingUtilities.invokeLater(
+ new Runnable() {
+ public void run() {
+ for (Enumeration e= fTestRunViews.elements(); e.hasMoreElements(); ) {
+ TestRunView v= (TestRunView) e.nextElement();
+ v.runFinished(testSuite, fTestResult);
+ }
+ }
+ }
+ );
+ }
+
+ protected CounterPanel createCounterPanel() {
+ return new CounterPanel();
+ }
+
+ protected JPanel createFailedPanel() {
+ JPanel failedPanel= new JPanel(new GridLayout(0, 1, 0, 2));
+ fRerunButton= new JButton("Run");
+ fRerunButton.setEnabled(false);
+ fRerunButton.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ rerun();
+ }
+ }
+ );
+ failedPanel.add(fRerunButton);
+ return failedPanel;
+ }
+
+ protected FailureDetailView createFailureDetailView() {
+ String className= BaseTestRunner.getPreference(FAILUREDETAILVIEW_KEY);
+ if (className != null) {
+ Class viewClass= null;
+ try {
+ viewClass= Class.forName(className);
+ return (FailureDetailView)viewClass.newInstance();
+ } catch(Exception e) {
+ JOptionPane.showMessageDialog(fFrame, "Could not create Failure DetailView - using default view");
+ }
+ }
+ return new DefaultFailureDetailView();
+ }
+
+ /**
+ * Creates the JUnit menu. Clients override this
+ * method to add additional menu items.
+ */
+ protected JMenu createJUnitMenu() {
+ JMenu menu= new JMenu("JUnit");
+ menu.setMnemonic('J');
+ JMenuItem mi1= new JMenuItem("About...");
+ mi1.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ about();
+ }
+ }
+ );
+ mi1.setMnemonic('A');
+ menu.add(mi1);
+
+ menu.addSeparator();
+ JMenuItem mi2= new JMenuItem(" Exit ");
+ mi2.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ terminate();
+ }
+ }
+ );
+ mi2.setMnemonic('x');
+ menu.add(mi2);
+
+ return menu;
+ }
+
+ protected JFrame createFrame() {
+ JFrame frame= new JFrame("JUnit");
+ Image icon= loadFrameIcon();
+ if (icon != null)
+ frame.setIconImage(icon);
+ frame.getContentPane().setLayout(new BorderLayout(0, 0));
+
+ frame.addWindowListener(
+ new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ terminate();
+ }
+ }
+ );
+ return frame;
+ }
+
+ protected JLabel createLogo() {
+ JLabel label;
+ Icon icon= getIconResource(BaseTestRunner.class, "logo.gif");
+ if (icon != null)
+ label= new JLabel(icon);
+ else
+ label= new JLabel("JV");
+ label.setToolTipText("JUnit Version "+Version.id());
+ return label;
+ }
+
+ protected void createMenus(JMenuBar mb) {
+ mb.add(createJUnitMenu());
+ }
+
+ protected JCheckBox createUseLoaderCheckBox() {
+ boolean useLoader= useReloadingTestSuiteLoader();
+ JCheckBox box= new JCheckBox("Reload classes every run", useLoader);
+ box.setToolTipText("Use a custom class loader to reload the classes for every run");
+ if (inVAJava())
+ box.setVisible(false);
+ return box;
+ }
+
+ protected JButton createQuitButton() {
+ // spaces required to avoid layout flicker
+ // Exit is shorter than Stop that shows in the same column
+ JButton quit= new JButton(" Exit ");
+ quit.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ terminate();
+ }
+ }
+ );
+ return quit;
+ }
+
+ protected JButton createRunButton() {
+ JButton run= new JButton("Run");
+ run.setEnabled(true);
+ run.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ runSuite();
+ }
+ }
+ );
+ return run;
+ }
+
+ protected Component createBrowseButton() {
+ JButton browse= new JButton("...");
+ browse.setToolTipText("Select a Test class");
+ browse.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ browseTestClasses();
+ }
+ }
+ );
+ return browse;
+ }
+
+ protected StatusLine createStatusLine() {
+ return new StatusLine(380);
+ }
+
+ protected JComboBox createSuiteCombo() {
+ JComboBox combo= new JComboBox();
+ combo.setEditable(true);
+ combo.setLightWeightPopupEnabled(false);
+
+ combo.getEditor().getEditorComponent().addKeyListener(
+ new KeyAdapter() {
+ public void keyTyped(KeyEvent e) {
+ textChanged();
+ if (e.getKeyChar() == KeyEvent.VK_ENTER)
+ runSuite();
+ }
+ }
+ );
+ try {
+ loadHistory(combo);
+ } catch (IOException e) {
+ // fails the first time
+ }
+ combo.addItemListener(
+ new ItemListener() {
+ public void itemStateChanged(ItemEvent event) {
+ if (event.getStateChange() == ItemEvent.SELECTED) {
+ textChanged();
+ }
+ }
+ }
+ );
+ return combo;
+ }
+
+ protected JTabbedPane createTestRunViews() {
+ JTabbedPane pane= new JTabbedPane(SwingConstants.BOTTOM);
+
+ FailureRunView lv= new FailureRunView(this);
+ fTestRunViews.addElement(lv);
+ lv.addTab(pane);
+
+ TestHierarchyRunView tv= new TestHierarchyRunView(this);
+ fTestRunViews.addElement(tv);
+ tv.addTab(pane);
+
+ pane.addChangeListener(
+ new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ testViewChanged();
+ }
+ }
+ );
+ return pane;
+ }
+
+ public void testViewChanged() {
+ TestRunView view= (TestRunView)fTestRunViews.elementAt(fTestViewTab.getSelectedIndex());
+ view.activate();
+ }
+
+ protected TestResult createTestResult() {
+ return new TestResult();
+ }
+
+ protected JFrame createUI(String suiteName) {
+ JFrame frame= createFrame();
+ JMenuBar mb= new JMenuBar();
+ createMenus(mb);
+ frame.setJMenuBar(mb);
+
+ JLabel suiteLabel= new JLabel("Test class name:");
+ fSuiteCombo= createSuiteCombo();
+ fRun= createRunButton();
+ frame.getRootPane().setDefaultButton(fRun);
+ Component browseButton= createBrowseButton();
+
+ fUseLoadingRunner= createUseLoaderCheckBox();
+
+ fStatusLine= createStatusLine();
+ if (inMac())
+ fProgressIndicator= new MacProgressBar(fStatusLine);
+ else
+ fProgressIndicator= new ProgressBar();
+ fCounterPanel= createCounterPanel();
+
+ fFailures= new DefaultListModel();
+
+ fTestViewTab= createTestRunViews();
+ JPanel failedPanel= createFailedPanel();
+
+ fFailureView= createFailureDetailView();
+ JScrollPane tracePane= new JScrollPane(fFailureView.getComponent(), ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
+
+
+
+ fQuitButton= createQuitButton();
+ fLogo= createLogo();
+
+ JPanel panel= new JPanel(new GridBagLayout());
+
+ addGrid(panel, suiteLabel, 0, 0, 2, GridBagConstraints.HORIZONTAL, 1.0, GridBagConstraints.WEST);
+ addGrid(panel, fSuiteCombo, 0, 1, 1, GridBagConstraints.HORIZONTAL, 1.0, GridBagConstraints.WEST);
+ addGrid(panel, browseButton, 1, 1, 1, GridBagConstraints.NONE, 0.0, GridBagConstraints.WEST);
+ addGrid(panel, fRun, 2, 1, 1, GridBagConstraints.HORIZONTAL, 0.0, GridBagConstraints.CENTER);
+
+ addGrid(panel, fUseLoadingRunner, 0, 2, 3, GridBagConstraints.NONE, 1.0, GridBagConstraints.WEST);
+ //addGrid(panel, new JSeparator(), 0, 3, 3, GridBagConstraints.HORIZONTAL, 1.0, GridBagConstraints.WEST);
+
+
+ addGrid(panel, fProgressIndicator, 0, 3, 2, GridBagConstraints.HORIZONTAL, 1.0, GridBagConstraints.WEST);
+ addGrid(panel, fLogo, 2, 3, 1, GridBagConstraints.NONE, 0.0, GridBagConstraints.NORTH);
+
+ addGrid(panel, fCounterPanel, 0, 4, 2, GridBagConstraints.NONE, 0.0, GridBagConstraints.WEST);
+ addGrid(panel, new JSeparator(), 0, 5, 2, GridBagConstraints.HORIZONTAL, 1.0, GridBagConstraints.WEST);
+ addGrid(panel, new JLabel("Results:"), 0, 6, 2, GridBagConstraints.HORIZONTAL, 1.0, GridBagConstraints.WEST);
+
+ JSplitPane splitter= new JSplitPane(JSplitPane.VERTICAL_SPLIT, fTestViewTab, tracePane);
+ addGrid(panel, splitter, 0, 7, 2, GridBagConstraints.BOTH, 1.0, GridBagConstraints.WEST);
+
+ addGrid(panel, failedPanel, 2, 7, 1, GridBagConstraints.HORIZONTAL, 0.0, GridBagConstraints.NORTH/*CENTER*/);
+
+ addGrid(panel, fStatusLine, 0, 9, 2, GridBagConstraints.HORIZONTAL, 1.0, GridBagConstraints.CENTER);
+ addGrid(panel, fQuitButton, 2, 9, 1, GridBagConstraints.HORIZONTAL, 0.0, GridBagConstraints.CENTER);
+
+ frame.setContentPane(panel);
+ frame.pack();
+ frame.setLocation(200, 200);
+ return frame;
+ }
+
+ private void addGrid(JPanel p, Component co, int x, int y, int w, int fill, double wx, int anchor) {
+ GridBagConstraints c= new GridBagConstraints();
+ c.gridx= x; c.gridy= y;
+ c.gridwidth= w;
+ c.anchor= anchor;
+ c.weightx= wx;
+ c.fill= fill;
+ if (fill == GridBagConstraints.BOTH || fill == GridBagConstraints.VERTICAL)
+ c.weighty= 1.0;
+ c.insets= new Insets(y == 0 ? 10 : 0, x == 0 ? 10 : GAP, GAP, GAP);
+ p.add(co, c);
+ }
+
+ protected String getSuiteText() {
+ if (fSuiteCombo == null)
+ return "";
+ return (String)fSuiteCombo.getEditor().getItem();
+ }
+
+ public ListModel getFailures() {
+ return fFailures;
+ }
+
+ public void insertUpdate(DocumentEvent event) {
+ textChanged();
+ }
+
+ protected Object instanciateClass(String fullClassName, Object param) {
+ try {
+ Class clazz= Class.forName(fullClassName);
+ if (param == null) {
+ return clazz.newInstance();
+ } else {
+ Class[] clazzParam= {param.getClass()};
+ Constructor clazzConstructor= clazz.getConstructor(clazzParam);
+ Object[] objectParam= {param};
+ return clazzConstructor.newInstance(objectParam);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public void browseTestClasses() {
+ TestCollector collector= createTestCollector();
+ TestSelector selector= new TestSelector(fFrame, collector);
+ if (selector.isEmpty()) {
+ JOptionPane.showMessageDialog(fFrame, "No Test Cases found.\nCheck that the configured \'TestCollector\' is supported on this platform.");
+ return;
+ }
+ selector.show();
+ String className= selector.getSelectedItem();
+ if (className != null)
+ setSuite(className);
+ }
+
+ TestCollector createTestCollector() {
+ String className= BaseTestRunner.getPreference(TESTCOLLECTOR_KEY);
+ if (className != null) {
+ Class collectorClass= null;
+ try {
+ collectorClass= Class.forName(className);
+ return (TestCollector)collectorClass.newInstance();
+ } catch(Exception e) {
+ JOptionPane.showMessageDialog(fFrame, "Could not create TestCollector - using default collector");
+ }
+ }
+ return new SimpleTestCollector();
+ }
+
+ private Image loadFrameIcon() {
+ ImageIcon icon= (ImageIcon)getIconResource(BaseTestRunner.class, "smalllogo.gif");
+ if (icon != null)
+ return icon.getImage();
+ return null;
+ }
+
+ private void loadHistory(JComboBox combo) throws IOException {
+ BufferedReader br= new BufferedReader(new FileReader(getSettingsFile()));
+ int itemCount= 0;
+ try {
+ String line;
+ while ((line= br.readLine()) != null) {
+ combo.addItem(line);
+ itemCount++;
+ }
+ if (itemCount > 0)
+ combo.setSelectedIndex(0);
+
+ } finally {
+ br.close();
+ }
+ }
+
+ private File getSettingsFile() {
+ String home= System.getProperty("user.home");
+ return new File(home,".junitsession");
+ }
+
+ private void postInfo(final String message) {
+ SwingUtilities.invokeLater(
+ new Runnable() {
+ public void run() {
+ showInfo(message);
+ }
+ }
+ );
+ }
+
+ private void postStatus(final String status) {
+ SwingUtilities.invokeLater(
+ new Runnable() {
+ public void run() {
+ showStatus(status);
+ }
+ }
+ );
+ }
+
+ public void removeUpdate(DocumentEvent event) {
+ textChanged();
+ }
+
+ private void rerun() {
+ TestRunView view= (TestRunView)fTestRunViews.elementAt(fTestViewTab.getSelectedIndex());
+ Test rerunTest= view.getSelectedTest();
+ if (rerunTest != null)
+ rerunTest(rerunTest);
+ }
+
+ private void rerunTest(Test test) {
+ if (!(test instanceof TestCase)) {
+ showInfo("Could not reload "+ test.toString());
+ return;
+ }
+ Test reloadedTest= null;
+ TestCase rerunTest= (TestCase)test;
+
+ try {
+ Class reloadedTestClass= getLoader().reload(test.getClass());
+ reloadedTest= TestSuite.createTest(reloadedTestClass, rerunTest.getName());
+ } catch(Exception e) {
+ showInfo("Could not reload "+ test.toString());
+ return;
+ }
+ TestResult result= new TestResult();
+ reloadedTest.run(result);
+
+ String message= reloadedTest.toString();
+ if(result.wasSuccessful())
+ showInfo(message+" was successful");
+ else if (result.errorCount() == 1)
+ showStatus(message+" had an error");
+ else
+ showStatus(message+" had a failure");
+ }
+
+ protected void reset() {
+ fCounterPanel.reset();
+ fProgressIndicator.reset();
+ fRerunButton.setEnabled(false);
+ fFailureView.clear();
+ fFailures.clear();
+ }
+
+ protected void runFailed(String message) {
+ showStatus(message);
+ fRun.setText("Run");
+ fRunner= null;
+ }
+
+ synchronized public void runSuite() {
+ if (fRunner != null) {
+ fTestResult.stop();
+ } else {
+ setLoading(shouldReload());
+ reset();
+ showInfo("Load Test Case...");
+ final String suiteName= getSuiteText();
+ final Test testSuite= getTest(suiteName);
+ if (testSuite != null) {
+ addToHistory(suiteName);
+ doRunTest(testSuite);
+ }
+ }
+ }
+
+ private boolean shouldReload() {
+ return !inVAJava() && fUseLoadingRunner.isSelected();
+ }
+
+
+ synchronized protected void runTest(final Test testSuite) {
+ if (fRunner != null) {
+ fTestResult.stop();
+ } else {
+ reset();
+ if (testSuite != null) {
+ doRunTest(testSuite);
+ }
+ }
+ }
+
+ private void doRunTest(final Test testSuite) {
+ setButtonLabel(fRun, "Stop");
+ fRunner= new Thread("TestRunner-Thread") {
+ public void run() {
+ TestRunner.this.start(testSuite);
+ postInfo("Running...");
+
+ long startTime= System.currentTimeMillis();
+ testSuite.run(fTestResult);
+
+ if (fTestResult.shouldStop()) {
+ postStatus("Stopped");
+ } else {
+ long endTime= System.currentTimeMillis();
+ long runTime= endTime-startTime;
+ postInfo("Finished: " + elapsedTimeAsString(runTime) + " seconds");
+ }
+ runFinished(testSuite);
+ setButtonLabel(fRun, "Run");
+ fRunner= null;
+ System.gc();
+ }
+ };
+ // make sure that the test result is created before we start the
+ // test runner thread so that listeners can register for it.
+ fTestResult= createTestResult();
+ fTestResult.addListener(TestRunner.this);
+ aboutToStart(testSuite);
+
+ fRunner.start();
+ }
+
+ private void saveHistory() throws IOException {
+ BufferedWriter bw= new BufferedWriter(new FileWriter(getSettingsFile()));
+ try {
+ for (int i= 0; i < fSuiteCombo.getItemCount(); i++) {
+ String testsuite= fSuiteCombo.getItemAt(i).toString();
+ bw.write(testsuite, 0, testsuite.length());
+ bw.newLine();
+ }
+ } finally {
+ bw.close();
+ }
+ }
+
+ private void setButtonLabel(final JButton button, final String label) {
+ SwingUtilities.invokeLater(
+ new Runnable() {
+ public void run() {
+ button.setText(label);
+ }
+ }
+ );
+ }
+
+ public void handleTestSelected(Test test) {
+ fRerunButton.setEnabled(test != null && (test instanceof TestCase));
+ showFailureDetail(test);
+ }
+
+ private void showFailureDetail(Test test) {
+ if (test != null) {
+ ListModel failures= getFailures();
+ for (int i= 0; i < failures.getSize(); i++) {
+ TestFailure failure= (TestFailure)failures.getElementAt(i);
+ if (failure.failedTest() == test) {
+ fFailureView.showFailure(failure);
+ return;
+ }
+ }
+ }
+ fFailureView.clear();
+ }
+
+ private void showInfo(String message) {
+ fStatusLine.showInfo(message);
+ }
+
+ private void showStatus(String status) {
+ fStatusLine.showError(status);
+ }
+
+ /**
+ * Starts the TestRunner
+ */
+ public void start(String[] args) {
+ String suiteName= processArguments(args);
+ fFrame= createUI(suiteName);
+ fFrame.pack();
+ fFrame.setVisible(true);
+
+ if (suiteName != null) {
+ setSuite(suiteName);
+ runSuite();
+ }
+ }
+
+ private void start(final Test test) {
+ SwingUtilities.invokeLater(
+ new Runnable() {
+ public void run() {
+ int total= test.countTestCases();
+ fProgressIndicator.start(total);
+ fCounterPanel.setTotal(total);
+ }
+ }
+ );
+ }
+
+ /**
+ * Wait until all the events are processed in the event thread
+ */
+ private void synchUI() {
+ try {
+ SwingUtilities.invokeAndWait(
+ new Runnable() {
+ public void run() {}
+ }
+ );
+ }
+ catch (Exception e) {
+ }
+ }
+
+ /**
+ * Terminates the TestRunner
+ */
+ public void terminate() {
+ fFrame.dispose();
+ try {
+ saveHistory();
+ } catch (IOException e) {
+ System.out.println("Couldn't save test run history");
+ }
+ System.exit(0);
+ }
+
+ public void textChanged() {
+ fRun.setEnabled(getSuiteText().length() > 0);
+ clearStatus();
+ }
+
+ protected void clearStatus() {
+ fStatusLine.clear();
+ }
+
+ public static Icon getIconResource(Class clazz, String name) {
+ URL url= clazz.getResource(name);
+ if (url == null) {
+ System.err.println("Warning: could not load \""+name+"\" icon");
+ return null;
+ }
+ return new ImageIcon(url);
+ }
+
+ private void about() {
+ AboutDialog about= new AboutDialog(fFrame);
+ about.show();
+ }
+}
diff --git a/src/junit/swingui/TestSelector.java b/src/junit/swingui/TestSelector.java
new file mode 100644
index 0000000..f0f1f9e
--- /dev/null
+++ b/src/junit/swingui/TestSelector.java
@@ -0,0 +1,285 @@
+package junit.swingui;
+
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JScrollPane;
+import javax.swing.ListModel;
+import javax.swing.ListSelectionModel;
+import javax.swing.UIManager;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import junit.runner.Sorter;
+import junit.runner.TestCollector;
+
+/**
+ * A test class selector. A simple dialog to pick the name of a test suite.
+ */
+public class TestSelector extends JDialog {
+ private JButton fCancel;
+ private JButton fOk;
+ private JList fList;
+ private JScrollPane fScrolledList;
+ private JLabel fDescription;
+ private String fSelectedItem;
+
+ /**
+ * Renders TestFailures in a JList
+ */
+ static class TestCellRenderer extends DefaultListCellRenderer {
+ Icon fLeafIcon;
+ Icon fSuiteIcon;
+
+ public TestCellRenderer() {
+ fLeafIcon= UIManager.getIcon("Tree.leafIcon");
+ fSuiteIcon= UIManager.getIcon("Tree.closedIcon");
+ }
+
+ public Component getListCellRendererComponent(
+ JList list, Object value, int modelIndex,
+ boolean isSelected, boolean cellHasFocus) {
+ Component c= super.getListCellRendererComponent(list, value, modelIndex, isSelected, cellHasFocus);
+ String displayString= displayString((String)value);
+
+ if (displayString.startsWith("AllTests"))
+ setIcon(fSuiteIcon);
+ else
+ setIcon(fLeafIcon);
+
+ setText(displayString);
+ return c;
+ }
+
+ public static String displayString(String className) {
+ int typeIndex= className.lastIndexOf('.');
+ if (typeIndex < 0)
+ return className;
+ return className.substring(typeIndex+1) + " - " + className.substring(0, typeIndex);
+ }
+
+ public static boolean matchesKey(String s, char ch) {
+ return ch == Character.toUpperCase(s.charAt(typeIndex(s)));
+ }
+
+ private static int typeIndex(String s) {
+ int typeIndex= s.lastIndexOf('.');
+ int i= 0;
+ if (typeIndex > 0)
+ i= typeIndex+1;
+ return i;
+ }
+ }
+
+ protected class DoubleClickListener extends MouseAdapter {
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ okSelected();
+ }
+ }
+ }
+
+ protected class KeySelectListener extends KeyAdapter {
+ public void keyTyped(KeyEvent e) {
+ keySelectTestClass(e.getKeyChar());
+ }
+ }
+
+ public TestSelector(Frame parent, TestCollector testCollector) {
+ super(parent, true);
+ setSize(350, 300);
+ setResizable(false);
+ // setLocationRelativeTo only exists in 1.4
+ try {
+ setLocationRelativeTo(parent);
+ } catch (NoSuchMethodError e) {
+ centerWindow(this);
+ }
+ setTitle("Test Selector");
+
+ Vector list= null;
+ try {
+ parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ list= createTestList(testCollector);
+ } finally {
+ parent.setCursor(Cursor.getDefaultCursor());
+ }
+ fList= new JList(list);
+ fList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ fList.setCellRenderer(new TestCellRenderer());
+ fScrolledList= new JScrollPane(fList);
+
+ fCancel= new JButton("Cancel");
+ fDescription= new JLabel("Select the Test class:");
+ fOk= new JButton("OK");
+ fOk.setEnabled(false);
+ getRootPane().setDefaultButton(fOk);
+
+ defineLayout();
+ addListeners();
+ }
+
+ public static void centerWindow(Component c) {
+ Dimension paneSize= c.getSize();
+ Dimension screenSize= c.getToolkit().getScreenSize();
+ c.setLocation((screenSize.width-paneSize.width)/2, (screenSize.height-paneSize.height)/2);
+ }
+
+ private void addListeners() {
+ fCancel.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ dispose();
+ }
+ }
+ );
+
+ fOk.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ okSelected();
+ }
+ }
+ );
+
+ fList.addMouseListener(new DoubleClickListener());
+ fList.addKeyListener(new KeySelectListener());
+ fList.addListSelectionListener(
+ new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ checkEnableOK(e);
+ }
+ }
+ );
+
+ addWindowListener(
+ new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ dispose();
+ }
+ }
+ );
+ }
+
+ private void defineLayout() {
+ getContentPane().setLayout(new GridBagLayout());
+ GridBagConstraints labelConstraints = new GridBagConstraints();
+ labelConstraints.gridx= 0; labelConstraints.gridy= 0;
+ labelConstraints.gridwidth= 1; labelConstraints.gridheight= 1;
+ labelConstraints.fill= GridBagConstraints.BOTH;
+ labelConstraints.anchor= GridBagConstraints.WEST;
+ labelConstraints.weightx= 1.0;
+ labelConstraints.weighty= 0.0;
+ labelConstraints.insets= new Insets(8, 8, 0, 8);
+ getContentPane().add(fDescription, labelConstraints);
+
+ GridBagConstraints listConstraints = new GridBagConstraints();
+ listConstraints.gridx= 0; listConstraints.gridy= 1;
+ listConstraints.gridwidth= 4; listConstraints.gridheight= 1;
+ listConstraints.fill= GridBagConstraints.BOTH;
+ listConstraints.anchor= GridBagConstraints.CENTER;
+ listConstraints.weightx= 1.0;
+ listConstraints.weighty= 1.0;
+ listConstraints.insets= new Insets(8, 8, 8, 8);
+ getContentPane().add(fScrolledList, listConstraints);
+
+ GridBagConstraints okConstraints= new GridBagConstraints();
+ okConstraints.gridx= 2; okConstraints.gridy= 2;
+ okConstraints.gridwidth= 1; okConstraints.gridheight= 1;
+ okConstraints.anchor= java.awt.GridBagConstraints.EAST;
+ okConstraints.insets= new Insets(0, 8, 8, 8);
+ getContentPane().add(fOk, okConstraints);
+
+
+ GridBagConstraints cancelConstraints = new GridBagConstraints();
+ cancelConstraints.gridx= 3; cancelConstraints.gridy= 2;
+ cancelConstraints.gridwidth= 1; cancelConstraints.gridheight= 1;
+ cancelConstraints.anchor= java.awt.GridBagConstraints.EAST;
+ cancelConstraints.insets= new Insets(0, 8, 8, 8);
+ getContentPane().add(fCancel, cancelConstraints);
+ }
+
+ public void checkEnableOK(ListSelectionEvent e) {
+ fOk.setEnabled(fList.getSelectedIndex() != -1);
+ }
+
+ public void okSelected() {
+ fSelectedItem= (String)fList.getSelectedValue();
+ dispose();
+ }
+
+ public boolean isEmpty() {
+ return fList.getModel().getSize() == 0;
+ }
+
+ public void keySelectTestClass(char ch) {
+ ListModel model= fList.getModel();
+ if (!Character.isJavaIdentifierStart(ch))
+ return;
+ for (int i= 0; i < model.getSize(); i++) {
+ String s= (String)model.getElementAt(i);
+ if (TestCellRenderer.matchesKey(s, Character.toUpperCase(ch))) {
+ fList.setSelectedIndex(i);
+ fList.ensureIndexIsVisible(i);
+ return;
+ }
+ }
+ Toolkit.getDefaultToolkit().beep();
+ }
+
+ public String getSelectedItem() {
+ return fSelectedItem;
+ }
+
+ private Vector createTestList(TestCollector collector) {
+ Enumeration each= collector.collectTests();
+ Vector v= new Vector(200);
+ Vector displayVector= new Vector(v.size());
+ while(each.hasMoreElements()) {
+ String s= (String)each.nextElement();
+ v.addElement(s);
+ displayVector.addElement(TestCellRenderer.displayString(s));
+ }
+ if (v.size() > 0)
+ Sorter.sortStrings(displayVector, 0, displayVector.size()-1, new ParallelSwapper(v));
+ return v;
+ }
+
+ private class ParallelSwapper implements Sorter.Swapper {
+ Vector fOther;
+
+ ParallelSwapper(Vector other) {
+ fOther= other;
+ }
+ public void swap(Vector values, int left, int right) {
+ Object tmp= values.elementAt(left);
+ values.setElementAt(values.elementAt(right), left);
+ values.setElementAt(tmp, right);
+ Object tmp2= fOther.elementAt(left);
+ fOther.setElementAt(fOther.elementAt(right), left);
+ fOther.setElementAt(tmp2, right);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/junit/swingui/TestSuitePanel.java b/src/junit/swingui/TestSuitePanel.java
new file mode 100644
index 0000000..d8902ad
--- /dev/null
+++ b/src/junit/swingui/TestSuitePanel.java
@@ -0,0 +1,172 @@
+package junit.swingui;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.util.Vector;
+
+import javax.swing.Icon;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTree;
+import javax.swing.SwingUtilities;
+import javax.swing.ToolTipManager;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestListener;
+
+/**
+ * A Panel showing a test suite as a tree.
+ */
+class TestSuitePanel extends JPanel implements TestListener {
+ private JTree fTree;
+ private JScrollPane fScrollTree;
+ private TestTreeModel fModel;
+
+ static class TestTreeCellRenderer extends DefaultTreeCellRenderer {
+ private Icon fErrorIcon;
+ private Icon fOkIcon;
+ private Icon fFailureIcon;
+
+ TestTreeCellRenderer() {
+ super();
+ loadIcons();
+ }
+
+ void loadIcons() {
+ fErrorIcon= TestRunner.getIconResource(getClass(), "icons/error.gif");
+ fOkIcon= TestRunner.getIconResource(getClass(), "icons/ok.gif");
+ fFailureIcon= TestRunner.getIconResource(getClass(), "icons/failure.gif");
+ }
+
+ String stripParenthesis(Object o) {
+ String text= o.toString ();
+ int pos= text.indexOf('(');
+ if (pos < 1)
+ return text;
+ return text.substring (0, pos);
+ }
+
+ public Component getTreeCellRendererComponent(JTree tree, Object value,
+ boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
+
+ Component c= super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
+ TreeModel model= tree.getModel();
+ if (model instanceof TestTreeModel) {
+ TestTreeModel testModel= (TestTreeModel)model;
+ Test t= (Test)value;
+ String s= "";
+ if (testModel.isFailure(t)) {
+ if (fFailureIcon != null)
+ setIcon(fFailureIcon);
+ s= " - Failed";
+ }
+ else if (testModel.isError(t)) {
+ if (fErrorIcon != null)
+ setIcon(fErrorIcon);
+ s= " - Error";
+ }
+ else if (testModel.wasRun(t)) {
+ if (fOkIcon != null)
+ setIcon(fOkIcon);
+ s= " - Passed";
+ }
+ if (c instanceof JComponent)
+ ((JComponent)c).setToolTipText(getText()+s);
+ }
+ setText(stripParenthesis(value));
+ return c;
+ }
+ }
+
+ public TestSuitePanel() {
+ super(new BorderLayout());
+ setPreferredSize(new Dimension(300, 100));
+ fTree= new JTree();
+ fTree.setModel(null);
+ fTree.setRowHeight(20);
+ ToolTipManager.sharedInstance().registerComponent(fTree);
+ fTree.putClientProperty("JTree.lineStyle", "Angled");
+ fScrollTree= new JScrollPane(fTree);
+ add(fScrollTree, BorderLayout.CENTER);
+ }
+
+ public void addError(final Test test, final Throwable t) {
+ fModel.addError(test);
+ fireTestChanged(test, true);
+ }
+
+ public void addFailure(final Test test, final AssertionFailedError t) {
+ fModel.addFailure(test);
+ fireTestChanged(test, true);
+ }
+
+ /**
+ * A test ended.
+ */
+ public void endTest(Test test) {
+ fModel.addRunTest(test);
+ fireTestChanged(test, false);
+ }
+
+ /**
+ * A test started.
+ */
+ public void startTest(Test test) {
+ }
+
+ /**
+ * Returns the selected test or null if multiple or none is selected
+ */
+ public Test getSelectedTest() {
+ TreePath[] paths= fTree.getSelectionPaths();
+ if (paths != null && paths.length == 1)
+ return (Test)paths[0].getLastPathComponent();
+ return null;
+ }
+
+ /**
+ * Returns the Tree
+ */
+ public JTree getTree() {
+ return fTree;
+ }
+
+ /**
+ * Shows the test hierarchy starting at the given test
+ */
+ public void showTestTree(Test root) {
+ fModel= new TestTreeModel(root);
+ fTree.setModel(fModel);
+ fTree.setCellRenderer(new TestTreeCellRenderer());
+ }
+
+ private void fireTestChanged(final Test test, final boolean expand) {
+ SwingUtilities.invokeLater(
+ new Runnable() {
+ public void run() {
+ Vector vpath= new Vector();
+ int index= fModel.findTest(test, (Test)fModel.getRoot(), vpath);
+ if (index >= 0) {
+ Object[] path= new Object[vpath.size()];
+ vpath.copyInto(path);
+ TreePath treePath= new TreePath(path);
+ fModel.fireNodeChanged(treePath, index);
+ if (expand) {
+ Object[] fullPath= new Object[vpath.size()+1];
+ vpath.copyInto(fullPath);
+ fullPath[vpath.size()]= fModel.getChild(treePath.getLastPathComponent(), index);;
+ TreePath fullTreePath= new TreePath(fullPath);
+ fTree.scrollPathToVisible(fullTreePath);
+ }
+ }
+ }
+ }
+ );
+ }
+} \ No newline at end of file
diff --git a/src/junit/swingui/TestTreeModel.java b/src/junit/swingui/TestTreeModel.java
new file mode 100644
index 0000000..9f3b0d3
--- /dev/null
+++ b/src/junit/swingui/TestTreeModel.java
@@ -0,0 +1,190 @@
+package junit.swingui;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreePath;
+
+import junit.extensions.TestDecorator;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * A tree model for a Test.
+ */
+class TestTreeModel implements TreeModel {
+ private Test fRoot;
+ private Vector fModelListeners= new Vector();
+ private Hashtable fFailures= new Hashtable();
+ private Hashtable fErrors= new Hashtable();
+ private Hashtable fRunTests= new Hashtable();
+
+ /**
+ * Constructs a tree model with the given test as its root.
+ */
+ public TestTreeModel(Test root) {
+ super();
+ fRoot= root;
+ }
+
+ /**
+ * adds a TreeModelListener
+ */
+ public void addTreeModelListener(TreeModelListener l) {
+ if (!fModelListeners.contains(l))
+ fModelListeners.addElement(l);
+ }
+ /**
+ * Removes a TestModelListener
+ */
+ public void removeTreeModelListener(TreeModelListener l) {
+ fModelListeners.removeElement(l);
+ }
+ /**
+ * Finds the path to a test. Returns the index of the test in its
+ * parent test suite.
+ */
+ public int findTest(Test target, Test node, Vector path) {
+ if (target.equals(node))
+ return 0;
+
+ TestSuite suite= isTestSuite(node);
+ for (int i= 0; i < getChildCount(node); i++) {
+ Test t= suite.testAt(i);
+ int index= findTest(target, t, path);
+ if (index >= 0) {
+ path.insertElementAt(node, 0);
+ if (path.size() == 1)
+ return i;
+ return index;
+ }
+ }
+ return -1;
+ }
+ /**
+ * Fires a node changed event
+ */
+ public void fireNodeChanged(TreePath path, int index) {
+ int[] indices= {index};
+ Object[] changedChildren= {getChild(path.getLastPathComponent(), index)};
+ TreeModelEvent event= new TreeModelEvent(this, path, indices, changedChildren);
+
+ Enumeration e= fModelListeners.elements();
+ while (e.hasMoreElements()) {
+ TreeModelListener l= (TreeModelListener) e.nextElement();
+ l.treeNodesChanged(event);
+ }
+ }
+ /**
+ * Gets the test at the given index
+ */
+ public Object getChild(Object parent, int index) {
+ TestSuite suite= isTestSuite(parent);
+ if (suite != null)
+ return suite.testAt(index);
+ return null;
+ }
+ /**
+ * Gets the number of tests.
+ */
+ public int getChildCount(Object parent) {
+ TestSuite suite= isTestSuite(parent);
+ if (suite != null)
+ return suite.testCount();
+ return 0;
+ }
+ /**
+ * Gets the index of a test in a test suite
+ */
+ public int getIndexOfChild(Object parent, Object child) {
+ TestSuite suite= isTestSuite(parent);
+ if (suite != null) {
+ int i= 0;
+ for (Enumeration e= suite.tests(); e.hasMoreElements(); i++) {
+ if (child.equals(e.nextElement()))
+ return i;
+ }
+ }
+ return -1;
+ }
+ /**
+ * Returns the root of the tree
+ */
+ public Object getRoot() {
+ return fRoot;
+ }
+ /**
+ * Tests if the test is a leaf.
+ */
+ public boolean isLeaf(Object node) {
+ return isTestSuite(node) == null;
+ }
+ /**
+ * Tests if the node is a TestSuite.
+ */
+ TestSuite isTestSuite(Object node) {
+ if (node instanceof TestSuite)
+ return (TestSuite)node;
+ if (node instanceof TestDecorator) {
+ Test baseTest= ((TestDecorator)node).getTest();
+ return isTestSuite(baseTest);
+ }
+ return null;
+ }
+
+ /**
+ * Called when the value of the model object was changed in the view
+ */
+ public void valueForPathChanged(TreePath path, Object newValue) {
+ // we don't support direct editing of the model
+ System.out.println("TreeModel.valueForPathChanged: not implemented");
+ }
+ /**
+ * Remembers a test failure
+ */
+ void addFailure(Test t) {
+ fFailures.put(t, t);
+ }
+ /**
+ * Remembers a test error
+ */
+ void addError(Test t) {
+ fErrors.put(t, t);
+ }
+ /**
+ * Remembers that a test was run
+ */
+ void addRunTest(Test t) {
+ fRunTests.put(t, t);
+ }
+ /**
+ * Returns whether a test was run
+ */
+ boolean wasRun(Test t) {
+ return fRunTests.get(t) != null;
+ }
+ /**
+ * Tests whether a test was an error
+ */
+ boolean isError(Test t) {
+ return (fErrors != null) && fErrors.get(t) != null;
+ }
+ /**
+ * Tests whether a test was a failure
+ */
+ boolean isFailure(Test t) {
+ return (fFailures != null) && fFailures.get(t) != null;
+ }
+ /**
+ * Resets the test results
+ */
+ void resetResults() {
+ fFailures= new Hashtable();
+ fRunTests= new Hashtable();
+ fErrors= new Hashtable();
+ }
+} \ No newline at end of file
diff --git a/src/junit/swingui/icons/error.gif b/src/junit/swingui/icons/error.gif
new file mode 100644
index 0000000..fe13a6a
--- /dev/null
+++ b/src/junit/swingui/icons/error.gif
Binary files differ
diff --git a/src/junit/swingui/icons/failure.gif b/src/junit/swingui/icons/failure.gif
new file mode 100644
index 0000000..156ecd6
--- /dev/null
+++ b/src/junit/swingui/icons/failure.gif
Binary files differ
diff --git a/src/junit/swingui/icons/hierarchy.gif b/src/junit/swingui/icons/hierarchy.gif
new file mode 100644
index 0000000..9f05ed2
--- /dev/null
+++ b/src/junit/swingui/icons/hierarchy.gif
Binary files differ
diff --git a/src/junit/swingui/icons/ok.gif b/src/junit/swingui/icons/ok.gif
new file mode 100644
index 0000000..034825b
--- /dev/null
+++ b/src/junit/swingui/icons/ok.gif
Binary files differ
diff --git a/src/junit/textui/ResultPrinter.java b/src/junit/textui/ResultPrinter.java
new file mode 100644
index 0000000..1ebb7a1
--- /dev/null
+++ b/src/junit/textui/ResultPrinter.java
@@ -0,0 +1,139 @@
+
+package junit.textui;
+
+import java.io.PrintStream;
+import java.text.NumberFormat;
+import java.util.Enumeration;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestFailure;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+import junit.runner.BaseTestRunner;
+
+public class ResultPrinter implements TestListener {
+ PrintStream fWriter;
+ int fColumn= 0;
+
+ public ResultPrinter(PrintStream writer) {
+ fWriter= writer;
+ }
+
+ /* API for use by textui.TestRunner
+ */
+
+ synchronized void print(TestResult result, long runTime) {
+ printHeader(runTime);
+ printErrors(result);
+ printFailures(result);
+ printFooter(result);
+ }
+
+ void printWaitPrompt() {
+ getWriter().println();
+ getWriter().println("<RETURN> to continue");
+ }
+
+ /* Internal methods
+ */
+
+ protected void printHeader(long runTime) {
+ getWriter().println();
+ getWriter().println("Time: "+elapsedTimeAsString(runTime));
+ }
+
+ protected void printErrors(TestResult result) {
+ printDefects(result.errors(), result.errorCount(), "error");
+ }
+
+ protected void printFailures(TestResult result) {
+ printDefects(result.failures(), result.failureCount(), "failure");
+ }
+
+ protected void printDefects(Enumeration booBoos, int count, String type) {
+ if (count == 0) return;
+ if (count == 1)
+ getWriter().println("There was " + count + " " + type + ":");
+ else
+ getWriter().println("There were " + count + " " + type + "s:");
+ for (int i= 1; booBoos.hasMoreElements(); i++) {
+ printDefect((TestFailure) booBoos.nextElement(), i);
+ }
+ }
+
+ public void printDefect(TestFailure booBoo, int count) { // only public for testing purposes
+ printDefectHeader(booBoo, count);
+ printDefectTrace(booBoo);
+ }
+
+ protected void printDefectHeader(TestFailure booBoo, int count) {
+ // I feel like making this a println, then adding a line giving the throwable a chance to print something
+ // before we get to the stack trace.
+ getWriter().print(count + ") " + booBoo.failedTest());
+ }
+
+ protected void printDefectTrace(TestFailure booBoo) {
+ getWriter().print(BaseTestRunner.getFilteredTrace(booBoo.trace()));
+ }
+
+ protected void printFooter(TestResult result) {
+ if (result.wasSuccessful()) {
+ getWriter().println();
+ getWriter().print("OK");
+ getWriter().println (" (" + result.runCount() + " test" + (result.runCount() == 1 ? "": "s") + ")");
+
+ } else {
+ getWriter().println();
+ getWriter().println("FAILURES!!!");
+ getWriter().println("Tests run: "+result.runCount()+
+ ", Failures: "+result.failureCount()+
+ ", Errors: "+result.errorCount());
+ }
+ getWriter().println();
+ }
+
+
+ /**
+ * Returns the formatted string of the elapsed time.
+ * Duplicated from BaseTestRunner. Fix it.
+ */
+ protected String elapsedTimeAsString(long runTime) {
+ return NumberFormat.getInstance().format((double)runTime/1000);
+ }
+
+ public PrintStream getWriter() {
+ return fWriter;
+ }
+ /**
+ * @see junit.framework.TestListener#addError(Test, Throwable)
+ */
+ public void addError(Test test, Throwable t) {
+ getWriter().print("E");
+ }
+
+ /**
+ * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
+ */
+ public void addFailure(Test test, AssertionFailedError t) {
+ getWriter().print("F");
+ }
+
+ /**
+ * @see junit.framework.TestListener#endTest(Test)
+ */
+ public void endTest(Test test) {
+ }
+
+ /**
+ * @see junit.framework.TestListener#startTest(Test)
+ */
+ public void startTest(Test test) {
+ getWriter().print(".");
+ if (fColumn++ >= 40) {
+ getWriter().println();
+ fColumn= 0;
+ }
+ }
+
+}
diff --git a/src/junit/textui/TestRunner.java b/src/junit/textui/TestRunner.java
new file mode 100644
index 0000000..01b9d6d
--- /dev/null
+++ b/src/junit/textui/TestRunner.java
@@ -0,0 +1,207 @@
+package junit.textui;
+
+
+import java.io.PrintStream;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+import junit.runner.BaseTestRunner;
+import junit.runner.StandardTestSuiteLoader;
+import junit.runner.TestSuiteLoader;
+import junit.runner.Version;
+
+/**
+ * A command line based tool to run tests.
+ * <pre>
+ * java junit.textui.TestRunner [-wait] TestCaseClass
+ * </pre>
+ * TestRunner expects the name of a TestCase class as argument.
+ * If this class defines a static <code>suite</code> method it
+ * will be invoked and the returned test is run. Otherwise all
+ * the methods starting with "test" having no arguments are run.
+ * <p>
+ * When the wait command line argument is given TestRunner
+ * waits until the users types RETURN.
+ * <p>
+ * TestRunner prints a trace as the tests are executed followed by a
+ * summary at the end.
+ */
+public class TestRunner extends BaseTestRunner {
+ private ResultPrinter fPrinter;
+
+ public static final int SUCCESS_EXIT= 0;
+ public static final int FAILURE_EXIT= 1;
+ public static final int EXCEPTION_EXIT= 2;
+
+ /**
+ * Constructs a TestRunner.
+ */
+ public TestRunner() {
+ this(System.out);
+ }
+
+ /**
+ * Constructs a TestRunner using the given stream for all the output
+ */
+ public TestRunner(PrintStream writer) {
+ this(new ResultPrinter(writer));
+ }
+
+ /**
+ * Constructs a TestRunner using the given ResultPrinter all the output
+ */
+ public TestRunner(ResultPrinter printer) {
+ fPrinter= printer;
+ }
+
+ /**
+ * Runs a suite extracted from a TestCase subclass.
+ */
+ static public void run(Class testClass) {
+ run(new TestSuite(testClass));
+ }
+
+ /**
+ * Runs a single test and collects its results.
+ * This method can be used to start a test run
+ * from your program.
+ * <pre>
+ * public static void main (String[] args) {
+ * test.textui.TestRunner.run(suite());
+ * }
+ * </pre>
+ */
+ static public TestResult run(Test test) {
+ TestRunner runner= new TestRunner();
+ return runner.doRun(test);
+ }
+
+ /**
+ * Runs a single test and waits until the user
+ * types RETURN.
+ */
+ static public void runAndWait(Test suite) {
+ TestRunner aTestRunner= new TestRunner();
+ aTestRunner.doRun(suite, true);
+ }
+
+ /**
+ * Always use the StandardTestSuiteLoader. Overridden from
+ * BaseTestRunner.
+ */
+ public TestSuiteLoader getLoader() {
+ return new StandardTestSuiteLoader();
+ }
+
+ public void testFailed(int status, Test test, Throwable t) {
+ }
+
+ public void testStarted(String testName) {
+ }
+
+ public void testEnded(String testName) {
+ }
+
+ /**
+ * Creates the TestResult to be used for the test run.
+ */
+ protected TestResult createTestResult() {
+ return new TestResult();
+ }
+
+ public TestResult doRun(Test test) {
+ return doRun(test, false);
+ }
+
+ public TestResult doRun(Test suite, boolean wait) {
+ TestResult result= createTestResult();
+ result.addListener(fPrinter);
+ long startTime= System.currentTimeMillis();
+ suite.run(result);
+ long endTime= System.currentTimeMillis();
+ long runTime= endTime-startTime;
+ fPrinter.print(result, runTime);
+
+ pause(wait);
+ return result;
+ }
+
+ protected void pause(boolean wait) {
+ if (!wait) return;
+ fPrinter.printWaitPrompt();
+ try {
+ System.in.read();
+ }
+ catch(Exception e) {
+ }
+ }
+
+ public static void main(String args[]) {
+ TestRunner aTestRunner= new TestRunner();
+ try {
+ TestResult r= aTestRunner.start(args);
+ if (!r.wasSuccessful())
+ System.exit(FAILURE_EXIT);
+ System.exit(SUCCESS_EXIT);
+ } catch(Exception e) {
+ System.err.println(e.getMessage());
+ System.exit(EXCEPTION_EXIT);
+ }
+ }
+
+ /**
+ * Starts a test run. Analyzes the command line arguments and runs the given
+ * test suite.
+ */
+ public TestResult start(String args[]) throws Exception {
+ String testCase= "";
+ String method= "";
+ boolean wait= false;
+
+ for (int i= 0; i < args.length; i++) {
+ if (args[i].equals("-wait"))
+ wait= true;
+ else if (args[i].equals("-c"))
+ testCase= extractClassName(args[++i]);
+ else if (args[i].equals("-m")) {
+ String arg= args[++i];
+ int lastIndex= arg.lastIndexOf('.');
+ testCase= arg.substring(0, lastIndex);
+ method= arg.substring(lastIndex + 1);
+ } else if (args[i].equals("-v"))
+ System.err.println("JUnit " + Version.id() + " by Kent Beck and Erich Gamma");
+ else
+ testCase= args[i];
+ }
+
+ if (testCase.equals(""))
+ throw new Exception("Usage: TestRunner [-wait] testCaseName, where name is the name of the TestCase class");
+
+ try {
+ if (!method.equals(""))
+ return runSingleMethod(testCase, method, wait);
+ Test suite= getTest(testCase);
+ return doRun(suite, wait);
+ } catch (Exception e) {
+ throw new Exception("Could not create and run test suite: " + e);
+ }
+ }
+
+ protected TestResult runSingleMethod(String testCase, String method, boolean wait) throws Exception {
+ Class testClass= loadSuiteClass(testCase);
+ Test test= TestSuite.createTest(testClass, method);
+ return doRun(test, wait);
+ }
+
+ protected void runFailed(String message) {
+ System.err.println(message);
+ System.exit(FAILURE_EXIT);
+ }
+
+ public void setPrinter(ResultPrinter printer) {
+ fPrinter= printer;
+ }
+
+
+} \ No newline at end of file