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

io.cucumber.core.plugin.ScenarioContextParallel Maven / Gradle / Ivy

There is a newer version: 4.2.8
Show newest version
package io.cucumber.core.plugin;


import net.serenitybdd.model.exceptions.SerenityManagedException;
import net.thucydides.core.steps.events.StepEventBusEvent;
import io.cucumber.messages.types.Scenario;
import io.cucumber.messages.types.Step;
import io.cucumber.messages.types.Tag;
import io.cucumber.plugin.event.TestCase;
import io.cucumber.plugin.event.TestStep;
import net.thucydides.model.domain.DataTable;
import net.thucydides.model.domain.DataTableRow;
import net.thucydides.model.domain.TestTag;
import net.thucydides.core.steps.BaseStepListener;
import net.thucydides.core.steps.StepEventBus;
import net.thucydides.core.steps.session.TestSession;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

import static java.util.stream.Collectors.toList;


public class ScenarioContextParallel {

    private static final Logger LOGGER = LoggerFactory.getLogger(ScenarioContextParallel.class);

    private final Map> highPriorityEventBusEvents = Collections.synchronizedMap(new HashMap<>());
    private final Map> stepQueue = Collections.synchronizedMap(new HashMap<>());
    private final Map> testStepQueue = Collections.synchronizedMap(new HashMap<>());
    private final Map examplesRunningMap = Collections.synchronizedMap(new HashMap<>());
    private final Map addingScenarioOutlineStepsMap = Collections.synchronizedMap(new HashMap<>());
    private final Map tableMap = Collections.synchronizedMap(new HashMap<>());
    //map1: keys are scenario ids
    //map2: keys are line numbers, entries are example rows (key=header, value=rowValue )
    private final Map>> exampleRowsMap = Collections.synchronizedMap(new HashMap<>());
    //keys are line numbers
    private Map> exampleTags;

    private final Map exampleCountMap = Collections.synchronizedMap(new HashMap<>());

    //key- ScenarioId
    private final Map waitingToProcessBackgroundSteps = Collections.synchronizedMap(new HashMap<>());

    private final List currentScenarioIdList = Collections.synchronizedList(new ArrayList<>());

    //key - scenarioId
    private final Map currentScenarioDefinitionMap = Collections.synchronizedMap(new HashMap<>());

    private final Map currentScenarioMap = Collections.synchronizedMap(new HashMap<>());

    private List featureTags = new ArrayList<>();

    private final FeaturePathFormatter featurePathFormatter = new FeaturePathFormatter();

    private final List baseStepListeners;


    // key-line in feature file; value - list with StepBusEvents corresponding to this line.
    private final Map> allTestEventsByLine = Collections.synchronizedMap(new TreeMap<>());

    private final URI scenarioContextURI;

    private StepEventBus stepEventBus;

    // key - scenarioId
    private final Map> scenarioTags = Collections.synchronizedMap(new HashMap<>());


    public ScenarioContextParallel(URI scenarioContextURI) {
        this.baseStepListeners = Collections.synchronizedList(new ArrayList<>());
        this.scenarioContextURI = scenarioContextURI;
        this.stepEventBus = stepEventBus(scenarioContextURI);
    }

    public synchronized Scenario currentScenarioOutline(String scenarioId) {
        return currentScenarioDefinitionMap.get(scenarioId);
    }

    public synchronized Queue getStepQueue(TestCase testCase) {
        stepQueue.computeIfAbsent(testCase.getId(), k -> new LinkedList<>());
        return stepQueue.get(testCase.getId());
    }

    public synchronized Queue getTestStepQueue(TestCase testCase/*String scenarioId*/) {
        testStepQueue.computeIfAbsent(testCase.getId(), k -> new LinkedList<>());
        return testStepQueue.get(testCase.getId());
    }

    public synchronized boolean examplesAreRunning(String scenarioId) {
        if (!examplesRunningMap.containsKey(scenarioId)) {
            return false;
        }
        return examplesRunningMap.get(scenarioId);
    }

