Skip to content

Commit

Permalink
Closed #10 TestNG scenario factory for cucumber BDD2Pickel
Browse files Browse the repository at this point in the history
supports cucumber options using property with cucumber prefix
  • Loading branch information
cjayswal committed Sep 3, 2021
1 parent a08caa1 commit 0b2906e
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 15 deletions.
16 changes: 8 additions & 8 deletions .classpath
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="test/src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=qaf-cucumber&amp;ivyXmlPath=ivy-test.xml&amp;confs=*"/>
<classpathentry kind="output" path="bin"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="test/src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=qaf-cucumber&amp;ivyXmlPath=ivy-test.xml&amp;confs=*&amp;ivySettingsPath=%24%7Bworkspace_loc%3Aqaf-parent%2Fivysettings.xml%7D&amp;loadSettingsOnDemand=false&amp;ivyUserDir=&amp;propertyFiles="/>
<classpathentry kind="output" path="bin"/>
</classpath>
1 change: 1 addition & 0 deletions .settings/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/org.apache.ivyde.eclipse.prefs
14 changes: 10 additions & 4 deletions src/com/qmetry/qaf/automation/cucumber/QAFCucumberPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ private MessageTypes getStepMessageType(io.cucumber.plugin.event.Status status,
@Override
public void receive(TestCaseStarted event) {
BDD2PickleWrapper bdd2Pickle = getBdd2Pickle(event.getTestCase());
bdd2Pickle.getMetaData().put("reference", event.getTestCase().getUri());
bdd2Pickle.getMetaData().put("reference", new File("./").getAbsoluteFile().getParentFile().toURI().relativize(event.getTestCase().getUri()));
QAFTestBase stb = TestBaseProvider.instance().get();
stb.getLog().clear();
stb.clearVerificationErrors();
Expand Down Expand Up @@ -305,6 +305,7 @@ private void deployResult(BDD2PickleWrapper bdd2Pickle, TestCase tc, Result even
List<String> steps = bdd2Pickle.getSteps().stream().map(s->s.getText()).collect(Collectors.toList());
TestCaseRunResult testCaseRunResult = new TestCaseRunResult(result, bdd2Pickle.getMetaData(),
new Object[] {bdd2Pickle.getTestData()}, executionInfo, steps,stTime, false, true);
testCaseRunResult.setClassName(((URI)bdd2Pickle.getMetaData().get("reference")).getPath());
testCaseRunResult.setThrowable(eventresult.getError());
ResultUpdator.updateResult(testCaseRunResult);
}else {
Expand Down Expand Up @@ -340,13 +341,18 @@ private void endReport(TestRunFinished event) {
QAFReporter.updateMetaInfo();
QAFReporter.updateOverview(null, true);
}
TestBaseProvider.instance().stopAll();
ResultUpdator.awaitTermination();
if(!getBundle().getBoolean("usingtestngrunner", false)) {
TestBaseProvider.instance().stopAll();
ResultUpdator.awaitTermination();
}
}
};

private static BDD2PickleWrapper getBdd2Pickle(Object testCase) {
public static BDD2PickleWrapper getBdd2Pickle(Object testCase) {
try {
if (testCase instanceof BDD2PickleWrapper)
return ((BDD2PickleWrapper) testCase);

Object pickle = getField("pickle", testCase);
if (pickle instanceof BDD2PickleWrapper) {
return ((BDD2PickleWrapper) pickle);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,5 +140,9 @@ public Map<String, Object> getTestData() {
public Map<String, Object> getMetaData() {
return ((Bdd2Pickle)pickle).getMetaData();
}

public void setMetaData(Map<String, Object> metadata) {
((Bdd2Pickle)pickle).setMetaData(metadata);
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.qmetry.qaf.automation.cucumber.bdd2.parser;

import static com.qmetry.qaf.automation.data.MetaDataScanner.formatMetaData;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -44,6 +42,10 @@ public Bdd2Pickle(String name, String language, List<PickleStep> steps, List<Pic
public Map<String, Object> getMetaData() {
return metaData;
}

public void setMetaData(Map<String, Object> metaData) {
this.metaData = metaData;
}

public Map<String, Object> getTestData() {
return testData;
Expand All @@ -53,6 +55,6 @@ private void initMetaData(Map<String, Object> inMetaData) {
metaData=new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER);
metaData.putAll(inMetaData);
metaData.put("name", getName());
formatMetaData(metaData);
//formatMetaData(metaData);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.qmetry.qaf.automation.cucumber.runner;

import static com.qmetry.qaf.automation.core.ConfigurationManager.getBundle;
import static com.qmetry.qaf.automation.cucumber.QAFCucumberPlugin.getBdd2Pickle;

import org.testng.ITestContext;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import com.qmetry.qaf.automation.cucumber.bdd2.model.BDD2PickleWrapper;
import com.qmetry.qaf.automation.step.client.Scenario;

import io.cucumber.core.eventbus.EventBus;
import io.cucumber.core.gherkin.Pickle;
import io.cucumber.core.runner.Runner;
import io.cucumber.plugin.event.TestRunFinished;

public class CucumberScenario extends Scenario {

private BDD2PickleWrapper bdd2pickle;
private Runner runner;

public CucumberScenario(String testName, Pickle pickle, Runner runner) {
super(testName, null, getBdd2Pickle(pickle).getMetaData());
bdd2pickle = getBdd2Pickle(pickle);
this.runner = runner;
bdd2pickle.setMetaData(getMetadata());
}

@Test(groups = "scenario")
public void scenario() {
beforeScanario();
runner.runPickle(bdd2pickle);
}

@BeforeTest(alwaysRun = true)
public void setTestName(ITestContext context) {
getBundle().setProperty("usingtestngrunner", true);
getBundle().setProperty("testname", context.getCurrentXmlTest().getName());
}

@AfterTest(alwaysRun = true)
public void testRunFinished(ITestContext context) {
EventBus eventBus = (EventBus) context.getAttribute("eventBus");
eventBus.send(new TestRunFinished(eventBus.getInstant()));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package com.qmetry.qaf.automation.cucumber.runner;

import static com.qmetry.qaf.automation.core.ConfigurationManager.getBundle;
import static java.util.Collections.max;
import static java.util.Collections.min;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;

import java.time.Clock;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.function.Supplier;

import org.apache.commons.configuration.ConfigurationConverter;
import org.apache.commons.exec.util.MapUtils;
import org.jsoup.internal.StringUtil;
import org.testng.ITestContext;
import org.testng.annotations.Factory;

import com.qmetry.qaf.automation.core.ConfigurationManager;
import com.qmetry.qaf.automation.cucumber.QAFCucumberPlugin;
import com.qmetry.qaf.automation.keys.ApplicationProperties;
import com.qmetry.qaf.automation.util.DateUtil;

import io.cucumber.core.eventbus.EventBus;
import io.cucumber.core.feature.FeatureParser;
import io.cucumber.core.filter.Filters;
import io.cucumber.core.gherkin.Feature;
import io.cucumber.core.gherkin.Pickle;
import io.cucumber.core.options.CucumberPropertiesParser;
import io.cucumber.core.options.RuntimeOptions;
import io.cucumber.core.plugin.PluginFactory;
import io.cucumber.core.plugin.Plugins;
import io.cucumber.core.resource.ClassLoaders;
import io.cucumber.core.runtime.BackendServiceLoader;
import io.cucumber.core.runtime.BackendSupplier;
import io.cucumber.core.runtime.FeaturePathFeatureSupplier;
import io.cucumber.core.runtime.FeatureSupplier;
import io.cucumber.core.runtime.ObjectFactoryServiceLoader;
import io.cucumber.core.runtime.ObjectFactorySupplier;
import io.cucumber.core.runtime.RunnerSupplier;
import io.cucumber.core.runtime.ScanningTypeRegistryConfigurerSupplier;
import io.cucumber.core.runtime.ThreadLocalObjectFactorySupplier;
import io.cucumber.core.runtime.ThreadLocalRunnerSupplier;
import io.cucumber.core.runtime.TimeServiceEventBus;
import io.cucumber.core.runtime.TypeRegistryConfigurerSupplier;
import io.cucumber.plugin.ConcurrentEventListener;
import io.cucumber.plugin.event.EventHandler;
import io.cucumber.plugin.event.EventPublisher;
import io.cucumber.plugin.event.Result;
import io.cucumber.plugin.event.Status;
import io.cucumber.plugin.event.TestCaseFinished;
import io.cucumber.plugin.event.TestSourceRead;

public class CucumberScenarioFactory {

@Factory
@SuppressWarnings({ "unchecked", "rawtypes" })
public Object[] getTestsFromFile(ITestContext context) {
ConfigurationManager.addAll(context.getCurrentXmlTest().getAllParameters());
context.getCurrentXmlTest().getLocalParameters().put("testname", context.getCurrentXmlTest().getName());
getBundle().setProperty("testname", context.getCurrentXmlTest().getName());

EventBus eventBus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID);
context.setAttribute("eventBus", eventBus);
if(!getBundle().containsKey("eventBus")) {
getBundle().setProperty("eventBus", new ArrayList<EventBus>());
}
((List<EventBus>)getBundle().getProperty("eventBus")).add(eventBus);

getBundle().setProperty("suite.name", context.getCurrentXmlTest().getSuite().getName());
if (StringUtil.isBlank(ApplicationProperties.JSON_REPORT_DIR.getStringVal(""))) {
String dir = ApplicationProperties.JSON_REPORT_ROOT_DIR.getStringVal("test-results") + "/"
+ DateUtil.getDate(0, "EdMMMyy_hhmmssa");
getBundle().setProperty(ApplicationProperties.JSON_REPORT_DIR.key, dir);
}
Properties source = ConfigurationConverter.getProperties(getBundle().subset("cucumber"));
Map props = new HashMap(MapUtils.prefix(source, "cucumber"));
RuntimeOptions runtimeOptions = new CucumberPropertiesParser().parse(props).build();

Supplier<ClassLoader> classLoader = ClassLoaders::getDefaultClassLoader;

final ObjectFactoryServiceLoader objectFactoryServiceLoader = new ObjectFactoryServiceLoader(runtimeOptions);
ObjectFactorySupplier objectFactorySupplier = new ThreadLocalObjectFactorySupplier(objectFactoryServiceLoader);
final BackendSupplier backendSupplier = new BackendServiceLoader(classLoader, objectFactorySupplier);
final Plugins plugins = new Plugins(new PluginFactory(), runtimeOptions);
final ExitStatus exitStatus = new ExitStatus(runtimeOptions);
plugins.addPlugin(exitStatus);
QAFCucumberPlugin qafCucumberPlugin = new QAFCucumberPlugin();
if (plugins.getPlugins().stream().noneMatch(p -> (p instanceof QAFCucumberPlugin))) {
plugins.addPlugin(qafCucumberPlugin);
System.out.println("Added QAFCucumberPlugin");
}

plugins.setSerialEventBusOnEventListenerPlugins(eventBus);
final TypeRegistryConfigurerSupplier typeRegistryConfigurerSupplier = new ScanningTypeRegistryConfigurerSupplier(
classLoader, runtimeOptions);
final RunnerSupplier runnerSupplier = new ThreadLocalRunnerSupplier(runtimeOptions, eventBus, backendSupplier,
objectFactorySupplier, typeRegistryConfigurerSupplier);
final FeatureParser parser = new FeatureParser(eventBus::generateId);
final FeatureSupplier featureSupplier = new FeaturePathFeatureSupplier(classLoader, runtimeOptions, parser);

final Predicate<Pickle> filter = new Filters(runtimeOptions);

final List<Feature> features = featureSupplier.get();

for (Feature feature : features) {
eventBus.send(new TestSourceRead(eventBus.getInstant(), feature.getUri(), feature.getSource()));
}

final List<CucumberScenario> cucumberScenarios = features.stream()
.flatMap(feature -> feature.getPickles().stream()).filter(filter)
.map(pickle -> new CucumberScenario(pickle.getName(), pickle, runnerSupplier.get())).collect(toList());

return cucumberScenarios.toArray();
}

static final class ExitStatus implements ConcurrentEventListener {
private static final byte DEFAULT = 0x0;
private static final byte ERRORS = 0x1;

private final List<Result> results = new ArrayList<>();
private final RuntimeOptions runtimeOptions;

private final EventHandler<TestCaseFinished> testCaseFinishedHandler = event -> results.add(event.getResult());

ExitStatus(RuntimeOptions runtimeOptions) {
this.runtimeOptions = runtimeOptions;
}

@Override
public void setEventPublisher(EventPublisher publisher) {
publisher.registerHandlerFor(TestCaseFinished.class, testCaseFinishedHandler);
}

byte exitStatus() {
if (results.isEmpty()) {
return DEFAULT;
}

if (runtimeOptions.isWip()) {
Result leastSeverResult = min(results, comparing(Result::getStatus));
return leastSeverResult.getStatus().is(Status.PASSED) ? ERRORS : DEFAULT;
} else {
Result mostSevereResult = max(results, comparing(Result::getStatus));
return mostSevereResult.getStatus().isOk(runtimeOptions.isStrict()) ? DEFAULT : ERRORS;
}
}
}

}

0 comments on commit 0b2906e

Please sign in to comment.