All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.conqat.engine.index.shared.tests.TestExecutionWithPartition Maven / Gradle / Ivy

There is a newer version: 2025.1.0-rc2
Show newest version
/*
 * Copyright (c) CQSE GmbH
 *
 * 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.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.conqat.engine.index.shared.CommitDescriptor;
import org.conqat.lib.commons.assertion.CCSMAssert;
import org.conqat.lib.commons.collections.CollectionUtils;
import org.conqat.lib.commons.js_export.ExportToTypeScript;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;

/** Representation of a test execution in a partition. */
@ExportToTypeScript
public class TestExecutionWithPartition implements Serializable {

	private static final long serialVersionUID = 1L;

	/** The name of the JSON property name for {@link #testExecution}. */
	private static final String TEST_EXECUTION_PROPERTY = "testExecution";

	/** The name of the JSON property name for {@link #partition}. */
	private static final String PARTITION_PROPERTY = "partition";

	/** The name of the JSON property name for {@link #commit}. */
	private static final String COMMIT_PROPERTY = "commit";

	/** The name of the JSON property name for {@link #predecessorCommits}. */
	private static final String PREDECESSOR_COMMITS_PROPERTY = "predecessorCommits";

	/** The test execution. */
	@JsonProperty(TEST_EXECUTION_PROPERTY)
	private final TestExecution testExecution;

	/** The partition. */
	@JsonProperty(PARTITION_PROPERTY)
	private final String partition;

	/** The commit of the report upload containing the {@link #testExecution}. */
	@JsonProperty(COMMIT_PROPERTY)
	private final CommitDescriptor commit;

	/**
	 * True if and only if this is a {@link TestExecutionWithPartition} created by
	 * {@link #merge(CommitDescriptor, Collection)}.
	 */
	@JsonProperty("isArtificialMergeTestExecution")
	private final boolean isArtificialMergeTestExecution;

	/**
	 * The commit of the previous report upload. May be on another branch. For a
	 * merge the {@link TestExecutionWithPartition#getCommit()} of the first parent
	 * branch is used as the predecessor if one is present.
	 */
	@JsonProperty(PREDECESSOR_COMMITS_PROPERTY)
	private final List predecessorCommits = new ArrayList<>();

	@JsonCreator
	public TestExecutionWithPartition(@JsonProperty(TEST_EXECUTION_PROPERTY) TestExecution testExecution,
			@JsonProperty(PARTITION_PROPERTY) String partition, @JsonProperty(COMMIT_PROPERTY) CommitDescriptor commit,
			@JsonProperty(PREDECESSOR_COMMITS_PROPERTY) Collection predecessorCommits) {
		this(testExecution, partition, commit, predecessorCommits, false);
	}

	@VisibleForTesting
	TestExecutionWithPartition(TestExecution testExecution, String partition, CommitDescriptor commit,
			Collection predecessorCommits, boolean isArtificialMergeTestExecution) {
		Preconditions.checkState(
				predecessorCommits.stream().allMatch(predecessorCommit -> predecessorCommit.compareTo(commit) < 0),
				"Predecessor commits " + predecessorCommits + " can't come after commit " + commit);

		this.testExecution = testExecution;
		this.partition = partition;
		this.commit = commit;
		this.isArtificialMergeTestExecution = isArtificialMergeTestExecution;

		// Ensures unique predecessor commits stable order for delta compression
		predecessorCommits.stream().distinct().forEach(this.predecessorCommits::add);
		Collections.sort(this.predecessorCommits);

	}

	/** @see #testExecution */
	public TestExecution getTestExecution() {
		return testExecution;
	}

	/** @see #partition */
	public String getPartition() {
		return partition;
	}

	/** @see #commit */
	public CommitDescriptor getCommit() {
		return commit;
	}

	/** @see #predecessorCommits */
	public List getPredecessorCommits() {
		return CollectionUtils.asUnmodifiable(predecessorCommits);
	}

	/**
	 * Returns true if this is a {@link TestExecutionWithPartition} which was
	 * created for a merge commit and doesn't represent a real test execution.
	 */
	public boolean isArtificialMergeTestExecution() {
		return isArtificialMergeTestExecution;
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (o == null || getClass() != o.getClass()) {
			return false;
		}
		TestExecutionWithPartition that = (TestExecutionWithPartition) o;
		return isArtificialMergeTestExecution == that.isArtificialMergeTestExecution
				&& Objects.equals(testExecution, that.testExecution) && Objects.equals(partition, that.partition)
				&& Objects.equals(commit, that.commit) && Objects.equals(predecessorCommits, that.predecessorCommits);
	}

	@Override
	public int hashCode() {
		return Objects.hash(testExecution, partition, commit, isArtificialMergeTestExecution, predecessorCommits);
	}

	@Override
	public String toString() {
		return ReflectionToStringBuilder.toString(this);
	}

	/**
	 * Merges a {@link Collection} of {@link TestExecutionWithPartition}s to a
	 * single one. Result is the the worst {@link ETestExecutionResult} with average
	 * duration in case there are is more than on {@link TestExecutionWithPartition}
	 * present.
	 */
	public static TestExecutionWithPartition merge(CommitDescriptor mergeCommit,
			Collection testExecutionsWithPartition) {
		Preconditions.checkArgument(!testExecutionsWithPartition.isEmpty(),
				"Can't merge emtpy collection of test executions with partition.");

		if (testExecutionsWithPartition.size() == 1) {
			return testExecutionsWithPartition.iterator().next();
		}

		String uniquePartition = testExecutionsWithPartition.iterator().next().getPartition();
		List testExecutions = new ArrayList<>();
		Set predecessorCommits = new HashSet<>();

		testExecutionsWithPartition.forEach(testExecutionWithPartition -> {
			String partition = testExecutionWithPartition.getPartition();

			predecessorCommits.add(testExecutionWithPartition.getCommit());
			testExecutions.add(testExecutionWithPartition.getTestExecution());

			CCSMAssert.isTrue(uniquePartition.equals(partition),
					() -> "Can't merge test executions from separate partitions: " + uniquePartition + "," + partition);
		});

		TestExecution mergedTestExecution = TestExecution.merge(testExecutions);

		return new TestExecutionWithPartition(mergedTestExecution, uniquePartition, mergeCommit, predecessorCommits,
				true);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy