/* * 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.internal.time.TimeProvider; import org.gradle.internal.id.IdGenerator; import org.testng.*; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; public class TestNGTestResultProcessorAdapter implements ISuiteListener, ITestListener, TestNGConfigurationListener { private final TestResultProcessor resultProcessor; private final IdGenerator<?> idGenerator; private final TimeProvider timeProvider; private final Object lock = new Object(); private final Map<ITestContext, Object> testId = new HashMap<ITestContext, Object>(); private final Map<ISuite, Object> suiteId = new HashMap<ISuite, Object>(); private final Map<ITestResult, Object> testMethodId = new HashMap<ITestResult, Object>(); private final Map<ITestNGMethod, Object> testMethodParentId = new HashMap<ITestNGMethod, Object>(); private final Set<ITestResult> failedConfigurations = new HashSet<ITestResult>(); public TestNGTestResultProcessorAdapter(TestResultProcessor resultProcessor, IdGenerator<?> idGenerator, TimeProvider timeProvider) { this.resultProcessor = resultProcessor; this.idGenerator = idGenerator; this.timeProvider = timeProvider; } @Override public void onStart(ISuite suite) { TestDescriptorInternal testInternal; synchronized (lock) { if (suiteId.containsKey(suite)) { // Can get duplicate start events return; } testInternal = new DefaultTestSuiteDescriptor(idGenerator.generateId(), suite.getName()); suiteId.put(suite, testInternal.getId()); } resultProcessor.started(testInternal, new TestStartEvent(timeProvider.getCurrentTime())); } @Override public void onFinish(ISuite suite) { Object id; synchronized (lock) { id = suiteId.remove(suite); if (id == null) { // Can get duplicate finish events return; } } resultProcessor.completed(id, new TestCompleteEvent(timeProvider.getCurrentTime())); } @Override public void onStart(ITestContext iTestContext) { TestDescriptorInternal testInternal; Object parentId; synchronized (lock) { testInternal = new DefaultTestSuiteDescriptor(idGenerator.generateId(), iTestContext.getName()); parentId = suiteId.get(iTestContext.getSuite()); testId.put(iTestContext, testInternal.getId()); for (ITestNGMethod method : iTestContext.getAllTestMethods()) { testMethodParentId.put(method, testInternal.getId()); } } resultProcessor.started(testInternal, new TestStartEvent(iTestContext.getStartDate().getTime(), parentId)); } @Override public void onFinish(ITestContext iTestContext) { Object id; synchronized (lock) { id = testId.remove(iTestContext); for (ITestNGMethod method : iTestContext.getAllTestMethods()) { testMethodParentId.remove(method); } } resultProcessor.completed(id, new TestCompleteEvent(iTestContext.getEndDate().getTime())); } @Override public void onTestStart(ITestResult iTestResult) { TestDescriptorInternal testInternal; Object parentId; synchronized (lock) { String name = calculateTestCaseName(iTestResult); testInternal = new DefaultTestMethodDescriptor(idGenerator.generateId(), iTestResult.getTestClass().getName(), name); Object oldTestId = testMethodId.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 = testMethodParentId.get(iTestResult.getMethod()); assert parentId != null; } resultProcessor.started(testInternal, new TestStartEvent(iTestResult.getStartMillis(), parentId)); if (iTestResult.getThrowable() instanceof UnrepresentableParameterException) { throw (UnrepresentableParameterException) iTestResult.getThrowable(); } } private String calculateTestCaseName(ITestResult iTestResult) { Object[] parameters = iTestResult.getParameters(); String name = iTestResult.getName(); if (parameters != null && parameters.length > 0) { StringBuilder builder = new StringBuilder(name). append("["). append(iTestResult.getMethod().getCurrentInvocationCount()). append("]"); StringBuilder paramsListBuilder = new StringBuilder("("); int i = 0; for (Object parameter : parameters) { if (parameter == null) { paramsListBuilder.append("null"); } else { try { paramsListBuilder.append(parameter.toString()); } catch (Exception e) { // This may be thrown by the caller of this method at a later time iTestResult.setThrowable(new UnrepresentableParameterException(iTestResult, i, e)); return builder.toString(); } } if (++i < parameters.length) { paramsListBuilder.append(", "); } } paramsListBuilder.append(")"); return builder.append(paramsListBuilder.toString()).toString(); } else { return name; } } @Override public void onTestSuccess(ITestResult iTestResult) { onTestFinished(iTestResult, TestResult.ResultType.SUCCESS); } @Override public void onTestFailure(ITestResult iTestResult) { onTestFinished(iTestResult, TestResult.ResultType.FAILURE); } @Override public void onTestSkipped(ITestResult iTestResult) { onTestFinished(iTestResult, TestResult.ResultType.SKIPPED); } @Override 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 = testMethodId.remove(iTestResult); if (testId == null) { // This can happen when a method fails which this method depends on testId = idGenerator.generateId(); Object parentId = testMethodParentId.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)); } @Override public void onConfigurationSuccess(ITestResult testResult) { } @Override public void onConfigurationSkip(ITestResult testResult) { } @Override public void onConfigurationFailure(ITestResult testResult) { synchronized (lock) { if (!failedConfigurations.add(testResult)) { // workaround for bug in TestNG 6.2 (apparently fixed in some 6.3.x): listener is notified twice per event return; } } // 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)); } @Override public void beforeConfiguration(ITestResult tr) { } }