sm junit: parallel execution for tests (IDEA-148602)

This commit is contained in:
Anna.Kozlova
2016-05-03 14:59:23 +02:00
parent 20f69c8111
commit cd1349cb1f
2 changed files with 205 additions and 21 deletions
@@ -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()));
}
@@ -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();
}
/**
* <code>myCurrentTest == null</code> 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
*
* <code>myCurrentTest.equals(description)</code> 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);