org.conqat.engine.index.shared.tests.TestExecution Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of teamscale-commons Show documentation
Show all versions of teamscale-commons Show documentation
Provides common DTOs for Teamscale
/*-------------------------------------------------------------------------+
| |
| Copyright (c) 2005-2018 The ConQAT Project |
| |
| 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.conqat.engine.index.shared.tests;
import java.io.Serializable;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.conqat.engine.sourcecode.coverage.TestUniformPathUtils;
import org.conqat.lib.commons.assessment.Assessment;
import org.conqat.lib.commons.assessment.ETrafficLightColor;
import org.conqat.lib.commons.js_export.ExportToTypeScript;
import org.conqat.lib.commons.string.StringUtils;
import org.conqat.lib.commons.uniformpath.RelativeUniformPath;
import org.conqat.lib.commons.uniformpath.UniformPath;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterables;
import com.teamscale.commons.utils.StringPool;
/**
* Representation of a single test (method) execution.
*/
@ExportToTypeScript
public class TestExecution implements Serializable {
private static final long serialVersionUID = 1L;
/** The name of the JSON property name for {@link #uniformPath}. */
private static final String UNIFORM_PATH_PROPERTY = "uniformPath";
/** The name of the JSON property name for {@link #durationSeconds}. */
private static final String DURATION_SECONDS_PROPERTY = "durationSeconds";
/** The name of the JSON property name for {@link #result}. */
private static final String RESULT_PROPERTY = "result";
/** The name of the JSON property name for {@link #message}. */
private static final String MESSAGE_PROPERTY = "message";
/**
* The uniform path of the test (method) that was executed. This is an absolute
* (i.e. hierarchical) reference which identifies the test uniquely in the scope
* of the Teamscale project. It may (but is not required to) correspond to the
* path of some automated test case source code known to Teamscale. If the test
* was parameterized, this path is expected to reflect the parameter in some
* manner.
*/
@JsonProperty(UNIFORM_PATH_PROPERTY)
private final String uniformPath;
/**
* Duration of the execution in seconds.
*/
@JsonProperty(DURATION_SECONDS_PROPERTY)
private final double durationSeconds;
/**
* The actual execution result state.
*/
@JsonProperty(RESULT_PROPERTY)
private final ETestExecutionResult result;
/**
* Optional message given for test failures (normally contains a stack trace).
* For skipped and ignored tests the message will hold the message of skipped
* and ignored tests. e.g. "Flickers see TS-12345". May be {@code null}.
*/
@JsonProperty(MESSAGE_PROPERTY)
@Nullable
private final String message;
private TestExecution(RelativeUniformPath uniformPath, double durationSeconds, ETestExecutionResult result,
String message) {
this(uniformPath.toString(), durationSeconds, result, message);
}
@JsonCreator
protected TestExecution(@JsonProperty(UNIFORM_PATH_PROPERTY) String uniformPath,
@JsonProperty(DURATION_SECONDS_PROPERTY) double durationSeconds,
@JsonProperty(RESULT_PROPERTY) ETestExecutionResult result,
@JsonProperty(MESSAGE_PROPERTY) String message) {
Preconditions.checkArgument(uniformPath != null, "Uniform path can't be null for test execution");
Preconditions.checkArgument(result != null, "Result can't be null for test execution");
Preconditions.checkArgument(durationSeconds >= 0, "Test duration can't be negative");
this.uniformPath = StringPool.intern(uniformPath);
this.durationSeconds = durationSeconds;
this.result = result;
this.message = message;
}
/**
* @see #uniformPath
*/
public String getUniformPath() {
return uniformPath;
}
/**
* @see #durationSeconds
*/
public double getDurationMillis() {
return durationSeconds * 1000.0;
}
/**
* @see #durationSeconds
*/
public double getDurationSeconds() {
return durationSeconds;
}
/**
* @see #result
*/
public ETestExecutionResult getResult() {
return result;
}
/**
* @see #message
*/
public Optional getMessage() {
return Optional.ofNullable(message);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TestExecution that = (TestExecution) o;
return Double.compare(that.durationSeconds, durationSeconds) == 0
&& Objects.equals(uniformPath, that.uniformPath) && result == that.result
&& Objects.equals(message, that.message);
}
@Override
public int hashCode() {
return Objects.hash(uniformPath, durationSeconds, result, message);
}
@Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
/**
* Returns whether something went wrong during execution of this test (either
* direct test failure or error during execution).
*/
public boolean isFailure() {
return result == ETestExecutionResult.ERROR || result == ETestExecutionResult.FAILURE;
}
/**
* Create an assessment for this test result.
*/
public Assessment createAssessment() {
switch (result) {
case PASSED:
return new Assessment(ETrafficLightColor.GREEN);
case IGNORED:
return new Assessment(ETrafficLightColor.YELLOW);
case SKIPPED:
return new Assessment(ETrafficLightColor.YELLOW);
case ERROR:
return new Assessment(ETrafficLightColor.RED);
case FAILURE:
return new Assessment(ETrafficLightColor.RED);
default:
return new Assessment(ETrafficLightColor.UNKNOWN);
}
}
/**
* Compute the test execution path.
*/
public UniformPath toUniformPath() {
return TestUniformPathUtils.convertToUniformPath(uniformPath);
}
/**
* Merges the {@link TestExecution}s. The result is the worst
* {@link ETestExecutionResult} with the maximum encountered
* {@link TestExecution#durationSeconds}.
*/
public static TestExecution merge(Collection testExecutions) {
Preconditions.checkArgument(!testExecutions.isEmpty(), "Can't merge emtpy collection of test executions.");
if (testExecutions.size() == 1) {
return Iterables.getOnlyElement(testExecutions);
}
Iterator it = testExecutions.iterator();
TestExecution worstExecution = it.next();
while (it.hasNext()) {
TestExecution nextExecution = it.next();
Preconditions.checkArgument(nextExecution.uniformPath.equals(worstExecution.uniformPath),
"Can't merge test executions from separate uniform paths.");
if (ETestExecutionResult.worst(nextExecution.result, worstExecution.result) != worstExecution.result) {
worstExecution = nextExecution;
}
}
double maxDurationSeconds = testExecutions.stream().mapToDouble(TestExecution::getDurationSeconds).max()
.getAsDouble();
return new TestExecution(worstExecution.uniformPath, maxDurationSeconds, worstExecution.result,
worstExecution.message);
}
/**
* Builder for creating {@link TestExecution} instances.
*/
public static class Builder {
private static final Pattern UNESCAPED_SLASH = Pattern.compile("(? pathSegments, String testName) {
return fromPath(RelativeUniformPath.of(pathSegments).addSuffix(UniformPath.escapeSegment(testName)));
}
/**
* Sets the failure message. Trims leading whitespace on each line.
*/
public Builder setFailureMessage(String failureMessage) {
if (failureMessage != null) {
failureMessage = StringUtils.removeWhitespaceAtBeginningOfLine(failureMessage.trim());
if (!failureMessage.isEmpty()) {
this.failureMessage = failureMessage;
}
}
return this;
}
/**
* Sets the test duration.
*/
public Builder setDuration(Duration duration) {
this.durationInSeconds = duration.toMillis() / 1000d;
return this;
}
/**
* Sets the duration.
*/
public Builder setDurationInSeconds(double durationInSeconds) {
this.durationInSeconds = durationInSeconds;
return this;
}
/**
* Sets the execution result.
*/
public Builder setResult(ETestExecutionResult result) {
this.result = result;
return this;
}
/**
* Creates a new {@link TestExecution} instance from this builder.
*/
public TestExecution build() {
return new TestExecution(uniformPath, durationInSeconds, result, failureMessage);
}
/**
* Returns the absolute uniform path of this test execution including test
* namespace/path and test name.
*/
public RelativeUniformPath getAbsolutePath() {
return uniformPath;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy