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

io.qameta.allure.jbehave.AllureJbehave Maven / Gradle / Ivy

/*
 *  Copyright 2016-2024 Qameta Software Inc
 *
 *  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 io.qameta.allure.jbehave;

import io.qameta.allure.Allure;
import io.qameta.allure.AllureLifecycle;
import io.qameta.allure.model.Label;
import io.qameta.allure.model.Parameter;
import io.qameta.allure.model.Stage;
import io.qameta.allure.model.Status;
import io.qameta.allure.model.StatusDetails;
import io.qameta.allure.model.StepResult;
import io.qameta.allure.model.TestResult;
import org.jbehave.core.failures.UUIDExceptionWrapper;
import org.jbehave.core.model.ExamplesTable;
import org.jbehave.core.model.Scenario;
import org.jbehave.core.model.Story;
import org.jbehave.core.reporters.NullStoryReporter;

import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;

import static io.qameta.allure.util.ResultsUtils.bytesToHex;
import static io.qameta.allure.util.ResultsUtils.createFrameworkLabel;
import static io.qameta.allure.util.ResultsUtils.createHostLabel;
import static io.qameta.allure.util.ResultsUtils.createLanguageLabel;
import static io.qameta.allure.util.ResultsUtils.createParameter;
import static io.qameta.allure.util.ResultsUtils.createStoryLabel;
import static io.qameta.allure.util.ResultsUtils.createThreadLabel;
import static io.qameta.allure.util.ResultsUtils.getMd5Digest;
import static io.qameta.allure.util.ResultsUtils.getStatus;
import static io.qameta.allure.util.ResultsUtils.getStatusDetails;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static java.util.Comparator.comparing;

/**
 * @author charlie (Dmitry Baev).
 */
public class AllureJbehave extends NullStoryReporter {

    private final AllureLifecycle lifecycle;

    private final ThreadLocal currentStory = new InheritableThreadLocal<>();

    private final ThreadLocal currentScenario = new InheritableThreadLocal<>();

    private final Map> scenarioUuids = new ConcurrentHashMap<>();

    private final ThreadLocal> givenStories = ThreadLocal.withInitial(LinkedList::new);

    private final ReadWriteLock lock = new ReentrantReadWriteLock();

    @SuppressWarnings("unused")
    public AllureJbehave() {
        this(Allure.getLifecycle());
    }

    public AllureJbehave(final AllureLifecycle lifecycle) {
        this.lifecycle = lifecycle;
    }

    @Override
    public void beforeStory(final Story story, final boolean givenStory) {
        if (givenStory) {
            givenStories.get().push(story);
        } else {
            currentStory.set(story);
        }
    }

    @Override
    public void afterStory(final boolean givenStory) {
        if (givenStory) {
            givenStories.get().pop();
        } else {
            currentStory.remove();
        }
    }

    @Override
    public void beforeScenario(final Scenario scenario) {
        if (isGivenStory()) {
            return;
        }
        currentScenario.set(scenario);

        if (notParameterised(scenario)) {
            final String uuid = UUID.randomUUID().toString();
            usingWriteLock(() -> scenarioUuids.put(scenario, new ArrayList<>(singletonList(uuid))));
            startTestCase(uuid, scenario, emptyMap());
        } else {
            usingWriteLock(() -> scenarioUuids.put(scenario, new ArrayList<>()));
        }
    }

    @Override
    public void beforeExamples(final List steps, final ExamplesTable table) {
        if (isGivenStory()) {
            return;
        }
        final Scenario scenario = currentScenario.get();
        lock.writeLock().lock();
        try {
            scenarioUuids.put(scenario, new ArrayList<>());
        } finally {
            lock.writeLock().unlock();
        }
    }

    @Override
    public void example(final Map tableRow, final int exampleIndex) {
        if (isGivenStory()) {
            return;
        }
        final Scenario scenario = currentScenario.get();
        final String uuid = UUID.randomUUID().toString();
        usingWriteLock(() -> scenarioUuids.getOrDefault(scenario, new ArrayList<>()).add(uuid));
        startTestCase(uuid, scenario, tableRow);
    }

    @Override
    public void afterScenario() {
        if (isGivenStory()) {
            return;
        }
        final Scenario scenario = currentScenario.get();
        usingReadLock(() -> {
            final List uuids = scenarioUuids.getOrDefault(scenario, emptyList());
            uuids.forEach(this::stopTestCase);
        });
        currentScenario.remove();
        usingWriteLock(() -> scenarioUuids.remove(scenario));
    }

    @Override
    public void beforeStep(final String step) {
        final String stepUuid = UUID.randomUUID().toString();
        getLifecycle().startStep(stepUuid, new StepResult().setName(step));
    }

    @Override
    public void successful(final String step) {
        getLifecycle().updateTestCase(result -> result.setStatus(Status.PASSED));
        getLifecycle().updateStep(result -> result.setStatus(Status.PASSED));
        getLifecycle().stopStep();
    }

    @Override
    public void ignorable(final String step) {
        beforeStep(step);
        getLifecycle().stopStep();
    }

    @Override
    public void pending(final String step) {
        beforeStep(step);
        getLifecycle().updateStep(result -> result.setStatus(Status.SKIPPED));
        getLifecycle().stopStep();
    }

    @Override
    public void notPerformed(final String step) {
        beforeStep(step);
        getLifecycle().stopStep();
    }

    @Override
    public void failed(final String step, final Throwable cause) {
        final Throwable unwrapped = cause instanceof UUIDExceptionWrapper
                ? cause.getCause()
                : cause;


        final Status status = getStatus(unwrapped).orElse(Status.FAILED);
        final StatusDetails statusDetails = getStatusDetails(unwrapped).orElseGet(StatusDetails::new);

        getLifecycle().updateStep(result -> {
            result.setStatus(status);
            result.setStatusDetails(statusDetails);
        });

        getLifecycle().updateTestCase(result -> {
            result.setStatus(status);
            result.setStatusDetails(statusDetails);
        });

        getLifecycle().stopStep();
    }


    public AllureLifecycle getLifecycle() {
        return lifecycle;
    }

    protected void startTestCase(final String uuid,
                                 final Scenario scenario,
                                 final Map tableRow) {
        final Story story = currentStory.get();

        final String name = scenario.getTitle();
        final String fullName = String.format("%s: %s", story.getName(), name);

        final List parameters = tableRow.entrySet().stream()
                .map(entry -> createParameter(entry.getKey(), entry.getValue()))
                .collect(Collectors.toList());

        final List




© 2015 - 2024 Weber Informatics LLC | Privacy Policy