    public synchronized Map> getExampleRows(String scenarioId) {
        return exampleRowsMap.get(scenarioId);
    }

    public synchronized void setExampleRows(String scenarioId, Map> exampleRows) {
        this.exampleRowsMap.put(scenarioId, exampleRows);
    }

    public synchronized Map> getExampleTags() {
        return exampleTags;
    }

    //TODO - use a map with scenarioId as key
    public synchronized void setExampleTags(Map> exampleTags) {
        this.exampleTags = exampleTags;
    }

    public synchronized int getExampleCount(String scenarioId) {
        if (exampleCountMap.containsKey(scenarioId)) {
            return exampleCountMap.get(scenarioId).get();
        }
        return 0;
    }

    public synchronized int decrementExampleCount(String scenarioId) {
        if (exampleCountMap.get(scenarioId) != null) {
            return exampleCountMap.get(scenarioId).decrementAndGet();
        }
        //single example
        return 0;
    }

    public synchronized DataTable getTable(String scenarioId) {
        return tableMap.get(scenarioId);
    }

    public synchronized boolean isWaitingToProcessBackgroundSteps(String scenarioId) {
        return waitingToProcessBackgroundSteps.getOrDefault(scenarioId, false);
    }

    public synchronized void addCurrentScenarioId(String scenarioId) {
        if (scenarioId != null) {
            currentScenarioIdList.add(scenarioId);
        } else {
            currentScenarioIdList.clear();
        }
    }

    public synchronized Scenario getCurrentScenarioDefinition(String scenarioId) {
        return currentScenarioDefinitionMap.get(scenarioId);
    }

    public synchronized String getCurrentScenario(String scenarioId) {
        return currentScenarioMap.get(scenarioId);
    }

    public synchronized void setCurrentScenario(String scenarioId, String currentScenario) {
        this.currentScenarioMap.put(scenarioId, currentScenario);
    }

    public synchronized List getFeatureTags() {
        return featureTags;
    }

    public synchronized boolean isAddingScenarioOutlineSteps(String scenarioId) {
        return addingScenarioOutlineStepsMap.get(scenarioId) != null ? addingScenarioOutlineStepsMap.get(scenarioId) : false;
    }

    public synchronized void doneAddingScenarioOutlineSteps(String scenarioId) {
        this.addingScenarioOutlineStepsMap.put(scenarioId, false);
    }

    public synchronized void setFeatureTags(List tags) {
        this.featureTags = new ArrayList<>(tags);
    }

    public synchronized void setCurrentScenarioDefinitionFrom(String scenarioId, TestSourcesModel.AstNode astNode) {
        this.currentScenarioDefinitionMap.put(scenarioId, TestSourcesModel.getScenarioDefinition(astNode));
    }

    public synchronized boolean isAScenarioOutline(String scenarioId) {
        return currentScenarioDefinitionMap.get(scenarioId) != null &&
                currentScenarioDefinitionMap.get(scenarioId).getExamples().size() > 0;
    }

    public synchronized void startNewExample(String scenarioId) {
        examplesRunningMap.put(scenarioId, true);
        addingScenarioOutlineStepsMap.put(scenarioId, true);
    }

    public synchronized void setExamplesRunning(String scenarioId, boolean examplesRunning) {
        examplesRunningMap.put(scenarioId, examplesRunning);
    }

    /*public synchronized List getScenarioTags() {
        return currentScenarioDefinition.getTags();
    }

    public synchronized String getScenarioName() {
        return currentScenarioDefinition.getName();
    }

    public synchronized List getScenarioExamples() {
        return currentScenarioDefinition.getExamples();
    }*/

    public synchronized void clearStepQueue(TestCase testCase/*String scenarioId*/) {
        getStepQueue(testCase).clear();
    }

    public synchronized void clearStepQueue() {
        //TODO check
        stepQueue.clear();
        //simpleStepQueue.clear();
    }

