Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import java.lang.annotation.Repeatable;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
Expand All @@ -57,6 +58,7 @@
import static io.qameta.allure.util.ResultsUtils.createLanguageLabel;
import static io.qameta.allure.util.ResultsUtils.createParameter;
import static io.qameta.allure.util.ResultsUtils.createSuiteLabel;
import static io.qameta.allure.util.ResultsUtils.createTestClassLabel;
import static io.qameta.allure.util.ResultsUtils.createThreadLabel;
import static io.qameta.allure.util.ResultsUtils.createTitlePath;
import static io.qameta.allure.util.ResultsUtils.getProvidedLabels;
Expand Down Expand Up @@ -241,6 +243,10 @@ private void startTest(final TestCase testCase) {
testClass.map(this::getLabels).ifPresent(result.getLabels()::addAll);
testClass.map(this::getLinks).ifPresent(result.getLinks()::addAll);

// the test is represented by a class only when the test case is class-backed (java dsl);
// the test method stays unknown — the test case name may be customized by the user
testClass.ifPresent(aClass -> result.getLabels().add(createTestClassLabel(aClass.getName())));

result.getLabels().addAll(
Arrays.asList(
createHostLabel(),
Expand All @@ -250,9 +256,10 @@ private void startTest(final TestCase testCase) {
)
);

final List<Label> defaultLabels = new ArrayList<>();
testClass.ifPresent(aClass -> {
final String suiteName = aClass.getCanonicalName();
result.getLabels().add(createSuiteLabel(suiteName));
defaultLabels.add(createSuiteLabel(suiteName));
});

final Optional<String> classDescription = testClass.flatMap(this::getDescription);
Expand All @@ -264,6 +271,7 @@ private void startTest(final TestCase testCase) {
result.setDescription(description);

getLifecycle().scheduleTest(testKey, result);
getLifecycle().addDefaultLabels(testKey, defaultLabels);
getLifecycle().startTest(testKey);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import io.qameta.allure.Allure;
import io.qameta.allure.AllureLifecycle;
import io.qameta.allure.Step;
import io.qameta.allure.model.Label;
import io.qameta.allure.model.Parameter;
import io.qameta.allure.model.Stage;
import io.qameta.allure.model.Status;
Expand Down Expand Up @@ -63,6 +64,26 @@ void shouldSetName() {
.containsExactly("com", "consol", "citrus", "dsl", "design", "DefaultTestDesigner");
}

@AllureFeatures.Base
@Test
void shouldSetTestClassLabelForClassBackedTests() {
final DefaultTestDesigner designer = new DefaultTestDesigner();
designer.name("Simple test");

final AllureResults results = run(designer);
assertThat(results.getTestResults())
.hasSize(1)
.flatExtracting(TestResult::getLabels)
.extracting(Label::getName, Label::getValue)
.contains(tuple("testClass", "com.consol.citrus.dsl.design.DefaultTestDesigner"));

// the test method is not a known code location for citrus test cases
assertThat(results.getTestResults())
.flatExtracting(TestResult::getLabels)
.extracting(Label::getName)
.doesNotContain("testMethod");
}

@AllureFeatures.PassedTests
@Test
void shouldSetStatus() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ private void handleTestCaseStarted(final TestCaseStarted event) {

final AllureExternalKey testKey = testKey(testCaseUuid);
lifecycle.scheduleTest(testKey, result);
lifecycle.addDefaultLabels(testKey, labelBuilder.getDefaultLabels());
lifecycle.startTest(testKey);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
import static io.qameta.allure.util.ResultsUtils.createLanguageLabel;
import static io.qameta.allure.util.ResultsUtils.createStoryLabel;
import static io.qameta.allure.util.ResultsUtils.createSuiteLabel;
import static io.qameta.allure.util.ResultsUtils.createTestClassLabel;
import static io.qameta.allure.util.ResultsUtils.createThreadLabel;

/**
Expand All @@ -60,6 +59,7 @@ final class LabelBuilder {
private static final String OWNER = "@OWNER";

private final List<Label> scenarioLabels = new ArrayList<>();
private final List<Label> defaultLabels = new ArrayList<>();
private final List<Link> scenarioLinks = new ArrayList<>();

LabelBuilder(final Feature feature, final TestCase scenario, final Deque<String> tags) {
Expand Down Expand Up @@ -121,16 +121,20 @@ final class LabelBuilder {
Arrays.asList(
createHostLabel(),
createThreadLabel(),
createFeatureLabel(featureName),
createStoryLabel(scenario.getName()),
createSuiteLabel(featureName),
createTestClassLabel(scenario.getName()),
createFrameworkLabel("cucumber7jvm"),
createLanguageLabel("java"),
createLabel("gherkin_uri", uri.toString())
)
);

defaultLabels.addAll(
Arrays.asList(
createFeatureLabel(featureName),
createStoryLabel(scenario.getName()),
createSuiteLabel(featureName)
)
);

featurePackage(uri.toString(), featureName)
.map(ResultsUtils::createPackageLabel)
.ifPresent(scenarioLabels::add);
Expand All @@ -140,6 +144,10 @@ List<Label> getScenarioLabels() {
return scenarioLabels;
}

List<Label> getDefaultLabels() {
return defaultLabels;
}

List<Link> getScenarioLinks() {
return scenarioLinks;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import static io.qameta.allure.util.ResultsUtils.PACKAGE_LABEL_NAME;
import static io.qameta.allure.util.ResultsUtils.SUITE_LABEL_NAME;
import static io.qameta.allure.util.ResultsUtils.TEST_CLASS_LABEL_NAME;
import static io.qameta.allure.util.ResultsUtils.TEST_METHOD_LABEL_NAME;
import static io.qameta.allure.util.ResultsUtils.md5;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
Expand Down Expand Up @@ -429,9 +430,14 @@ void shouldAddCommonLabels() {
.extracting(Label::getName, Label::getValue)
.contains(
tuple(PACKAGE_LABEL_NAME, "src.test.resources.features.tags_feature.Test Simple Scenarios"),
tuple(SUITE_LABEL_NAME, "Test Simple Scenarios"),
tuple(TEST_CLASS_LABEL_NAME, "Add a to b")
tuple(SUITE_LABEL_NAME, "Test Simple Scenarios")
);

// scenarios are not represented by code, so the code-location labels stay unknown
assertThat(testResults)
.flatExtracting(TestResult::getLabels)
.extracting(Label::getName)
.doesNotContain(TEST_CLASS_LABEL_NAME, TEST_METHOD_LABEL_NAME);
}

@AllureFeatures.Steps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io.qameta.allure.listener.TestLifecycleListener;
import io.qameta.allure.model.Attachment;
import io.qameta.allure.model.FixtureResult;
import io.qameta.allure.model.Label;
import io.qameta.allure.model.Parameter;
import io.qameta.allure.model.ScopeFixtureResult;
import io.qameta.allure.model.ScopeFixtureType;
Expand All @@ -47,6 +48,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
Expand All @@ -58,6 +60,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
Expand Down Expand Up @@ -329,10 +332,34 @@ public void updateTest(final Consumer<TestResult> update) {
}

/**
* Stops test by given key. The test must be running; scope metadata is merged into the test here. If the test has
* a test case id but no history id, a compatibility history id is generated from the test case id and the final
* parameters. A history id supplied by a {@link TestLifecycleListener#beforeTestStop(TestResult)} listener is
* preserved. Unbinds the calling thread only if the test is the calling thread's root.
* Registers default labels for the test with given key. Default labels do not appear on the test result until
* the test stops: {@link #stopTest(AllureExternalKey)} merges them after scope metadata, adding, for each
* distinct label name, the default labels with that name only when the test has no labels with that name by
* then. Labels provided by the user — through annotations, the runtime API, or before fixtures — thus take
* precedence over defaults instead of being duplicated by them. Repeated calls accumulate.
*
* <p>Intended for the framework-computed grouping labels a user may legitimately override: the suite family
* and BDD structure labels. System labels — framework, language, host, thread, package, testClass, testMethod
* — are facts about the run, not defaults: set them eagerly on the result (host and thread are overridable
* only through their dedicated properties, see {@code ResultsUtils}).</p>
*
* @param key the external test key
* @param labels the default labels
*/
public void addDefaultLabels(final AllureExternalKey key, final Collection<Label> labels) {
final TestItem item = getItem(key, TestItem.class, "add default labels");
if (Objects.isNull(item)) {
return;
}
item.defaultLabels().addAll(labels);
}

/**
* Stops test by given key. The test must be running; scope metadata is merged into the test here, then default
* labels are applied for each label name the test still has no labels for. If the test has a test case id but
* no history id, a compatibility history id is generated from the test case id and the final parameters. A
* history id supplied by a {@link TestLifecycleListener#beforeTestStop(TestResult)} listener is preserved.
* Unbinds the calling thread only if the test is the calling thread's root.
*
* @param key the external test key
*/
Expand All @@ -357,6 +384,7 @@ public void stopTest(final AllureExternalKey key) {
testResult.setParameters(new ArrayList<>());
}
applyScopeMetadata(item);
applyDefaultLabels(item);
if (Objects.isNull(testResult.getHistoryId()) && Objects.nonNull(testResult.getTestCaseId())) {
testResult.setHistoryId(calculateHistoryId(testResult.getTestCaseId(), testResult.getParameters()));
}
Expand Down Expand Up @@ -1312,6 +1340,34 @@ private void applyScopeMetadata(final TestItem item) {
}
}

/**
* Adds the test's registered default labels — for each distinct label name, only when the test has no labels
* with that name. Runs after scope metadata is merged, so labels set from before fixtures also take precedence
* over defaults.
*/
private static void applyDefaultLabels(final TestItem item) {
if (item.defaultLabels().isEmpty()) {
return;
}
final TestResult testResult = item.result();
// the result may carry an immutable label list, so merge into a mutable copy
final List<Label> labels = Objects.isNull(testResult.getLabels())
? new ArrayList<>()
: new ArrayList<>(testResult.getLabels());
final Set<String> presentNames = new HashSet<>();
for (final Label label : labels) {
if (Objects.nonNull(label)) {
presentNames.add(label.getName());
}
}
for (final Label label : item.defaultLabels()) {
if (Objects.nonNull(label) && !presentNames.contains(label.getName())) {
labels.add(label);
}
}
testResult.setLabels(labels);
}

private static void mergeScopeMetadata(final ScopeResult scope, final TestResult testResult) {
testResult.getLabels().addAll(scope.getLabels());
testResult.getLinks().addAll(scope.getLinks());
Expand Down Expand Up @@ -1360,12 +1416,14 @@ private static void normalizeScope(final ScopeResult scope) {

/**
* Internal item of a scheduled or running test: the result model, the scopes the test is linked to (their
* metadata is merged into the test at stop), and the async attachment futures awaited before the test is written.
* metadata is merged into the test at stop), the default labels (applied at stop for label names the test has
* no labels for), and the async attachment futures awaited before the test is written.
*/
private record TestItem(TestResult result, Set<AllureExternalKey> scopes, Set<CompletableFuture<?>> futures) {
private record TestItem(TestResult result, Set<AllureExternalKey> scopes, List<Label> defaultLabels,
Set<CompletableFuture<?>> futures) {

private TestItem(final TestResult result) {
this(result, ConcurrentHashMap.newKeySet(), ConcurrentHashMap.newKeySet());
this(result, ConcurrentHashMap.newKeySet(), new CopyOnWriteArrayList<>(), ConcurrentHashMap.newKeySet());
}
}

Expand Down
Loading
Loading