diff --git a/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/Server.java b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/Server.java
index 42729df58..b25628f3d 100644
--- a/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/Server.java
+++ b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/Server.java
@@ -116,19 +116,24 @@ public class Server {
}
/** start a server to the specified port */
- public void start() {
- try {
- start(false);
- } catch (InterruptedException e){
- }
- }
-
- /** start a server to the specified port and wait for end */
- public void start(boolean flag) throws InterruptedException {
- Worker worker = new Worker();
- worker.start();
- if (flag){
- worker.join();
+ public void start(boolean loop) throws IOException {
+ server = new ServerSocket(port);
+ while (server != null) {
+ client = server.accept();
+ messenger = new Messenger(client.getInputStream(), client.getOutputStream());
+ TestRunEvent evt = null;
+ try {
+ while ( (evt = messenger.read()) != null ) {
+ dispatcher.dispatchEvent(evt);
+ }
+ } catch (Exception e){
+ e.printStackTrace();
+ //@fixme this stacktrace might be normal when closing
+ // the socket. So decompose the above in distinct steps
+ }
+ if (!loop){
+ break;
+ }
}
}
@@ -169,26 +174,4 @@ public class Server {
} catch (IOException e) {
}
}
-
-//-----
-
- private class Worker extends Thread {
- public void run() {
- try {
- server = new ServerSocket(port);
- client = server.accept();
- messenger = new Messenger(client.getInputStream(), client.getOutputStream());
- TestRunEvent evt = null;
- while ( (evt = messenger.read()) != null ) {
- dispatcher.dispatchEvent(evt);
- }
- } catch (Exception e) {
- //@fixme this stacktrace might be normal when closing
- // the socket. So decompose the above in distinct steps
- } finally {
- cancel();
- shutdown();
- }
- }
- }
}
diff --git a/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/SocketUtil.java b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/SocketUtil.java
deleted file mode 100644
index 70bbe06c7..000000000
--- a/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/SocketUtil.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * The Apache Software License, Version 1.1
- *
- * Copyright (c) 2002 The Apache Software Foundation. All rights
- * reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * 3. The end-user documentation included with the redistribution, if
- * any, must include the following acknowlegement:
- * "This product includes software developed by the
- * Apache Software Foundation (http://www.apache.org/)."
- * Alternately, this acknowlegement may appear in the software itself,
- * if and wherever such third-party acknowlegements normally appear.
- *
- * 4. The names "The Jakarta Project", "Ant", and "Apache Software
- * Foundation" must not be used to endorse or promote products derived
- * from this software without prior written permission. For written
- * permission, please contact apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache"
- * nor may "Apache" appear in their names without prior written
- * permission of the Apache Group.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * .
- */
-package org.apache.tools.ant.taskdefs.optional.rjunit.remote;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-
-/**
- * A set of helper methods related to sockets.
- *
- * @author Stephane Bailliez
- */
-public class SocketUtil {
-
-
- /**
- * Helper method to deserialize an object
- * @param bytes the binary data representing the serialized object.
- * @return the deserialized object.
- * @throws Exception a generic exception if an error occurs when
- * deserializing the object.
- */
- public static Object deserialize(byte[] bytes) throws Exception {
- ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
- return ois.readObject();
- }
-
- /**
- * Helper method to serialize an object
- * @param o the object to serialize.
- * @return the binary data representing the serialized object.
- * @throws Exception a generic exception if an error occurs when
- * serializing the object.
- */
- public static byte[] serialize(Object o) throws Exception {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(out);
- oos.writeObject(o);
- oos.close();
- return out.toByteArray();
- }
-}
diff --git a/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/TestRunEvent.java b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/TestRunEvent.java
index 23e57718a..c44ef3e74 100644
--- a/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/TestRunEvent.java
+++ b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/TestRunEvent.java
@@ -59,6 +59,9 @@ import java.util.Properties;
import org.apache.tools.ant.util.StringUtils;
/**
+ * Provide the basic events to be used during the tests.
+ * This is not very extensible but since the events should be somewhat
+ * limited, for now this is better to do it like this.
*
* @author Stephane Bailliez
*/
@@ -81,7 +84,7 @@ public class TestRunEvent extends EventObject {
/** the type of event */
private int type = -1;
- /** timestamp for all tests */
+ /** timestamp for all events */
private long timestamp = System.currentTimeMillis();
/** name of testcase(method name) or testsuite (classname) */
@@ -93,19 +96,28 @@ public class TestRunEvent extends EventObject {
/** properties for end of testrun */
private Properties props;
+ /** handy result for each end of sequence */
+ private TestSummary result;
+
public TestRunEvent(Integer id, int type){
super(id);
this.type = type;
}
+ public TestRunEvent(Integer id, int type, String name, TestSummary result){
+ this(id, type, name);
+ this.result = result;
+ }
+
public TestRunEvent(Integer id, int type, String name){
this(id, type);
this.name = name;
}
- public TestRunEvent(Integer id, int type, Properties props){
+ public TestRunEvent(Integer id, int type, Properties props, TestSummary result){
this(id, type);
this.props = props;
+ this.result = result;
}
public TestRunEvent(Integer id, int type, String name, Throwable t){
@@ -145,6 +157,10 @@ public class TestRunEvent extends EventObject {
return name;
}
+ public TestSummary getSummary(){
+ return result;
+ }
+
public String getStackTrace(){
return stacktrace;
}
@@ -160,7 +176,8 @@ public class TestRunEvent extends EventObject {
(timestamp == other.timestamp) &&
( name == null ? other.name == null : name.equals(other.name) ) &&
( stacktrace == null ? other.stacktrace == null : stacktrace.equals(other.stacktrace) ) &&
- ( props == null ? other.props == null : props.equals(other.props) ) ) ;
+ ( props == null ? other.props == null : props.equals(other.props) ) &&
+ ( result == null ? other.result == null : result.equals(other.result) ) );
}
return false;
}
diff --git a/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/TestRunner.java b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/TestRunner.java
index dd9c04fb4..d475b649a 100644
--- a/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/TestRunner.java
+++ b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/TestRunner.java
@@ -62,6 +62,11 @@ import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.Random;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
import junit.framework.AssertionFailedError;
import junit.framework.Test;
@@ -71,6 +76,8 @@ import junit.framework.TestResult;
import junit.framework.TestSuite;
import org.apache.tools.ant.taskdefs.optional.rjunit.JUnitHelper;
+import org.apache.tools.ant.taskdefs.optional.rjunit.formatter.Formatter;
+import org.apache.tools.ant.taskdefs.optional.rjunit.formatter.PlainFormatter;
import org.apache.tools.ant.util.StringUtils;
/**
@@ -95,10 +102,11 @@ public class TestRunner implements TestListener {
/** port to connect to */
private int port = -1;
+ /** handy debug flag */
private boolean debug = false;
/** the list of test class names to run */
- private Vector testClassNames = new Vector();
+ private final ArrayList testClassNames = new ArrayList();
/** result of the current test */
private TestResult testResult;
@@ -109,8 +117,14 @@ public class TestRunner implements TestListener {
/** writer to send message to the server */
private Messenger messenger;
+ /** helpful formatter to debug events directly here */
+ private final Formatter debugFormatter = new PlainFormatter();
+
/** bean constructor */
public TestRunner() {
+ Properties props = new Properties();
+ props.setProperty("file", "rjunit-client-debug.log");
+ debugFormatter.init(props);
}
/**
@@ -142,7 +156,7 @@ public class TestRunner implements TestListener {
* @param classname the class name of the test to run.
*/
public void addTestClassName(String classname) {
- testClassNames.addElement(classname);
+ testClassNames.add(classname);
}
/**
@@ -265,16 +279,16 @@ public class TestRunner implements TestListener {
* @throws Exception a generic exception that can be thrown while
* instantiating a test case.
*/
- protected Test[] getSuites() throws Exception {
+ protected Map getSuites() throws Exception {
final int count = testClassNames.size();
log("Extracting testcases from " + count + " classnames...");
- final Vector suites = new Vector(count);
+ final Map suites = new HashMap();
for (int i = 0; i < count; i++) {
- String classname = (String) testClassNames.elementAt(i);
+ String classname = (String) testClassNames.get(i);
try {
Test test = JUnitHelper.getTest(null, classname);
if (test != null) {
- suites.addElement(test);
+ suites.put(classname, test);
}
} catch (Exception e) {
// notify log error instead ?
@@ -283,9 +297,7 @@ public class TestRunner implements TestListener {
}
}
log("Extracted " + suites.size() + " testcases.");
- Test[] array = new Test[suites.size()];
- suites.copyInto(array);
- return array;
+ return suites;
}
/**
@@ -293,41 +305,75 @@ public class TestRunner implements TestListener {
*/
private void runTests() throws Exception {
- Test[] suites = getSuites();
+ Map suites = getSuites();
// count all testMethods and inform TestRunListeners
- int count = countTests(suites);
+ int count = countTests(suites.values());
log("Total tests to run: " + count);
- fireEvent(new TestRunEvent(id, TestRunEvent.RUN_STARTED));
-
- long startTime = System.currentTimeMillis();
- for (int i = 0; i < suites.length; i++) {
- String name = suites[i].getClass().getName();
- if (suites[i] instanceof TestCase) {
- suites[i] = new TestSuite(name);
+ TestRunEvent evt = new TestRunEvent(id, TestRunEvent.RUN_STARTED);
+ if (debug){
+ debugFormatter.onRunStarted(evt);
+ }
+ fireEvent(evt);
+
+ TestSummary runSummary = new TestSummary();
+ runSummary.start(testResult);
+ for (Iterator it = suites.entrySet().iterator(); it.hasNext(); ) {
+ Map.Entry entry = (Map.Entry)it.next();
+ String name = (String)entry.getKey();
+ Test test = (Test)entry.getValue();
+ if (test instanceof TestCase) {
+ test = new TestSuite(name);
}
- log("running suite: " + suites[i]);
- fireEvent(new TestRunEvent(id, TestRunEvent.SUITE_STARTED, name));
- suites[i].run(testResult);
- fireEvent(new TestRunEvent(id, TestRunEvent.SUITE_ENDED, name));
+ runTest(test, name);
}
+ runSummary.stop(testResult);
// inform TestRunListeners of test end
- long elapsedTime = System.currentTimeMillis() - startTime;
- if (testResult == null || testResult.shouldStop()) {
- fireEvent(new TestRunEvent(id, TestRunEvent.RUN_STOPPED, System.getProperties()));
- } else {
- fireEvent(new TestRunEvent(id, TestRunEvent.RUN_ENDED, System.getProperties()));
+ int type = (testResult == null || testResult.shouldStop()) ?
+ TestRunEvent.RUN_STOPPED : TestRunEvent.RUN_ENDED;
+ evt = new TestRunEvent(id, type, System.getProperties(), runSummary);
+ if (debug){
+ debugFormatter.onRunEnded(evt);
}
- log("Finished after " + elapsedTime + "ms");
+ fireEvent(evt);
+ log("Finished after " + runSummary.elapsedTime() + "ms");
shutDown();
}
- /** count the number of test methods in all tests */
- private final int countTests(Test[] tests) {
+ /**
+ * run a single suite and dispatch its results.
+ * @param test the instance of the testsuite to run.
+ * @param name the name of the testsuite (classname)
+ */
+ private void runTest(Test test, String name){
+ TestRunEvent evt = new TestRunEvent(id, TestRunEvent.SUITE_STARTED, name);
+ if (debug){
+ debugFormatter.onSuiteStarted(evt);
+ }
+ fireEvent(evt);
+ TestSummary suiteSummary = new TestSummary();
+ suiteSummary.start(testResult);
+ try {
+ test.run(testResult);
+ } finally {
+ suiteSummary.stop(testResult);
+ evt = new TestRunEvent(id, TestRunEvent.SUITE_ENDED, name, suiteSummary);
+ if (debug){
+ debugFormatter.onSuiteEnded(evt);
+ }
+ fireEvent(evt);
+ }
+ }
+
+ /**
+ * count the number of test methods in all tests
+ */
+ private final int countTests(Collection tests) {
int count = 0;
- for (int i = 0; i < tests.length; i++) {
- count = count + tests[i].countTestCases();
+ for (Iterator it = tests.iterator(); it.hasNext(); ) {
+ Test test = (Test)it.next();
+ count = count + test.countTestCases();
}
return count;
}
@@ -383,14 +429,20 @@ public class TestRunner implements TestListener {
public void startTest(Test test) {
String testName = test.toString();
- log("starting test: " + test);
- fireEvent(new TestRunEvent(id, TestRunEvent.TEST_STARTED, testName));
+ TestRunEvent evt = new TestRunEvent(id, TestRunEvent.TEST_STARTED, testName);
+ if (debug){
+ debugFormatter.onTestStarted(evt);
+ }
+ fireEvent(evt);
}
public void addError(Test test, Throwable t) {
- log("Adding error for test: " + test);
String testName = test.toString();
- fireEvent(new TestRunEvent(id, TestRunEvent.TEST_ERROR, testName, t));
+ TestRunEvent evt = new TestRunEvent(id, TestRunEvent.TEST_ERROR, testName, t);
+ if (debug){
+ debugFormatter.onTestError(evt);
+ }
+ fireEvent(evt);
}
/**
@@ -406,15 +458,21 @@ public class TestRunner implements TestListener {
* @see addFailure(Test, AssertionFailedError)
*/
public void addFailure(Test test, Throwable t) {
- log("Adding failure for test: " + test);
String testName = test.toString();
- fireEvent(new TestRunEvent(id, TestRunEvent.TEST_FAILURE, testName, t));
+ TestRunEvent evt = new TestRunEvent(id, TestRunEvent.TEST_FAILURE, testName, t);
+ if (debug){
+ debugFormatter.onTestFailure(evt);
+ }
+ fireEvent(evt);
}
public void endTest(Test test) {
- log("Ending test: " + test);
String testName = test.toString();
- fireEvent(new TestRunEvent(id, TestRunEvent.TEST_ENDED, testName));
+ TestRunEvent evt = new TestRunEvent(id, TestRunEvent.TEST_ENDED, testName);
+ if (debug){
+ debugFormatter.onTestEnded(evt);
+ }
+ fireEvent(evt);
}
public void log(String msg) {
diff --git a/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/TestSummary.java b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/TestSummary.java
new file mode 100644
index 000000000..79d71e682
--- /dev/null
+++ b/proposal/sandbox/junit/src/main/org/apache/tools/ant/taskdefs/optional/rjunit/remote/TestSummary.java
@@ -0,0 +1,177 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+package org.apache.tools.ant.taskdefs.optional.rjunit.remote;
+
+import java.io.Serializable;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+
+/**
+ * A helpful test summary that is somewhat similar to TestResult.
+ * Here the difference is that this test summary should register to
+ * the test result the time you wan to collect information.
+ *
+ * @author Stephane Bailliez
+ */
+public final class TestSummary implements Serializable, TestListener {
+
+ /** time elapsed during tests run in ms */
+ private long elapsedTime;
+
+ /** number of errors */
+ private int errorCount;
+
+ /** number of successes */
+ private int successCount;
+
+ /** number of failures */
+ private int failureCount;
+
+ /** number of runs */
+ private int runCount;
+
+ private transient String toString;
+
+ /** bean constructor */
+ public TestSummary() {
+ }
+
+ /**
+ * @return the number of errors that occurred in this test.
+ */
+ public int errorCount() {
+ return errorCount;
+ }
+
+ /**
+ * @return the number of successes that occurred in this test.
+ */
+ public int successCount() {
+ return successCount;
+ }
+
+ /**
+ * @return the number of failures that occurred in this test.
+ */
+ public int failureCount() {
+ return failureCount;
+ }
+
+ /**
+ * @return the number of runs that occurred in this test.
+ * a run is the sum of failures + errors + successes.
+ */
+ public int runCount() {
+ return runCount;
+ }
+
+ /**
+ * @return the elapsed time in ms
+ */
+ public long elapsedTime() {
+ return elapsedTime;
+ }
+
+//
+ /**
+ * register to the TestResult and starts the time counter.
+ * @param result the instance to register to.
+ * @see #stop()
+ */
+ public void start(TestResult result){
+ elapsedTime = System.currentTimeMillis();
+ result.addListener(this);
+ }
+
+ /**
+ * unregister from the TestResult and stops the time counter.
+ * @param result the instance to unregister from.
+ * @see #start()
+ */
+ public void stop(TestResult result){
+ elapsedTime = System.currentTimeMillis() - elapsedTime;
+ result.removeListener(this);
+ }
+
+// test listener implementation
+
+ public void addError(Test test, Throwable throwable) {
+ errorCount++;
+ }
+
+ public void addFailure(Test test, AssertionFailedError error) {
+ failureCount++;
+ }
+
+ public void endTest(Test test) {
+ successCount++;
+ }
+
+ public void startTest(Test test) {
+ runCount++;
+ }
+
+ public String toString(){
+ StringBuffer buf = new StringBuffer();
+ buf.append("run: ").append(runCount);
+ buf.append(" success: ").append(successCount);
+ buf.append(" failures: ").append(failureCount);
+ buf.append(" errors: ").append(errorCount);
+ buf.append(" elapsed: ").append(elapsedTime);
+ return buf.toString();
+ }
+}