package com.gfk.senbot.framework.context; import java.awt.Desktop; import java.awt.HeadlessException; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.gfk.senbot.framework.cucumber.CucumberReportGenerator; import com.gfk.senbot.framework.cucumber.stepdefinitions.ScenarioGlobals; import com.gfk.senbot.framework.cucumber.stepdefinitions.ScenarionCreationShutdownHook; import cucumber.api.Scenario; public class CucumberManager { private static Logger log = LoggerFactory.getLogger(CucumberManager.class); /** * map the last when a scenario started so that the after scenario can do some checks on what the scenario * has touched in its lifetime. This for example helps us check if a scenario uses Selenium or not */ private static Map<Thread, ScenarioGlobals> scenarioGlobalsMap = new HashMap<Thread, ScenarioGlobals>(); private ScenarionCreationShutdownHook scenarioCreationHook; /** * Should the resulting report be opened in the browser after completion of the suite */ private boolean openResultingReportAfterCompletion; /** * How many threads should be spawned for the different feature files */ private int parallelFeatureThreads; /** * After how many seconds should SenBot cancel the cucumber feature file execution. */ private int featureFileTimeout; private static Thread shutDownHook; /** * Constructor * * @param scenarioGlobalsCreationHookClass the name (or null) in {@link String} format of the implementing {@link ScenarionCreationShutdownHook} * class used to setup the {@link ScenarioGlobals} on each {@link Scenario} creation * * @throws ClassNotFoundException * @throws NoSuchMethodException * @throws SecurityException * @throws InvocationTargetException * @throws IllegalAccessException * @throws InstantiationException * @throws IllegalArgumentException */ public CucumberManager( String scenarioGlobalsCreationHookClass, String defaultCucumberOptionsString, boolean openResultingReportAfterCompletion, int parallelFeatureThreads, int featureFileTimeout) throws SecurityException, NoSuchMethodException, ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { this.parallelFeatureThreads = parallelFeatureThreads; this.featureFileTimeout = featureFileTimeout; this.setOpenResultingReportAfterCompletion(openResultingReportAfterCompletion); if(System.getProperty("cucumber.options") == null) { String overwrite = null; if(System.getProperty("features") != null) { //allow for a shorthand for the cucumber.options overwrite = System.getProperty("features"); } else if(!StringUtils.isBlank(defaultCucumberOptionsString)) { overwrite = defaultCucumberOptionsString; } if(overwrite != null) { System.setProperty("cucumber.options", overwrite); } } if(!StringUtils.isBlank(scenarioGlobalsCreationHookClass)) { Constructor<?> constructor = Class.forName(scenarioGlobalsCreationHookClass).getConstructor(); scenarioCreationHook = (ScenarionCreationShutdownHook) constructor.newInstance(); } } /** * Called when a new scenario is started and associates a new {@link ScenarioGlobals} with the current thread so that * they can be obtained again from within any process executed by the current {@link Thread} * * @return {@link ScenarioGlobals} */ public ScenarioGlobals startNewScenario() { ScenarioGlobals scenarioGlobals = new ScenarioGlobals(); scenarioGlobalsMap.put(Thread.currentThread(), scenarioGlobals); if(scenarioCreationHook != null) { scenarioCreationHook.scenarionStarted(scenarioGlobals); } return scenarioGlobals; } /** * @return the scenario globals */ public ScenarioGlobals getCurrentScenarioGlobals() { return scenarioGlobalsMap.get(Thread.currentThread()); } /** * Called at the end of an scenario? * * @return {@link ScenarioGlobals} */ public ScenarioGlobals stopNewScenario() { if(scenarioCreationHook != null) { scenarioCreationHook.scenarionShutdown(getCurrentScenarioGlobals()); } return scenarioGlobalsMap.remove(Thread.currentThread()); } public void cleanUp() { scenarioGlobalsMap.clear(); } public boolean isOpenResultingReportAfterCompletion() { return openResultingReportAfterCompletion; } public void setOpenResultingReportAfterCompletion(boolean openResultingReportAfterCompletion) { this.openResultingReportAfterCompletion = openResultingReportAfterCompletion; } public Thread getReportingShutdownHook(final String testResultFolder) { if(shutDownHook == null) { final boolean openBrowser = isOpenResultingReportAfterCompletion(); shutDownHook = new Thread() { public void run(){ try { String generatedReportLocation = CucumberReportGenerator.generateReport(testResultFolder); if(openBrowser) { if(generatedReportLocation == null) { //see if there is a index.html in the root of the test result folder String defaultPrettyReportLocation = testResultFolder + "/index.html"; File file = new File(defaultPrettyReportLocation); if(file.exists()) { generatedReportLocation = defaultPrettyReportLocation; } } if(generatedReportLocation != null) { try { Desktop.getDesktop().browse(new URI("file:///" + generatedReportLocation)); } catch (HeadlessException he) { log.warn("This process is running headless, can't open the report in a browser."); } } } } catch (Exception e) { throw new RuntimeException("Exception whole trying to generate the Cucumber report", e); } } }; } return shutDownHook; } public int getParallelFeatureThreads() { return parallelFeatureThreads; } public int getFeatureFileTimeout() { return featureFileTimeout; } }