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

org.conqat.engine.commons.findings.location.ManualTestCaseTextRegionLocation Maven / Gradle / Ivy

The newest version!
package org.conqat.engine.commons.findings.location;

import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.conqat.lib.commons.assertion.CCSMAssert;
import org.conqat.lib.commons.collections.CollectionUtils;
import org.conqat.lib.commons.collections.UnmodifiableList;
import org.conqat.lib.commons.string.StringUtils;
import org.conqat.lib.commons.test.IndexValueClass;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

/**
 * This class represents a region of text in the test steps table in a manual test case description.
 * It specifies
 * 
    *
  • the indices of the test steps (table rows) where the text region starts and ends
  • *
  • whether it starts and ends in the 'action' or the 'check' part (table column) of the * respective start and end test steps.
  • *
  • the locations, if any, of gaps within the overall text region, e.g., for gapped clones.
  • *
* In principle, there are three kinds of supported locations, which are listed below in decreasing * order of specificity. Each location kind subsumes the one listed above. *
    *
  • Single cell locations: The text region starts and ends within the same table cell, * i.e., start and end test steps (table row) as well as start and end columns are the same.
  • *
  • Single step locations: The text region starts and ends within the same table row, * i.e., start and end test steps are the same but start and end columns are different (start in * 'action' and end in 'check').
  • *
  • Unrestricted locations: The text region starts and ends in different table rows, i.e., * start and end steps are different while start and end columns may or may not be different.
  • *
*/ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = ManualTestCaseTextRegionLocation.class) @IndexValueClass(containedInBackup = true) public class ManualTestCaseTextRegionLocation extends TeamscaleIssueFieldLocation { @Serial private static final long serialVersionUID = 1L; /** * The table cell where the text region starts. */ @JsonProperty("startTestStepIdentifier") private final @NonNull TestStepCellIdentifier startTestStepIdentifier; /** * The table cell where the text region ends. */ @JsonProperty("endTestStepIdentifier") private final @NonNull TestStepCellIdentifier endTestStepIdentifier; /** The gaps within the overall text region, if any. */ @JsonProperty("gapLocations") private final @NonNull UnmodifiableList gapLocations; /** * Constructs a text region location with potentially different start and end test steps and step * parts ('action' or 'check'). */ public ManualTestCaseTextRegionLocation(TeamscaleIssueFieldLocation fieldLocation, int startStepIndex, boolean startStepAction, int endStepIndex, boolean endStepAction, @Nullable List gapLocations) { super(fieldLocation); CCSMAssert.isTrue(startStepIndex >= 0 && endStepIndex >= 0, () -> String.format("Test steps cannot be negative (start=%d, end=%d).", startStepIndex, endStepIndex)); CCSMAssert.isTrue(startStepIndex <= endStepIndex, () -> String .format("Start step (%d) cannot be greater than end step (%d).", startStepIndex, endStepIndex)); if (startStepIndex == endStepIndex) { CCSMAssert.isTrue(startStepAction == endStepAction || startStepAction, "Single step locations cannot start in 'check' and end in 'action'."); } startTestStepIdentifier = new TestStepCellIdentifier(startStepIndex, startStepAction); endTestStepIdentifier = new TestStepCellIdentifier(endStepIndex, endStepAction); if (gapLocations == null) { this.gapLocations = CollectionUtils.emptyList(); } else { this.gapLocations = new UnmodifiableList<>(new ArrayList<>(gapLocations)); } } /** * Constructs a text region location with potentially different start and end test steps and step * parts ('action' or 'check'). */ public ManualTestCaseTextRegionLocation(String uniformPath, String issueId, RawAndFieldSpecific startOffset, RawAndFieldSpecific endOffset, RawAndFieldSpecific startLine, RawAndFieldSpecific endLine, int startStepIndex, boolean startStepAction, int endStepIndex, boolean endStepAction, String testStepsFieldName, @Nullable List gapLocations) { this(new TeamscaleIssueFieldLocation(uniformPath, issueId, startOffset, endOffset, startLine, endLine, testStepsFieldName), startStepIndex, startStepAction, endStepIndex, endStepAction, gapLocations); } /** * Returns the 0-based index of the test step where the text regions starts. */ public int getStartStepIndex() { return startTestStepIdentifier.index; } /** * Returns the 0-based index of the test step where the text regions ends. */ public int getEndStepIndex() { return endTestStepIdentifier.index; } /** * Returns {@code true} if the text region starts in the 'action' part of the start test step and * {@code false} if it starts in the 'check' part. */ public boolean startsInStepAction() { return startTestStepIdentifier.isAction; } /** * Returns {@code true} if the text region ends in the 'action' part of the end test step and * {@code false} if it ends in the 'check' part. */ public boolean endsInStepAction() { return endTestStepIdentifier.isAction; } /** * Returns {@code true} if the text region resides within a single test step cell and {@code false} * otherwise. */ public boolean isSingleCellLocation() { return isSingleStepLocation() && startTestStepIdentifier.isAction == endTestStepIdentifier.isAction; } /** * Returns {@code true} if the text region resides within a single test step and {@code false} * otherwise. */ public boolean isSingleStepLocation() { return startTestStepIdentifier.index == endTestStepIdentifier.index; } /** * Returns the gaps contained in this location. The returned list may be empty. */ public @NonNull UnmodifiableList getGapLocations() { return gapLocations; } @Override public String toLocationString() { String fieldPath = String.join(ElementLocation.INTERNAL_PATH_SEPARATOR, getUniformPath(), getAffectedField()); String startStep = String.valueOf(getStartStepIndex() + 1); // User visible step index starts from 1 String startColumn = StringUtils.alternativeOnCondition(startsInStepAction(), "action", "check"); if (isSingleCellLocation()) { return String.join(ElementLocation.INTERNAL_PATH_SEPARATOR, fieldPath, startStep, startColumn, getFieldStartLine() + "-" + getFieldEndLine()); } else if (isSingleStepLocation()) { String endColumn = StringUtils.alternativeOnCondition(endsInStepAction(), "action", "check"); return String.join(ElementLocation.INTERNAL_PATH_SEPARATOR, fieldPath, startStep, startColumn + "@" + getFieldStartLine() + "-" + endColumn + "@" + getFieldEndLine()); } else { String endColumn = StringUtils.alternativeOnCondition(endsInStepAction(), "action", "check"); String endStep = String.valueOf(getEndStepIndex() + 1); // User visible step index starts from 1 return String.join(ElementLocation.INTERNAL_PATH_SEPARATOR, fieldPath, startStep + "|" + startColumn + "@" + getFieldStartLine() + "-" + endStep + "|" + endColumn + "@" + getFieldEndLine()); } } @Override protected boolean canEqual(Object other) { return other instanceof ManualTestCaseTextRegionLocation; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof ManualTestCaseTextRegionLocation that)) { return false; } return that.canEqual(this) && Objects.equals(startTestStepIdentifier, that.startTestStepIdentifier) && Objects.equals(endTestStepIdentifier, that.endTestStepIdentifier) && Objects.equals(gapLocations, that.gapLocations) && super.equals(o); } @Override public int hashCode() { return Objects.hash(super.hashCode(), startTestStepIdentifier, endTestStepIdentifier, gapLocations); } /** * Identifier for a cell in the test steps table. Note that the table row index is 0-based. */ @IndexValueClass(containedInBackup = true) public static final class TestStepCellIdentifier implements Serializable { @Serial private static final long serialVersionUID = 1L; /** * The 0-based index of the test step inside the test case description (table row). */ @JsonProperty("index") public final int index; /** * Differentiates between 'action' or 'check' column. {@code true} means 'action', {@code false} * means 'check'. */ @JsonProperty("isAction") public final boolean isAction; public TestStepCellIdentifier(int index, boolean isAction) { this.index = index; this.isAction = isAction; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof TestStepCellIdentifier that)) { return false; } return index == that.index && isAction == that.isAction; } @Override public int hashCode() { return Objects.hash(index, isAction); } @Override public String toString() { return index + "/" + StringUtils.alternativeOnCondition(isAction, "action", "check"); } } /** * A gap location within a text region inside the test step table. Gap locations work in principle * the same as the {@link ManualTestCaseTextRegionLocation} itself. */ @IndexValueClass(containedInBackup = true) public static final class GapLocation implements Serializable { @Serial private static final long serialVersionUID = 1L; /** The cell where the gap starts. */ @JsonProperty("startCell") public final TestStepCellIdentifier startCell; /** The cell where the gap ends. */ @JsonProperty("endCell") public final TestStepCellIdentifier endCell; /** The start offset within {@link #startCell}, 0-based inclusive. */ @JsonProperty("fieldStartOffset") public final int startOffset; /** The end offset within {@link #endCell}, 0-based inclusive. */ @JsonProperty("fieldEndOffset") public final int endOffset; public GapLocation(TestStepCellIdentifier startCell, TestStepCellIdentifier endCell, int startOffset, int endOffset) { this.startCell = startCell; this.endCell = endCell; this.startOffset = startOffset; this.endOffset = endOffset; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof GapLocation that)) { return false; } return Objects.equals(startCell, that.startCell) && Objects.equals(endCell, that.endCell) && startOffset == that.startOffset && endOffset == that.endOffset; } @Override public int hashCode() { return Objects.hash(startCell, endCell, startOffset, endOffset); } @Override public String toString() { return "GapLocation[" + startCell + "@" + startOffset + "-" + endCell + "@" + endOffset + "]"; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy