Skip to content

Commit

Permalink
Use UUID for token ids, PromptOnScenarioEndExecutionListener
Browse files Browse the repository at this point in the history
  • Loading branch information
nickebbutt committed Apr 17, 2018
1 parent 89aa8b6 commit 0e775ba
Show file tree
Hide file tree
Showing 16 changed files with 98 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.chorusbdd.chorus.selenium.config.SeleniumConfigBuilderFactory;
import org.chorusbdd.chorus.selenium.config.SeleniumDriverType;
import org.chorusbdd.chorus.selenium.manager.SeleniumManager;
import org.chorusbdd.chorus.util.ChorusException;
import org.chorusbdd.chorus.util.ScopeUtils;
import org.chorusbdd.chorus.util.handler.HandlerPatterns;
import org.openqa.selenium.WebDriver;
Expand Down Expand Up @@ -140,18 +141,10 @@ public void executeScriptInDefaultBrowser(String scriptPath) {

@Step(".*execute the script (.*) in the " + HandlerPatterns.namePattern + " browser")
public void executeScriptInNamedBrowser(String scriptPath, String configName) {
seleniumManager.executeScriptFile(configName, scriptPath);
}

//Can be added to a feature when we want to investigate a test failure
@Step(".*leave the browser open(?: at the end of the feature)?")
public void leaveTheBrowserOpen() {
leaveTheNamedBrowserOpen(SeleniumManager.LAST_OPENED_BROWSER);
}

@Step(".*leave the " + HandlerPatterns.namePattern + " browser open(?: at the end of the feature)?")
public void leaveTheNamedBrowserOpen(String configName) {
seleniumManager.leaveBrowserOpenAtFeatureEnd(configName);
Object o = seleniumManager.executeScriptFile(configName, scriptPath);
if ( o instanceof Boolean && !((Boolean)o)) {
throw new ChorusException("Execution of script failed (script returned false)");
}
}

private Properties getConfig(String configName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ public interface SeleniumManager extends Subsystem {

void leaveBrowserOpenAtFeatureEnd(String configName);

void executeScriptFile(String configName, String scriptPath);
/**
* return One of Boolean, Long, Double, String, List, Map or WebElement. Or null.
*/
Object executeScriptFile(String configName, String scriptPath);

/**
* This is here to allow users to write handlers which reuse the SeleniumManager for creation of the WebDriver instance,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,15 @@ public void leaveBrowserOpenAtFeatureEnd(String configName) {
}

@Override
public void executeScriptFile(String configName, String scriptPath) {
public Object executeScriptFile(String configName, String scriptPath) {
//Resolve the script path relative to the feature file
File script = feature.getFeatureDir().toPath().resolve(scriptPath).toFile();
String scriptContents = FileUtils.readScriptFile(log, configName, scriptPath, script);

log.trace(format("About to execute script on web driver %s: [%n%s%n]", configName, scriptContents));
((JavascriptExecutor)getWebDriver(configName)).executeScript(scriptContents);
Object result = ((JavascriptExecutor)getWebDriver(configName)).executeScript(scriptContents);
log.debug("Finished executing script from " + scriptPath);
return result;
}

public WebDriver getWebDriver(String configName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ private void safelyInvokeCallback(String callbackName, Consumer<ExecutionListene
try {
c.accept(l);
} catch (Exception e) {
log.error(format("ExecutionListener of class %s throw exception during callback %s", l.getClass().getName(), callbackName));
log.error(format("ExecutionListener of class %s throw exception during callback %s", l.getClass().getName(), callbackName), e);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.chorusbdd.chorus.interpreter.util;
package org.chorusbdd.chorus.executionlistener.util;

import org.chorusbdd.chorus.annotations.ExecutionPriority;
import org.chorusbdd.chorus.executionlistener.ExecutionListenerAdapter;
import org.chorusbdd.chorus.logging.ChorusLog;
import org.chorusbdd.chorus.logging.ChorusLogFactory;
import org.chorusbdd.chorus.logging.ChorusOut;
import org.chorusbdd.chorus.results.EndState;
import org.chorusbdd.chorus.results.ExecutionToken;
import org.chorusbdd.chorus.results.ScenarioToken;

Expand All @@ -37,31 +36,32 @@
/**
* Created by nickebbutt on 20/03/2018.
*
* An experimental execution listener which can be added to pause execution after each failed scenario to give the
* An experimental execution listener which can be added to pause execution after each scenario to give the
* user a chance to inspect state
*
* This listener has a priority which will ensure it is called before system execution listeners run clean up
* (e.g. before closing Selenium web drivers and web sockets to shut down browsers..)
*/
@ExecutionPriority(200)
public class PromptOnScenarioFailExecutionListener extends ExecutionListenerAdapter {
public class PromptOnScenarioEndExecutionListener extends ExecutionListenerAdapter {

private ChorusLog log = ChorusLogFactory.getLog(PromptOnScenarioFailExecutionListener.class);
private ChorusLog log = ChorusLogFactory.getLog(PromptOnScenarioEndExecutionListener.class);

@Override
public void scenarioCompleted(ExecutionToken testExecutionToken, ScenarioToken scenario) {

if ( scenario.getEndState() == EndState.FAILED) {
if ( shouldPrompt(scenario)) {

ChorusOut.out.println("Scenario " + scenario.getName() + " failed...");
ChorusOut.out.println("Scenario " + scenario.getName() + " " + scenario.getEndState());
ChorusOut.out.println("Do you want to proceed? (y/n)");

Console console = System.console();
String l = console.readLine();
if ( ! "y".equalsIgnoreCase(l)) {
log.error("Exiting early on user request due to scenario failure");
log.error("Exiting early on user request");
System.exit(1);
}
}
}

protected boolean shouldPrompt(ScenarioToken scenarioToken) {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* MIT License
*
* Copyright (c) 2018 Chorus BDD Organisation.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.chorusbdd.chorus.executionlistener.util;

import org.chorusbdd.chorus.annotations.ExecutionPriority;
import org.chorusbdd.chorus.results.EndState;
import org.chorusbdd.chorus.results.ScenarioToken;

/**
* Created by nickebbutt on 20/03/2018.
*
* An execution listener which can be added to pause execution after each failed scenario to give the
* user a chance to inspect state
*
* This listener has a priority which will ensure it is called before system execution listeners run clean up
* (e.g. before closing Selenium web drivers and web sockets to shut down browsers..)
*/
@ExecutionPriority(200)
public class PromptOnScenarioFailExecutionListener extends PromptOnScenarioEndExecutionListener {

protected boolean shouldPrompt(ScenarioToken scenarioToken) {
return scenarioToken.getEndState() == EndState.FAILED;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;

/**
Expand All @@ -37,11 +38,7 @@
*/
public abstract class AbstractToken implements Token {

//The token id only needs to be unique within the context of each
//test execution. Here it will be unique to the JVM instance which is even better
private static final AtomicLong lastId = new AtomicLong();

private final long tokenId;
private final String tokenId = UUID.randomUUID().toString();

//not serializing throwable, there's a chance remote processes will not be able deserialize if they don't have the Throwable subclass
//instead we convert to Strings
Expand All @@ -54,10 +51,6 @@ public abstract class AbstractToken implements Token {
*/
private List<String> interpreterOutput = new LinkedList<>();

public AbstractToken(long tokenId) {
this.tokenId = tokenId;
}

public List<String> getInterpreterOutput() {
return interpreterOutput;
}
Expand All @@ -66,14 +59,10 @@ public void addInterpreterOutput(String output) {
interpreterOutput.add(output);
}

public long getTokenId() {
public String getTokenId() {
return tokenId;
}

protected static long getNextId() {
return lastId.incrementAndGet();
}


public String getStackTrace() {
return stackTrace;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,14 @@ public SimpleDateFormat initialValue() {
private Map executionParameters = new HashMap();

public ExecutionToken(String testSuiteName) {
this(getNextId(), testSuiteName, System.currentTimeMillis(), NetworkUtils.getHostname());
this(testSuiteName, System.currentTimeMillis(), NetworkUtils.getHostname());
}

public ExecutionToken(String testSuiteName, long executionStartTime) {
this(getNextId(), testSuiteName, executionStartTime, NetworkUtils.getHostname());
this(testSuiteName, executionStartTime, NetworkUtils.getHostname());
}

private ExecutionToken(long id, String testSuiteName, long executionStartTime, String executionHost) {
super(id);
private ExecutionToken(String testSuiteName, long executionStartTime, String executionHost) {
this.testSuiteName = testSuiteName;
this.executionStartTime = executionStartTime;
this.executionHost = executionHost;
Expand Down Expand Up @@ -271,7 +270,7 @@ public void calculateTimeTaken() {

public ExecutionToken deepCopy() {
ExecutionToken t = new ExecutionToken(
getNextId(), testSuiteName, executionStartTime, executionHost
testSuiteName, executionStartTime, executionHost
);
super.deepCopy(t);
t.resultsSummary = resultsSummary.deepCopy();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,7 @@ public class FeatureToken extends AbstractToken implements PassPendingFailToken
private transient File featureFile;

private String unavailableHandlersMessage;

public FeatureToken() {
this(getNextId());
}

private FeatureToken(long tokenId) {
super(tokenId);
}


public String getName() {
return name;
}
Expand Down Expand Up @@ -215,7 +207,7 @@ public void accept(TokenVisitor tokenVisitor) {
* @return a deep copy of the feature results and all its sub tokens
*/
public FeatureToken deepCopy() {
FeatureToken copy = new FeatureToken(getNextId());
FeatureToken copy = new FeatureToken();
super.deepCopy(copy);
copy.name = this.name;
copy.usesHandlers = usesHandlers.clone();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,7 @@ public class ResultsSummary extends AbstractToken implements PassPendingFailToke
private int stepsSkipped = 0;

private long timeTaken = 0; //time taken to run the test suite in millis

public ResultsSummary() {
super(getNextId());
}

private ResultsSummary(long tokenId) {
super(tokenId);
}


public int getScenariosPassed() {
return scenariosPassed;
}
Expand Down Expand Up @@ -261,7 +253,7 @@ public void accept(TokenVisitor tokenVisitor) {
}

public ResultsSummary deepCopy() {
ResultsSummary s = new ResultsSummary(getNextId());
ResultsSummary s = new ResultsSummary();
super.deepCopy(s);
s.featuresFailed = featuresFailed;
s.featuresPending = featuresPending;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,6 @@ public class ScenarioToken extends AbstractToken implements PassPendingFailToken
*/
private Map attachments;

public ScenarioToken() {
super(getNextId());
}

private ScenarioToken(long tokenId) {
super(tokenId);
}

public String getName() {
return name;
}
Expand Down Expand Up @@ -113,7 +105,7 @@ public boolean isStartOrEndScenario() {
}

public ScenarioToken deepCopy() {
ScenarioToken copy = new ScenarioToken(getNextId());
ScenarioToken copy = new ScenarioToken();
super.deepCopy(copy);
copy.name = this.name;
copy.steps = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ public class StepToken extends AbstractToken {
/**
* Use the static factory methods to create an instance of a StepToken
*/
private StepToken(long id, String type, String action) {
super(id);
private StepToken(String type, String action) {
Objects.requireNonNull(action, "action cannot be null");
Objects.requireNonNull(type, "type cannot be null");
this.type = type;
Expand Down Expand Up @@ -200,15 +199,15 @@ public boolean isStepMacro() {
}

public static StepToken createDirective(String action) {
return new StepToken(getNextId(), DIRECTIVE_TYPE, action);
return new StepToken(DIRECTIVE_TYPE, action);
}

public static StepToken createStep(String type, String action) {
return new StepToken(getNextId(), type, action);
return new StepToken(type, action);
}

public StepToken deepCopy() {
StepToken copy = new StepToken(getNextId(), this.type, this.action);
StepToken copy = new StepToken(this.type, this.action);
super.deepCopy(copy);
copy.endState = this.endState;
copy.message = this.message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
public interface Token extends Serializable, DeepCopy {

/**
* It is useful for each token to supply an immutable id which logically
* It is useful for each token to supply an immutable UUID which logically
* represents this token within the context of the currently executing test suite
* (i.e. the current TestExecutionToken)
*
Expand All @@ -48,9 +48,9 @@ public interface Token extends Serializable, DeepCopy {
* remotely to match two received token instances - it simplifies things greatly
* if the token has an id which is guaranteed not to change
*
* @return an immutable id representing this token
* @return an immutable UUID representing this token
*/
long getTokenId();
String getTokenId();

void accept(TokenVisitor tokenVisitor);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@ public class FeatureTokenBean {
private String description;
private List<ScenarioToken> scenarios ;
private EndState endState;
private long tokenId;
private String tokenId;

public FeatureTokenBean(){}

@XmlAttribute
public long getTokenId() {
public String getTokenId() {
return tokenId;
}

public void setTokenId(long tokenId) {
public void setTokenId(String tokenId) {
this.tokenId = tokenId;
}

Expand Down
Loading

0 comments on commit 0e775ba

Please sign in to comment.