Permalink
Browse files

GRADLE-1532 Fixed the issue with TestNG parallel tests. The problem w…

…as caused because method name was used to identify the test id. When someone used TestNG's feature @Test(invocationCount = 2, threadPoolSize = 2) then just the method name was not enough for identification. My fix is using the ITestResult from TestNG as an identifier for internal use in a hashmap. Also I did very simple refactoring that should help in debugging similar issues in future.
  • Loading branch information...
1 parent af75bd0 commit 96539748ac6965c5cea7b0a11d6d7064ad7fde4f @szczepiq szczepiq committed Jun 18, 2011
View
36 ...integ-test/src/integTest/groovy/org/gradle/integtests/testng/TestNGIntegrationTest.groovy
@@ -23,6 +23,7 @@ import org.gradle.integtests.fixtures.GradleDistributionExecuter
import org.gradle.integtests.fixtures.TestResources
import org.junit.Rule
import org.junit.Test
+import spock.lang.Issue
import static org.gradle.util.Matchers.containsLine
import static org.hamcrest.Matchers.*
import static org.junit.Assert.assertThat
@@ -109,4 +110,39 @@ public class TestNGIntegrationTest {
assertThat(execution.error, containsString('Test org.gradle.BrokenAfterSuite FAILED'))
assertThat(execution.error, containsString('Test org.gradle.TestWithBrokenMethodDependency FAILED'))
}
+
+ @Issue("GRADLE-1532")
+ @Test
+ void supportsThreadPoolSize() {
+ dist.testDir.file('src/test/java/SomeTest.java') << """
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class SomeTest {
+ @Test(invocationCount = 2, threadPoolSize = 2)
+ public void someTest() {
+ Assert.assertTrue(true);
+ }
+}
+"""
+
+ dist.testDir.file("build.gradle") << """
+apply plugin: "java"
+
+repositories {
+ mavenCentral()
+ mavenLocal()
+}
+
+dependencies {
+ testCompile 'org.testng:testng:5.14'
+}
+
+test {
+ useTestNG()
+}
+
+"""
+ executer.withTasks("test").run()
+ }
}
View
8 ...roovy/org/gradle/api/internal/tasks/testing/results/StateTrackingTestResultProcessor.java
@@ -46,7 +46,8 @@ public void completed(Object testId, TestCompleteEvent event) {
TestState testState = executing.remove(testId);
if (testState == null) {
throw new IllegalArgumentException(String.format(
- "Received a completed event for test with unknown id '%s'.", testId));
+ "Received a completed event for test with unknown id '%s'. Registered test ids: '%s'",
+ testId, executing.keySet()));
}
testState.completed(event);
@@ -56,8 +57,9 @@ public void completed(Object testId, TestCompleteEvent event) {
public void failure(Object testId, Throwable result) {
TestState testState = executing.get(testId);
if (testState == null) {
- throw new IllegalArgumentException(String.format("Received a failure event for test with unknown id '%s'.",
- testId));
+ throw new IllegalArgumentException(String.format(
+ "Received a failure event for test with unknown id '%s'. Registered test ids: '%s'",
+ testId, executing.keySet()));
}
testState.failures.add(result);
}
View
267 ...groovy/org/gradle/api/internal/tasks/testing/testng/TestNGTestResultProcessorAdapter.java
@@ -1,132 +1,135 @@
-/*
- * Copyright 2010 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.gradle.api.internal.tasks.testing.testng;
-
-import org.gradle.api.internal.tasks.testing.*;
-import org.gradle.api.tasks.testing.TestResult;
-import org.gradle.util.IdGenerator;
-import org.testng.ITestContext;
-import org.testng.ITestListener;
-import org.testng.ITestNGMethod;
-import org.testng.ITestResult;
-import org.testng.internal.IConfigurationListener;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class TestNGTestResultProcessorAdapter implements ITestListener, IConfigurationListener {
- private final TestResultProcessor resultProcessor;
- private final IdGenerator<?> idGenerator;
- private final Object lock = new Object();
- private Map<String, Object> suites = new HashMap<String, Object>();
- private Map<String, Object> tests = new HashMap<String, Object>();
- private Map<ITestNGMethod, Object> testMethodToSuiteMapping = new HashMap<ITestNGMethod, Object>();
-
- public TestNGTestResultProcessorAdapter(TestResultProcessor resultProcessor, IdGenerator<?> idGenerator) {
- this.resultProcessor = resultProcessor;
- this.idGenerator = idGenerator;
- }
-
- public void onStart(ITestContext iTestContext) {
- TestDescriptorInternal testInternal;
- synchronized (lock) {
- testInternal = new DefaultTestSuiteDescriptor(idGenerator.generateId(), iTestContext.getName());
- suites.put(testInternal.getName(), testInternal.getId());
- for (ITestNGMethod method : iTestContext.getAllTestMethods()) {
- testMethodToSuiteMapping.put(method, testInternal.getId());
- }
- }
- resultProcessor.started(testInternal, new TestStartEvent(iTestContext.getStartDate().getTime()));
- }
-
- public void onFinish(ITestContext iTestContext) {
- Object id;
- synchronized (lock) {
- id = suites.remove(iTestContext.getName());
- for (ITestNGMethod method : iTestContext.getAllTestMethods()) {
- testMethodToSuiteMapping.remove(method);
- }
- }
- resultProcessor.completed(id, new TestCompleteEvent(iTestContext.getEndDate().getTime()));
- }
-
- public void onTestStart(ITestResult iTestResult) {
- TestDescriptorInternal testInternal;
- Object parentId;
- synchronized (lock) {
- testInternal = new DefaultTestMethodDescriptor(idGenerator.generateId(), iTestResult.getTestClass().getName(), iTestResult.getName());
- Object oldTestId = tests.put(testInternal.getName(), testInternal.getId());
- assert oldTestId == null;
- parentId = testMethodToSuiteMapping.get(iTestResult.getMethod());
- assert parentId != null;
- }
- resultProcessor.started(testInternal, new TestStartEvent(iTestResult.getStartMillis(), parentId));
- }
-
- public void onTestSuccess(ITestResult iTestResult) {
- onTestFinished(iTestResult, TestResult.ResultType.SUCCESS);
- }
-
- public void onTestFailure(ITestResult iTestResult) {
- onTestFinished(iTestResult, TestResult.ResultType.FAILURE);
- }
-
- public void onTestSkipped(ITestResult iTestResult) {
- onTestFinished(iTestResult, TestResult.ResultType.SKIPPED);
- }
-
- public void onTestFailedButWithinSuccessPercentage(ITestResult iTestResult) {
- onTestFinished(iTestResult, TestResult.ResultType.SUCCESS);
- }
-
- private void onTestFinished(ITestResult iTestResult, TestResult.ResultType resultType) {
- Object testId;
- TestStartEvent startEvent = null;
- synchronized (lock) {
- testId = tests.remove(iTestResult.getName());
- if (testId == null) {
- // This can happen when a method fails which this method depends on
- testId = idGenerator.generateId();
- Object parentId = testMethodToSuiteMapping.get(iTestResult.getMethod());
- startEvent = new TestStartEvent(iTestResult.getStartMillis(), parentId);
- }
- }
- if (startEvent != null) {
- // Synthesize a start event
- resultProcessor.started(new DefaultTestMethodDescriptor(testId, iTestResult.getTestClass().getName(), iTestResult.getName()), startEvent);
- }
- if (resultType == TestResult.ResultType.FAILURE) {
- resultProcessor.failure(testId, iTestResult.getThrowable());
- }
- resultProcessor.completed(testId, new TestCompleteEvent(iTestResult.getEndMillis(), resultType));
- }
-
- public void onConfigurationSuccess(ITestResult testResult) {
- }
-
- public void onConfigurationSkip(ITestResult testResult) {
- }
-
- public void onConfigurationFailure(ITestResult testResult) {
- // Synthesise a test for the broken configuration method
- TestDescriptorInternal test = new DefaultTestMethodDescriptor(idGenerator.generateId(),
- testResult.getMethod().getTestClass().getName(), testResult.getMethod().getMethodName());
- resultProcessor.started(test, new TestStartEvent(testResult.getStartMillis()));
- resultProcessor.failure(test.getId(), testResult.getThrowable());
- resultProcessor.completed(test.getId(), new TestCompleteEvent(testResult.getEndMillis(), TestResult.ResultType.FAILURE));
- }
-}
+/*
+ * Copyright 2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.internal.tasks.testing.testng;
+
+import org.gradle.api.internal.tasks.testing.*;
+import org.gradle.api.tasks.testing.TestResult;
+import org.gradle.util.IdGenerator;
+import org.testng.ITestContext;
+import org.testng.ITestListener;
+import org.testng.ITestNGMethod;
+import org.testng.ITestResult;
+import org.testng.internal.IConfigurationListener;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class TestNGTestResultProcessorAdapter implements ITestListener, IConfigurationListener {
+ private final TestResultProcessor resultProcessor;
+ private final IdGenerator<?> idGenerator;
+ private final Object lock = new Object();
+ private Map<String, Object> suites = new HashMap<String, Object>();
+ private Map<ITestResult, Object> tests = new HashMap<ITestResult, Object>();
+ private Map<ITestNGMethod, Object> testMethodToSuiteMapping = new HashMap<ITestNGMethod, Object>();
+
+ public TestNGTestResultProcessorAdapter(TestResultProcessor resultProcessor, IdGenerator<?> idGenerator) {
+ this.resultProcessor = resultProcessor;
+ this.idGenerator = idGenerator;
+ }
+
+ public void onStart(ITestContext iTestContext) {
+ TestDescriptorInternal testInternal;
+ synchronized (lock) {
+ testInternal = new DefaultTestSuiteDescriptor(idGenerator.generateId(), iTestContext.getName());
+ suites.put(testInternal.getName(), testInternal.getId());
+ for (ITestNGMethod method : iTestContext.getAllTestMethods()) {
+ testMethodToSuiteMapping.put(method, testInternal.getId());
+ }
+ }
+ resultProcessor.started(testInternal, new TestStartEvent(iTestContext.getStartDate().getTime()));
+ }
+
+ public void onFinish(ITestContext iTestContext) {
+ Object id;
+ synchronized (lock) {
+ id = suites.remove(iTestContext.getName());
+ for (ITestNGMethod method : iTestContext.getAllTestMethods()) {
+ testMethodToSuiteMapping.remove(method);
+ }
+ }
+ resultProcessor.completed(id, new TestCompleteEvent(iTestContext.getEndDate().getTime()));
+ }
+
+ public void onTestStart(ITestResult iTestResult) {
+ TestDescriptorInternal testInternal;
+ Object parentId;
+ synchronized (lock) {
+ testInternal = new DefaultTestMethodDescriptor(idGenerator.generateId(), iTestResult.getTestClass().getName(), iTestResult.getName());
+ Object oldTestId = tests.put(iTestResult, testInternal.getId());
+ assert oldTestId == null : "Apparently some other test has started but it hasn't finished. "
+ + "Expect the resultProcessor to break. "
+ + "Don't expect to see this assertion stack trace due to the current architecture";
+
+ parentId = testMethodToSuiteMapping.get(iTestResult.getMethod());
+ assert parentId != null;
+ }
+ resultProcessor.started(testInternal, new TestStartEvent(iTestResult.getStartMillis(), parentId));
+ }
+
+ public void onTestSuccess(ITestResult iTestResult) {
+ onTestFinished(iTestResult, TestResult.ResultType.SUCCESS);
+ }
+
+ public void onTestFailure(ITestResult iTestResult) {
+ onTestFinished(iTestResult, TestResult.ResultType.FAILURE);
+ }
+
+ public void onTestSkipped(ITestResult iTestResult) {
+ onTestFinished(iTestResult, TestResult.ResultType.SKIPPED);
+ }
+
+ public void onTestFailedButWithinSuccessPercentage(ITestResult iTestResult) {
+ onTestFinished(iTestResult, TestResult.ResultType.SUCCESS);
+ }
+
+ private void onTestFinished(ITestResult iTestResult, TestResult.ResultType resultType) {
+ Object testId;
+ TestStartEvent startEvent = null;
+ synchronized (lock) {
+ testId = tests.remove(iTestResult);
+ if (testId == null) {
+ // This can happen when a method fails which this method depends on
+ testId = idGenerator.generateId();
+ Object parentId = testMethodToSuiteMapping.get(iTestResult.getMethod());
+ startEvent = new TestStartEvent(iTestResult.getStartMillis(), parentId);
+ }
+ }
+ if (startEvent != null) {
+ // Synthesize a start event
+ resultProcessor.started(new DefaultTestMethodDescriptor(testId, iTestResult.getTestClass().getName(), iTestResult.getName()), startEvent);
+ }
+ if (resultType == TestResult.ResultType.FAILURE) {
+ resultProcessor.failure(testId, iTestResult.getThrowable());
+ }
+ resultProcessor.completed(testId, new TestCompleteEvent(iTestResult.getEndMillis(), resultType));
+ }
+
+ public void onConfigurationSuccess(ITestResult testResult) {
+ }
+
+ public void onConfigurationSkip(ITestResult testResult) {
+ }
+
+ public void onConfigurationFailure(ITestResult testResult) {
+ // Synthesise a test for the broken configuration method
+ TestDescriptorInternal test = new DefaultTestMethodDescriptor(idGenerator.generateId(),
+ testResult.getMethod().getTestClass().getName(), testResult.getMethod().getMethodName());
+ resultProcessor.started(test, new TestStartEvent(testResult.getStartMillis()));
+ resultProcessor.failure(test.getId(), testResult.getThrowable());
+ resultProcessor.completed(test.getId(), new TestCompleteEvent(testResult.getEndMillis(), TestResult.ResultType.FAILURE));
+ }
+}

0 comments on commit 9653974

Please sign in to comment.