    public synchronized void clearTestStepQueue() {
        testStepQueue.clear();
        //simpleStepTestQueue.clear();
    }

    public synchronized void queueStep(TestCase testCase/*String scenarioId,*/, Step step) {
        getStepQueue(testCase).add(step);
    }

    public synchronized void queueTestStep(/*String scenarioId*/TestCase testCase, TestStep testStep) {
        getTestStepQueue(testCase).add(testStep);
    }

    public synchronized Step getCurrentStep(TestCase testCase/*String scenarioId*/) {
        return getStepQueue(testCase/*scenarioId*/).peek();
    }

    public synchronized Step nextStep(TestCase testCase/*String scenarioId*/) {
        return getStepQueue(testCase/*scenarioId*/).poll();
    }

    public synchronized TestStep nextTestStep(TestCase testCase/*String scenarioId*/) {
        return getTestStepQueue(testCase/*scenarioId*/).poll();
    }

    public synchronized boolean noStepsAreQueued(/*String scenarioId*/TestCase testCase) {
        return getStepQueue(testCase/*scenarioId*/).isEmpty();
    }

    public synchronized boolean hasScenarioId(String scenarioId) {
        return (currentScenarioIdList.contains(scenarioId));
    }

    public synchronized void setTable(String scenarioId, DataTable table) {
        this.tableMap.put(scenarioId, table);
        exampleCountMap.put(scenarioId, new AtomicInteger(table.getSize()));
    }

    public synchronized void addTableRows(String scenarioId, List headers,
                                          List> rows,
                                          String name,
                                          String description,
                                          Map lineNumbersOfEachRow) {
        DataTable table = tableMap.get(scenarioId);
        table.startNewDataSet(name, description);

        AtomicInteger rowNumber = new AtomicInteger();
        rows.forEach(
                row -> table.appendRow(newRow(headers, lineNumbersOfEachRow, rowNumber.getAndIncrement(), row))
        );
        table.updateLineNumbers(lineNumbersOfEachRow);
        exampleCountMap.put(scenarioId, new AtomicInteger(table.getSize()));
    }

    @NotNull
    private DataTableRow newRow(List headers,
                                Map lineNumbersOfEachRow,
                                int rowNumber,
                                Map row) {
        return new DataTableRow(
                rowValuesFrom(headers, row),
                lineNumbersOfEachRow.getOrDefault(rowNumber, 0L));
    }

    private List rowValuesFrom(List headers, Map row) {
        return headers.stream().map(row::get).collect(toList());
    }

    public synchronized void addTableTags(String scenarioId, List tags) {
        DataTable table = tableMap.get(scenarioId);
        table.addTagsToLatestDataSet(tags);
    }

    public synchronized void clearTable() {
        tableMap.clear();
    }

    private synchronized StepEventBus stepEventBus(URI featurePath) {
        URI prefixedPath = featurePathFormatter.featurePathWithPrefixIfNecessary(featurePath);
        return StepEventBus.eventBusFor(prefixedPath);
    }

    public synchronized StepEventBus stepEventBus() {
        return this.stepEventBus;
    }

    public void setStepEventBus(StepEventBus stepEventBus) {
        this.stepEventBus = stepEventBus;
    }

    public void addBaseStepListener(BaseStepListener baseStepListener) {
        baseStepListeners.add(baseStepListener);
        stepEventBus.registerListener(baseStepListener);
    }


    public synchronized void collectAllBaseStepListeners(List allBaseStepListeners) {
        allBaseStepListeners.addAll(baseStepListeners);
    }


    public void setWaitingToProcessBackgroundSteps(String scenarioId, boolean waitingToProcessBackgroundSteps) {
        this.waitingToProcessBackgroundSteps.put(scenarioId, waitingToProcessBackgroundSteps);
    }

