Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow additional parameters in feature methods #566

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,15 @@ private void rewriteWhereStat(ListIterator<Statement> stats) throws InvalidSpecC
rewriteSimpleParameterization(binExpr, stat);
else if (leftExpr instanceof ListExpression)
rewriteMultiParameterization(binExpr, stat);
else
else
notAParameterization(stat);
} else if (type == Types.ASSIGN)
rewriteDerivedParameterization(binExpr, stat);
else if (getOrExpression(binExpr) != null) {
stats.previous();
rewriteTableLikeParameterization(stats);
}
else
else
notAParameterization(stat);
}

Expand All @@ -104,20 +104,21 @@ private void createDataProviderMethod(Expression dataProviderExpr, int nextDataV

dataProviderExpr = dataProviderExpr.transformExpression(new DataTablePreviousVariableTransformer());

final Parameter[] previousParameters = getPreviousParameters(nextDataVariableIndex);
MethodNode method =
new MethodNode(
InternalIdentifiers.getDataProviderName(whereBlock.getParent().getAst().getName(), dataProviderCount++),
Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC,
Opcodes.ACC_PUBLIC /*| Opcodes.ACC_SYNTHETIC*/,
ClassHelper.OBJECT_TYPE,
getPreviousParameters(nextDataVariableIndex),
previousParameters,
ClassNode.EMPTY_ARRAY,
new BlockStatement(
Arrays.<Statement> asList(
new ReturnStatement(
new ExpressionStatement(dataProviderExpr))),
new VariableScope()));

method.addAnnotation(createDataProviderAnnotation(dataProviderExpr, nextDataVariableIndex));
method.addAnnotation(createDataProviderAnnotation(dataProviderExpr, nextDataVariableIndex, previousParameters));
whereBlock.getParent().getParent().getAst().addMethod(method);
}

Expand All @@ -129,13 +130,20 @@ private Parameter[] getPreviousParameters(int nextDataVariableIndex) {
return results;
}

private AnnotationNode createDataProviderAnnotation(Expression dataProviderExpr, int nextDataVariableIndex) {
private AnnotationNode createDataProviderAnnotation(Expression dataProviderExpr, int nextDataVariableIndex, Parameter[] parameters) {
AnnotationNode ann = new AnnotationNode(resources.getAstNodeCache().DataProviderMetadata);
ann.addMember(DataProviderMetadata.LINE, new ConstantExpression(dataProviderExpr.getLineNumber()));

List<Expression> dataVariableNames = new ArrayList<Expression>();
for (int i = nextDataVariableIndex; i < dataProcessorVars.size(); i++)
dataVariableNames.add(new ConstantExpression(dataProcessorVars.get(i).getName()));
ann.addMember(DataProviderMetadata.DATA_VARIABLES, new ListExpression(dataVariableNames));

List<Expression> parameterNames = new ArrayList<Expression>();
for (Parameter parameter : parameters)
parameterNames.add(new ConstantExpression(parameter.getName()));
ann.addMember(DataProviderMetadata.PARAMETERS, new ListExpression(parameterNames));

return ann;
}

Expand Down Expand Up @@ -273,19 +281,19 @@ private void splitRow(Expression row, List<Expression> parts) {
splitRow(orExpr.getRightExpression(), parts);
}
}

private BinaryExpression getOrExpression(Statement stat) {
Expression expr = AstUtil.getExpression(stat, Expression.class);
return getOrExpression(expr);
}

private BinaryExpression getOrExpression(Expression expr) {
BinaryExpression binExpr = ObjectUtil.asInstance(expr, BinaryExpression.class);
if (binExpr == null) return null;

int binExprType = binExpr.getOperation().getType();
if (binExprType == Types.BITWISE_OR || binExprType == Types.LOGICAL_OR) return binExpr;

return null;
}

Expand Down Expand Up @@ -333,14 +341,6 @@ private void handleFeatureParameters() {
Parameter[] parameters = whereBlock.getParent().getAst().getParameters();
if (parameters.length == 0)
addFeatureParameters();
else
checkAllParametersAreDataVariables(parameters);
}

