diff --git a/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java b/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java
index 91b76b9a7a14..9e558b585a07 100644
--- a/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java
+++ b/plugins/junit/test/com/intellij/execution/junit/JUnitTreeByDescriptionHierarchyTest.java
@@ -367,7 +367,9 @@ public class JUnitTreeByDescriptionHierarchyTest {
sender.testRunStarted(testA);
final Exception exception = new Exception();
exception.setStackTrace(new StackTraceElement[0]);
+ sender.testStarted(testA);
sender.testAssumptionFailure(new Failure(testA, exception));
+ sender.testFinished(testA);
sender.testRunFinished(new Result());
Assert.assertEquals("output: " + buf, "##teamcity[rootName name = 'root' location = 'java:suite://root']\n" +
@@ -404,7 +406,9 @@ public class JUnitTreeByDescriptionHierarchyTest {
sender.testRunStarted(testA);
final Exception exception = new Exception();
exception.setStackTrace(new StackTraceElement[0]);
+ sender.testStarted(testA);
sender.testFailure(new Failure(testA, exception));
+ sender.testFinished(testA);
sender.testRunFinished(new Result());
Assert.assertEquals("output: " + buf, "##teamcity[rootName name = 'root' location = 'java:suite://root']\n" +
@@ -422,7 +426,52 @@ public class JUnitTreeByDescriptionHierarchyTest {
"##teamcity[testFinished name='TestA.testName']\n" +
"##teamcity[testSuiteFinished name='TestA']\n", StringUtil.convertLineSeparators(buf.toString()));
}
-
+
+ @Test
+ public void testParallelExecution() throws Exception {
+ final Description root = Description.createSuiteDescription("root");
+ Description testA = Description.createTestDescription("TestA", "test1");
+ root.addChild(testA);
+
+ Description testB = Description.createTestDescription("TestB", "test2");
+ root.addChild(testB);
+
+ final StringBuffer buf = new StringBuffer();
+ final JUnit4TestListener sender = createListener(buf);
+ sender.sendTree(root);
+
+ Assert.assertEquals("output: " + buf, "##teamcity[enteredTheMatrix]\n" +
+ "##teamcity[suiteTreeNode name='TestA.test1' locationHint='java:test://TestA.test1']\n" +
+ "##teamcity[suiteTreeNode name='TestB.test2' locationHint='java:test://TestB.test2']\n" +
+ "##teamcity[treeEnded]\n", StringUtil.convertLineSeparators(buf.toString()));
+
+ buf.setLength(0);
+
+ sender.testRunStarted(root);
+ sender.testStarted(testA);
+
+ sender.testStarted(testB);
+ sender.testFinished(testB);
+
+ final Exception exception = new Exception();
+ exception.setStackTrace(new StackTraceElement[0]);
+ sender.testFailure(new Failure(testA, exception));
+ sender.testFinished(testA);
+ sender.testRunFinished(new Result());
+
+ Assert.assertEquals("output: " + buf, "##teamcity[rootName name = 'root' location = 'java:suite://root']\n" +
+ "\n" +
+ "##teamcity[testStarted name='TestA.test1' locationHint='java:test://TestA.test1']\n" +
+ "\n" +
+ "##teamcity[testFailed name='TestA.test1' details='java.lang.Exception|n' error='true' message='']\n" +
+ "\n" +
+ "##teamcity[testFinished name='TestA.test1']\n" +
+ "\n" +
+ "##teamcity[testStarted name='TestB.test2' locationHint='java:test://TestB.test2']\n" +
+ "\n" +
+ "##teamcity[testFinished name='TestB.test2']\n", StringUtil.convertLineSeparators(buf.toString()));
+ }
+
@Test
public void testTearDownClassFailure() throws Exception {
final Description root = Description.createSuiteDescription("root");
@@ -448,7 +497,9 @@ public class JUnitTreeByDescriptionHierarchyTest {
exception.setStackTrace(new StackTraceElement[0]);
sender.testStarted(testName);
sender.testFinished(testName);
+ sender.testStarted(testA);
sender.testFailure(new Failure(testA, exception));
+ sender.testFinished(testA);
sender.testRunFinished(new Result());
Assert.assertEquals("output: " + buf, "##teamcity[rootName name = 'root' location = 'java:suite://root']\n" +
@@ -492,7 +543,9 @@ public class JUnitTreeByDescriptionHierarchyTest {
sender.testRunStarted(testA);
final Exception exception = new Exception();
exception.setStackTrace(new StackTraceElement[0]);
+ sender.testStarted(testA);
sender.testAssumptionFailure(new Failure(testA, exception));
+ sender.testFinished(testA);
sender.testRunFinished(new Result());
Assert.assertEquals("output: " + buf, "##teamcity[rootName name = 'root' location = 'java:suite://root']\n" +
@@ -508,14 +561,21 @@ public class JUnitTreeByDescriptionHierarchyTest {
"##teamcity[testSuiteFinished name='TestA']\n", StringUtil.convertLineSeparators(buf.toString()));
buf.setLength(0);
- //testStarted and testFinished are called by the framework
sender.testRunStarted(testA);
+ sender.testStarted(testName);
sender.testAssumptionFailure(new Failure(testName, exception));
+ sender.testFinished(testName);
sender.testRunFinished(new Result());
Assert.assertEquals("output: " + buf, "##teamcity[rootName name = 'root' location = 'java:suite://root']\n" +
+ "##teamcity[testSuiteStarted name='TestA']\n" +
"\n" +
- "##teamcity[testIgnored name='TestA.testName' details='java.lang.Exception|n' error='true' message='']\n", StringUtil.convertLineSeparators(buf.toString()));
+ "##teamcity[testStarted name='TestA.testName' locationHint='java:test://TestA.testName']\n" +
+ "\n" +
+ "##teamcity[testIgnored name='TestA.testName' details='java.lang.Exception|n' error='true' message='']\n" +
+ "\n" +
+ "##teamcity[testFinished name='TestA.testName']\n" +
+ "##teamcity[testSuiteFinished name='TestA']\n", StringUtil.convertLineSeparators(buf.toString()));
}
diff --git a/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java
index a8b978d18dde..4b121044e0ba 100644
--- a/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java
+++ b/plugins/junit_rt/src/com/intellij/junit4/JUnit4TestListener.java
@@ -46,6 +46,10 @@ public class JUnit4TestListener extends RunListener {
private long myCurrentTestStart;
private int myFinishedCount = 0;
+ private Description myCurrentTest;
+ private Map myWaitingQueue = new LinkedHashMap();
+
+
public JUnit4TestListener() {
this(System.out);
}
@@ -77,7 +81,8 @@ public class JUnit4TestListener extends RunListener {
}
}
- public void testRunFinished(Result result) throws Exception {
+ public void testRunFinished(Result result) {
+ dumpQueue();
for (int i = myStartedSuites.size() - 1; i>= 0; i--) {
Object parent = JUnit4ReflectionUtil.getClassName((Description)myStartedSuites.get(i));
myPrintStream.println("##teamcity[testSuiteFinished name=\'" + escapeName(getShortName((String)parent)) + "\']");
@@ -85,7 +90,14 @@ public class JUnit4TestListener extends RunListener {
myStartedSuites.clear();
}
- public void testStarted(Description description) throws Exception {
+ public void testStarted(Description description) {
+ if (myCurrentTest != null) {
+ myWaitingQueue.put(description, new TestEvent());
+ return;
+ }
+
+ myCurrentTest = description;
+
final String classFQN = JUnit4ReflectionUtil.getClassName(description);
final List parents = (List)myParents.get(description);
@@ -148,7 +160,32 @@ public class JUnit4TestListener extends RunListener {
return System.currentTimeMillis();
}
- public void testFinished(Description description) throws Exception {
+ public void testFinished(Description description) {
+ if (startedInParallel(description)) {
+ TestEvent testEvent = (TestEvent)myWaitingQueue.get(description);
+ testEvent.setFinished(true);
+ return;
+ }
+ testFinishedNoDumping(description);
+
+ dumpQueue();
+ }
+
+ /**
+ * myCurrentTest == null means that initially started test was already finished but no new test were started:
+ * test1 started | myCurrentTest == test1
+ * test2 started | myWaitingQueue contains test2
+ * test1 finished | myCurrentTest == null
+ * test2 finished | testEvent from myWaitingQueue is marked as finished. Next dumping would dump test2 started/test2 finished
+ *
+ * myCurrentTest.equals(description) means that finish/failure/ignore event was fired against last started test
+ * if in the previous example test2 finishes before test1, then myCurrentTest != description, testEvent in myWaitingQueue needs to be updated
+ */
+ private boolean startedInParallel(Description description) {
+ return myCurrentTest == null || !myCurrentTest.equals(description);
+ }
+
+ private void testFinishedNoDumping(Description description) {
final String methodName = getFullMethodName(description);
if (methodName != null) {
myFinishedCount++;
@@ -156,16 +193,27 @@ public class JUnit4TestListener extends RunListener {
myPrintStream.println("\n##teamcity[testFinished name=\'" + escapeName(methodName) +
(duration > 0 ? "\' duration=\'" + Long.toString(duration) : "") + "\']");
}
+ myCurrentTest = null;
}
- public void testFailure(Failure failure) throws Exception {
- testFailure(failure, failure.getDescription(), MapSerializerUtil.TEST_FAILED, true);
+ public void testFailure(Failure failure) {
+ testFailure(failure, failure.getDescription(), MapSerializerUtil.TEST_FAILED);
}
- private void testFailure(Failure failure, Description description, String messageName, boolean local) throws Exception {
+ private void testFailure(Failure failure, Description description, String messageName) {
+ final boolean isIgnored = MapSerializerUtil.TEST_IGNORED.equals(messageName);
+ if (startedInParallel(description)) {
+ TestEvent testEvent = (TestEvent)myWaitingQueue.get(description);
+ if (testEvent == null) {
+ testEvent = new TestEvent();
+ myWaitingQueue.put(description, testEvent);
+ }
+ testEvent.setIgnored(isIgnored);
+ testEvent.setFailure(failure);
+ return;
+ }
String methodName = getFullMethodName(description);
if (methodName == null) { //class setUp/tearDown failed
- final boolean isIgnored = MapSerializerUtil.TEST_IGNORED.equals(messageName);
if (!isIgnored) {
methodName = "Class Configuration";
myPrintStream.println("##teamcity[testStarted name=\'" + escapeName(methodName) + "\' " + getClassLocation(JUnit4ReflectionUtil.getClassName(description))+ " ]");
@@ -176,14 +224,15 @@ public class JUnit4TestListener extends RunListener {
if (myFinishedCount == 0) {
//only setup failures
for (Iterator iterator = description.getChildren().iterator(); iterator.hasNext(); ) {
- testFailure(isIgnored ? failure : null, (Description)iterator.next(), MapSerializerUtil.TEST_IGNORED, false);
+ Description next = (Description)iterator.next();
+ testStarted(next);
+ testFailure(isIgnored ? failure : null, next, MapSerializerUtil.TEST_IGNORED);
+ testFinished(next);
}
}
}
else {
- if (!local) testStarted(description);
testFailure(failure, messageName, methodName);
- if (!local) testFinished(description);
}
}
@@ -218,11 +267,7 @@ public class JUnit4TestListener extends RunListener {
}
public void testAssumptionFailure(Failure failure) {
- final Description description = failure.getDescription();
- try {
- testFailure(failure, description, MapSerializerUtil.TEST_IGNORED, true);
- }
- catch (Exception ignore) {}
+ testFailure(failure, failure.getDescription(), MapSerializerUtil.TEST_IGNORED);
}
private String getFullMethodName(Description description) {
@@ -246,7 +291,7 @@ public class JUnit4TestListener extends RunListener {
return methodName;
}
- public synchronized void testIgnored(Description description) throws Exception {
+ public void testIgnored(Description description) {
final String methodName = getFullMethodName(description);
if (methodName == null) {
for (Iterator iterator = description.getChildren().iterator(); iterator.hasNext(); ) {
@@ -259,7 +304,7 @@ public class JUnit4TestListener extends RunListener {
}
}
- private void testIgnored(Description description, String methodName) throws Exception {
+ private void testIgnored(Description description, String methodName) {
testStarted(description);
Map attrs = new HashMap();
try {
@@ -275,10 +320,89 @@ public class JUnit4TestListener extends RunListener {
//junit < 4.4
}
attrs.put("name", methodName);
- myPrintStream.println(MapSerializerUtil.asString(MapSerializerUtil.TEST_IGNORED, attrs));
+
+ if (startedInParallel(description)) {
+ TestEvent testEvent = (TestEvent)myWaitingQueue.get(description);
+ if (testEvent == null) {
+ testEvent = new TestEvent();
+ myWaitingQueue.put(description, testEvent);
+ }
+ testEvent.setIgnored(true);
+ testEvent.setAttrs(attrs);
+ }
+ else {
+ myPrintStream.println(MapSerializerUtil.asString(MapSerializerUtil.TEST_IGNORED, attrs));
+ }
testFinished(description);
}
+ private void dumpQueue() {
+ for (Iterator iterator = myWaitingQueue.keySet().iterator(); iterator.hasNext(); ) {
+ Description description = (Description)iterator.next();
+ TestEvent testEvent = (TestEvent)myWaitingQueue.get(description);
+ if (testEvent != null && testEvent.isFinished()) {
+ iterator.remove();
+
+ testStarted(description);
+
+ Failure failure = testEvent.getFailure();
+ if (testEvent.isIgnored()) {
+ Map attrs = testEvent.getAttrs();
+ if (attrs == null) {
+ testFailure(failure, description, MapSerializerUtil.TEST_IGNORED);
+ }
+ else {
+ myPrintStream.println(MapSerializerUtil.asString(MapSerializerUtil.TEST_IGNORED, attrs));
+ }
+ }
+ else if (failure != null) {
+ testFailure(failure);
+ }
+
+ testFinishedNoDumping(description);
+ }
+ }
+ }
+
+ private static class TestEvent {
+ private Failure myFailure;
+ private boolean myIgnored;
+ private boolean myFinished;
+ private Map myAttrs;
+
+ public Failure getFailure() {
+ return myFailure;
+ }
+
+ public boolean isIgnored() {
+ return myIgnored;
+ }
+
+ public boolean isFinished() {
+ return myFinished;
+ }
+
+ public void setFinished(boolean finished) {
+ myFinished = finished;
+ }
+
+ public void setFailure(Failure failure) {
+ myFailure = failure;
+ }
+
+ public void setIgnored(boolean ignored) {
+ myIgnored = ignored;
+ }
+
+ public void setAttrs(Map attrs) {
+ myAttrs = attrs;
+ }
+
+ public Map getAttrs() {
+ return myAttrs;
+ }
+ }
+
private void sendTree(Description description, Description parent, List currentParents) {
List pParents = new ArrayList(3);
pParents.addAll(currentParents);