Skip to content

Commit

Permalink
runtime evaluation of expressions against the context as it exists at…
Browse files Browse the repository at this point in the history
… the time
  • Loading branch information
iantmoore committed Jan 13, 2017
1 parent ce31c5d commit 8da5d70
Show file tree
Hide file tree
Showing 12 changed files with 350 additions and 55 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Requirements
* Replaced Apache config with [Typesafe Config](https://github.com/typesafehub/config) - similar functionality but provides better nesting of properties, variable substitution
* Added System property switch to use original properties files over new .conf files (`-Dsubsteps.use.dot.properties=true`)
* Enable any parameters to be substituted with values from config - user ${config.expression}. Delimitters can be specified and Charset conversion too, see core-api reference.conf for details
* Enabled arguments to be evaluated at runtime against objects in the execution context

1.0.3
-----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@
*/
package com.technophobia.substeps.runner;

import com.google.common.collect.Sets;
import com.technophobia.substeps.model.Scope;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
Expand All @@ -32,6 +36,8 @@
*/
public final class ExecutionContext {

private static final Logger log = LoggerFactory.getLogger(ExecutionContext.class);

private static final ThreadLocal<ExecutionContext> executionContextThreadLocal = new ThreadLocal<ExecutionContext>() {
@Override
protected ExecutionContext initialValue() {
Expand Down Expand Up @@ -78,4 +84,28 @@ private Object getInternal(final Scope scope, final String key) {
public static void clear(final Scope scope) {
executionContextThreadLocal.get().scopedData.remove(scope);
}

public static Map<String, Object> flatten(){

// return a single Map with the items in the narrowest scope remaining

ExecutionContext ec = executionContextThreadLocal.get();

Map newMaster = new HashMap<String, Object>();
for (Scope scope : Scope.values()){

Map<String, Object> scopedMap = ec.scopedData.get(scope);
if (scopedMap != null) {
Sets.SetView intersection = Sets.intersection(newMaster.keySet(), scopedMap.keySet());

if (!intersection.isEmpty()) {
StringBuilder buf = new StringBuilder();
intersection.stream().forEach(s -> log.warn("existing key value " + s + " is being overwritten flattening the ExecutionContext"));
}

newMaster.putAll(scopedMap);
}
}
return newMaster;
}
}
13 changes: 7 additions & 6 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,7 @@
<groupId>velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.5</version>
<!--<exclusions>-->
<!--<exclusion>-->
<!--<groupId>commons-lang</groupId>-->
<!--<artifactId>commons-lang</artifactId>-->
<!--</exclusion>-->
<!--</exclusions>-->

</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
Expand All @@ -97,6 +92,12 @@
<version>${json4s.version}</version>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-jexl3</artifactId>
<version>3.0</version>
</dependency>

<!-- Test Dependencies -->
<dependency>
<groupId>junit</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import com.technophobia.substeps.model.exception.SubstepsRuntimeException;
import com.technophobia.substeps.model.parameter.Converter;
import com.technophobia.substeps.model.parameter.ConverterFactory;
import com.technophobia.substeps.runner.ExecutionContext;
import com.typesafe.config.Config;
import org.apache.commons.jexl3.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -35,8 +37,8 @@
/**
* @author ian
*/
public final class Util {
private static final Logger log = LoggerFactory.getLogger(Util.class);
public final class Arguments {
private static final Logger log = LoggerFactory.getLogger(Arguments.class);

private static final boolean substituteParameters = Configuration.INSTANCE.getConfig().getBoolean("parameter.substitution.enabled");
private static final String startDelimiter = Configuration.INSTANCE.getConfig().getString("parameter.substitution.start");
Expand All @@ -46,33 +48,64 @@ public final class Util {
private static final String normalizeFrom = Configuration.INSTANCE.getConfig().getString("parameter.substitution.normalize.from");
private static final String normalizeTo = Configuration.INSTANCE.getConfig().getString("parameter.substitution.normalize.to");

private static final JexlEngine jexl = new JexlBuilder().cache(512).strict(false).silent(false).create();


private Util() {
private Arguments() {
// no op
}

public static Object evaluateExpression(String expressionWithDelimiters){

public static String substituteValues(String src) {
// TODO - check that the expression doesn't contain any of the bad words
// or and eq ne lt gt le ge div mod not null true false new var return
// any of those words need to be qutoed or [' ']
// http://commons.apache.org/proper/commons-jexl/reference/syntax.html

// try evaluating this expression against the executionContext

// TODO check flag to see whether we can evaluate things from the ec

if (expressionWithDelimiters != null && substituteParameters && expressionWithDelimiters.startsWith(startDelimiter)) {
String expression = StringUtils.stripStart(StringUtils.stripEnd(expressionWithDelimiters, endDelimiter), startDelimiter);

JexlContext context = new MapContext(ExecutionContext.flatten());

JexlExpression e = jexl.createExpression(expression);

return e.evaluate(context);
}
else {
return expressionWithDelimiters;
}
}

public static String substituteValues(String src) {

if (src != null && substituteParameters && src.startsWith(startDelimiter)){
String key = StringUtils.stripStart(StringUtils.stripEnd(src, endDelimiter), startDelimiter);
String substitute = Configuration.INSTANCE.getString(key);
if (substitute == null){
throw new SubstepsRuntimeException("Failed to resolve property " + src + " to be substituted ");
}
String normalizedValue = substitute;

if (normalizeValues) {
// This part will support the conversion of properties files containing accented characters
try {
normalizedValue = new String(substitute.getBytes(normalizeFrom), normalizeTo);
} catch (UnsupportedEncodingException e) {
log.error("error substituting accented characters", e);

String normalizedValue = src;

if (Configuration.INSTANCE.getConfig().hasPath(key)){
String substitute = Configuration.INSTANCE.getString(key);

if (substitute == null){
throw new SubstepsRuntimeException("Failed to resolve property " + src + " to be substituted ");
}
normalizedValue = substitute;
if (normalizeValues) {
// This part will support the conversion of properties files containing accented characters
try {
normalizedValue = new String(substitute.getBytes(normalizeFrom), normalizeTo);
} catch (UnsupportedEncodingException e) {
log.error("error substituting accented characters", e);
}
}
}



return normalizedValue;
}
return src;
Expand All @@ -83,7 +116,7 @@ public static String substituteValues(String src) {
// - could they be combined ??
public static String[] getArgs(final String patternString, final String sourceString, final String[] keywordPrecedence, Config cfg) {

log.debug("Util getArgs String[] with pattern: " + patternString + " and sourceStr: "
log.debug("Arguments getArgs String[] with pattern: " + patternString + " and sourceStr: "
+ sourceString);

String[] rtn = null;
Expand Down Expand Up @@ -148,7 +181,7 @@ public static String[] getArgs(final String patternString, final String sourceSt
public static List<Object> getArgs(final String patternString, final String sourceString,
final Class<?>[] parameterTypes, final Class<? extends Converter<?>>[] converterTypes) {

log.debug("Util getArgs List<Object> with pattern: " + patternString + " and sourceStr: "
log.debug("Arguments getArgs List<Object> with pattern: " + patternString + " and sourceStr: "
+ sourceString);

List<Object> argsList = null;
Expand All @@ -170,8 +203,20 @@ public static List<Object> getArgs(final String patternString, final String sour
argsList = new ArrayList<Object>();
}
String substituted = substituteValues(arg);
argsList.add(getObjectArg(substituted, parameterTypes[argIdx], converterTypes[argIdx]));

// if (substituted.equals(arg)){
// // no change, lets try against the context
// Object result = evaluateExpression(arg);
// if (result != null){
// argsList.add(result);
// }
// else {
// argsList.add(getObjectArg(substituted, parameterTypes[argIdx], converterTypes[argIdx]));
// }
// }
// else {
argsList.add(getObjectArg(substituted, parameterTypes[argIdx], converterTypes[argIdx]));
// }
}
argIdx++;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public List<Step> getSteps() {
public void initialiseParamValues(final Step step) {
final HashMap<String, String> map = new HashMap<String, String>();

final String[] paramValues = Util.getArgs(this.parent.getPattern(),
final String[] paramValues = Arguments.getArgs(this.parent.getPattern(),
step.getLine(), null, Configuration.INSTANCE.getConfig());

if (paramValues != null) {
Expand All @@ -97,7 +97,7 @@ public void initialiseParamValues(final int lineNumber, final String line, Strin

log.debug("initialiseParamValues with line: " + line);

final String[] paramValues = Util.getArgs(this.parent.getPattern(),
final String[] paramValues = Arguments.getArgs(this.parent.getPattern(),
line, keywordPrecedence, Configuration.INSTANCE.getConfig());

if (paramValues != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ private Object[] getStepMethodArguments(final String stepParameter, final Map<St
final String substitutedStepParam = substitutePlaceholders(stepParameter, parentArguments);

stepNode.setLine(substitutedStepParam);
List<Object> argsList = Util.getArgs(stepImplementationPattern, substitutedStepParam, parameterTypes,
List<Object> argsList = Arguments.getArgs(stepImplementationPattern, substitutedStepParam, parameterTypes,
converterTypes);

if (inlineTable != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,58 @@

import com.technophobia.substeps.execution.node.RootNodeExecutionContext;
import com.technophobia.substeps.execution.node.StepImplementationNode;
import com.technophobia.substeps.model.Scope;
import com.technophobia.substeps.model.*;
import com.technophobia.substeps.model.parameter.Converter;
import com.technophobia.substeps.runner.ExecutionNodeRunner;
import com.technophobia.substeps.runner.ProvidesScreenshot;
import com.technophobia.substeps.runner.SubstepExecutionFailure;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StepImplementationNodeRunner extends AbstractNodeRunner<StepImplementationNode, Void> {

private static final Logger log = LoggerFactory.getLogger(StepImplementationNodeRunner.class);



@Override
protected boolean execute(StepImplementationNode node, RootNodeExecutionContext context) {

boolean success = false;

try {

// run through any args - if there's any expressions in there, evaluate them now
Object[] evaluatedArgs = null;

if (node.getMethodArgs() != null && node.getMethodArgs().length > 0) {
List<Object> evaluatedArgsList = new ArrayList<>();
for (Object o : node.getMethodArgs()) {

if (o instanceof String) {

Object result = Arguments.evaluateExpression((String) o);
evaluatedArgsList.add(result);

} else {
evaluatedArgsList.add(o);
}
}
evaluatedArgs = evaluatedArgsList.toArray();
}


context.getMethodExecutor().executeMethod(node.getTargetClass(), node.getTargetMethod(),
node.getMethodArgs());
evaluatedArgs);
context.setTestsHaveRun();
success = true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import com.technophobia.substeps.execution.Feature;
import com.technophobia.substeps.execution.ImplementationCache;
import com.technophobia.substeps.execution.node.*;
import com.technophobia.substeps.model.Util;
import com.technophobia.substeps.model.Arguments;
import com.technophobia.substeps.model.exception.SubstepsConfigurationException;
import com.technophobia.substeps.model.exception.UnimplementedStepException;
import com.technophobia.substeps.runner.setupteardown.Annotations.BeforeAllFeatures;
Expand Down Expand Up @@ -713,18 +713,18 @@ public void testArgSubstituion() {

final String patternString = "Given a substep that takes one parameter \"([^\"]*)\"";
final String[] keywordPrecedence = new String[]{"Given", "And"};
String[] args1 = Util.getArgs(patternString, srcString1, keywordPrecedence, cfg);
String[] args1 = Arguments.getArgs(patternString, srcString1, keywordPrecedence, cfg);


String[] args2 = Util.getArgs(patternString, srcString2, keywordPrecedence, cfg);
String[] args2 = Arguments.getArgs(patternString, srcString2, keywordPrecedence, cfg);

Assert.assertNotNull(args2);
Assert.assertThat(args2[0], is("src2"));

Assert.assertNotNull(args1);
Assert.assertThat(args1[0], is("src1"));

String[] args3 = Util.getArgs(patternString, srcString3, keywordPrecedence, cfg);
String[] args3 = Arguments.getArgs(patternString, srcString3, keywordPrecedence, cfg);
Assert.assertNotNull(args3);
Assert.assertThat(args3[0], is("bob"));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package com.technophobia.substeps.runner;

import com.technophobia.substeps.model.ParentStep;
import com.technophobia.substeps.model.Scope;
import com.technophobia.substeps.model.Step;
import org.junit.Assert;
import org.junit.Test;
Expand All @@ -36,6 +37,8 @@
* @author ian
*/
public class ParsingTests {


@Test
public void testRegEx() {
final String stepParameter = "bob <start_locateButton>";
Expand Down Expand Up @@ -199,7 +202,7 @@ public void testParentParameterChaining() {
final ParentStep parentStep = new ParentStep(aParentSubStep);

parentStep.initialiseParamValues(topLevelStepReDefinedInSubSteps);
// String[] paramValues = Util.getArgs(this.parent.pattern, step.param);
// String[] paramValues = Arguments.getArgs(this.parent.pattern, step.param);

final Map<String, String> paramValueMap = parentStep.getParamValueMap().getParameters();
Assert.assertNotNull(paramValueMap);
Expand Down
Loading

0 comments on commit 8da5d70

Please sign in to comment.