private void checkAllParametersAreDataVariables(Parameter[] parameters) {
for (Parameter param : parameters)
if (!isDataProcessorVariable(param.getName()))
resources.getErrorReporter().error(param, "Parameter '%s' does not refer to a data variable", param.getName());
}

private void addFeatureParameters() {
Expand All @@ -353,24 +353,44 @@ private void addFeatureParameters() {
@SuppressWarnings("unchecked")
private void createDataProcessorMethod() {
if (dataProcessorVars.isEmpty()) return;

dataProcessorStats.add(
new ReturnStatement(
new ArrayExpression(
ClassHelper.OBJECT_TYPE,
(List) dataProcessorVars)));

final VariableExpression resultMap = new VariableExpression("$spock_result", new ClassNode(Map.class));

dataProcessorStats.add( // Map $spock_result = new HashMap();
new ExpressionStatement(
new DeclarationExpression(
resultMap,
Token.newSymbol(Types.EQUALS, 0, 0),
new ConstructorCallExpression(new ClassNode(HashMap.class), ArgumentListExpression.EMPTY_ARGUMENTS))));

for (VariableExpression dataProcessorVar : dataProcessorVars) {
dataProcessorStats.add( // $spock_result.put(variable_name, variable);
new ExpressionStatement(
new MethodCallExpression(
resultMap,
"put",
new ArgumentListExpression(Arrays.asList(
new ConstantExpression(dataProcessorVar.getName()),
dataProcessorVar)))));
}

dataProcessorStats.add( // return $spock_result
new ReturnStatement(resultMap));

BlockStatement blockStat = new BlockStatement(dataProcessorStats, new VariableScope());

new DataProcessorVariableRewriter().visitBlockStatement(blockStat);

final MethodNode method = new MethodNode(
InternalIdentifiers.getDataProcessorName(whereBlock.getParent().getAst().getName()),
Opcodes.ACC_PUBLIC /*| Opcodes.ACC_SYNTHETIC*/,
ClassHelper.OBJECT_TYPE,
dataProcessorParams.toArray(new Parameter[dataProcessorParams.size()]),
ClassNode.EMPTY_ARRAY,
blockStat);

whereBlock.getParent().getParent().getAst().addMethod(
new MethodNode(
InternalIdentifiers.getDataProcessorName(whereBlock.getParent().getAst().getName()),
Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC,
ClassHelper.OBJECT_TYPE,
dataProcessorParams.toArray(new Parameter[dataProcessorParams.size()]),
ClassNode.EMPTY_ARRAY,
blockStat));
method);
}

private static void notAParameterization(ASTNode stat) throws InvalidSpecCompileException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@

import spock.lang.Specification;

import java.util.Collections;
import java.util.List;
import java.util.Map;

import static org.spockframework.runtime.RunStatus.*;

/**
Expand Down Expand Up @@ -266,19 +270,19 @@ public void doRunFeature() {
private void runSimpleFeature() {
if (runStatus != OK) return;

initializeAndRunIteration(EMPTY_ARGS, 1);
initializeAndRunIteration(Collections.<String, Object>emptyMap(), 1);
resetStatus(ITERATION);
}

protected void initializeAndRunIteration(Object[] dataValues, int estimatedNumIterations) {
protected void initializeAndRunIteration(Map<String, Object> dataValues, int estimatedNumIterations) {
if (runStatus != OK) return;

createSpecInstance(false);
runInitializer();
runIteration(dataValues, estimatedNumIterations);
}

private void runIteration(Object[] dataValues, int estimatedNumIterations) {
private void runIteration(Map<String, Object> dataValues, int estimatedNumIterations) {
if (runStatus != OK) return;

currentIteration = createIterationInfo(dataValues, estimatedNumIterations);
Expand All @@ -292,7 +296,7 @@ private void runIteration(Object[] dataValues, int estimatedNumIterations) {
currentIteration = null;
}

private IterationInfo createIterationInfo(Object[] dataValues, int estimatedNumIterations) {
private IterationInfo createIterationInfo(Map<String, Object> dataValues, int estimatedNumIterations) {
IterationInfo result = new IterationInfo(currentFeature, dataValues, estimatedNumIterations);
String iterationName = currentFeature.getIterationNameProvider().getName(result);
result.setName(iterationName);
Expand Down Expand Up @@ -403,7 +407,20 @@ public void doRunSetup(SpecInfo spec) {

private void runFeatureMethod() {
if (runStatus != OK) return;
invoke(currentInstance, currentFeature.getFeatureMethod(), currentIteration.getDataValues());
invoke(currentInstance, currentFeature.getFeatureMethod(), dataValuesToArray(currentFeature.getParameterNames(), currentIteration.getDataValues()));
}

private Object[] dataValuesToArray(List<String> parameterNames, Map<String, Object> dataValues) {
Object[] result = new Object[parameterNames.size()];
for (int i = 0; i < parameterNames.size(); i++) {
final String parameterName = parameterNames.get(i);
if (dataValues.containsKey(parameterName)){
result[i] = dataValues.get(parameterName);
}else {
throw new SpockExecutionException("Parameter '%s' was not set").withArgs(parameterName);
}
}
return result;
}

private void runCleanup() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,28 +48,53 @@ private Object[] createDataProviders() {
List<DataProviderInfo> dataProviderInfos = currentFeature.getDataProviders();
Object[] dataProviders = new Object[dataProviderInfos.size()];

int dataTableOffset = 0;

if (!dataProviderInfos.isEmpty()) {
for (int i = 0; i < dataProviderInfos.size(); i++) {
DataProviderInfo dataProviderInfo = dataProviderInfos.get(i);

MethodInfo method = dataProviderInfo.getDataProviderMethod();
Object[] arguments = Arrays.copyOf(dataProviders, getDataTableOffset(dataProviderInfo));
Object provider = invokeRaw(sharedInstance, method, arguments);

if (runStatus != OK)
final List<String> parameters = dataProviderInfo.getParameters();
List<Object> arguments = new ArrayList<Object>();
for (String parameter : parameters) {
arguments.add(findArgument(dataProviderInfos, i, dataProviders, parameter));
}
Object provider = invokeRaw(sharedInstance, method, arguments.toArray());

if (runStatus != OK)
return null;
else if (provider == null) {
SpockExecutionException error = new SpockExecutionException("Data provider is null!");
runStatus = supervisor.error(new ErrorInfo(method, error));
return null;
}
dataProviders[i] = provider;
dataTableOffset += dataProviderInfo.getDataVariables().size();
}
}

return dataProviders;
}

private Object findArgument(List<DataProviderInfo> dataProviderInfos, int currentDataProviderIndex, Object[] dataProviders, String parameter) {
for (int i = 0; i < currentDataProviderIndex; i++) {
DataProviderInfo providerInfo = dataProviderInfos.get(i);
if (providerInfo.getDataVariables().contains(parameter)) {
return dataProviders[i];
}
}
return null; // possible we have derived parametrization. With current implementation Spock does not support reference on derived variables.
}

private List<String> extractParameterNames(List<DataProviderInfo> dataProviderInfos, int currentDataProviderIndex) {
List<String> result = new ArrayList<String>();
for (int i = 0; i < currentDataProviderIndex; i++) {
result.addAll(dataProviderInfos.get(i).getDataVariables());
}
return result;
}

private int getDataTableOffset(DataProviderInfo dataProviderInfo) {
int result = 0;
for (String variableName : dataProviderInfo.getDataVariables()) {
Expand All @@ -94,14 +119,14 @@ private Iterator[] createIterators(Object[] dataProviders) {
Iterator<?> iter = GroovyRuntimeUtil.asIterator(dataProviders[i]);
if (iter == null) {
runStatus = supervisor.error(
new ErrorInfo(currentFeature.getDataProviders().get(i).getDataProviderMethod(),
new ErrorInfo(currentFeature.getDataProviders().get(i).getDataProviderMethod(),
new SpockExecutionException("Data provider's iterator() method returned null")));
return null;
}
iterators[i] = iter;
} catch (Throwable t) {
runStatus = supervisor.error(
new ErrorInfo(currentFeature.getDataProviders().get(i).getDataProviderMethod(), t));
new ErrorInfo(currentFeature.getDataProviders().get(i).getDataProviderMethod(), t));
return null;
}

Expand Down Expand Up @@ -165,13 +190,13 @@ private boolean haveNext(Iterator[] iterators) {
else if (haveNext != hasNext) {
DataProviderInfo provider = currentFeature.getDataProviders().get(i);
runStatus = supervisor.error(new ErrorInfo(provider.getDataProviderMethod(),
createDifferentNumberOfDataValuesException(provider, hasNext)));
createDifferentNumberOfDataValuesException(provider, hasNext)));
return false;
}

} catch (Throwable t) {
runStatus = supervisor.error(
new ErrorInfo(currentFeature.getDataProviders().get(i).getDataProviderMethod(), t));
new ErrorInfo(currentFeature.getDataProviders().get(i).getDataProviderMethod(), t));
return false;
}

Expand All @@ -192,7 +217,7 @@ private SpockExecutionException createDifferentNumberOfDataValuesException(DataP
}

// advances iterators and computes args
private Object[] nextArgs(Iterator[] iterators) {
private Map<String, Object> nextArgs(Iterator[] iterators) {
if (runStatus != OK) return null;

Object[] next = new Object[iterators.length];
Expand All @@ -201,15 +226,16 @@ private Object[] nextArgs(Iterator[] iterators) {
next[i] = iterators[i].next();
} catch (Throwable t) {
runStatus = supervisor.error(
new ErrorInfo(currentFeature.getDataProviders().get(i).getDataProviderMethod(), t));
new ErrorInfo(currentFeature.getDataProviders().get(i).getDataProviderMethod(), t));
return null;
}

try {
return (Object[])invokeRaw(sharedInstance, currentFeature.getDataProcessorMethod(), next);
//noinspection unchecked
return (Map)invokeRaw(sharedInstance, currentFeature.getDataProcessorMethod(), next);
} catch (Throwable t) {
runStatus = supervisor.error(
new ErrorInfo(currentFeature.getDataProcessorMethod(), t));
new ErrorInfo(currentFeature.getDataProcessorMethod(), t));
return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ private DataProviderInfo createDataProvider(FeatureInfo feature, MethodInfo meth
provider.setParent(feature);
provider.setLine(metadata.line());
provider.setDataVariables(Arrays.asList(metadata.dataVariables()));
provider.setParameters(Arrays.asList(metadata.parameters()));
provider.setDataProviderMethod(method);
return provider;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.spockframework.runtime.extension.builtin;

import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -44,7 +45,7 @@ public String getName(IterationInfo iterationInfo) {
return nameFor(iterationInfo.getDataValues());
}

String nameFor(Object... dataValues) {
String nameFor(Map<String, Object> dataValues) {
StringBuffer result = new StringBuffer();
expressionMatcher.reset();

Expand All @@ -59,7 +60,7 @@ String nameFor(Object... dataValues) {
return result.toString();
}

private String evaluateExpression(String expr, Object[] dataValues) {
private String evaluateExpression(String expr, Map<String, Object> dataValues) {
String[] exprParts = expr.split("\\.");
String firstPart = exprParts[0];
Object result;
Expand All @@ -69,9 +70,11 @@ private String evaluateExpression(String expr, Object[] dataValues) {
} else if (firstPart.equals("iterationCount")) {
result = String.valueOf(iterationCount);
} else {
int index = feature.getDataVariables().indexOf(firstPart);
if (index < 0) return "#Error:" + expr;
result = dataValues[index];
if (dataValues.containsKey(firstPart)) {
result = dataValues.get(firstPart);
} else {
return "#Error:" + expr;
}
}

try {
Expand Down
Loading