From 84e93cebd7b1ab2bec732250e1e90df4bb0fc2e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pap=20L=C5=91rinc?= Date: Mon, 30 Nov 2015 14:23:28 +0200 Subject: [PATCH 1/5] Reformatted affected files to simplify code review --- .../org/spockframework/compiler/AstUtil.java | 48 +++--- .../spockframework/compiler/SpecRewriter.java | 140 +++++++++--------- .../compiler/WhereBlockRewriter.java | 112 +++++++------- .../runtime/BaseSpecRunner.java | 12 +- .../runtime/ParameterizedSpecRunner.java | 34 ++--- .../runtime/SpecInfoBuilder.java | 16 +- .../runtime/model/DataProviderMetadata.java | 2 +- .../runtime/model/FeatureInfo.java | 13 +- .../runtime/model/IterationInfo.java | 5 +- .../spockframework/util/CollectionUtil.java | 10 +- .../util/InternalIdentifiers.java | 6 +- .../runtime/AsyncRunListenerSpec.groovy | 4 +- .../SafeIterationNameProviderSpec.groovy | 19 +-- .../org/spockframework/smoke/Blocks.groovy | 17 +-- .../smoke/parameterization/DataTables.groovy | 27 ++-- .../InvalidWhereBlocks.groovy | 8 +- .../ParameterizationScopes.groovy | 15 +- 17 files changed, 230 insertions(+), 258 deletions(-) diff --git a/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java b/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java index b678f867d0..b1ba243b77 100755 --- a/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java +++ b/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java @@ -16,25 +16,21 @@ package org.spockframework.compiler; -import java.util.*; - import org.codehaus.groovy.ast.*; import org.codehaus.groovy.ast.expr.*; import org.codehaus.groovy.ast.stmt.*; -import org.codehaus.groovy.runtime.dgmimpl.arrays.*; - +import org.codehaus.groovy.runtime.dgmimpl.arrays.IntegerArrayGetAtMetaMethod; import org.objectweb.asm.Opcodes; - import org.spockframework.lang.Wildcard; import org.spockframework.runtime.SpockRuntime; -import org.spockframework.util.Nullable; - -import org.spockframework.util.ObjectUtil; +import org.spockframework.util.*; import spock.lang.Specification; +import java.util.*; + /** * Utility methods for AST processing. - * + * * @author Peter Niederwieser */ public abstract class AstUtil { @@ -43,7 +39,7 @@ public abstract class AstUtil { /** * Tells whether the given node has an annotation of the given type. * - * @param node an AST node + * @param node an AST node * @param annotationType an annotation type * @return true iff the given node has an annotation of the given type */ @@ -54,7 +50,7 @@ public static boolean hasAnnotation(ASTNode node, Class annotationType) { public static AnnotationNode getAnnotation(ASTNode node, Class annotationType) { if (!(node instanceof AnnotatedNode)) return null; - AnnotatedNode annotated = (AnnotatedNode)node; + AnnotatedNode annotated = (AnnotatedNode) node; @SuppressWarnings("unchecked") List annotations = annotated.getAnnotations(); for (AnnotationNode a : annotations) @@ -77,15 +73,15 @@ public static List getStatements(MethodNode method) { if (code != null) block.addStatement(code); method.setCode(block); } - return ((BlockStatement)method.getCode()).getStatements(); + return ((BlockStatement) method.getCode()).getStatements(); } @SuppressWarnings("unchecked") public static List getStatements(ClosureExpression closure) { - BlockStatement blockStat = (BlockStatement)closure.getCode(); + BlockStatement blockStat = (BlockStatement) closure.getCode(); return blockStat == null ? - Collections. emptyList() : // it's not possible to add any statements to such a ClosureExpression, so immutable list is OK - blockStat.getStatements(); + Collections.emptyList() : // it's not possible to add any statements to such a ClosureExpression, so immutable list is OK + blockStat.getStatements(); } public static boolean isInvocationWithImplicitThis(Expression invocation) { @@ -162,7 +158,7 @@ public static boolean isSynthetic(MethodNode method) { */ public static boolean hasPlausibleSourcePosition(ASTNode node) { return node.getLineNumber() > 0 && node.getLastLineNumber() >= node.getLineNumber() - && node.getColumnNumber() > 0 && node.getLastColumnNumber() > node.getColumnNumber(); + && node.getColumnNumber() > 0 && node.getLastColumnNumber() > node.getColumnNumber(); } public static String getMethodName(Expression invocation) { @@ -229,13 +225,13 @@ public static Expression toArgumentArray(List argList, IRewriteResou return new ArrayExpression(ClassHelper.OBJECT_TYPE, argList); return new MethodCallExpression( - new ClassExpression(resources.getAstNodeCache().SpockRuntime), - new ConstantExpression(SpockRuntime.DESPREAD_LIST), - new ArgumentListExpression( - new ArrayExpression(ClassHelper.OBJECT_TYPE, normalArgs), - new ArrayExpression(ClassHelper.OBJECT_TYPE, spreadArgs), - new ArrayExpression(ClassHelper.int_TYPE, spreadPositions) - )); + new ClassExpression(resources.getAstNodeCache().SpockRuntime), + new ConstantExpression(SpockRuntime.DESPREAD_LIST), + new ArgumentListExpression( + new ArrayExpression(ClassHelper.OBJECT_TYPE, normalArgs), + new ArrayExpression(ClassHelper.OBJECT_TYPE, spreadArgs), + new ArrayExpression(ClassHelper.int_TYPE, spreadPositions) + )); } public static void copySourcePosition(ASTNode from, ASTNode to) { @@ -255,12 +251,12 @@ public static Expression getAssertionMessage(AssertStatement stat) { public static boolean isThisExpression(Expression expr) { return expr instanceof VariableExpression - && ((VariableExpression) expr).isThisExpression(); + && ((VariableExpression) expr).isThisExpression(); } public static boolean isSuperExpression(Expression expr) { return expr instanceof VariableExpression - && ((VariableExpression) expr).isSuperExpression(); + && ((VariableExpression) expr).isSuperExpression(); } public static boolean isThisOrSuperExpression(Expression expr) { @@ -274,7 +270,7 @@ public static void setVisibility(MethodNode method, int visibility) { } public static int getVisibility(FieldNode field) { - return field.getModifiers() & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE); + return field.getModifiers() & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE); } public static void setVisibility(FieldNode field, int visibility) { diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java index 39de377464..3d1874108a 100755 --- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java +++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java @@ -16,24 +16,22 @@ package org.spockframework.compiler; -import java.util.*; - import org.codehaus.groovy.ast.*; import org.codehaus.groovy.ast.expr.*; import org.codehaus.groovy.ast.stmt.*; import org.codehaus.groovy.runtime.MetaClassHelper; -import org.codehaus.groovy.syntax.Token; -import org.codehaus.groovy.syntax.Types; +import org.codehaus.groovy.syntax.*; import org.objectweb.asm.Opcodes; - import org.spockframework.compiler.model.*; import org.spockframework.mock.runtime.MockController; import org.spockframework.runtime.SpecificationContext; import org.spockframework.util.*; +import java.util.*; + /** * A Spec visitor responsible for most of the rewriting of a Spec's AST. - * + * * @author Peter Niederwieser */ // IDEA: mock controller / leaveScope calls should only be inserted when necessary (increases robustness) @@ -96,22 +94,22 @@ private void createSharedFieldGetter(Field field) { MethodNode getter = spec.getAst().getMethod(getterName, Parameter.EMPTY_ARRAY); if (getter != null) { errorReporter.error(field.getAst(), - "@Shared field '%s' conflicts with method '%s'; please rename either of them", - field.getName(), getter.getName()); + "@Shared field '%s' conflicts with method '%s'; please rename either of them", + field.getName(), getter.getName()); return; } BlockStatement getterBlock = new BlockStatement(); getter = new MethodNode(getterName, determineVisibilityForSharedFieldAccessor(field) | Opcodes.ACC_SYNTHETIC, - ClassHelper.DYNAMIC_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getterBlock); + ClassHelper.DYNAMIC_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getterBlock); getterBlock.addStatement( - new ReturnStatement( - new ExpressionStatement( - new AttributeExpression( - getSharedInstance(), - // use internal name - new ConstantExpression(field.getAst().getName()))))); + new ReturnStatement( + new ExpressionStatement( + new AttributeExpression( + getSharedInstance(), + // use internal name + new ConstantExpression(field.getAst().getName()))))); getter.setSourcePosition(field.getAst()); spec.getAst().addMethod(getter); @@ -119,28 +117,28 @@ private void createSharedFieldGetter(Field field) { private void createSharedFieldSetter(Field field) { String setterName = "set" + MetaClassHelper.capitalize(field.getName()); - Parameter[] params = new Parameter[] { new Parameter(field.getAst().getType(), "$spock_value") }; + Parameter[] params = new Parameter[]{new Parameter(field.getAst().getType(), "$spock_value")}; MethodNode setter = spec.getAst().getMethod(setterName, params); if (setter != null) { errorReporter.error(field.getAst(), - "@Shared field '%s' conflicts with method '%s'; please rename either of them", - field.getName(), setter.getName()); + "@Shared field '%s' conflicts with method '%s'; please rename either of them", + field.getName(), setter.getName()); return; } BlockStatement setterBlock = new BlockStatement(); setter = new MethodNode(setterName, determineVisibilityForSharedFieldAccessor(field) | Opcodes.ACC_SYNTHETIC, - ClassHelper.VOID_TYPE, params, ClassNode.EMPTY_ARRAY, setterBlock); + ClassHelper.VOID_TYPE, params, ClassNode.EMPTY_ARRAY, setterBlock); setterBlock.addStatement( - new ExpressionStatement( - new BinaryExpression( - new AttributeExpression( - getSharedInstance(), - // use internal name - new ConstantExpression(field.getAst().getName())), - Token.newSymbol(Types.ASSIGN, -1, -1), - new VariableExpression("$spock_value")))); + new ExpressionStatement( + new BinaryExpression( + new AttributeExpression( + getSharedInstance(), + // use internal name + new ConstantExpression(field.getAst().getName())), + Token.newSymbol(Types.ASSIGN, -1, -1), + new VariableExpression("$spock_value")))); setter.setSourcePosition(field.getAst()); spec.getAst().addMethod(setter); @@ -176,7 +174,7 @@ def sharedFieldGetter() { sharedInstance.sharedField } class DerivedSpec extends BaseSpec {} // when DerivedSpec is run: - + def sharedInstance = new DerivedSpec() def spec = new DerivedSpec() @@ -222,8 +220,8 @@ private void handleNonSharedField(Field field) { */ private void moveInitializer(Field field, Method method, int position) { method.getFirstBlock().getAst().add(position, - new ExpressionStatement( - new FieldInitializationExpression(field.getAst()))); + new ExpressionStatement( + new FieldInitializationExpression(field.getAst()))); field.getAst().setInitialValueExpression(null); } @@ -264,7 +262,7 @@ private String createInternalName(FeatureMethod feature) { private MethodNode copyMethod(MethodNode method, String newName) { // can't hurt to set return type to void MethodNode newMethod = new MethodNode(newName, method.getModifiers(), - ClassHelper.VOID_TYPE, method.getParameters(), method.getExceptions(), method.getCode()); + ClassHelper.VOID_TYPE, method.getParameters(), method.getExceptions(), method.getCode()); newMethod.addAnnotations(method.getAnnotations()); newMethod.setSynthetic(method.isSynthetic()); @@ -342,7 +340,7 @@ private void moveInteractions(List interactions, ThenBlock block) { List statsBeforeWhenBlock = block.getPrevious(WhenBlock.class).getPrevious().getAst(); statsBeforeWhenBlock.add(createMockControllerCall( - block.isFirstInChain() ? MockController.ENTER_SCOPE : MockController.ADD_BARRIER)); + block.isFirstInChain() ? MockController.ENTER_SCOPE : MockController.ADD_BARRIER)); statsBeforeWhenBlock.addAll(interactions); @@ -354,10 +352,10 @@ private void moveInteractions(List interactions, ThenBlock block) { private Statement createMockControllerCall(String methodName) { return new ExpressionStatement( - new MethodCallExpression( - getMockInvocationMatcher(), - methodName, - ArgumentListExpression.EMPTY_ARGUMENTS)); + new MethodCallExpression( + getMockInvocationMatcher(), + methodName, + ArgumentListExpression.EMPTY_ARGUMENTS)); } public void visitCleanupBlock(CleanupBlock block) { @@ -376,9 +374,9 @@ public void visitCleanupBlock(CleanupBlock block) { finallyStats.addAll(block.getAst()); TryCatchStatement tryFinally = - new TryCatchStatement( - new BlockStatement(tryStats, new VariableScope()), - new BlockStatement(finallyStats, new VariableScope())); + new TryCatchStatement( + new BlockStatement(tryStats, new VariableScope()), + new BlockStatement(finallyStats, new VariableScope())); method.getStatements().add(tryFinally); @@ -406,11 +404,11 @@ public void defineValueRecorder(List stats) { // recorder variable needs to be defined in outermost scope, // hence we insert it at the beginning of the block stats.add(0, - new ExpressionStatement( - new DeclarationExpression( - new VariableExpression("$spock_valueRecorder"), - Token.newSymbol(Types.ASSIGN, -1, -1), - new ConstructorCallExpression( + new ExpressionStatement( + new DeclarationExpression( + new VariableExpression("$spock_valueRecorder"), + Token.newSymbol(Types.ASSIGN, -1, -1), + new ConstructorCallExpression( nodeCache.ValueRecorder, ArgumentListExpression.EMPTY_ARGUMENTS)))); } @@ -418,9 +416,9 @@ public void defineValueRecorder(List stats) { public VariableExpression captureOldValue(Expression oldValue) { VariableExpression var = new OldValueExpression(oldValue, "$spock_oldValue" + oldValueCount++); DeclarationExpression decl = new DeclarationExpression( - var, - Token.newSymbol(Types.ASSIGN, -1, -1), - oldValue); + var, + Token.newSymbol(Types.ASSIGN, -1, -1), + oldValue); decl.setSourcePosition(oldValue); // add declaration at end of block immediately preceding when-block @@ -431,22 +429,22 @@ public VariableExpression captureOldValue(Expression oldValue) { public MethodCallExpression getSpecificationContext() { return AstUtil.createDirectMethodCall(VariableExpression.THIS_EXPRESSION, - nodeCache.SpecInternals_GetSpecificationContext, ArgumentListExpression.EMPTY_ARGUMENTS); + nodeCache.SpecInternals_GetSpecificationContext, ArgumentListExpression.EMPTY_ARGUMENTS); } public MethodCallExpression getMockInvocationMatcher() { return new MethodCallExpression(getSpecificationContext(), - SpecificationContext.GET_MOCK_CONTROLLER, ArgumentListExpression.EMPTY_ARGUMENTS); + SpecificationContext.GET_MOCK_CONTROLLER, ArgumentListExpression.EMPTY_ARGUMENTS); } public MethodCallExpression setThrownException(Expression value) { return new MethodCallExpression(getSpecificationContext(), - SpecificationContext.SET_THROWN_EXCEPTION, new ArgumentListExpression(value)); + SpecificationContext.SET_THROWN_EXCEPTION, new ArgumentListExpression(value)); } public MethodCallExpression getSharedInstance() { return new MethodCallExpression(getSpecificationContext(), - SpecificationContext.GET_SHARED_INSTANCE, ArgumentListExpression.EMPTY_ARGUMENTS); + SpecificationContext.GET_SHARED_INSTANCE, ArgumentListExpression.EMPTY_ARGUMENTS); } public AstNodeCache getAstNodeCache() { @@ -457,7 +455,7 @@ private FixtureMethod getInitializerMethod() { if (spec.getInitializerMethod() == null) { // method is private s.t. multiple initializer methods along hierarchy can be called independently MethodNode gMethod = new MethodNode(InternalIdentifiers.INITIALIZER_METHOD, Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC, - ClassHelper.DYNAMIC_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement()); + ClassHelper.DYNAMIC_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement()); spec.getAst().addMethod(gMethod); FixtureMethod method = new FixtureMethod(spec, gMethod); method.addBlock(new AnonymousBlock(method)); @@ -479,7 +477,7 @@ private FixtureMethod getSharedInitializerMethod() { if (spec.getSharedInitializerMethod() == null) { // method is private s.t. multiple initializer methods along hierarchy can be called independently MethodNode gMethod = new MethodNode(InternalIdentifiers.SHARED_INITIALIZER_METHOD, Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC, - ClassHelper.DYNAMIC_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement()); + ClassHelper.DYNAMIC_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement()); spec.getAst().addMethod(gMethod); FixtureMethod method = new FixtureMethod(spec, gMethod); method.addBlock(new AnonymousBlock(method)); @@ -495,8 +493,8 @@ private void rewriteWhenBlockForExceptionCondition(WhenBlock block) { block.setAst(blockStats); blockStats.add( - new ExpressionStatement( - setThrownException(ConstantExpression.NULL))); + new ExpressionStatement( + setThrownException(ConstantExpression.NULL))); // ensure variables remain in same scope // by moving variable defs to the very beginning of the method, they don't @@ -504,21 +502,21 @@ private void rewriteWhenBlockForExceptionCondition(WhenBlock block) { moveVariableDeclarations(tryStats, method.getStatements()); TryCatchStatement tryCatchStat = - new TryCatchStatement( - new BlockStatement(tryStats, new VariableScope()), - new BlockStatement()); + new TryCatchStatement( + new BlockStatement(tryStats, new VariableScope()), + new BlockStatement()); blockStats.add(tryCatchStat); tryCatchStat.addCatch( - new CatchStatement( - new Parameter(nodeCache.Throwable, "$spock_ex"), - new BlockStatement( - Arrays. asList( - new ExpressionStatement( - setThrownException( - new VariableExpression("$spock_ex")))), - new VariableScope()))); + new CatchStatement( + new Parameter(nodeCache.Throwable, "$spock_ex"), + new BlockStatement( + Arrays.asList( + new ExpressionStatement( + setThrownException( + new VariableExpression("$spock_ex")))), + new VariableScope()))); } /* @@ -531,10 +529,10 @@ private void moveVariableDeclarations(List from, List to) if (declExpr == null) continue; ((ExpressionStatement) stat).setExpression( - new BinaryExpression( - copyLhsVariableExpressions(declExpr), - Token.newSymbol(Types.ASSIGN, -1, -1), - declExpr.getRightExpression())); + new BinaryExpression( + copyLhsVariableExpressions(declExpr), + Token.newSymbol(Types.ASSIGN, -1, -1), + declExpr.getRightExpression())); declExpr.setRightExpression(createDefaultValueInitializer(declExpr)); to.add(new ExpressionStatement(declExpr)); @@ -555,7 +553,7 @@ private Expression createDefaultValueInitializer(DeclarationExpression expr) { for (Expression elementExpr : tupleExpr.getExpressions()) { Variable variable = (Variable) elementExpr; listExpr.addExpression(new ConstantExpression( - ReflectionUtil.getDefaultValue(variable.getOriginType().getTypeClass()))); + ReflectionUtil.getDefaultValue(variable.getOriginType().getTypeClass()))); } return listExpr; diff --git a/spock-core/src/main/java/org/spockframework/compiler/WhereBlockRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/WhereBlockRewriter.java index 0e2bb7887d..3deb751a34 100755 --- a/spock-core/src/main/java/org/spockframework/compiler/WhereBlockRewriter.java +++ b/spock-core/src/main/java/org/spockframework/compiler/WhereBlockRewriter.java @@ -16,24 +16,21 @@ package org.spockframework.compiler; -import java.util.*; - import org.codehaus.groovy.ast.*; import org.codehaus.groovy.ast.expr.*; import org.codehaus.groovy.ast.stmt.*; import org.codehaus.groovy.control.SourceUnit; -import org.codehaus.groovy.syntax.Token; -import org.codehaus.groovy.syntax.Types; +import org.codehaus.groovy.syntax.*; import org.objectweb.asm.Opcodes; - import org.spockframework.compiler.model.WhereBlock; import org.spockframework.runtime.model.DataProviderMetadata; import org.spockframework.util.*; +import java.util.*; + import static org.spockframework.compiler.AstUtil.createGetAtMethod; /** - * * @author Peter Niederwieser */ public class WhereBlockRewriter { @@ -87,15 +84,14 @@ private void rewriteWhereStat(ListIterator 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); } @@ -105,17 +101,17 @@ private void createDataProviderMethod(Expression dataProviderExpr, int nextDataV dataProviderExpr = dataProviderExpr.transformExpression(new DataTablePreviousVariableTransformer()); MethodNode method = - new MethodNode( - InternalIdentifiers.getDataProviderName(whereBlock.getParent().getAst().getName(), dataProviderCount++), - Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, - ClassHelper.OBJECT_TYPE, - getPreviousParameters(nextDataVariableIndex), - ClassNode.EMPTY_ARRAY, - new BlockStatement( - Arrays. asList( - new ReturnStatement( - new ExpressionStatement(dataProviderExpr))), - new VariableScope())); + new MethodNode( + InternalIdentifiers.getDataProviderName(whereBlock.getParent().getAst().getName(), dataProviderCount++), + Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, + ClassHelper.OBJECT_TYPE, + getPreviousParameters(nextDataVariableIndex), + ClassNode.EMPTY_ARRAY, + new BlockStatement( + Arrays.asList( + new ReturnStatement( + new ExpressionStatement(dataProviderExpr))), + new VariableScope())); method.addAnnotation(createDataProviderAnnotation(dataProviderExpr, nextDataVariableIndex)); whereBlock.getParent().getParent().getAst().addMethod(method); @@ -147,17 +143,17 @@ private Parameter createDataProcessorParameter() { // generates: arg = argMethodParam private void rewriteSimpleParameterization(BinaryExpression binExpr, ASTNode sourcePos) - throws InvalidSpecCompileException { + throws InvalidSpecCompileException { int nextDataVariableIndex = dataProcessorVars.size(); Parameter dataProcessorParameter = createDataProcessorParameter(); VariableExpression arg = (VariableExpression) binExpr.getLeftExpression(); VariableExpression dataVar = createDataProcessorVariable(arg, sourcePos); ExpressionStatement exprStat = new ExpressionStatement( - new DeclarationExpression( - dataVar, - Token.newSymbol(Types.ASSIGN, -1, -1), - new VariableExpression(dataProcessorParameter))); + new DeclarationExpression( + dataVar, + Token.newSymbol(Types.ASSIGN, -1, -1), + new VariableExpression(dataProcessorParameter))); exprStat.setSourcePosition(sourcePos); dataProcessorStats.add(exprStat); @@ -168,7 +164,7 @@ private void rewriteSimpleParameterization(BinaryExpression binExpr, ASTNode sou // arg0 = argMethodParam.getAt(0) // arg1 = argMethodParam.getAt(1) private void rewriteMultiParameterization(BinaryExpression binExpr, Statement enclosingStat) - throws InvalidSpecCompileException { + throws InvalidSpecCompileException { int nextDataVariableIndex = dataProcessorVars.size(); Parameter dataProcessorParameter = createDataProcessorParameter(); ListExpression list = (ListExpression) binExpr.getLeftExpression(); @@ -180,11 +176,11 @@ private void rewriteMultiParameterization(BinaryExpression binExpr, Statement en if (AstUtil.isWildcardRef(listElem)) continue; VariableExpression dataVar = createDataProcessorVariable(listElem, enclosingStat); ExpressionStatement exprStat = - new ExpressionStatement( - new DeclarationExpression( - dataVar, - Token.newSymbol(Types.ASSIGN, -1, -1), - createGetAtMethod(new VariableExpression(dataProcessorParameter), i))); + new ExpressionStatement( + new DeclarationExpression( + dataVar, + Token.newSymbol(Types.ASSIGN, -1, -1), + createGetAtMethod(new VariableExpression(dataProcessorParameter), i))); exprStat.setSourcePosition(enclosingStat); dataProcessorStats.add(exprStat); } @@ -193,15 +189,15 @@ private void rewriteMultiParameterization(BinaryExpression binExpr, Statement en } private void rewriteDerivedParameterization(BinaryExpression parameterization, Statement enclosingStat) - throws InvalidSpecCompileException { + throws InvalidSpecCompileException { VariableExpression dataVar = createDataProcessorVariable(parameterization.getLeftExpression(), enclosingStat); ExpressionStatement exprStat = - new ExpressionStatement( - new DeclarationExpression( - dataVar, - Token.newSymbol(Types.ASSIGN, -1, -1), - parameterization.getRightExpression())); + new ExpressionStatement( + new DeclarationExpression( + dataVar, + Token.newSymbol(Types.ASSIGN, -1, -1), + parameterization.getRightExpression())); exprStat.setSourcePosition(enclosingStat); dataProcessorStats.add(exprStat); @@ -247,7 +243,7 @@ private void turnIntoSimpleParameterization(List column) throws Inva VariableExpression varExpr = ObjectUtil.asInstance(column.get(0), VariableExpression.class); if (varExpr == null) throw new InvalidSpecCompileException(column.get(0), - "Header of data table may only contain variable names"); + "Header of data table may only contain variable names"); if (AstUtil.isWildcardRef(varExpr)) { // assertion: column has a wildcard header, but the method's // explicit parameter list does not have a wildcard parameter @@ -273,28 +269,28 @@ private void splitRow(Expression row, List 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; } private VariableExpression createDataProcessorVariable(Expression varExpr, ASTNode sourcePos) - throws InvalidSpecCompileException { + throws InvalidSpecCompileException { if (!(varExpr instanceof VariableExpression)) notAParameterization(sourcePos); - VariableExpression typedVarExpr = (VariableExpression)varExpr; + VariableExpression typedVarExpr = (VariableExpression) varExpr; verifyDataProcessorVariable(typedVarExpr); VariableExpression result = new VariableExpression(typedVarExpr.getName(), typedVarExpr.getType()); @@ -317,8 +313,8 @@ private void verifyDataProcessorVariable(VariableExpression varExpr) { if (whereBlock.getParent().getAst().getParameters().length > 0 && !(accessedVar instanceof Parameter)) { resources.getErrorReporter().error(varExpr, - "Data variable '%s' needs to be declared as method parameter", - varExpr.getName()); + "Data variable '%s' needs to be declared as method parameter", + varExpr.getName()); } } @@ -353,29 +349,29 @@ private void addFeatureParameters() { @SuppressWarnings("unchecked") private void createDataProcessorMethod() { if (dataProcessorVars.isEmpty()) return; - + dataProcessorStats.add( - new ReturnStatement( - new ArrayExpression( - ClassHelper.OBJECT_TYPE, - (List) dataProcessorVars))); + new ReturnStatement( + new ArrayExpression( + ClassHelper.OBJECT_TYPE, + (List) dataProcessorVars))); BlockStatement blockStat = new BlockStatement(dataProcessorStats, new VariableScope()); new DataProcessorVariableRewriter().visitBlockStatement(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)); + 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)); } private static void notAParameterization(ASTNode stat) throws InvalidSpecCompileException { throw new InvalidSpecCompileException(stat, -"where-blocks may only contain parameterizations (e.g. 'salary << [1000, 5000, 9000]; salaryk = salary / 1000')"); + "where-blocks may only contain parameterizations (e.g. 'salary << [1000, 5000, 9000]; salaryk = salary / 1000')"); } private class DataProcessorVariableRewriter extends ClassCodeVisitorSupport { diff --git a/spock-core/src/main/java/org/spockframework/runtime/BaseSpecRunner.java b/spock-core/src/main/java/org/spockframework/runtime/BaseSpecRunner.java index c8d13ae825..a4005eeb9f 100755 --- a/spock-core/src/main/java/org/spockframework/runtime/BaseSpecRunner.java +++ b/spock-core/src/main/java/org/spockframework/runtime/BaseSpecRunner.java @@ -17,13 +17,9 @@ package org.spockframework.runtime; import org.junit.runner.Description; - -import org.spockframework.runtime.extension.IMethodInterceptor; -import org.spockframework.runtime.extension.MethodInvocation; +import org.spockframework.runtime.extension.*; import org.spockframework.runtime.model.*; -import org.spockframework.util.CollectionUtil; -import org.spockframework.util.InternalSpockError; - +import org.spockframework.util.*; import spock.lang.Specification; import static org.spockframework.runtime.RunStatus.*; @@ -297,7 +293,7 @@ private IterationInfo createIterationInfo(Object[] dataValues, int estimatedNumI String iterationName = currentFeature.getIterationNameProvider().getName(result); result.setName(iterationName); Description description = Description.createTestDescription(spec.getReflection(), - iterationName, currentFeature.getFeatureMethod().getAnnotations()); + iterationName, currentFeature.getFeatureMethod().getAnnotations()); result.setDescription(description); return result; } @@ -467,7 +463,7 @@ private void invoke(Object target, MethodInfo method, Object... arguments) { // slow lane MethodInvocation invocation = new MethodInvocation(currentFeature, - currentIteration, sharedInstance, currentInstance, target, method, arguments); + currentIteration, sharedInstance, currentInstance, target, method, arguments); try { invocation.proceed(); } catch (Throwable t) { diff --git a/spock-core/src/main/java/org/spockframework/runtime/ParameterizedSpecRunner.java b/spock-core/src/main/java/org/spockframework/runtime/ParameterizedSpecRunner.java index 2f592499da..947b2fb79f 100755 --- a/spock-core/src/main/java/org/spockframework/runtime/ParameterizedSpecRunner.java +++ b/spock-core/src/main/java/org/spockframework/runtime/ParameterizedSpecRunner.java @@ -16,10 +16,11 @@ package org.spockframework.runtime; +import org.spockframework.runtime.model.*; + import java.util.*; import static org.spockframework.runtime.RunStatus.*; -import org.spockframework.runtime.model.*; /** * Adds the ability to run parameterized features. @@ -55,8 +56,8 @@ private Object[] createDataProviders() { MethodInfo method = dataProviderInfo.getDataProviderMethod(); Object[] arguments = Arrays.copyOf(dataProviders, getDataTableOffset(dataProviderInfo)); Object provider = invokeRaw(sharedInstance, method, arguments); - - if (runStatus != OK) + + if (runStatus != OK) return null; else if (provider == null) { SpockExecutionException error = new SpockExecutionException("Data provider is null!"); @@ -94,14 +95,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 SpockExecutionException("Data provider's iterator() method returned null"))); + 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; } @@ -140,7 +141,7 @@ private void runIterations(Iterator[] iterators, int estimatedNumIterations) { if (resetStatus(ITERATION) != OK) break; // no iterators => no data providers => only derived parameterizations => limit to one iteration - if(iterators.length == 0) break; + if (iterators.length == 0) break; } } @@ -165,13 +166,12 @@ 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; } @@ -179,15 +179,15 @@ else if (haveNext != hasNext) { } private SpockExecutionException createDifferentNumberOfDataValuesException(DataProviderInfo provider, - boolean hasNext) { + boolean hasNext) { String msg = String.format("Data provider for variable '%s' has %s values than previous data provider(s)", - provider.getDataVariables().get(0), hasNext ? "more" : "fewer"); + provider.getDataVariables().get(0), hasNext ? "more" : "fewer"); SpockExecutionException exception = new SpockExecutionException(msg); FeatureInfo feature = provider.getParent(); SpecInfo spec = feature.getParent(); StackTraceElement elem = new StackTraceElement(spec.getReflection().getName(), - feature.getName(), spec.getFilename(), provider.getLine()); - exception.setStackTrace(new StackTraceElement[] { elem }); + feature.getName(), spec.getFilename(), provider.getLine()); + exception.setStackTrace(new StackTraceElement[]{elem}); return exception; } @@ -201,15 +201,15 @@ 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); + return (Object[]) invokeRaw(sharedInstance, currentFeature.getDataProcessorMethod(), next); } catch (Throwable t) { runStatus = supervisor.error( - new ErrorInfo(currentFeature.getDataProcessorMethod(), t)); + new ErrorInfo(currentFeature.getDataProcessorMethod(), t)); return null; } } diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpecInfoBuilder.java b/spock-core/src/main/java/org/spockframework/runtime/SpecInfoBuilder.java index a59f881e58..d8cb63d62c 100755 --- a/spock-core/src/main/java/org/spockframework/runtime/SpecInfoBuilder.java +++ b/spock-core/src/main/java/org/spockframework/runtime/SpecInfoBuilder.java @@ -16,21 +16,15 @@ package org.spockframework.runtime; -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.*; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; +import org.junit.*; import org.spockframework.runtime.model.*; import org.spockframework.util.*; - import spock.lang.Specification; +import java.lang.annotation.Annotation; +import java.lang.reflect.*; +import java.util.*; + /** * Builds a SpecInfo from a Class instance. * diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/DataProviderMetadata.java b/spock-core/src/main/java/org/spockframework/runtime/model/DataProviderMetadata.java index b481745d17..b68356e1f3 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/model/DataProviderMetadata.java +++ b/spock-core/src/main/java/org/spockframework/runtime/model/DataProviderMetadata.java @@ -10,7 +10,7 @@ public @interface DataProviderMetadata { String LINE = "line"; String DATA_VARIABLES = "dataVariables"; - + int line(); String[] dataVariables(); } diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java index b5f935c714..5dc39f66ad 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java +++ b/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java @@ -1,12 +1,11 @@ package org.spockframework.runtime.model; -import java.lang.reflect.AnnotatedElement; -import java.util.ArrayList; -import java.util.List; - import org.spockframework.runtime.extension.IMethodInterceptor; import org.spockframework.util.Nullable; +import java.lang.reflect.AnnotatedElement; +import java.util.*; + /** * @author Peter Niederwieser */ @@ -57,7 +56,7 @@ public List getParameterNames() { public void addParameterName(String parameterName) { parameterNames.add(parameterName); } - + public List getDataVariables() { return parameterNames; // currently the same } @@ -109,7 +108,7 @@ public boolean isParameterized() { public boolean isReportIterations() { return reportIterations; } - + public void setReportIterations(boolean flag) { reportIterations = flag; } @@ -118,7 +117,7 @@ public void setReportIterations(boolean flag) { public NameProvider getIterationNameProvider() { return iterationNameProvider; } - + public void setIterationNameProvider(NameProvider provider) { iterationNameProvider = provider; } diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/IterationInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/IterationInfo.java index c38f0f110e..3c00b928b0 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/model/IterationInfo.java +++ b/spock-core/src/main/java/org/spockframework/runtime/model/IterationInfo.java @@ -15,8 +15,7 @@ package org.spockframework.runtime.model; import java.lang.reflect.AnnotatedElement; -import java.util.ArrayList; -import java.util.List; +import java.util.*; /** * Runtime information about an iteration of a feature method. @@ -57,7 +56,7 @@ public String getName() { * Return this iteration's data values for the ongoing execution of the * owning feature method. The names of the data values (in the same order) * are available through {@link FeatureInfo#getDataVariables}. - * + * * @return this iteration's data values for the ongoing execution of the * owning feature method */ diff --git a/spock-core/src/main/java/org/spockframework/util/CollectionUtil.java b/spock-core/src/main/java/org/spockframework/util/CollectionUtil.java index d5a8529efe..ea962770e4 100644 --- a/spock-core/src/main/java/org/spockframework/util/CollectionUtil.java +++ b/spock-core/src/main/java/org/spockframework/util/CollectionUtil.java @@ -56,7 +56,7 @@ public static List arrayToList(Object array) { public static @Nullable T getLastElement(List list) { Assert.that(list.size() > 0); - + return list.get(list.size() - 1); } @@ -144,7 +144,7 @@ public static Map mapOf(K key, V value, K key2, V value2, K key3, V public static Map filterNullValues(Map map) { Iterator iterator = map.entrySet().iterator(); - while(iterator.hasNext()) { + while (iterator.hasNext()) { Map.Entry next = iterator.next(); if (next.getValue() == null) { iterator.remove(); @@ -200,8 +200,8 @@ public void remove() { } public static boolean containsAny(Iterable iterable, Object... elements) { - for (Object curr: iterable) { - for (Object elem: elements) { + for (Object curr : iterable) { + for (Object elem : elements) { if (ObjectUtil.equals(curr, elem)) return true; } } @@ -216,4 +216,4 @@ public static int findIndexOf(Iterable iterable, IFunction> null expect: provider.getName(iteration) == "feature" } - + def "returns default if other provider blows up"() { other.getName(iteration) >> { throw new RuntimeException() } expect: provider.getName(iteration) == "feature" } - + def "iteration name defaults to feature name when iterations aren't reported"() { feature.reportIterations = false diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/Blocks.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/Blocks.groovy index 143a3eda12..864a9c132d 100644 --- a/spock-specs/src/test/groovy/org/spockframework/smoke/Blocks.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/Blocks.groovy @@ -17,12 +17,11 @@ package org.spockframework.smoke import org.spockframework.EmbeddedSpecification - import org.spockframework.compiler.InvalidSpecCompileException -import static org.spockframework.runtime.model.BlockKind.* -import org.spockframework.runtime.RunContext import org.spockframework.runtime.SpecInfoBuilder +import static org.spockframework.runtime.model.BlockKind.* + class Blocks extends EmbeddedSpecification { def "labels and comments"() { def specClass = compiler.compileSpecBody(""" @@ -56,18 +55,18 @@ def m3() { expect: def m1 = specInfo.features[0] - m1.blocks*.kind == [SETUP,WHEN,THEN,WHERE] - m1.blocks*.texts.flatten() == ["setup","setup2","when","when2","then","then2","where","where2"] + m1.blocks*.kind == [SETUP, WHEN, THEN, WHERE] + m1.blocks*.texts.flatten() == ["setup", "setup2", "when", "when2", "then", "then2", "where", "where2"] and: def m2 = specInfo.features[1] m2.blocks*.kind == [EXPECT] - m2.blocks*.texts.flatten() == ["expect","expect2"] + m2.blocks*.texts.flatten() == ["expect", "expect2"] and: def m3 = specInfo.features[2] - m3.blocks*.kind == [SETUP,EXPECT,WHERE] - m3.blocks*.texts.flatten() == ["given","and",""] + m3.blocks*.kind == [SETUP, EXPECT, WHERE] + m3.blocks*.texts.flatten() == ["given", "and", ""] } def "unknown label"() { @@ -80,4 +79,4 @@ setuppp: def a = 1 InvalidSpecCompileException e = thrown() e.line == 1 } -} \ No newline at end of file +} diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataTables.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataTables.groovy index ecfaa5747c..0928d8df7e 100755 --- a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataTables.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataTables.groovy @@ -19,7 +19,6 @@ import org.junit.runners.model.MultipleFailureException import org.spockframework.EmbeddedSpecification import org.spockframework.compiler.InvalidSpecCompileException import org.spockframework.runtime.SpockExecutionException - import spock.lang.* class DataTables extends EmbeddedSpecification { @@ -117,7 +116,7 @@ a | local thrown(InvalidSpecCompileException) } - def "header variable names must not clash with each other"() { + def "header variable names must not clash with each other"() { when: compiler.compileFeatureBody """ expect: @@ -257,7 +256,7 @@ local | 1 @Unroll def 'a = #a, b = #b'() { expect: true - + where: a | b 0 | a + 1 @@ -273,7 +272,7 @@ local | 1 runner.runFeatureBody ''' expect: false - + where: a | b b | 1 @@ -288,7 +287,7 @@ local | 1 runner.runFeatureBody ''' expect: false - + where: a | b 1 | b + 1 @@ -326,21 +325,21 @@ local | 1 runner.runFeatureBody ''' expect: g == 12 - + where: a = 1 b = a + 1 - + c << [b + 1] - + d = c + 1 - + e | f b + c + d | e + 1 - + g << [f + 1] - - h = g + 1 + + h = g + 1 ''' } @@ -375,8 +374,8 @@ a | b | c a + b == c where: - a | b || c - 1 | 2 || 3 + a | b || c + 1 | 2 || 3 4 || 5 | 9 } diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/InvalidWhereBlocks.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/InvalidWhereBlocks.groovy index 4445d124ae..82d20c10c4 100644 --- a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/InvalidWhereBlocks.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/InvalidWhereBlocks.groovy @@ -145,8 +145,8 @@ def foo() { e.message.contains("@Shared") where: - rhs |_ - "[1, instanceField, 2]" |_ - "{ -> { -> [instanceField] }() }()" |_ + rhs | _ + "[1, instanceField, 2]" | _ + "{ -> { -> [instanceField] }() }()" | _ } -} \ No newline at end of file +} diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/ParameterizationScopes.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/ParameterizationScopes.groovy index cdfd65b9ec..e150dae853 100755 --- a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/ParameterizationScopes.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/ParameterizationScopes.groovy @@ -16,13 +16,14 @@ package org.spockframework.smoke.parameterization -import spock.lang.Issue -import spock.lang.Specification +import spock.lang.* class ParameterizationScopes extends Specification { def "in outer scope"() { - expect: x == 1 - where: x << 1 + expect: + x == 1 + where: + x << 1 } def "in inner scope"() { @@ -75,7 +76,9 @@ class ParameterizationScopes extends Specification { where: val1 = 2 - val2 = [1, 2, 3].findAll { it >= val1 } + val2 = [1, 2, 3].findAll { + it >= val1 + } } @Issue("http://issues.spockframework.org/detail?id=286") @@ -92,4 +95,4 @@ class ParameterizationScopes extends Specification { } } } -} \ No newline at end of file +} From 64dad793f7eacedfe9311cd435475e0bf725becf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pap=20L=C5=91rinc?= Date: Wed, 25 Nov 2015 21:56:19 +0200 Subject: [PATCH 2/5] Removed unused iteration number estimation methods --- .../runtime/BaseSpecRunner.java | 14 +++--- .../runtime/ParameterizedSpecRunner.java | 34 ++----------- .../runtime/model/IterationInfo.java | 18 +------ .../runtime/AsyncRunListenerSpec.groovy | 2 +- .../EstimatedNumberOfIterations.groovy | 48 ------------------- .../SafeIterationNameProviderSpec.groovy | 2 +- 6 files changed, 14 insertions(+), 104 deletions(-) delete mode 100755 spock-specs/src/test/groovy/org/spockframework/runtime/EstimatedNumberOfIterations.groovy diff --git a/spock-core/src/main/java/org/spockframework/runtime/BaseSpecRunner.java b/spock-core/src/main/java/org/spockframework/runtime/BaseSpecRunner.java index a4005eeb9f..04b28421d0 100755 --- a/spock-core/src/main/java/org/spockframework/runtime/BaseSpecRunner.java +++ b/spock-core/src/main/java/org/spockframework/runtime/BaseSpecRunner.java @@ -262,22 +262,22 @@ public void doRunFeature() { private void runSimpleFeature() { if (runStatus != OK) return; - initializeAndRunIteration(EMPTY_ARGS, 1); + initializeAndRunIteration(EMPTY_ARGS); resetStatus(ITERATION); } - protected void initializeAndRunIteration(Object[] dataValues, int estimatedNumIterations) { + protected void initializeAndRunIteration(Object[] dataValues) { if (runStatus != OK) return; createSpecInstance(false); runInitializer(); - runIteration(dataValues, estimatedNumIterations); + runIteration(dataValues); } - private void runIteration(Object[] dataValues, int estimatedNumIterations) { + private void runIteration(Object[] dataValues) { if (runStatus != OK) return; - currentIteration = createIterationInfo(dataValues, estimatedNumIterations); + currentIteration = createIterationInfo(dataValues); getSpecificationContext().setCurrentIteration(currentIteration); supervisor.beforeIteration(currentIteration); @@ -288,8 +288,8 @@ private void runIteration(Object[] dataValues, int estimatedNumIterations) { currentIteration = null; } - private IterationInfo createIterationInfo(Object[] dataValues, int estimatedNumIterations) { - IterationInfo result = new IterationInfo(currentFeature, dataValues, estimatedNumIterations); + private IterationInfo createIterationInfo(Object[] dataValues) { + IterationInfo result = new IterationInfo(currentFeature, dataValues); String iterationName = currentFeature.getIterationNameProvider().getName(result); result.setName(iterationName); Description description = Description.createTestDescription(spec.getReflection(), diff --git a/spock-core/src/main/java/org/spockframework/runtime/ParameterizedSpecRunner.java b/spock-core/src/main/java/org/spockframework/runtime/ParameterizedSpecRunner.java index 947b2fb79f..0b7bb390ef 100755 --- a/spock-core/src/main/java/org/spockframework/runtime/ParameterizedSpecRunner.java +++ b/spock-core/src/main/java/org/spockframework/runtime/ParameterizedSpecRunner.java @@ -37,9 +37,8 @@ protected void runParameterizedFeature() { if (runStatus != OK) return; Object[] dataProviders = createDataProviders(); - int numIterations = estimateNumIterations(dataProviders); Iterator[] iterators = createIterators(dataProviders); - runIterations(iterators, numIterations); + runIterations(iterators); closeDataProviders(dataProviders); } @@ -109,35 +108,11 @@ private Iterator[] createIterators(Object[] dataProviders) { return iterators; } - // -1 => unknown - private int estimateNumIterations(Object[] dataProviders) { - if (runStatus != OK) return -1; - if (dataProviders.length == 0) return 1; - - int result = Integer.MAX_VALUE; - for (Object prov : dataProviders) { - if (prov instanceof Iterator) - // unbelievably, DGM provides a size() method for Iterators, - // although it is of course destructive (i.e. it exhausts the Iterator) - continue; - - Object rawSize = GroovyRuntimeUtil.invokeMethodQuietly(prov, "size"); - if (!(rawSize instanceof Number)) continue; - - int size = ((Number) rawSize).intValue(); - if (size < 0 || size >= result) continue; - - result = size; - } - - return result == Integer.MAX_VALUE ? -1 : result; - } - - private void runIterations(Iterator[] iterators, int estimatedNumIterations) { + private void runIterations(Iterator[] iterators) { if (runStatus != OK) return; while (haveNext(iterators)) { - initializeAndRunIteration(nextArgs(iterators), estimatedNumIterations); + initializeAndRunIteration(nextArgs(iterators)); if (resetStatus(ITERATION) != OK) break; // no iterators => no data providers => only derived parameterizations => limit to one iteration @@ -146,8 +121,7 @@ private void runIterations(Iterator[] iterators, int estimatedNumIterations) { } private void closeDataProviders(Object[] dataProviders) { - if (action(runStatus) == ABORT) return; - if (dataProviders == null) return; // there was an error creating the providers + if ((action(runStatus) != OK) || (dataProviders == null)) return; for (Object provider : dataProviders) { GroovyRuntimeUtil.invokeMethodQuietly(provider, "close"); diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/IterationInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/IterationInfo.java index 3c00b928b0..152f67c6bc 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/model/IterationInfo.java +++ b/spock-core/src/main/java/org/spockframework/runtime/model/IterationInfo.java @@ -22,13 +22,11 @@ */ public class IterationInfo extends NodeInfo { private final Object[] dataValues; - private final int estimatedNumIterations; private final List cleanups = new ArrayList(); - public IterationInfo(FeatureInfo feature, Object[] dataValues, int estimatedNumIterations) { + public IterationInfo(FeatureInfo feature, Object[] dataValues) { setParent(feature); this.dataValues = dataValues; - this.estimatedNumIterations = estimatedNumIterations; } public FeatureInfo getFeature() { @@ -64,20 +62,6 @@ public Object[] getDataValues() { return dataValues; } - /** - * Returns the estimated total number of iterations for the ongoing execution - * of the owning feature method. The value is obtained by calling - * size() on each data provider before the first iteration is run. - * It is only an estimate and won't change during feature execution (i.e. all - * FeatureInfos will return the same value). - * - * @return the estimated total number of iterations for the execution - * of the owning feature method - */ - public int getEstimatedNumIterations() { - return estimatedNumIterations; - } - public void addCleanup(Runnable cleanup) { cleanups.add(cleanup); } diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/AsyncRunListenerSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/AsyncRunListenerSpec.groovy index d1b217f27f..8aa7b366f1 100644 --- a/spock-specs/src/test/groovy/org/spockframework/runtime/AsyncRunListenerSpec.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/runtime/AsyncRunListenerSpec.groovy @@ -10,7 +10,7 @@ class AsyncRunListenerSpec extends Specification { def "replays events in correct order in a separate thread"() { def specInfo = new SpecInfoBuilder(getClass()).build() def featureInfo = specInfo.features[0] - def iterationInfo = new IterationInfo(featureInfo, [] as Object[], 1) + def iterationInfo = new IterationInfo(featureInfo, [] as Object[]) def errorInfo = new ErrorInfo(featureInfo.featureMethod, new Exception()) when: diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/EstimatedNumberOfIterations.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/EstimatedNumberOfIterations.groovy deleted file mode 100755 index a6e1633727..0000000000 --- a/spock-specs/src/test/groovy/org/spockframework/runtime/EstimatedNumberOfIterations.groovy +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2009 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.spockframework.runtime - -import spock.lang.Specification - -class EstimatedNumberOfIterations extends Specification { - def runner = new ParameterizedSpecRunner(null, null) - - def "w/o data provider"() { - expect: "estimation is 1" - runner.estimateNumIterations(new Object[0]) == 1 - } - - def "w/ data provider that doesn't respond to size"() { - expect: "estimation is 'unknown', represented as -1" - runner.estimateNumIterations([1] as Object[]) == -1 - } - - def "w/ data provider that responds to size"() { - expect: "estimation is size" - runner.estimateNumIterations([[1, 2, 3]] as Object[]) == 3 - } - - def "w/ multiple data providers, all of which respond to size"() { - expect: "estimation is minimum" - runner.estimateNumIterations([[1], [1, 2], [1, 2, 3]] as Object[]) == 1 - } - - def "w/ multiple data providers, one of which doesn't respond to size"() { - expect: "estimation is minimum of others" - runner.estimateNumIterations([1, [1, 2], [1, 2, 3]] as Object[]) == 2 - } -} \ No newline at end of file diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/SafeIterationNameProviderSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/SafeIterationNameProviderSpec.groovy index 7e4e110009..a779dd5335 100644 --- a/spock-specs/src/test/groovy/org/spockframework/runtime/SafeIterationNameProviderSpec.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/runtime/SafeIterationNameProviderSpec.groovy @@ -21,7 +21,7 @@ import spock.lang.Specification class SafeIterationNameProviderSpec extends Specification { def feature = new FeatureInfo() - def iteration = new IterationInfo(feature, [] as Object[], 3) + def iteration = new IterationInfo(feature, [] as Object[]) def other = Mock(NameProvider) def provider = new SafeIterationNameProvider(other) From ae5e680ef6006ae81551ea9b81689dcd7a2856d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pap=20L=C5=91rinc?= Date: Mon, 30 Nov 2015 14:31:32 +0200 Subject: [PATCH 3/5] WhereBlockRewriter based cleanup Refactored WhereBlockRewriter to prepare it for making the derived parameterization a data provider also --- .../org/spockframework/compiler/AstUtil.java | 4 +- .../spockframework/compiler/SpecRewriter.java | 3 +- .../compiler/WhereBlockRewriter.java | 247 +++++++++--------- .../runtime/BaseSpecRunner.java | 17 +- .../runtime/ParameterizedSpecRunner.java | 102 ++++---- .../runtime/SpecInfoBuilder.java | 1 + .../runtime/model/DataProviderInfo.java | 9 + .../runtime/model/DataProviderMetadata.java | 2 + .../runtime/model/FeatureInfo.java | 7 + .../spockframework/util/CollectionUtil.java | 17 +- .../util/InternalIdentifiers.java | 3 +- .../runtime/ClosingOfDataProviders.groovy | 11 +- 12 files changed, 228 insertions(+), 195 deletions(-) diff --git a/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java b/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java index b1ba243b77..492a23e85b 100755 --- a/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java +++ b/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java @@ -312,10 +312,10 @@ public static void fixUpLocalVariables(Variable[] localVariables, VariableScope fixUpLocalVariables(Arrays.asList(localVariables), scope, isClosureScope); } - public static MethodCallExpression createGetAtMethod(Expression expression, int index) { + public static MethodCallExpression createGetAtMethod(Expression expression, Expression index) { return new MethodCallExpression(expression, GET_AT_METHOD_NAME, - new ConstantExpression(index)); + index); } /** diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java index 3d1874108a..b947eea52f 100755 --- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java +++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java @@ -256,7 +256,8 @@ private void transplantMethod(Method method) { } private String createInternalName(FeatureMethod feature) { - return String.format("$spock_feature_%d_%d", specDepth, feature.getOrdinal()); + return String.format("%s_%d_%d", + InternalIdentifiers.FEATURE_METHOD, specDepth, feature.getOrdinal()); } private MethodNode copyMethod(MethodNode method, String newName) { diff --git a/spock-core/src/main/java/org/spockframework/compiler/WhereBlockRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/WhereBlockRewriter.java index 3deb751a34..1837f3c964 100755 --- a/spock-core/src/main/java/org/spockframework/compiler/WhereBlockRewriter.java +++ b/spock-core/src/main/java/org/spockframework/compiler/WhereBlockRewriter.java @@ -23,12 +23,13 @@ import org.codehaus.groovy.syntax.*; import org.objectweb.asm.Opcodes; import org.spockframework.compiler.model.WhereBlock; -import org.spockframework.runtime.model.DataProviderMetadata; import org.spockframework.util.*; import java.util.*; import static org.spockframework.compiler.AstUtil.createGetAtMethod; +import static org.spockframework.runtime.model.DataProviderMetadata.*; +import static org.spockframework.util.CollectionUtil.toArray; /** * @author Peter Niederwieser @@ -39,12 +40,10 @@ public class WhereBlockRewriter { private final InstanceFieldAccessChecker instanceFieldAccessChecker; private int dataProviderCount = 0; - // parameters of the data processor method (one for each data provider) - private final List dataProcessorParams = new ArrayList(); - // statements of the data processor method (one for each parameterization variable) - private final List dataProcessorStats = new ArrayList(); - // parameterization variables of the data processor method - private final List dataProcessorVars = new ArrayList(); + + private final List dataProcessorParams = new ArrayList(); // parameters of the data processor method (one for each data provider) + private final List dataProcessorStats = new ArrayList(); // statements of the data processor method (one for each parameterization variable) + private final List dataProcessorVars = new ArrayList(); // parameterization variables of the data processor method // TODO unify with dataProcessorStats private WhereBlockRewriter(WhereBlock whereBlock, IRewriteResources resources) { this.whereBlock = whereBlock; @@ -57,8 +56,7 @@ public static void rewrite(WhereBlock block, IRewriteResources resources) { } private void rewrite() { - ListIterator stats = whereBlock.getAst().listIterator(); - while (stats.hasNext()) + for (ListIterator stats = whereBlock.getAst().listIterator(); stats.hasNext(); ) try { rewriteWhereStat(stats); } catch (InvalidSpecCompileException e) { @@ -78,128 +76,134 @@ private void rewriteWhereStat(ListIterator stats) throws InvalidSpecC int type = binExpr.getOperation().getType(); - if (type == Types.LEFT_SHIFT) { - Expression leftExpr = binExpr.getLeftExpression(); - if (leftExpr instanceof VariableExpression) - rewriteSimpleParameterization(binExpr, stat); - else if (leftExpr instanceof ListExpression) - rewriteMultiParameterization(binExpr, stat); - else - notAParameterization(stat); - } else if (type == Types.ASSIGN) - rewriteDerivedParameterization(binExpr, stat); - else if (getOrExpression(binExpr) != null) { + if (type == Types.LEFT_SHIFT) + rewriteDataPipeParameterization(stat, binExpr); // e.g. `a << [1]` + else if (type == Types.ASSIGN) + rewriteDerivedParameterization(stat, binExpr); // e.g. `a = 1` + else if (isOrExpression(type)) { stats.previous(); - rewriteTableLikeParameterization(stats); - } else - notAParameterization(stat); + rewriteTableLikeParameterization(stats); // e.g. a | b \n 0 | 1 + } else notAParameterization(stat); } - private void createDataProviderMethod(Expression dataProviderExpr, int nextDataVariableIndex) { + private void rewriteDataPipeParameterization(ASTNode parent, BinaryExpression binExpr) throws InvalidSpecCompileException { + Expression leftExpr = binExpr.getLeftExpression(); + if (leftExpr instanceof VariableExpression) + rewriteSimpleParameterization(parent, binExpr); // e.g. a << [1,2] + else if (leftExpr instanceof ListExpression) + rewriteMultiParameterization(parent, binExpr); // e.g. [a,b] << [1,2] + else notAParameterization(parent); + } + + private void createDataProviderMethod(Expression dataProviderExpr, int nextDataVariableIndex, boolean isCollection) { instanceFieldAccessChecker.check(dataProviderExpr); dataProviderExpr = dataProviderExpr.transformExpression(new DataTablePreviousVariableTransformer()); - MethodNode method = - new MethodNode( - InternalIdentifiers.getDataProviderName(whereBlock.getParent().getAst().getName(), dataProviderCount++), - Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, - ClassHelper.OBJECT_TYPE, - getPreviousParameters(nextDataVariableIndex), - ClassNode.EMPTY_ARRAY, - new BlockStatement( - Arrays.asList( - new ReturnStatement( - new ExpressionStatement(dataProviderExpr))), - new VariableScope())); - - method.addAnnotation(createDataProviderAnnotation(dataProviderExpr, nextDataVariableIndex)); + MethodNode method = new MethodNode( + InternalIdentifiers.getDataProviderName(whereBlock.getParent().getAst().getName(), dataProviderCount++), + Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, + ClassHelper.DYNAMIC_TYPE, + getParameters(nextDataVariableIndex), + ClassNode.EMPTY_ARRAY, + new BlockStatement(Arrays.asList(new ExpressionStatement(dataProviderExpr)), new VariableScope()) + ); + + method.addAnnotation(createDataProviderAnnotation(dataProviderExpr, nextDataVariableIndex, isCollection)); whereBlock.getParent().getParent().getAst().addMethod(method); } - private Parameter[] getPreviousParameters(int nextDataVariableIndex) { - Parameter[] results = new Parameter[nextDataVariableIndex]; + /** + * every data provider depends on the previous data providers + */ + private Parameter[] getParameters(int nextDataVariableIndex) { + List results = new ArrayList(); + for (int i = 0; i < nextDataVariableIndex; i++) - results[i] = new Parameter(ClassHelper.DYNAMIC_TYPE, - dataProcessorVars.get(i).getName()); - return results; + results.add(new Parameter(ClassHelper.DYNAMIC_TYPE, + dataProcessorVars.get(i).getName())); + + return toArray(results, Parameter.class); } - private AnnotationNode createDataProviderAnnotation(Expression dataProviderExpr, int nextDataVariableIndex) { + private AnnotationNode createDataProviderAnnotation(Expression dataProviderExpr, int nextDataVariableIndex, boolean isCollection) { AnnotationNode ann = new AnnotationNode(resources.getAstNodeCache().DataProviderMetadata); - ann.addMember(DataProviderMetadata.LINE, new ConstantExpression(dataProviderExpr.getLineNumber())); + + ann.addMember(LINE, new ConstantExpression(dataProviderExpr.getLineNumber())); + ann.addMember(IS_COLLECTION, new ConstantExpression(isCollection)); + List dataVariableNames = new ArrayList(); for (int i = nextDataVariableIndex; i < dataProcessorVars.size(); i++) dataVariableNames.add(new ConstantExpression(dataProcessorVars.get(i).getName())); - ann.addMember(DataProviderMetadata.DATA_VARIABLES, new ListExpression(dataVariableNames)); + + ann.addMember(DATA_VARIABLES, new ListExpression(dataVariableNames)); + return ann; } - private Parameter createDataProcessorParameter() { + private VariableExpression createDataProcessorVariable() { Parameter p = new Parameter(ClassHelper.DYNAMIC_TYPE, "$spock_p" + dataProcessorParams.size()); dataProcessorParams.add(p); - return p; + return new VariableExpression(p); } // generates: arg = argMethodParam - private void rewriteSimpleParameterization(BinaryExpression binExpr, ASTNode sourcePos) - throws InvalidSpecCompileException { + private void rewriteSimpleParameterization(ASTNode parent, BinaryExpression binExpr) throws InvalidSpecCompileException { int nextDataVariableIndex = dataProcessorVars.size(); - Parameter dataProcessorParameter = createDataProcessorParameter(); - VariableExpression arg = (VariableExpression) binExpr.getLeftExpression(); - - VariableExpression dataVar = createDataProcessorVariable(arg, sourcePos); - ExpressionStatement exprStat = new ExpressionStatement( - new DeclarationExpression( - dataVar, - Token.newSymbol(Types.ASSIGN, -1, -1), - new VariableExpression(dataProcessorParameter))); - exprStat.setSourcePosition(sourcePos); - dataProcessorStats.add(exprStat); - createDataProviderMethod(binExpr.getRightExpression(), nextDataVariableIndex); + addDataProcessorStat( + parent, + binExpr.getLeftExpression(), + createDataProcessorVariable() + ); + + createDataProviderMethod(binExpr.getRightExpression(), nextDataVariableIndex, true); } // generates: // arg0 = argMethodParam.getAt(0) // arg1 = argMethodParam.getAt(1) - private void rewriteMultiParameterization(BinaryExpression binExpr, Statement enclosingStat) - throws InvalidSpecCompileException { + private void rewriteMultiParameterization(ASTNode parent, BinaryExpression binExpr) throws InvalidSpecCompileException { int nextDataVariableIndex = dataProcessorVars.size(); - Parameter dataProcessorParameter = createDataProcessorParameter(); - ListExpression list = (ListExpression) binExpr.getLeftExpression(); - @SuppressWarnings("unchecked") - List listElems = list.getExpressions(); + VariableExpression dataProcessorVariable = createDataProcessorVariable(); + List listElems = ((ListExpression) binExpr.getLeftExpression()).getExpressions(); + for (int i = 0; i < listElems.size(); i++) { - Expression listElem = listElems.get(i); - if (AstUtil.isWildcardRef(listElem)) continue; - VariableExpression dataVar = createDataProcessorVariable(listElem, enclosingStat); - ExpressionStatement exprStat = - new ExpressionStatement( - new DeclarationExpression( - dataVar, - Token.newSymbol(Types.ASSIGN, -1, -1), - createGetAtMethod(new VariableExpression(dataProcessorParameter), i))); - exprStat.setSourcePosition(enclosingStat); - dataProcessorStats.add(exprStat); + addDataProcessorStat( + parent, + listElems.get(i), + createGetAtMethod(dataProcessorVariable, new ConstantExpression(i)) + ); } - createDataProviderMethod(binExpr.getRightExpression(), nextDataVariableIndex); + createDataProviderMethod(binExpr.getRightExpression(), nextDataVariableIndex, true); } - private void rewriteDerivedParameterization(BinaryExpression parameterization, Statement enclosingStat) - throws InvalidSpecCompileException { - VariableExpression dataVar = createDataProcessorVariable(parameterization.getLeftExpression(), enclosingStat); + /* TODO make this a data provider also (see tests in DataTables) + * TODO IS_COLLECTION -> false + * TODO in case there are no collection data providers, it should iterate only once + * TODO should be recomputed for every iteration (can depend on previous data provider values) + * TODO inner closures should be able to reference previous data values also + * TODO other data providers should be able to reference this value + */ + private void rewriteDerivedParameterization(ASTNode parent, BinaryExpression binExpr) throws InvalidSpecCompileException { + addDataProcessorStat( + parent, + binExpr.getLeftExpression(), + binExpr.getRightExpression() + ); + } - ExpressionStatement exprStat = - new ExpressionStatement( - new DeclarationExpression( - dataVar, - Token.newSymbol(Types.ASSIGN, -1, -1), - parameterization.getRightExpression())); + private void addDataProcessorStat(ASTNode parent, Expression left, Expression right) throws InvalidSpecCompileException { + if (AstUtil.isWildcardRef(left)) return; - exprStat.setSourcePosition(enclosingStat); + ExpressionStatement exprStat = new ExpressionStatement(new DeclarationExpression( + createDataProcessorVariable(left, parent), + Token.newSymbol(Types.ASSIGN, -1, -1), + right) + ); + exprStat.setSourcePosition(parent); dataProcessorStats.add(exprStat); } @@ -257,7 +261,7 @@ private void turnIntoSimpleParameterization(List column) throws Inva // unlikely to make it into a compile error, because header variable has already been checked, and the // assignment itself is unlikely to cause a compile error. (It's more likely that the rval causes a // compile error, but the rval's source position is retained.) - rewriteSimpleParameterization(binExpr, varExpr); + rewriteSimpleParameterization(varExpr, binExpr); } private void splitRow(Expression row, List parts) { @@ -275,18 +279,20 @@ private BinaryExpression getOrExpression(Statement stat) { return getOrExpression(expr); } - private BinaryExpression getOrExpression(Expression expr) { + private @Nullable 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; + if (isOrExpression(binExpr.getOperation().getType())) return binExpr; + else return null; + } - return null; + private boolean isOrExpression(int binExprType) { + return (binExprType == Types.BITWISE_OR) + || (binExprType == Types.LOGICAL_OR); } - private VariableExpression createDataProcessorVariable(Expression varExpr, ASTNode sourcePos) - throws InvalidSpecCompileException { + private VariableExpression createDataProcessorVariable(Expression varExpr, ASTNode sourcePos) throws InvalidSpecCompileException { if (!(varExpr instanceof VariableExpression)) notAParameterization(sourcePos); @@ -320,7 +326,7 @@ private void verifyDataProcessorVariable(VariableExpression varExpr) { private boolean isDataProcessorVariable(String name) { for (VariableExpression var : dataProcessorVars) - if (var.getName().equals(name)) + if (name.equals(var.getName())) return true; return false; } @@ -329,8 +335,7 @@ private void handleFeatureParameters() { Parameter[] parameters = whereBlock.getParent().getAst().getParameters(); if (parameters.length == 0) addFeatureParameters(); - else - checkAllParametersAreDataVariables(parameters); + else checkAllParametersAreDataVariables(parameters); } private void checkAllParametersAreDataVariables(Parameter[] parameters) { @@ -341,8 +346,10 @@ private void checkAllParametersAreDataVariables(Parameter[] parameters) { private void addFeatureParameters() { Parameter[] parameters = new Parameter[dataProcessorVars.size()]; + for (int i = 0; i < dataProcessorVars.size(); i++) parameters[i] = new Parameter(ClassHelper.DYNAMIC_TYPE, dataProcessorVars.get(i).getName()); + whereBlock.getParent().getAst().setParameters(parameters); } @@ -350,30 +357,31 @@ private void addFeatureParameters() { private void createDataProcessorMethod() { if (dataProcessorVars.isEmpty()) return; - dataProcessorStats.add( - new ReturnStatement( - new ArrayExpression( - ClassHelper.OBJECT_TYPE, - (List) dataProcessorVars))); + dataProcessorStats.add(new ReturnStatement(new ArrayExpression(ClassHelper.DYNAMIC_TYPE, (List) dataProcessorVars))); BlockStatement blockStat = new BlockStatement(dataProcessorStats, new VariableScope()); new DataProcessorVariableRewriter().visitBlockStatement(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)); + whereBlock.getParent().getParent().getAst().addMethod(new MethodNode( + InternalIdentifiers.getDataProcessorName(whereBlock.getParent().getAst().getName()), + Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, + ClassHelper.OBJECT_TYPE, + toArray(dataProcessorParams, Parameter.class), + ClassNode.EMPTY_ARRAY, + blockStat) + ); } private static void notAParameterization(ASTNode stat) throws InvalidSpecCompileException { throw new InvalidSpecCompileException(stat, - "where-blocks may only contain parameterizations (e.g. 'salary << [1000, 5000, 9000]; salaryk = salary / 1000')"); + "where-blocks may only contain parameterizations" + + " (e.g. 'salary << [1000, 5000, 9000];" + + " salaryk = salary / 1000')"); } + /** + * TODO should be generalized for all data providers (see: DataTables#'in closure scope of data provider') + */ private class DataProcessorVariableRewriter extends ClassCodeVisitorSupport { @Override protected SourceUnit getSourceUnit() { @@ -393,6 +401,12 @@ public void visitBlockStatement(BlockStatement stat) { } } + /** + * Transformer, used to access previous cell references from Data Tables + * Only works for random access collections (e.g. lists), i.e. those that have a getAt method. + * Iterables wouldn't work, since the same data provider can be accessed multiple times + * and Iterables don't have a 'current' method, just a 'next'. + */ private class DataTablePreviousVariableTransformer extends ClassCodeExpressionTransformer { private int depth = 0, rowIndex = 0; @@ -401,9 +415,8 @@ private class DataTablePreviousVariableTransformer extends ClassCodeExpressionTr @Override public Expression transform(Expression expression) { - if ((expression instanceof VariableExpression) && isDataProcessorVariable(expression.getText())) { - return AstUtil.createGetAtMethod(expression, rowIndex); - } + if ((expression instanceof VariableExpression) && isDataProcessorVariable(expression.getText())) + return createGetAtMethod(expression, new ConstantExpression(rowIndex)); depth++; Expression transform = super.transform(expression); diff --git a/spock-core/src/main/java/org/spockframework/runtime/BaseSpecRunner.java b/spock-core/src/main/java/org/spockframework/runtime/BaseSpecRunner.java index 04b28421d0..a9eba930bd 100755 --- a/spock-core/src/main/java/org/spockframework/runtime/BaseSpecRunner.java +++ b/spock-core/src/main/java/org/spockframework/runtime/BaseSpecRunner.java @@ -22,6 +22,9 @@ import org.spockframework.util.*; import spock.lang.Specification; +import java.util.List; + +import static java.util.Collections.emptyList; import static org.spockframework.runtime.RunStatus.*; /** @@ -32,8 +35,6 @@ * @author Peter Niederwieser */ public class BaseSpecRunner { - protected static final Object[] EMPTY_ARGS = new Object[0]; - protected final SpecInfo spec; protected final IRunSupervisor supervisor; @@ -262,19 +263,19 @@ public void doRunFeature() { private void runSimpleFeature() { if (runStatus != OK) return; - initializeAndRunIteration(EMPTY_ARGS); + initializeAndRunIteration(emptyList()); resetStatus(ITERATION); } - protected void initializeAndRunIteration(Object[] dataValues) { - if (runStatus != OK) return; + protected void initializeAndRunIteration(@Nullable List dataValues) { + if ((dataValues == null) || (runStatus != OK)) return; createSpecInstance(false); runInitializer(); runIteration(dataValues); } - private void runIteration(Object[] dataValues) { + private void runIteration(List dataValues) { if (runStatus != OK) return; currentIteration = createIterationInfo(dataValues); @@ -288,8 +289,8 @@ private void runIteration(Object[] dataValues) { currentIteration = null; } - private IterationInfo createIterationInfo(Object[] dataValues) { - IterationInfo result = new IterationInfo(currentFeature, dataValues); + private IterationInfo createIterationInfo(List dataValues) { + IterationInfo result = new IterationInfo(currentFeature, dataValues.toArray()); String iterationName = currentFeature.getIterationNameProvider().getName(result); result.setName(iterationName); Description description = Description.createTestDescription(spec.getReflection(), diff --git a/spock-core/src/main/java/org/spockframework/runtime/ParameterizedSpecRunner.java b/spock-core/src/main/java/org/spockframework/runtime/ParameterizedSpecRunner.java index 0b7bb390ef..89f6c446d1 100755 --- a/spock-core/src/main/java/org/spockframework/runtime/ParameterizedSpecRunner.java +++ b/spock-core/src/main/java/org/spockframework/runtime/ParameterizedSpecRunner.java @@ -18,8 +18,10 @@ import org.spockframework.runtime.model.*; +import java.util.ArrayList; import java.util.*; +import static java.util.Arrays.*; import static org.spockframework.runtime.RunStatus.*; /** @@ -36,40 +38,34 @@ public ParameterizedSpecRunner(SpecInfo spec, IRunSupervisor supervisor) { protected void runParameterizedFeature() { if (runStatus != OK) return; - Object[] dataProviders = createDataProviders(); - Iterator[] iterators = createIterators(dataProviders); - runIterations(iterators); + List dataProviders = createDataProviders(); + runIterations(dataProviders); closeDataProviders(dataProviders); } - private Object[] createDataProviders() { + private List createDataProviders() { if (runStatus != OK) return null; - List dataProviderInfos = currentFeature.getDataProviders(); - Object[] dataProviders = new Object[dataProviderInfos.size()]; + List dataProviders = new ArrayList(); + for (DataProviderInfo dataProviderInfo : currentFeature.getDataProviders()) { + MethodInfo method = dataProviderInfo.getDataProviderMethod(); + Object[] arguments = copyOf(dataProviders.toArray(), getDataTableOffset(dataProviderInfo)); + Object provider = invokeRaw(sharedInstance, method, arguments); - 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) - 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; + 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.add(provider); } return dataProviders; } + /* TODO remove when all *where* elements are data providers */ private int getDataTableOffset(DataProviderInfo dataProviderInfo) { int result = 0; for (String variableName : dataProviderInfo.getDataVariables()) { @@ -85,57 +81,54 @@ private int getDataTableOffset(DataProviderInfo dataProviderInfo) { currentFeature.getParameterNames())); } - private Iterator[] createIterators(Object[] dataProviders) { + private List createIterators(List dataProviders) { if (runStatus != OK) return null; - Iterator[] iterators = new Iterator[dataProviders.length]; - for (int i = 0; i < dataProviders.length; i++) + List iterators = new ArrayList(); + for (int i = 0; i < dataProviders.size(); i++) try { - Iterator iter = GroovyRuntimeUtil.asIterator(dataProviders[i]); + Iterator iter = GroovyRuntimeUtil.asIterator(dataProviders.get(i)); if (iter == null) { - runStatus = supervisor.error( - new ErrorInfo(currentFeature.getDataProviders().get(i).getDataProviderMethod(), - new SpockExecutionException("Data provider's iterator() method returned null"))); + runStatus = supervisor.error(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)); + iterators.add(iter); + } catch (RuntimeException e) { + runStatus = supervisor.error(new ErrorInfo(currentFeature.getDataProviders().get(i).getDataProviderMethod(), e)); return null; } return iterators; } - private void runIterations(Iterator[] iterators) { + private void runIterations(List dataProviders) { if (runStatus != OK) return; - while (haveNext(iterators)) { + for (List iterators = createIterators(dataProviders); haveNext(iterators); ) { initializeAndRunIteration(nextArgs(iterators)); if (resetStatus(ITERATION) != OK) break; // no iterators => no data providers => only derived parameterizations => limit to one iteration - if (iterators.length == 0) break; + if (iterators.isEmpty()) break; } } - private void closeDataProviders(Object[] dataProviders) { + private void closeDataProviders(List dataProviders) { if ((action(runStatus) != OK) || (dataProviders == null)) return; - for (Object provider : dataProviders) { + for (Object provider : dataProviders) GroovyRuntimeUtil.invokeMethodQuietly(provider, "close"); - } } - private boolean haveNext(Iterator[] iterators) { + private boolean haveNext(List iterators) { if (runStatus != OK) return false; boolean haveNext = true; - for (int i = 0; i < iterators.length; i++) + for (int i = 0; i < iterators.size(); i++) try { - boolean hasNext = iterators[i].hasNext(); + boolean hasNext = iterators.get(i).hasNext(); if (i == 0) haveNext = hasNext; else if (haveNext != hasNext) { DataProviderInfo provider = currentFeature.getDataProviders().get(i); @@ -143,9 +136,8 @@ else if (haveNext != hasNext) { createDifferentNumberOfDataValuesException(provider, hasNext))); return false; } - } catch (Throwable t) { - runStatus = supervisor.error( - new ErrorInfo(currentFeature.getDataProviders().get(i).getDataProviderMethod(), t)); + } catch (RuntimeException e) { + runStatus = supervisor.error(new ErrorInfo(currentFeature.getDataProviders().get(i).getDataProviderMethod(), e)); return false; } @@ -166,24 +158,22 @@ private SpockExecutionException createDifferentNumberOfDataValuesException(DataP } // advances iterators and computes args - private Object[] nextArgs(Iterator[] iterators) { + private List nextArgs(List iterators) { if (runStatus != OK) return null; - Object[] next = new Object[iterators.length]; - for (int i = 0; i < iterators.length; i++) + List next = new ArrayList(); + for (int i = 0; i < iterators.size(); i++) try { - next[i] = iterators[i].next(); - } catch (Throwable t) { - runStatus = supervisor.error( - new ErrorInfo(currentFeature.getDataProviders().get(i).getDataProviderMethod(), t)); + next.add(iterators.get(i).next()); + } catch (RuntimeException e) { + runStatus = supervisor.error(new ErrorInfo(currentFeature.getDataProviders().get(i).getDataProviderMethod(), e)); return null; } try { - return (Object[]) invokeRaw(sharedInstance, currentFeature.getDataProcessorMethod(), next); - } catch (Throwable t) { - runStatus = supervisor.error( - new ErrorInfo(currentFeature.getDataProcessorMethod(), t)); + return asList((Object[]) invokeRaw(sharedInstance, currentFeature.getDataProcessorMethod(), next.toArray())); + } catch (RuntimeException e) { + runStatus = supervisor.error(new ErrorInfo(currentFeature.getDataProcessorMethod(), e)); return null; } } diff --git a/spock-core/src/main/java/org/spockframework/runtime/SpecInfoBuilder.java b/spock-core/src/main/java/org/spockframework/runtime/SpecInfoBuilder.java index d8cb63d62c..f50e1ffb8f 100755 --- a/spock-core/src/main/java/org/spockframework/runtime/SpecInfoBuilder.java +++ b/spock-core/src/main/java/org/spockframework/runtime/SpecInfoBuilder.java @@ -174,6 +174,7 @@ private DataProviderInfo createDataProvider(FeatureInfo feature, MethodInfo meth provider.setParent(feature); provider.setLine(metadata.line()); provider.setDataVariables(Arrays.asList(metadata.dataVariables())); + provider.setCollection(metadata.isCollection()); provider.setDataProviderMethod(method); return provider; } diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/DataProviderInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/DataProviderInfo.java index 02b9f2568b..e0f8ed2a92 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/model/DataProviderInfo.java +++ b/spock-core/src/main/java/org/spockframework/runtime/model/DataProviderInfo.java @@ -11,6 +11,7 @@ public class DataProviderInfo extends NodeInfo { private List dataVariables; private MethodInfo dataProviderMethod; + private boolean isCollection; @Override public AnnotatedElement getReflection() { @@ -32,4 +33,12 @@ public MethodInfo getDataProviderMethod() { public void setDataProviderMethod(MethodInfo dataProviderMethod) { this.dataProviderMethod = dataProviderMethod; } + + public boolean isCollection() { + return isCollection; + } + + public void setCollection(boolean isCollection) { + this.isCollection = isCollection; + } } diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/DataProviderMetadata.java b/spock-core/src/main/java/org/spockframework/runtime/model/DataProviderMetadata.java index b68356e1f3..0fa49c3ecb 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/model/DataProviderMetadata.java +++ b/spock-core/src/main/java/org/spockframework/runtime/model/DataProviderMetadata.java @@ -10,7 +10,9 @@ public @interface DataProviderMetadata { String LINE = "line"; String DATA_VARIABLES = "dataVariables"; + String IS_COLLECTION = "isCollection"; int line(); String[] dataVariables(); + boolean isCollection(); } diff --git a/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java b/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java index 5dc39f66ad..9dee795410 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java +++ b/spock-core/src/main/java/org/spockframework/runtime/model/FeatureInfo.java @@ -137,4 +137,11 @@ public boolean hasBytecodeName(String name) { if (provider.getDataProviderMethod().hasBytecodeName(name)) return true; return false; } + + public boolean hasMultipleIterations() { + for (DataProviderInfo provider : dataProviders) + if (provider.isCollection()) + return true; + return false; + } } diff --git a/spock-core/src/main/java/org/spockframework/util/CollectionUtil.java b/spock-core/src/main/java/org/spockframework/util/CollectionUtil.java index ea962770e4..84b1f72f27 100644 --- a/spock-core/src/main/java/org/spockframework/util/CollectionUtil.java +++ b/spock-core/src/main/java/org/spockframework/util/CollectionUtil.java @@ -200,20 +200,25 @@ public void remove() { } public static boolean containsAny(Iterable iterable, Object... elements) { - for (Object curr : iterable) { - for (Object elem : elements) { - if (ObjectUtil.equals(curr, elem)) return true; - } - } + for (Object curr : iterable) + for (Object elem : elements) + if (ObjectUtil.equals(curr, elem)) + return true; return false; } public static int findIndexOf(Iterable iterable, IFunction predicate) { int index = 0; for (T elem : iterable) { - if (predicate.apply(elem)) return index; + if (predicate.apply(elem)) + return index; index++; } return -1; } + + public static T[] toArray(Collection elems, Class type) { + T[] results = (T[]) Array.newInstance(type, elems.size()); + return elems.toArray(results); + } } diff --git a/spock-core/src/main/java/org/spockframework/util/InternalIdentifiers.java b/spock-core/src/main/java/org/spockframework/util/InternalIdentifiers.java index 0f26af8382..c2b5dbac82 100644 --- a/spock-core/src/main/java/org/spockframework/util/InternalIdentifiers.java +++ b/spock-core/src/main/java/org/spockframework/util/InternalIdentifiers.java @@ -26,6 +26,7 @@ public class InternalIdentifiers { public static final String INITIALIZER_METHOD = "$spock_initializeFields"; public static final String SHARED_INITIALIZER_METHOD = "$spock_initializeSharedFields"; + public static final String FEATURE_METHOD = "$spock_feature"; public static final List INITIALIZER_METHODS = Arrays.asList(INITIALIZER_METHOD, SHARED_INITIALIZER_METHOD); @@ -46,7 +47,7 @@ public static String getDataProviderName(String featureName, int providerIndex) } public static boolean isFeatureMethodName(String name) { - return name.startsWith("$spock_feature"); + return name.startsWith(FEATURE_METHOD); } public static String getSharedFieldName(String baseName) { diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/ClosingOfDataProviders.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/ClosingOfDataProviders.groovy index 1a05bbeb51..3ef7147985 100755 --- a/spock-specs/src/test/groovy/org/spockframework/runtime/ClosingOfDataProviders.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/runtime/ClosingOfDataProviders.groovy @@ -18,6 +18,9 @@ package org.spockframework.runtime import spock.lang.Specification +import static java.util.Arrays.asList + +// TODO these should be tested through their public interface, i.e. a closable generator in the where block class ClosingOfDataProviders extends Specification { def runner = new ParameterizedSpecRunner(null, null) @@ -25,7 +28,7 @@ class ClosingOfDataProviders extends Specification { MyCloseable provider = Mock() when: - runner.closeDataProviders(provider) + runner.closeDataProviders(asList(provider)) then: 1 * provider.close() >> { action() } @@ -37,10 +40,10 @@ class ClosingOfDataProviders extends Specification { def "close multiple providers which potentially throw an exception"() { MyCloseable provider1 = Mock() - java.io.Closeable provider2 = Mock() + Closeable provider2 = Mock() when: - runner.closeDataProviders(provider1, provider2) + runner.closeDataProviders(asList(provider1, provider2)) then: 1 * provider1.close() >> { action() } @@ -54,4 +57,4 @@ class ClosingOfDataProviders extends Specification { interface MyCloseable { void close() -} \ No newline at end of file +} From 0120bde44e0a96f68518d68f03c27378932f80b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pap=20L=C5=91rinc?= Date: Mon, 30 Nov 2015 14:35:03 +0200 Subject: [PATCH 4/5] Added specifications for how derived data providers should behave --- .../smoke/parameterization/DataTables.groovy | 111 +++++++++++++++--- 1 file changed, 97 insertions(+), 14 deletions(-) diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataTables.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataTables.groovy index 0928d8df7e..a05436f170 100755 --- a/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataTables.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/parameterization/DataTables.groovy @@ -229,6 +229,23 @@ local | 1 thrown(MissingPropertyException) } + def "no data tables don't cause infinite loop"() { + expect: + a + 1 == b + + where: + a = 1 + b = 2 + } + + def 'generated data table'() { + expect: + runner.runFeatureBody ''' + expect: true + where: [a, b] << [[0, 1], [2, 3]].combinations() + ''' + } + def 'cells can reference previous cells'() { expect: [a, b, c] == [0, 1, 2] @@ -297,6 +314,28 @@ local | 1 thrown Exception } + def "in closure scope of derived data value"() { + expect: + runner.runFeatureBody ''' + expect: val2 == [2, 3] + + where: val1 = 2 + val2 = [1, 2, 3].findAll { it >= val1 } + ''' + } + + @PendingFeature + def "in closure scope of data provider"() { + expect: + runner.runFeatureBody ''' + expect: val2 == [2, 3] + + where: val1 << [1, 2] + val2 << [[1, 2, 3].findAll { it > val1 }, [1, 2, 3].findAll { it >= val1 }] + ''' + } + + def 'cell references are working with simple parameterization also'() { expect: c == 2 @@ -304,7 +343,7 @@ local | 1 where: a << [1, 2] b << [3, 4] - c << [b - a, b - a] + c << [b - a, 3 * a - b] } def 'data tables can be referenced from following variables'() { @@ -319,28 +358,72 @@ local | 1 c = b + 1 } - @Ignore - def 'data table elements can reference each other'() { + @PendingFeature + def 'assignments can refer to non-first data table row'() { expect: runner.runFeatureBody ''' - expect: - g == 12 + expect: c == d - where: - a = 1 - b = a + 1 + where: a << [1, 2] + b = a + 1 + c << [b + 1, b + 2] + d << [3, 5] + ''' + } - c << [b + 1] + @PendingFeature + def 'data tables can reference variable initializations'() { + expect: + runner.runFeatureBody ''' + expect: [a, b, c] == [1, 2, 3] - d = c + 1 + where: a = 1 + b || c + a + 1 || a + b + ''' + } - e | f - b + c + d | e + 1 + @PendingFeature + def 'data pipes can reference constants'() { + expect: + runner.runFeatureBody ''' + expect: CONST > 3 && b < 3 - g << [f + 1] + where: CONST = Math.PI + b << [CONST / 2, CONST / 3] + ''' + } - h = g + 1 + @PendingFeature + def 'data table elements can reference each other'() { + when: + def result = runner.runWithImports ''' + class Foo extends Specification { + @Unroll def test() { + expect: a + b + c + d + e + f + g + h == 53 + + where: a = 1 + b = a + 1 + + c << [b + 1, b + 2] + + d = c + 1 + + e | f + c + d + 1 | e + 1 + c + d - 1 | e - 1 + + g << [e + 1, f + 2] + + h = e + g + } + } ''' + + then: + result.runCount == 2 + result.failureCount == 0 + result.ignoreCount == 0 } def "rows must have same number of elements as header"() { From 77a46ed36b25ec445adaa36a659124935dc18eac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pap=20L=C5=91rinc?= Date: Mon, 30 Nov 2015 14:35:27 +0200 Subject: [PATCH 5/5] Updated the documentation --- docs/data_driven_testing.adoc | 65 +++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/docs/data_driven_testing.adoc b/docs/data_driven_testing.adoc index ce0b59c563..a349c29296 100644 --- a/docs/data_driven_testing.adoc +++ b/docs/data_driven_testing.adoc @@ -10,7 +10,7 @@ Suppose we want to specify the behavior of the +Math.max+ method: [source,groovy] ---- class MathSpec extends Specification { - def "maximum of two numbers"() { + def 'maximum of two numbers'() { expect: // exercise math method for a few different inputs Math.max(1, 3) == 3 @@ -35,7 +35,7 @@ hard-coded integer values: [source,groovy] ---- class MathSpec extends Specification { - def "maximum of two numbers"(int a, int b, int c) { + def 'maximum of two numbers'(int a, int b, int c) { expect: Math.max(a, b) == c @@ -55,7 +55,7 @@ Data tables are a convenient way to exercise a feature method with a fixed set o [source,groovy] ---- class Math extends Specification { - def "maximum of two numbers"(int a, int b, int c) { + def 'maximum of two numbers'(int a, int b, int c) { expect: Math.max(a, b) == c @@ -73,6 +73,15 @@ _table rows_, hold the corresponding values. For each row, the feature method wi _iteration_ of the method. If an iteration fails, the remaining iterations will nevertheless be executed. All failures will be reported. +Since version 1.1 data table cells can reference previous ones also (works for data pipes also), e.g. + +[source,groovy] +---- +where: +a | b +1 | a + 1 +---- + Data tables must have at least two columns. A single-column table can be written as: [source,groovy] @@ -104,17 +113,21 @@ spec, all of which can be kept in the same file. This achieves better isolation The previous code can be tweaked in a few ways. First, since the `where:` block already declares all data variables, the method parameters can be omitted.footnote:[The idea behind allowing method parameters is to enable better IDE support. -However, recent versions of IntelliJ IDEA recognize data variables automatically, and even infer their types from the -values contained in the data table.] +However, recent versions of IntelliJ IDEA recognize data variables automatically, and often infer their types from the +values contained in the data table. They might still be needed if you want `@CompileStatic` or `@TypeChecked` support.] + +Since version 1.1 you can also declare the types of the data table variables selectively (in their declaration order), +omitting the obvious ones. + Second, inputs and expected outputs can be separated with a double pipe symbol (`||`) to visually set them apart. With this, the code becomes: [source,groovy] ---- class DataDriven extends Specification { - def "maximum of two numbers"() { + def 'maximum of two numbers'() { expect: - Math.max(a, b) == c + max(a, b) == c where: a | b || c @@ -135,7 +148,7 @@ maximum of two numbers FAILED Condition not satisfied: -Math.max(a, b) == c +max(a, b) == c | | | | | | 7 0 | 7 42 false @@ -154,7 +167,7 @@ A method annotated with `@Unroll` will have its iterations reported independentl [source,groovy] ---- @Unroll -def "maximum of two numbers"() { ... } +def 'maximum of two numbers'() { ... } ---- .Why isn't `@Unroll` the default? @@ -171,7 +184,7 @@ Depending on the execution environment, the output will look something like: maximum of two numbers[0] PASSED maximum of two numbers[1] FAILED -Math.max(a, b) == c +max(a, b) == c | | | | | | 7 0 | 7 42 false @@ -184,7 +197,7 @@ This tells us that the second iteration (with index 1) failed. With a bit of eff [source,groovy] ---- @Unroll -def "maximum of #a and #b is #c"() { ... } +def 'maximum of #a and #b is #c'() { ... } ---- This method name uses placeholders, denoted by a leading hash sign (`#`), to refer to data variables `a`, `b`, @@ -194,7 +207,7 @@ and `c`. In the output, the placeholders will be replaced with concrete values: maximum of 3 and 5 is 5 PASSED maximum of 7 and 0 is 7 FAILED -Math.max(a, b) == c +max(a, b) == c | | | | | | 7 0 | 7 42 false @@ -218,14 +231,14 @@ one or more _data pipes_: ... where: a << [3, 7, 0] -b << [5, 0, 0] -c << [5, 7, 0] +b << [5, 0, 1] +c << [a+1, b+1, a+b] ---- A data pipe, indicated by the left-shift (`<<`) operator, connects a data variable to a _data provider_. The data provider holds all values for the variable, one per iteration. Any object that Groovy knows how to iterate over can be -used as a data provider. This includes objects of type `Collection`, `String`, `Iterable`, and objects implementing the -`Iterable` contract. Data providers don't necessarily have to _be_ the data (as in the case of a `Collection`); +used as a data provider. This includes objects implementing the `Iterable` contract. +Data providers don't necessarily have to _be_ the data (as in the case of a `Collection`); they can fetch data from external sources like text files, databases and spreadsheets, or generate data randomly. Data providers are queried for their next value only when needed (before the next iteration). @@ -237,12 +250,12 @@ but uses brackets instead of parentheses on the left-hand side: [source,groovy] ---- -@Shared sql = Sql.newInstance("jdbc:h2:mem:", "org.h2.Driver") +@Shared sql = Sql.newInstance('jdbc:h2:mem:', 'org.h2.Driver') -def "maximum of two numbers"() { +def 'maximum of two numbers'() { ... where: - [a, b, c] << sql.rows("select a, b, c from maxdata") + [a, b, c] << sql.rows('select a, b, c from maxdata') } ---- @@ -252,7 +265,7 @@ Data values that aren't of interest can be ignored with an underscore (`_`): ---- ... where: -[a, b, _, c] << sql.rows("select * from maxdata") +[a, b, _, c] << sql.rows('select * from maxdata') ---- == Data Variable Assignment @@ -265,7 +278,7 @@ A data variable can be directly assigned a value: where: a = 3 b = Math.random() * 100 -c = a > b ? a : b +c = (a > b) ? a : b ---- Assignments are re-evaluated for every iteration. As already shown above, the right-hand side of an assignment may refer @@ -275,7 +288,7 @@ to other data variables: ---- ... where: -row << sql.rows("select * from maxdata") +row << sql.rows('select * from maxdata') // pick apart columns a = row.a b = row.b @@ -325,8 +338,8 @@ following are valid method names: [source,groovy] ---- -def "#person is #person.age years old"() { ... } // property access -def "#person.name.toUpperCase()"() { ... } // zero-arg method call +def '#person is #person.age years old'() { ... } // property access +def '#person.name.toUpperCase()'() { ... } // zero-arg method call ---- Non-string values (like `#person` above) are converted to Strings according to Groovy semantics. @@ -336,14 +349,14 @@ The following are invalid method names: [source,groovy] ---- def "#person.name.split(' ')[1]" { ... } // cannot have method arguments -def "#person.age / 2" { ... } // cannot use operators +def '#person.age / 2' { ... } // cannot use operators ---- If necessary, additional data variables can be introduced to hold more complex expression: [source,groovy] ---- -def "#lastName"() { +def '#lastName'() { ... where: person << ...