org.gradle.integtests.fixtures.executer.OutputScrapingExecutionFailure Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 6.9.1 API redistribution.
/*
* Copyright 2011 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.integtests.fixtures.executer;
import junit.framework.AssertionFailedError;
import org.gradle.internal.Pair;
import org.gradle.util.TextUtil;
import org.hamcrest.Matcher;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import static org.gradle.util.Matchers.isEmpty;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
public class OutputScrapingExecutionFailure extends OutputScrapingExecutionResult implements ExecutionFailure {
private static final Pattern FAILURE_PATTERN = Pattern.compile("FAILURE: (.+)");
private static final Pattern CAUSE_PATTERN = Pattern.compile("(?m)(^\\s*> )");
private static final Pattern DESCRIPTION_PATTERN = Pattern.compile("(?ms)^\\* What went wrong:$(.+?)^\\* Try:$");
private static final Pattern LOCATION_PATTERN = Pattern.compile("(?ms)^\\* Where:((.+)'.+') line: (\\d+)$");
private static final Pattern RESOLUTION_PATTERN = Pattern.compile("(?ms)^\\* Try:$(.+?)^\\* Exception is:$");
private static final Pattern EXCEPTION_PATTERN = Pattern.compile("(?ms)^\\* Exception is:$(.+?):(.+?)$");
private static final Pattern EXCEPTION_CAUSE_PATTERN = Pattern.compile("(?ms)^Caused by: (.+?):(.+?)$");
private final String summary;
private final List descriptions = new ArrayList();
private final String lineNumber;
private final String fileName;
private final String resolution;
private final Exception exception;
// with normalized line endings
private final List causes = new ArrayList();
private final LogContent mainContent;
static boolean hasFailure(String error) {
return FAILURE_PATTERN.matcher(error).find();
}
/**
* Creates a result from the output of a single Gradle invocation.
*
* @param output The raw build stdout chars.
* @param error The raw build stderr chars.
* @return A {@link OutputScrapingExecutionResult} for a successful build, or a {@link OutputScrapingExecutionFailure} for a failed build.
*/
public static OutputScrapingExecutionFailure from(String output, String error) {
return new OutputScrapingExecutionFailure(output, error);
}
protected OutputScrapingExecutionFailure(String output, String error) {
super(LogContent.of(output), LogContent.of(error));
LogContent withoutDebug = LogContent.of(output).removeAnsiChars().removeDebugPrefix();
// Find failure section
Pair match = withoutDebug.splitOnFirstMatchingLine(FAILURE_PATTERN);
if (match == null) {
// Not present in output, check error output.
match = LogContent.of(error).removeAnsiChars().removeDebugPrefix().splitOnFirstMatchingLine(FAILURE_PATTERN);
if (match != null) {
match = Pair.of(withoutDebug, match.getRight());
} else {
// Not present, assume no failure details
match = Pair.of(withoutDebug, LogContent.empty());
}
} else {
if (match.getRight().countMatches(FAILURE_PATTERN) != 1) {
throw new IllegalArgumentException("Found multiple failure sections in log output: " + output);
}
}
LogContent failureContent = match.getRight();
this.mainContent = match.getLeft();
String failureText = failureContent.withNormalizedEol();
java.util.regex.Matcher matcher = FAILURE_PATTERN.matcher(failureText);
if (matcher.lookingAt()) {
summary = matcher.group(1);
} else {
summary = "";
}
matcher = LOCATION_PATTERN.matcher(failureText);
if (matcher.find()) {
fileName = matcher.group(1).trim();
lineNumber = matcher.group(3);
} else {
fileName = "";
lineNumber = "";
}
matcher = DESCRIPTION_PATTERN.matcher(failureText);
if (matcher.find()) {
String problemStr = matcher.group(1);
Problem problem = extract(problemStr);
descriptions.add(problem.description);
causes.addAll(problem.causes);
while (matcher.find()) {
problemStr = matcher.group(1);
problem = extract(problemStr);
descriptions.add(problem.description);
causes.addAll(problem.causes);
}
}
matcher = RESOLUTION_PATTERN.matcher(failureText);
if (!matcher.find()) {
resolution = "";
} else {
resolution = matcher.group(1).trim();
}
matcher = EXCEPTION_PATTERN.matcher(failureText);
if (!matcher.find()) {
exception = null;
} else {
String exceptionClass = matcher.group(1).trim();
String exceptionMessage = matcher.group(2).trim();
matcher = EXCEPTION_CAUSE_PATTERN.matcher(failureText);
exception = recreateException(exceptionClass, exceptionMessage, matcher);
}
}
@Override
public LogContent getMainContent() {
return mainContent;
}
private Problem extract(String problem) {
java.util.regex.Matcher matcher = CAUSE_PATTERN.matcher(problem);
String description;
List causes = new ArrayList();
if (!matcher.find()) {
description = TextUtil.normaliseLineSeparators(problem.trim());
} else {
description = TextUtil.normaliseLineSeparators(problem.substring(0, matcher.start()).trim());
while (true) {
int pos = matcher.end();
int prefix = matcher.group(1).length();
String prefixPattern = toPrefixPattern(prefix);
if (matcher.find(pos)) {
String cause = TextUtil.normaliseLineSeparators(problem.substring(pos, matcher.start()).trim().replaceAll(prefixPattern, ""));
causes.add(cause);
} else {
String cause = TextUtil.normaliseLineSeparators(problem.substring(pos).trim().replaceAll(prefixPattern, ""));
causes.add(cause);
break;
}
}
}
return new Problem(description, causes);
}
private Exception recreateException(String className, String message, java.util.regex.Matcher exceptionCauseMatcher) {
Exception causedBy = null;
if (exceptionCauseMatcher.find()) {
String causedByClass = exceptionCauseMatcher.group(1).trim();
String causedByMessage = exceptionCauseMatcher.group(2).trim();
causedBy = recreateException(causedByClass, causedByMessage, exceptionCauseMatcher);
}
try {
if (causedBy == null) {
return (Exception) Class.forName(className).getConstructor(String.class).newInstance(message);
} else {
return (Exception) Class.forName(className).getConstructor(String.class, Throwable.class).newInstance(message, causedBy);
}
} catch (Exception e) {
return new Exception(message);
}
}
private String toPrefixPattern(int prefix) {
StringBuilder builder = new StringBuilder("(?m)^");
for (int i = 0; i < prefix; i++) {
builder.append(' ');
}
return builder.toString();
}
public ExecutionFailure assertHasLineNumber(int lineNumber) {
assertThat(this.lineNumber, equalTo(String.valueOf(lineNumber)));
return this;
}
public ExecutionFailure assertHasFileName(String filename) {
assertThat(this.fileName, equalTo(filename));
return this;
}
@Override
public ExecutionFailure assertHasFailures(int count) {
assertThat(this.descriptions.size(), equalTo(count));
if (count == 1) {
assertThat(summary, equalTo("Build failed with an exception."));
} else {
assertThat(summary, equalTo(String.format("Build completed with %s failures.", count)));
}
return this;
}
public ExecutionFailure assertHasCause(String description) {
assertThatCause(startsWith(description));
return this;
}
public ExecutionFailure assertThatCause(Matcher matcher) {
for (String cause : causes) {
if (matcher.matches(cause)) {
return this;
}
}
fail(String.format("No matching cause found in %s. Output: [%s], Error: [%s]", causes, getOutput(), getError()));
return this;
}
public ExecutionFailure assertHasResolution(String resolution) {
assertThat(this.resolution, containsString(resolution));
return this;
}
@Override
public ExecutionFailure assertHasNoCause(String description) {
Matcher matcher = containsString(description);
for (String cause : causes) {
if (matcher.matches(cause)) {
throw new AssertionFailedError(String.format("Expected no failure with description '%s', found: %s", description, cause));
}
}
return this;
}
public ExecutionFailure assertHasNoCause() {
assertThat(causes, isEmpty());
return this;
}
public ExecutionFailure assertHasDescription(String context) {
assertThatDescription(startsWith(context));
return this;
}
public ExecutionFailure assertThatDescription(Matcher matcher) {
assertThat(descriptions, hasItem(matcher));
return this;
}
public ExecutionFailure assertTestsFailed() {
new DetailedExecutionFailure(this).assertTestsFailed();
return this;
}
public DependencyResolutionFailure assertResolutionFailure(String configurationPath) {
return new DependencyResolutionFailure(this, configurationPath);
}
public Exception getException() {
return exception;
}
private static class Problem {
final String description;
final List causes;
private Problem(String description, List causes) {
this.description = description;
this.causes = causes;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy