Skip to content

Commit

Permalink
Fixed an error where dependency-violation-related failures were swall…
Browse files Browse the repository at this point in the history
…owed
  • Loading branch information
wakaleo committed May 11, 2024
1 parent 7ae988d commit 4cc47a5
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 71 deletions.
@@ -1,6 +1,7 @@
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;
Expand All @@ -24,34 +25,33 @@
import static java.util.stream.Collectors.toList;



public class ScenarioContextParallel {

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

private final Map<String,List<StepEventBusEvent>> highPriorityEventBusEvents = Collections.synchronizedMap(new HashMap<>());
private final Map<UUID,Queue<Step>> stepQueue = Collections.synchronizedMap(new HashMap<>());
private final Map<UUID,Queue<TestStep>> testStepQueue = Collections.synchronizedMap(new HashMap<>());
private final Map<String,Boolean> examplesRunningMap = Collections.synchronizedMap(new HashMap<>());
private final Map<String,Boolean> addingScenarioOutlineStepsMap = Collections.synchronizedMap(new HashMap<>());
private final Map<String,DataTable> tableMap = Collections.synchronizedMap(new HashMap<>());
private final Map<String, List<StepEventBusEvent>> highPriorityEventBusEvents = Collections.synchronizedMap(new HashMap<>());
private final Map<UUID, Queue<Step>> stepQueue = Collections.synchronizedMap(new HashMap<>());
private final Map<UUID, Queue<TestStep>> testStepQueue = Collections.synchronizedMap(new HashMap<>());
private final Map<String, Boolean> examplesRunningMap = Collections.synchronizedMap(new HashMap<>());
private final Map<String, Boolean> addingScenarioOutlineStepsMap = Collections.synchronizedMap(new HashMap<>());
private final Map<String, DataTable> 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<String,Map<Long, Map<String, String>>> exampleRowsMap = Collections.synchronizedMap(new HashMap<>());
private final Map<String, Map<Long, Map<String, String>>> exampleRowsMap = Collections.synchronizedMap(new HashMap<>());
//keys are line numbers
private Map<Long, List<Tag>> exampleTags;

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

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

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

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

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

private List<Tag> featureTags = new ArrayList<>();

Expand All @@ -61,14 +61,14 @@ public class ScenarioContextParallel {


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

private final URI scenarioContextURI;

private StepEventBus stepEventBus;

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


public ScenarioContextParallel(URI scenarioContextURI) {
Expand All @@ -92,7 +92,7 @@ public synchronized Queue<TestStep> getTestStepQueue(TestCase testCase/*String s
}

public synchronized boolean examplesAreRunning(String scenarioId) {
if(!examplesRunningMap.containsKey(scenarioId)) {
if (!examplesRunningMap.containsKey(scenarioId)) {
return false;
}
return examplesRunningMap.get(scenarioId);
Expand All @@ -102,8 +102,8 @@ public synchronized Map<Long, Map<String, String>> getExampleRows(String scenari
return exampleRowsMap.get(scenarioId);
}

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

public synchronized Map<Long, List<Tag>> getExampleTags() {
Expand All @@ -112,18 +112,18 @@ public synchronized Map<Long, List<Tag>> getExampleTags() {

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

public synchronized int getExampleCount(String scenarioId) {
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) {
if (exampleCountMap.get(scenarioId) != null) {
return exampleCountMap.get(scenarioId).decrementAndGet();
}
//single example
Expand All @@ -139,10 +139,9 @@ public synchronized boolean isWaitingToProcessBackgroundSteps(String scenarioId)
}

public synchronized void addCurrentScenarioId(String scenarioId) {
if(scenarioId != null) {
if (scenarioId != null) {
currentScenarioIdList.add(scenarioId);
}
else {
} else {
currentScenarioIdList.clear();
}
}
Expand All @@ -155,8 +154,8 @@ 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 void setCurrentScenario(String scenarioId, String currentScenario) {
this.currentScenarioMap.put(scenarioId, currentScenario);
}

public synchronized List<Tag> getFeatureTags() {
Expand All @@ -168,28 +167,28 @@ public synchronized boolean isAddingScenarioOutlineSteps(String scenarioId) {
}

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

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

public synchronized void setCurrentScenarioDefinitionFrom(String scenarioId,TestSourcesModel.AstNode astNode) {
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 &&
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);
examplesRunningMap.put(scenarioId, true);
addingScenarioOutlineStepsMap.put(scenarioId, true);
}

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

Expand Down Expand Up @@ -220,11 +219,11 @@ public synchronized void clearTestStepQueue() {
//simpleStepTestQueue.clear();
}

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

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

Expand All @@ -248,16 +247,16 @@ 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 setTable(String scenarioId, DataTable table) {
this.tableMap.put(scenarioId, table);
exampleCountMap.put(scenarioId, new AtomicInteger(table.getSize()));
}

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

Expand All @@ -266,7 +265,7 @@ public synchronized void addTableRows(String scenarioId,List<String> headers,
row -> table.appendRow(newRow(headers, lineNumbersOfEachRow, rowNumber.getAndIncrement(), row))
);
table.updateLineNumbers(lineNumbersOfEachRow);
exampleCountMap.put(scenarioId,new AtomicInteger(table.getSize()));
exampleCountMap.put(scenarioId, new AtomicInteger(table.getSize()));
}

@NotNull
Expand All @@ -283,7 +282,7 @@ private List<String> rowValuesFrom(List<String> headers, Map<String, String> row
return headers.stream().map(row::get).collect(toList());
}

public synchronized void addTableTags(String scenarioId,List<TestTag> tags) {
public synchronized void addTableTags(String scenarioId, List<TestTag> tags) {
DataTable table = tableMap.get(scenarioId);
table.addTagsToLatestDataSet(tags);
}
Expand All @@ -305,19 +304,19 @@ public void setStepEventBus(StepEventBus stepEventBus) {
this.stepEventBus = stepEventBus;
}

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


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


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

/**
Expand All @@ -327,32 +326,33 @@ public void setWaitingToProcessBackgroundSteps(String scenarioId, boolean waitin
* @param event
*/
public void addHighPriorityStepEventBusEvent(String scenarioId, StepEventBusEvent event) {
LOGGER.debug("SRP:addHighPriorityStepEventBusEvent " + event + " " + Thread.currentThread() + " " + scenarioId);
List<StepEventBusEvent> eventList = highPriorityEventBusEvents.computeIfAbsent(scenarioId,k->Collections.synchronizedList(new LinkedList<>()));
LOGGER.debug("SRP:addHighPriorityStepEventBusEvent " + event + " " + Thread.currentThread() + " " + scenarioId);
List<StepEventBusEvent> eventList = highPriorityEventBusEvents.computeIfAbsent(scenarioId, k -> Collections.synchronizedList(new LinkedList<>()));
eventList.add(event);
event.setStepEventBus(stepEventBus);
}

public void addStepEventBusEvent(StepEventBusEvent event) {
if(TestSession.isSessionStarted()) {
if (TestSession.isSessionStarted()) {
TestSession.addEvent(event);
event.setStepEventBus(stepEventBus);
} else {
LOGGER.debug("SRP:ignored event " + event + " " + Thread.currentThread() + " because session not opened ", new Exception());
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()) {
public void storeAllStepEventBusEventsForLine(int line, TestCase testCase) {
if (TestSession.isSessionStarted()) {
TestSession.closeSession();
List<StepEventBusEvent> stepEventBusEvents = TestSession.getSessionEvents();
List<StepEventBusEvent> clonedEvents = new ArrayList<>(stepEventBusEvents);
allTestEventsByLine.put(line,clonedEvents);
allTestEventsByLine.put(line, clonedEvents);
TestSession.cleanupSession();
}
}
Expand All @@ -361,8 +361,25 @@ public void storeAllStepEventBusEventsForLine(int line, TestCase testCase){
* Called with TestRunFinished - all tests events are replayed
*/
public synchronized void playAllTestEvents() {
LOGGER.debug("SRP:playAllTestEvents for URI " + scenarioContextURI + "--" + allTestEventsByLine);
allTestEventsByLine.entrySet().forEach((entry) -> replayAllTestCaseEventsForLine(entry.getKey(),entry.getValue()));
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();
}
Expand All @@ -371,26 +388,26 @@ private void replayAllTestCaseEventsForLine(Integer lineNumber, List<StepEventBu
LOGGER.debug("SRP:PLAY session events for line " + lineNumber);
Optional<StepEventBusEvent> eventWithScenarioId = stepEventBusEvents.stream().filter(event -> !event.getScenarioId().isEmpty()).findFirst();
LOGGER.debug("SRP:EventWithscenarioId " + eventWithScenarioId);
if(eventWithScenarioId.isPresent() && highPriorityEventBusEvents.get(eventWithScenarioId.get().getScenarioId()) != null){
if (eventWithScenarioId.isPresent() && highPriorityEventBusEvents.get(eventWithScenarioId.get().getScenarioId()) != null) {
List<StepEventBusEvent> highPriorityEvents = highPriorityEventBusEvents.get(eventWithScenarioId.get().getScenarioId());
for(StepEventBusEvent currentStepBusEvent : highPriorityEvents) {
LOGGER.trace("SRP:PLAY session high priority event " + currentStepBusEvent);
currentStepBusEvent.play();
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();
}
for (StepEventBusEvent currentStepBusEvent : stepEventBusEvents) {
LOGGER.trace("SRP:PLAY session event " + currentStepBusEvent + " " + Thread.currentThread() + " " + currentStepBusEvent.hashCode());
currentStepBusEvent.play();
}
}

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

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

Expand Up @@ -25,6 +25,7 @@
import net.serenitybdd.cucumber.formatting.ScenarioOutlineDescription;
import net.serenitybdd.cucumber.util.PathUtils;
import net.serenitybdd.cucumber.util.StepDefinitionAnnotationReader;
import net.serenitybdd.model.exceptions.SerenityManagedException;
import net.thucydides.core.model.screenshots.StepDefinitionAnnotations;
import net.thucydides.model.domain.DataTable;
import net.thucydides.model.domain.Rule;
Expand All @@ -36,6 +37,7 @@
import net.thucydides.core.steps.events.*;
import net.thucydides.core.steps.session.TestSession;
import net.thucydides.model.steps.ExecutedStepDescription;
import net.thucydides.model.steps.StepFailure;
import net.thucydides.model.steps.TestSourceType;
import net.thucydides.model.util.Inflector;
import net.thucydides.model.webdriver.Configuration;
Expand Down Expand Up @@ -515,12 +517,9 @@ private void handleTestStepFinished(TestStepFinished event) {

private void handleTestRunFinished(TestRunFinished event) {
LOGGER.debug("SRP:handleTestRunFinished " + Thread.currentThread() + " " + contextURISet);
try {
contextURISet.forEach(featurePath -> {
getContext(featurePath).playAllTestEvents();
});
} catch (Throwable th) {
th.printStackTrace();

for (URI featurePath : contextURISet) {
getContext(featurePath).playAllTestEvents();
}
enrichOutcomes();
generateReports();
Expand Down

0 comments on commit 4cc47a5

Please sign in to comment.