/*
* Copyright 2015 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.tooling.internal.provider.runner;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.gradle.api.Task;
import org.gradle.api.execution.internal.ExecuteTaskBuildOperationDetails;
import org.gradle.api.internal.tasks.testing.TestCompleteEvent;
import org.gradle.api.internal.tasks.testing.TestDescriptorInternal;
import org.gradle.api.internal.tasks.testing.TestStartEvent;
import org.gradle.api.internal.tasks.testing.results.TestListenerInternal;
import org.gradle.api.tasks.testing.TestExecutionException;
import org.gradle.api.tasks.testing.TestOutputEvent;
import org.gradle.api.tasks.testing.TestResult;
import org.gradle.internal.progress.BuildOperationDescriptor;
import org.gradle.internal.progress.BuildOperationListener;
import org.gradle.internal.progress.OperationFinishEvent;
import org.gradle.internal.progress.OperationStartEvent;
import org.gradle.tooling.internal.protocol.events.InternalTestDescriptor;
import org.gradle.tooling.internal.protocol.test.InternalJvmTestRequest;
import org.gradle.tooling.internal.provider.TestExecutionRequestAction;
import org.gradle.tooling.internal.provider.events.DefaultTestDescriptor;
import java.util.Collection;
import java.util.List;
import java.util.Map;
class TestExecutionResultEvaluator implements TestListenerInternal, BuildOperationListener {
private static final String INDENT = " ";
private long resultCount;
private Map<Object, String> runningTasks = Maps.newHashMap();
private TestExecutionRequestAction internalTestExecutionRequest;
private List<FailedTest> failedTests = Lists.newArrayList();
public TestExecutionResultEvaluator(TestExecutionRequestAction internalTestExecutionRequest) {
this.internalTestExecutionRequest = internalTestExecutionRequest;
}
public boolean hasUnmatchedTests() {
return resultCount == 0;
}
public boolean hasFailedTests() {
return !failedTests.isEmpty();
}
public void evaluate() {
if (hasUnmatchedTests()) {
String formattedTestRequest = formatInternalTestExecutionRequest();
throw new TestExecutionException("No matching tests found in any candidate test task.\n" + formattedTestRequest);
}
if (hasFailedTests()) {
StringBuilder failedTestsMessage = new StringBuilder("Test failed.\n")
.append(INDENT).append("Failed tests:");
for (FailedTest failedTest : failedTests) {
failedTestsMessage.append("\n").append(Strings.repeat(INDENT, 2)).append(failedTest.getDescription());
}
throw new TestExecutionException(failedTestsMessage.toString());
}
}
private String formatInternalTestExecutionRequest() {
StringBuilder requestDetails = new StringBuilder(INDENT).append("Requested tests:");
for (InternalTestDescriptor internalTestDescriptor : internalTestExecutionRequest.getTestExecutionDescriptors()) {
requestDetails.append("\n").append(Strings.repeat(INDENT, 2)).append(internalTestDescriptor.getDisplayName());
requestDetails.append(" (Task: '").append(((DefaultTestDescriptor) internalTestDescriptor).getTaskPath()).append("')");
}
final Collection<InternalJvmTestRequest> internalJvmTestRequests = internalTestExecutionRequest.getInternalJvmTestRequests();
for (InternalJvmTestRequest internalJvmTestRequest : internalJvmTestRequests) {
final String className = internalJvmTestRequest.getClassName();
final String methodName = internalJvmTestRequest.getMethodName();
if (methodName == null) {
requestDetails.append("\n").append(Strings.repeat(INDENT, 2)).append("Test class ").append(className);
} else {
requestDetails.append("\n").append(Strings.repeat(INDENT, 2)).append("Test method ").append(className).append(".").append(methodName).append("()");
}
}
return requestDetails.toString();
}
@Override
public void started(TestDescriptorInternal testDescriptor, TestStartEvent startEvent) {
}
@Override
public void completed(TestDescriptorInternal testDescriptor, TestResult testResult, TestCompleteEvent completeEvent) {
if (testDescriptor.getParent() == null) {
resultCount = resultCount + testResult.getTestCount();
}
if (!testDescriptor.isComposite() && testResult.getFailedTestCount() != 0) {
failedTests.add(new FailedTest(testDescriptor.getName(), testDescriptor.getClassName(), getTaskPath(testDescriptor)));
}
}
private String getTaskPath(TestDescriptorInternal givenDescriptor) {
TestDescriptorInternal descriptor = givenDescriptor;
while (descriptor.getOwnerBuildOperationId() == null && descriptor.getParent() != null) {
descriptor = descriptor.getParent();
}
String taskPath = runningTasks.get(descriptor.getOwnerBuildOperationId());
if (taskPath == null) {
throw new IllegalStateException("No parent task for test " + givenDescriptor);
}
return taskPath;
}
@Override
public void output(TestDescriptorInternal testDescriptor, TestOutputEvent event) {
}
@Override
public void started(BuildOperationDescriptor buildOperation, OperationStartEvent startEvent) {
if (!(buildOperation.getDetails() instanceof ExecuteTaskBuildOperationDetails)) {
return;
}
Task task = ((ExecuteTaskBuildOperationDetails) buildOperation.getDetails()).getTask();
runningTasks.put(buildOperation.getId(), task.getPath());
}
@Override
public void finished(BuildOperationDescriptor buildOperation, OperationFinishEvent finishEvent) {
if (!(buildOperation.getDetails() instanceof ExecuteTaskBuildOperationDetails)) {
return;
}
runningTasks.remove(buildOperation.getId());
}
private static class FailedTest {
final String name;
final String className;
final String taskPath;
public FailedTest(String name, String className, String taskPath) {
this.name = name;
this.className = className;
this.taskPath = taskPath;
}
public String getDescription() {
StringBuilder stringBuilder = new StringBuilder("Test ")
.append(className).append("#").append(name)
.append(" (Task: ").append(taskPath).append(")");
return stringBuilder.toString();
}
}
}