    /**
     * Some events have to be added ad the beginning of the event list.
     *
     * @param scenarioId
     * @param event
     */
    public void addHighPriorityStepEventBusEvent(String scenarioId, StepEventBusEvent event) {
        LOGGER.debug("SRP:addHighPriorityStepEventBusEvent " + event + " " + Thread.currentThread() + " " + scenarioId);
        List eventList = highPriorityEventBusEvents.computeIfAbsent(scenarioId, k -> Collections.synchronizedList(new LinkedList<>()));
        eventList.add(event);
        event.setStepEventBus(stepEventBus);
    }

    public void addStepEventBusEvent(StepEventBusEvent event) {
        if (TestSession.isSessionStarted()) {
            TestSession.addEvent(event);
            event.setStepEventBus(stepEventBus);
        } else {
            LOGGER.debug("SRP:ignored event " + event + " " + Thread.currentThread() + " because session not opened ", new Exception());
        }
    }

    /**
     * Called with TestCaseFinished
     *
     * @param line
     * @param testCase
     */
    public void storeAllStepEventBusEventsForLine(int line, TestCase testCase) {
        if (TestSession.isSessionStarted()) {
            TestSession.closeSession();
            List stepEventBusEvents = TestSession.getSessionEvents();
            List clonedEvents = new ArrayList<>(stepEventBusEvents);
            allTestEventsByLine.put(line, clonedEvents);
            TestSession.cleanupSession();
        }
    }

    /**
     * Called with TestRunFinished - all tests events are replayed
     */
    public synchronized void playAllTestEvents() {
        LOGGER.debug("SRP:playAllTestEvents for URI " + scenarioContextURI + "--" + allTestEventsByLine);
        for (var entry : allTestEventsByLine.entrySet()) {
            try {
                replayAllTestCaseEventsForLine(entry.getKey(), entry.getValue());
            } catch (Throwable exception) {
                LOGGER.error("An unrecoverable error occurred during test execution: " + exception.getMessage(), exception);
                exception.printStackTrace();
                recordUnexpectedFailure(new SerenityManagedException("An unrecoverable error occurred during test execution: " + exception.getMessage(),
                                        exception));
            }
        }
        clearEventBus();
    }

    public synchronized void recordUnexpectedFailure(Throwable throwable) {
        stepEventBus.getBaseStepListener().getCurrentTestOutcome().testFailedWith(throwable);
    }

    public synchronized void clearEventBus() {
        stepEventBus.clear();
        StepEventBus.getParallelEventBus().clear();
    }

    private void replayAllTestCaseEventsForLine(Integer lineNumber, List stepEventBusEvents) {
        LOGGER.debug("SRP:PLAY session events for line   " + lineNumber);
        Optional eventWithScenarioId = stepEventBusEvents.stream().filter(event -> !event.getScenarioId().isEmpty()).findFirst();
        LOGGER.debug("SRP:EventWithscenarioId   " + eventWithScenarioId);
        if (eventWithScenarioId.isPresent() && highPriorityEventBusEvents.get(eventWithScenarioId.get().getScenarioId()) != null) {
            List highPriorityEvents = highPriorityEventBusEvents.get(eventWithScenarioId.get().getScenarioId());
            for (StepEventBusEvent currentStepBusEvent : highPriorityEvents) {
                LOGGER.trace("SRP:PLAY session high priority event  " + currentStepBusEvent);
                currentStepBusEvent.play();
            }
            highPriorityEventBusEvents.remove(eventWithScenarioId.get().getScenarioId());
        }
        for (StepEventBusEvent currentStepBusEvent : stepEventBusEvents) {
            LOGGER.trace("SRP:PLAY session event  " + currentStepBusEvent + " " + Thread.currentThread() + " " + currentStepBusEvent.hashCode());
            currentStepBusEvent.play();
        }
    }

    public List getScenarioTags(String scenarioId) {
        return scenarioTags.get(scenarioId);
    }

    public void setScenarioTags(String scenarioId, List scenarioTags) {
        this.scenarioTags.put(scenarioId, scenarioTags);
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy