diff --git a/docs/release_notes.adoc b/docs/release_notes.adoc index 4be8ecafb8..27d68d43c4 100644 --- a/docs/release_notes.adoc +++ b/docs/release_notes.adoc @@ -8,6 +8,7 @@ include::include.adoc[] === Enhancements +* Add support for statically compiled specifications: conditions, interactions, `old()`, and data tables referencing previous columns now work in specs or feature methods annotated with `@CompileStatic` or `@TypeChecked`, see <> spockPull:2397[] * Add support for `final` local variables in `where:` blocks, declared at their beginning and evaluated once per feature, scoped to the where-block spockIssue:138[] * Improve `TooManyInvocationsError` now reports unsatisfied interactions with argument mismatch details, making it easier to diagnose why invocations didn't match expected interactions spockPull:2315[] diff --git a/docs/spock_primer.adoc b/docs/spock_primer.adoc index 74d16d7e8a..42ad5f27d7 100644 --- a/docs/spock_primer.adoc +++ b/docs/spock_primer.adoc @@ -788,6 +788,35 @@ will be rendered as include::{snapshotdir}/primer/VerifyEachDocSpec/verifyEach_with_index_method.txt[] ---- +[[statically-compiled-specifications]] +== Statically Compiled Specifications + +Specifications can be compiled with Groovy's static compilation by annotating the specification class or individual +feature methods with `@CompileStatic` or `@TypeChecked`. All major features work under static compilation: conditions +including their failure rendering, exception conditions, interactions and stubbing, `old()`, `with`, `verifyAll`, and +data driven features. + +Since the static type checker analyzes the code after Spock has rewritten it, a few rules apply: + +* Mock creation must use a typed variable declaration, e.g. `List list = Mock()` or `List list = Mock(List)`. + With `def list = Mock(List)` the variable is typed as `Object`, so calls on it will not pass type checking. +* Data variables in `where:` blocks default to `Object`. Declare typed feature method parameters to use them + with methods or operators, e.g. `def "maximum of two numbers"(int a, int b, int c)`. +* Precondition closures of `@IgnoreIf` and `@Requires` need an explicitly typed parameter, e.g. + `@IgnoreIf({ PreconditionContext it -> it.os.windows })`. + +Some limitations remain: + +* Global Groovy mocks (e.g. `GroovySpy(Foo, global: true)`) rely on Groovy's meta-object protocol and therefore + cannot intercept calls made from statically compiled code. The real method is called instead. +* Dynamic language features, like dynamic method names or `methodMissing`, are not available, as in any + statically compiled Groovy code. +* Spock detects static compilation via the annotations. If static compilation is instead applied globally through + a compiler configuration script, Spock does not see it and conditions will fail to compile. +* Condition rendering can differ in minor ways. For example, the target class of a static method call like + `Math.max(a, b)` is not rendered, because statically compiled code never evaluates the receiver of a static + method call, so there is no value to record. + [[specifications-as-documentation]] == Specifications as Documentation 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 194101c631..9120c34b73 100644 --- a/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java +++ b/spock-core/src/main/java/org/spockframework/compiler/AstUtil.java @@ -322,6 +322,45 @@ public static boolean isJointCompiled(ClassNode clazz) { return clazz.getModule().getUnit().getConfig().getJointCompilationOptions() != null; } + /** + * Tells whether the given method is subject to static type checking, i.e. it is + * annotated with {@code @CompileStatic} or {@code @TypeChecked}, or declared in a + * class that is. {@code @CompileDynamic} and {@code TypeCheckingMode.SKIP} opt + * out again. Static type checking applied by other means, e.g. a compiler + * configuration script, is not detected. + */ + public static boolean isStaticallyTypeChecked(MethodNode method) { + Boolean methodLevel = staticTypeCheckingState(method.getAnnotations()); + if (methodLevel != null) return methodLevel; + for (ClassNode clazz = method.getDeclaringClass(); clazz != null; clazz = clazz.getOuterClass()) { + Boolean classLevel = staticTypeCheckingState(clazz.getAnnotations()); + if (classLevel != null) return classLevel; + } + return false; + } + + private static Boolean staticTypeCheckingState(List annotations) { + for (AnnotationNode annotation : annotations) { + String name = annotation.getClassNode().getName(); + if ("groovy.transform.CompileDynamic".equals(name)) return false; + if ("groovy.transform.CompileStatic".equals(name) || "groovy.transform.TypeChecked".equals(name)) { + Expression typeCheckingMode = annotation.getMember("value"); + return typeCheckingMode == null || !typeCheckingMode.getText().endsWith("SKIP"); + } + } + return null; + } + + /** + * Creates a fresh expression referencing {@code this}. Generated code must not share + * {@link VariableExpression#THIS_EXPRESSION} between use sites, because the static type + * checker stores inferred types as per-node metadata; a singleton node used both inside + * and outside of closures ends up with conflicting casts in the generated bytecode. + */ + public static VariableExpression createThisExpression() { + return new VariableExpression("this"); + } + public static MethodCallExpression createMethodCall(Expression target, String methodName, Expression arguments) { MethodCallExpression result = new MethodCallExpression(target, methodName, arguments); // and https://github.com/spockframework/spock/issues/1200 diff --git a/spock-core/src/main/java/org/spockframework/compiler/ConditionRewriter.java b/spock-core/src/main/java/org/spockframework/compiler/ConditionRewriter.java index 6bfad87439..b0e6226c6f 100644 --- a/spock-core/src/main/java/org/spockframework/compiler/ConditionRewriter.java +++ b/spock-core/src/main/java/org/spockframework/compiler/ConditionRewriter.java @@ -73,12 +73,16 @@ public class ConditionRewriter extends AbstractExpressionConverter i private final String errorCollectorName; + private final boolean staticallyTypeChecked; + private int recordCount = 0; private ConditionRewriter(IRewriteResources resources, String valueRecorderSuffix, String errorCollectorSuffix) { this.resources = resources; valueRecorderName = SpockNames.VALUE_RECORDER + valueRecorderSuffix; errorCollectorName = SpockNames.ERROR_COLLECTOR + errorCollectorSuffix; + org.spockframework.compiler.model.Method currentMethod = resources.getCurrentMethod(); + staticallyTypeChecked = currentMethod != null && AstUtil.isStaticallyTypeChecked(currentMethod.getAst()); } public static Statement rewriteExplicitCondition(AssertStatement stat, IRewriteResources resources) { @@ -116,22 +120,47 @@ public void visitMethodCallExpression(MethodCallExpression expr) { && (!AstUtil.hasPlausibleSourcePosition(expr.getMethod()) // before GROOVY-4344 fix || (expr.getMethod().getColumnNumber() == expr.getObjectExpression().getColumnNumber())); // after GROOVY-4344 fix + Expression method = expr.getMethod(); MethodCallExpression conversion = new MethodCallExpression( expr.isImplicitThis() ? expr.getObjectExpression() : convert(expr.getObjectExpression()), objectExprSeenAsMethodNameAtRuntime ? - expr.getMethod() : - convert(expr.getMethod()), + method : + convertMethodName(method), convert(expr.getArguments())); conversion.setSafe(expr.isSafe()); conversion.setSpreadSafe(expr.isSpreadSafe()); + if (staticallyTypeChecked) { + // must be kept in sync, otherwise a qualified call with a constant method + // name would be dispatched as an implicit-this call, e.g. to a local + // closure variable of the same name; only done for statically checked code + // to keep the dispatch of dynamic code untouched + conversion.setImplicitThis(expr.isImplicitThis()); + } conversion.setSourcePosition(expr); result = record(conversion); } + /** + * Wrapping a method name in record() turns the call into a dynamic method name + * call, which does not pass static type checking. Method names synthesized by an + * earlier rewrite step (e.g. old() -> oldImpl()) and, in statically type checked + * code, all constant method names therefore stay as they are; their value slot is + * still allocated to keep the numbering intact, it just stays N/A, which the + * renderer never displays for method names anyway. Dynamic code keeps the recorded + * method name to leave its dispatch semantics untouched. + */ + private Expression convertMethodName(Expression method) { + if (method instanceof ConstantExpression + && (staticallyTypeChecked || !AstUtil.hasPlausibleSourcePosition(method))) { + return recordNa(method); + } + return convert(method); + } + // only used for statically imported methods called by their simple name @Override public void visitStaticMethodCallExpression(StaticMethodCallExpression expr) { 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 1911fe7c72..f2dc52561c 100644 --- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java +++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java @@ -470,7 +470,7 @@ private MethodCallExpression createBlockListenerCall(Block block, MethodNode blo new ClassExpression(nodeCache.SpockRuntime), blockListenerMethod, new ArgumentListExpression( - VariableExpression.THIS_EXPRESSION, + AstUtil.createThisExpression(), new ConstantExpression(block.getBlockMetaDataIndex(), true) )); } @@ -693,7 +693,7 @@ public VariableExpression captureOldValue(Expression oldValue) { } public MethodCallExpression getSpecificationContext() { - return createDirectMethodCall(VariableExpression.THIS_EXPRESSION, + return createDirectMethodCall(AstUtil.createThisExpression(), nodeCache.Specification_GetSpecificationContext, ArgumentListExpression.EMPTY_ARGUMENTS); } diff --git a/spock-core/src/main/java/org/spockframework/compiler/SpecialMethodCall.java b/spock-core/src/main/java/org/spockframework/compiler/SpecialMethodCall.java index dcfaefb304..2bb37e81bd 100644 --- a/spock-core/src/main/java/org/spockframework/compiler/SpecialMethodCall.java +++ b/spock-core/src/main/java/org/spockframework/compiler/SpecialMethodCall.java @@ -174,7 +174,7 @@ public ClosureExpression getClosureExpr() { @Override public void expand() { List args = new ArrayList<>(); - args.add(VariableExpression.THIS_EXPRESSION); + args.add(AstUtil.createThisExpression()); args.add(inferredName); args.add(inferredType); args.addAll(AstUtil.getArgumentList(methodCallExpr)); 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 9ec7991d54..8690d67820 100644 --- a/spock-core/src/main/java/org/spockframework/compiler/WhereBlockRewriter.java +++ b/spock-core/src/main/java/org/spockframework/compiler/WhereBlockRewriter.java @@ -804,18 +804,36 @@ private void turnIntoSimpleParameterization(List column) throws Inva private void generatePreviousColumnExtractorStatements(Set referencedPreviousVariables, int row, List statements) { for (String referencedPreviousVariable : referencedPreviousVariables) { + ClassNode declaredType = getDeclaredDataVariableType(referencedPreviousVariable); + Expression rowValue = createDirectMethodCall( + new VariableExpression(getDataTableParameterName(referencedPreviousVariable)), + resources.getAstNodeCache().List_Get, + new ConstantExpression(row)); + if (declaredType != null) { + // use the declared feature parameter type, so that cell expressions + // referencing previous columns pass static type checking + CastExpression coercedRowValue = new CastExpression(declaredType, rowValue); + coercedRowValue.setCoerce(true); + rowValue = coercedRowValue; + } statements.add(new ExpressionStatement( + // Type x = $spock_p_x.get(row) as Type + // or, without a declared parameter type // def x = $spock_p_x.get(row) new DeclarationExpression( - new VariableExpression(referencedPreviousVariable), + new VariableExpression(referencedPreviousVariable, declaredType == null ? ClassHelper.DYNAMIC_TYPE : declaredType), Token.newSymbol(Types.ASSIGN, -1, -1), - createDirectMethodCall( - new VariableExpression(getDataTableParameterName(referencedPreviousVariable)), - resources.getAstNodeCache().List_Get, - new ConstantExpression(row))))); + rowValue))); } } + private ClassNode getDeclaredDataVariableType(String name) { + for (Parameter parameter : whereBlock.getParent().getAst().getParameters()) { + if (parameter.getName().equals(name) && !parameter.isDynamicTyped()) return parameter.getType(); + } + return null; + } + private List getReferencedPreviousVariables(List previousVariables, Expression providerExpression) { return previousVariables .stream() diff --git a/spock-core/src/main/java/org/spockframework/runtime/ValueRecorder.java b/spock-core/src/main/java/org/spockframework/runtime/ValueRecorder.java index fbd4d04636..176fd8ef9f 100644 --- a/spock-core/src/main/java/org/spockframework/runtime/ValueRecorder.java +++ b/spock-core/src/main/java/org/spockframework/runtime/ValueRecorder.java @@ -41,8 +41,10 @@ public ValueRecorder reset() { /** * Records and returns the specified value. Hence an expression can be replaced * with record(expression) without impacting evaluation of the expression. + * The generic return type preserves the static type of the wrapped expression, + * allowing conditions to pass static type checking (e.g. under {@code @CompileStatic}). */ - public Object record(int index, Object value) { + public T record(int index, T value) { realizeNas(index + 1, null); values.set(index, value); @@ -72,8 +74,9 @@ public int startRecordingValue(int index){ /** * Materializes N/A values without recording a new value. + * Generic for the same reason as {@link #record(int, Object)}. */ - public Object realizeNas(int index, Object value) { + public T realizeNas(int index, T value) { for (int i = values.size(); i < index; i++) values.add(ExpressionInfo.VALUE_NOT_AVAILABLE); return value; diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/CompileStaticSupport.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/CompileStaticSupport.groovy new file mode 100644 index 0000000000..3871f8fad7 --- /dev/null +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/CompileStaticSupport.groovy @@ -0,0 +1,352 @@ +/* + * Copyright 2026 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 + * + * https://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.smoke + +import groovy.transform.CompileStatic +import org.spockframework.EmbeddedSpecification +import org.spockframework.runtime.ConditionNotSatisfiedError +import org.spockframework.runtime.SpockComparisonFailure + +/** + * Specs annotated with {@code @CompileStatic} must compile even though + * Spock rewrites their conditions, and must render failed conditions + * with the recorded values. + */ +class CompileStaticSupport extends EmbeddedSpecification { + + def setup() { + compiler.addClassImport(CompileStatic) + runner.addClassImport(CompileStatic) + } + + def "comparison conditions on typed variables compile"() { + when: + compiler.compileWithImports """ +@CompileStatic +class ASpec extends Specification { + def "comparisons"() { + given: + int x = 42 + + expect: + x > 40 + x >= 42 + x < 43 + x <= 42 + } +} +""" + + then: + noExceptionThrown() + } + + def "property access in conditions compiles"() { + when: + compiler.compileWithImports """ +@CompileStatic +class ASpec extends Specification { + def "property access"() { + given: + Pogo pogo = new Pogo(name: "Fred") + + expect: + pogo.name == "Fred" + } +} + +@CompileStatic +class Pogo { + String name +} +""" + + then: + noExceptionThrown() + } + + def "indexing in conditions compiles"() { + when: + compiler.compileWithImports """ +@CompileStatic +class ASpec extends Specification { + def "indexing"() { + given: + List list = ['a'] + + expect: + list[0] == 'a' + } +} +""" + + then: + noExceptionThrown() + } + + def "top-level method call conditions compile"() { + when: + compiler.compileWithImports """ +@CompileStatic +class ASpec extends Specification { + def "method call condition"() { + given: + List list = ['a'] + + expect: + list.contains('a') + } +} +""" + + then: + noExceptionThrown() + } + + def "old() compiles and evaluates"() { + when: + runner.runWithImports """ +@CompileStatic +class ASpec extends Specification { + def "old"() { + given: + List list = ['a'] + int before = list.size() + + when: + list.add('b') + int after = list.size() + + then: + after == old(before) + 1 + } +} +""" + + then: + noExceptionThrown() + } + + def "data table cells referencing previous columns compile"() { + when: + runner.runWithImports """ +@CompileStatic +class ASpec extends Specification { + def "previous column reference"(int a, int b) { + when: + int expected = a + 1 + + then: + b == expected + + where: + a | b + 1 | a + 1 + 5 | a + 1 + } +} +""" + + then: + noExceptionThrown() + } + + def "interactions in mock init closures work"() { + when: + runner.runWithImports """ +@CompileStatic +class ASpec extends Specification { + def "mock init closure"() { + given: + List list = Mock(List) { + size() >> 3 + } + + when: + int size = list.size() + + then: + size == 3 + } +} +""" + + then: + noExceptionThrown() + } + + def "interactions in interaction blocks work"() { + when: + runner.runWithImports """ +@CompileStatic +class ASpec extends Specification { + def "interaction block"() { + given: + List list = Mock() + + when: + list.add("x") + + then: + interaction { + 1 * list.add(_) + } + } +} +""" + + then: + noExceptionThrown() + } + + def "method calls in conditions compile"() { + when: + compiler.compileWithImports """ +@CompileStatic +class ASpec extends Specification { + def "method call conditions"() { + given: + List list = ['a', 'b'] + + expect: + list.size() == 2 + !list.isEmpty() + list.first().toUpperCase() == 'A' + this.helper(1) == 2 + helper(1) == 2 + } + + int helper(int i) { + i + 1 + } +} +""" + + then: + noExceptionThrown() + } + + def "method-level CompileStatic annotation is honored"() { + when: + runner.runWithImports """ +class ASpec extends Specification { + @CompileStatic + def "statically checked feature"() { + given: + List list = ['a'] + + expect: + list.size() == 1 + } +} +""" + + then: + noExceptionThrown() + } + + def "exception property access in conditions compiles"() { + when: + runner.runWithImports """ +@CompileStatic +class ASpec extends Specification { + def "thrown message"() { + when: + throw new IllegalStateException("bam") + + then: + IllegalStateException e = thrown() + e.message == "bam" + } +} +""" + + then: + noExceptionThrown() + } + + def "method calls on the delegate of with blocks compile"() { + when: + runner.runWithImports """ +@CompileStatic +class ASpec extends Specification { + def "with block"() { + given: + List list = ['a', 'b'] + + expect: + with(list) { + size() == 2 + first() == 'a' + } + } +} +""" + + then: + noExceptionThrown() + } + + def "failed method call condition renders recorded values"() { + when: + runner.runWithImports """ +@CompileStatic +class ASpec extends Specification { + def "method call"() { + given: + List list = ['a'] + + expect: + list.size() == 2 + } +} +""" + + then: + SpockComparisonFailure e = thrown() + e.condition.rendering.trim() == """ +list.size() == 2 +| | | +[a] 1 false +""".trim() + } + + def "failed comparison condition renders recorded values"() { + when: + runner.runWithImports """ +@CompileStatic +class ASpec extends Specification { + def "comparison"() { + given: + int x = 42 + + expect: + x > 43 + } +} +""" + + then: + ConditionNotSatisfiedError e = thrown() + e.condition.rendering.trim() == """ +x > 43 +| | +| false +42 +""".trim() + } +} diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/condition/CompileStaticConditionRendering.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/condition/CompileStaticConditionRendering.groovy new file mode 100644 index 0000000000..0dad856e9c --- /dev/null +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/condition/CompileStaticConditionRendering.groovy @@ -0,0 +1,267 @@ +/* + * Copyright 2026 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 + * + * https://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.smoke.condition + +import groovy.transform.CompileStatic + +/** + * Describes rendering of conditions in statically compiled code. + * The rendered output must match the one of dynamic code, see {@link ConditionRendering}. + */ +@CompileStatic +class CompileStaticConditionRendering extends ConditionRenderingSpec { + def "simple condition"() { + expect: + isRendered """ +x == 1 +| | +2 false + """, { + int x = 2 + assert x == 1 + } + } + + private int one(int x) { 0 } + + def "MethodCallExpression with implicit target"() { + expect: + isRendered """ +one(a) +| | +0 1 + """, { + int a = 1 + assert one(a) + } + } + + def "MethodCallExpression with explicit target"() { + expect: + isRendered """ +a.get(b) == null +| | | | +| 1 0 false +[1] + """, { + List a = [1] + int b = 0 + assert a.get(b) == null + } + } + + def "MethodCallExpression invoking static method"() { + expect: + // unlike in dynamic code, the target class of a static method call is not + // rendered, because statically compiled code never evaluates the receiver + // expression of a static method call, so its value is never recorded + isRendered """ +Math.max(a,b) == c + | | | | | + 2 1 2 | 0 + false + """, { + int a = 1 + int b = 2 + int c = 0 + assert Math.max(a,b) == c + } + } + + def "MethodCallExpression with spread-dot operator"() { + expect: + isRendered """ +["1", "22"]*.size() == null + | | + [1, 2] false + """, { + assert ["1", "22"]*.size() == null + } + } + + def "MethodCallExpression with safe operator"() { + expect: + isRendered """ +a?.length() +| | +| null +null + """, { + String a = null + assert a?.length() + } + } + + def "top-level MethodCallExpression"() { + expect: + isRendered """ +a.contains(b) +| | | +| false 2 +[0] + """, { + List a = [0] + int b = 2 + assert a.contains(b) + } + } + + def "chained MethodCallExpressions"() { + expect: + isRendered """ +a.first().toUpperCase() == "B" +| | | | +| a A false +[a] 1 difference (0% similarity) + (A) + (B) + """, { + List a = ["a"] + assert a.first().toUpperCase() == "B" + } + } + + def "TernaryExpression"() { + expect: + isRendered """ +a ? b : c +| | +1 0 + """, { + int a = 1 + int b = 0 + int c = 1 + assert a ? b : c + } + } + + def "BinaryExpression"() { + expect: + isRendered """ +a * b +| | | +0 0 1 + """, { + int a = 0 + int b = 1 + assert a * b + } + + isRendered """ +a[b] +||| +||0 +|false +[false] + """, { + List a = [false] + int b = 0 + assert a[b] + } + } + + def "comparison operators"() { + expect: + isRendered """ +x > 43 +| | +| false +42 + """, { + int x = 42 + assert x > 43 + } + } + + def "BooleanExpression"() { + expect: + isRendered """ +a +| +null + """, { + String a = null + assert a + } + } + + def "PropertyExpression"() { + expect: + isRendered """ +a.empty == true +| | | +| false false +[9] + """, { + List a = [9] + assert a.empty == true + } + } + + def "instanceof expression"() { + expect: + isRendered """ +x instanceof Integer +| | | +| false class java.lang.Integer +foo (java.lang.String) + """, { + Object x = "foo" + assert x instanceof Integer + } + } + + def "VariableExpression"() { + expect: + isRendered """ +x +| +0 + """, { + Integer x = 0 + assert x + } + } + + def "GStringExpression"() { + expect: + isRendered ''' +"$a and ${b + c}" == null + | | | | | + 1 2 5 3 false + ''', { + int a = 1 + int b = 2 + int c = 3 + assert "$a and ${b + c}" == null + } + } + + def "condition in with block"() { + expect: + isRendered """ +size() == 3 +| | +2 false + """, { + List list = ["a", "b"] + with(list) { + assert size() == 3 + } + } + } +} diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy index 559426607e..77824f2171 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsAsSet_is_transformed_correctly.groovy @@ -13,7 +13,7 @@ public void $spock_feature_0_0() { org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0) org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 1) try { - org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsAsSet'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5) + org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x =~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, 'matchCollectionsAsSet', new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5) } catch (java.lang.Throwable $spock_condition_throwable) { org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x =~ [1]', 4, 9, null, $spock_condition_throwable)} diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy index 1c5f107dd4..33398d5745 100644 --- a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/CollectionConditionAstSpec/collection_condition_matchCollectionsInAnyOrder_is_transformed_correctly.groovy @@ -13,7 +13,7 @@ public void $spock_feature_0_0() { org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0) org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 1) try { - org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'matchCollectionsInAnyOrder'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5) + org.spockframework.runtime.SpockRuntime.verifyMethodCondition($spock_errorCollector, $spock_valueRecorder.reset(), 'x ==~ [1]', 4, 9, null, org.spockframework.runtime.SpockRuntime, 'matchCollectionsInAnyOrder', new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), x), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(3), [$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(2), 1)])}, $spock_valueRecorder.realizeNas(6, false), false, 5) } catch (java.lang.Throwable $spock_condition_throwable) { org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'x ==~ [1]', 4, 9, null, $spock_condition_throwable)}