diff --git a/.github/codecov.yml b/.github/codecov.yml index 599046d6e8..358f7b767b 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -3,4 +3,4 @@ codecov: comment: layout: "reach, diff, flags, files" - after_n_builds: 18 + after_n_builds: 24 diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 9488befd4d..b0c3e37a1e 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -20,6 +20,11 @@ matchCurrentValue: "/^4\\./", allowedVersions: "(,5.0)" }, + { + matchPackageNames: ["/^org.apache.groovy:/"], + matchCurrentValue: "/^5\\./", + allowedVersions: "(,6.0)" + }, { matchPackageNames: ["/^org.mockito:/"], matchCurrentValue: "/^4\\./", @@ -34,6 +39,11 @@ matchPackageNames: ["/^org.spockframework:spock-/"], matchCurrentVersion: "/-groovy-4\\.0$/", allowedVersions: "/-groovy-4\\.0$/" + }, + { + matchPackageNames: ["/^org.spockframework:spock-/"], + matchCurrentVersion: "/-groovy-5\\.0$/", + allowedVersions: "/-groovy-5\\.0$/" } ] } diff --git a/.github/workflows/branches-and-prs.yaml b/.github/workflows/branches-and-prs.yaml index 464411c3b9..167a8d5f76 100644 --- a/.github/workflows/branches-and-prs.yaml +++ b/.github/workflows/branches-and-prs.yaml @@ -56,6 +56,7 @@ jobs: - '2.5' - '3.0' - '4.0' + - '5.0' java: - '8' - '11' @@ -74,6 +75,9 @@ jobs: - variant: '2.5' java: '25' os: 'ubuntu-latest' + - variant: '5.0' + java: '8' + os: 'ubuntu-latest' include: - variant: '2.5' java: '8' @@ -84,6 +88,9 @@ jobs: - variant: '4.0' java: '8' os: 'windows-latest' + - variant: '5.0' + java: '11' + os: 'windows-latest' - variant: '2.5' java: '8' os: 'macos-latest' @@ -93,6 +100,9 @@ jobs: - variant: '4.0' java: '8' os: 'macos-latest' + - variant: '5.0' + java: '11' + os: 'macos-latest' steps: - id: 'step-0' name: 'Checkout Repository' diff --git a/.github/workflows/codeql-analysis.main.kts b/.github/workflows/codeql-analysis.main.kts index 1d94b799b2..cb4d553080 100755 --- a/.github/workflows/codeql-analysis.main.kts +++ b/.github/workflows/codeql-analysis.main.kts @@ -125,7 +125,8 @@ workflow( "--stacktrace", "--no-build-cache", "testClasses", - """"-Dvariant=${expr(Matrix.variant)}"""" + """"-Dvariant=${expr(Matrix.variant)}"""", + """"-DjavaVersion=${expr("${Matrix.variant} == '5.0' && '11' || '${Matrix.axes.javaVersions.first()}'")}"""" ).joinToString(" ") ) uses( diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml index 8757e31019..350b3946f0 100644 --- a/.github/workflows/codeql-analysis.yaml +++ b/.github/workflows/codeql-analysis.yaml @@ -40,6 +40,7 @@ jobs: - '2.5' - '3.0' - '4.0' + - '5.0' steps: - id: 'step-0' name: 'Checkout Repository' @@ -52,7 +53,7 @@ jobs: uses: 'github/codeql-action/init@v3' - id: 'step-3' name: 'Build Spock Classes' - run: './gradlew --stacktrace --no-build-cache testClasses "-Dvariant=${{ matrix.variant }}"' + run: './gradlew --stacktrace --no-build-cache testClasses "-Dvariant=${{ matrix.variant }}" "-DjavaVersion=${{ matrix.variant == ''5.0'' && ''11'' || ''8'' }}"' - id: 'step-4' name: 'Perform CodeQL Analysis' uses: 'github/codeql-action/analyze@v3' diff --git a/.github/workflows/common.main.kts b/.github/workflows/common.main.kts index 3411c9f10b..14dd1e346a 100755 --- a/.github/workflows/common.main.kts +++ b/.github/workflows/common.main.kts @@ -139,15 +139,20 @@ val Matrix.Companion.full operatingSystems = listOf("ubuntu-latest"), variants = axes.variants, javaVersions = axes.javaVersions + axes.additionalJavaTestVersions, - exclude = { (variant == "2.5") && (javaVersion!!.toInt() >= 17) }, + exclude = { + ((variant == "2.5") && (javaVersion!!.toInt() >= 17)) || + ((variant == "5.0") && (javaVersion!!.toInt() < 11)) + }, includes = listOf("windows-latest", "macos-latest") - .map { - Matrix.Element( - operatingSystem = it, - javaVersion = axes.javaVersions.first() - ) + .map { Matrix.Element(operatingSystem = it) } + .flatMap { element -> + axes.variants.map { + element.copy( + variant = it, + javaVersion = if (it == "5.0") "11" else axes.javaVersions.first() + ) + } } - .flatMap { element -> axes.variants.map { element.copy(variant = it) } } ) val Matrix.Companion.axes by lazy { diff --git a/.github/workflows/docs-pr.yaml b/.github/workflows/docs-pr.yaml index ccc1cab344..50400f7b7c 100644 --- a/.github/workflows/docs-pr.yaml +++ b/.github/workflows/docs-pr.yaml @@ -48,7 +48,7 @@ jobs: run: 'sudo apt update && sudo apt install --yes graphviz' - id: 'step-3' name: 'Build Docs' - run: './gradlew --stacktrace asciidoctor javadoc "-Dvariant=4.0" "-DjavaVersion=25"' + run: './gradlew --stacktrace asciidoctor javadoc "-Dvariant=5.0" "-DjavaVersion=25"' - id: 'step-4' name: 'Archive and upload docs' uses: 'actions/upload-artifact@v4' diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index fbb467c5ed..fb6df78eb1 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -36,6 +36,7 @@ jobs: - '2.5' - '3.0' - '4.0' + - '5.0' java: - '8' - '11' @@ -54,6 +55,9 @@ jobs: - variant: '2.5' java: '25' os: 'ubuntu-latest' + - variant: '5.0' + java: '8' + os: 'ubuntu-latest' include: - variant: '2.5' java: '8' @@ -64,6 +68,9 @@ jobs: - variant: '4.0' java: '8' os: 'windows-latest' + - variant: '5.0' + java: '11' + os: 'windows-latest' - variant: '2.5' java: '8' os: 'macos-latest' @@ -73,6 +80,9 @@ jobs: - variant: '4.0' java: '8' os: 'macos-latest' + - variant: '5.0' + java: '11' + os: 'macos-latest' steps: - id: 'step-0' name: 'Checkout Repository' @@ -109,6 +119,7 @@ jobs: - '2.5' - '3.0' - '4.0' + - '5.0' java: - '8' os: @@ -140,7 +151,7 @@ jobs: strategy: matrix: variant: - - '4.0' + - '5.0' java: - '25' os: diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc index 94d40323a0..ea74ac3eb6 100644 --- a/CONTRIBUTING.adoc +++ b/CONTRIBUTING.adoc @@ -137,10 +137,10 @@ Functional tests for `spock-core` are located under https://github.spockframewor === Command Line Build Spock is built with https://www.gradle.org[Gradle]. -The only prerequisite for executing the build is an installation of JDK 8 and JDK 17. -The build itself must be run with JDK 17, but JDK 8 must be available via toolchain. +The only prerequisite for executing the build is an installation of JDK 11 and JDK 17. +The build itself must be run with JDK 17, but JDK 11 must be available via toolchain. You can check if Gradle can locate the necessary JDKs by running `./gradlew javaToolchains`. -If gradle can't locate your JDK 8, you can make the location of the JDK 8 available via an environment variable called `JDK8`. +If Gradle can't locate your JDK 11, you can make the location of the JDK 11 available via an environment variable called `JDK11`. After cloning the https://github.com/spockframework/spock[GitHub repository], cd into the top directory and execute `./gradlew build`. The build should succeed without any errors. `./gradlew tasks` lists the available tasks. Always use the Gradle Wrapper (`gradlew` command) rather than your own Gradle installation. diff --git a/README.adoc b/README.adoc index b29b4bfb81..a5e62ee0eb 100644 --- a/README.adoc +++ b/README.adoc @@ -29,10 +29,11 @@ https://groovyconsole.dev/[Groovy Web Console]. * The latest 2.x release version is *{spock-release-version}* ({spock-release-version}-groovy-2.5, {spock-release-version}-groovy-3.0, {spock-release-version}-groovy-4.0), released on {spock-release-date}. * The current development version is *{spock-snapshot-version}-SNAPSHOT* -({spock-snapshot-version}-groovy-2.5-SNAPSHOT, {spock-snapshot-version}-groovy-3.0-SNAPSHOT, {spock-snapshot-version}-groovy-4.0-SNAPSHOT). +({spock-snapshot-version}-groovy-2.5-SNAPSHOT, {spock-snapshot-version}-groovy-3.0-SNAPSHOT, +{spock-snapshot-version}-groovy-4.0-SNAPSHOT), {spock-snapshot-version}-groovy-5.0-SNAPSHOT). *NOTE:* Spock 2.x is based on the JUnit 5 Platform and requires Java -8+/groovy-2.5+ (Groovy 3.0 or 4.0 is recommended, especially in projects using +8+/groovy-2.5+ (Groovy 3.0 or newer is recommended, especially in projects using Java 12+). Releases are available from @@ -86,25 +87,26 @@ https://tapestry.apache.org/[Tapestry 5] IoC container. ==== Prerequisites -Spock needs both a JDK 8 and JDK 17+ installed. +Spock needs both a JDK 11 and JDK 17+ installed. -* JDK 8 is required to compile Spock via toolchains (automatic download is disabled). +* JDK 11 is required to compile Spock via toolchains (automatic download is disabled). * The gradle build itself requires at least JDK 17 to run. JDK locations must be made known to toolchains via `JDK=` environment -variable, e.g., `JDK8=/path/to/jdk8`. +variable if they are not in standard places recognized by Gradle, e.g. `JDK11=/path/to/jdk11`. ==== Supported versions Spock is supported for Java version 8+. -Spock is supported for Groovy versions 2.5, 3.0, and 4.0. +Spock is supported for Groovy versions 2.5, 3.0, 4.0, and 5.0. The tests are testing Spock with the specific versions (variants) of Groovy and Java. Default Groovy version is 2.5. The Groovy 3.0 and 4.0 variant should pass on all supported JDK versions, Groovy 2.5 does not work with Java 17+: +Groovy 5.0 and newer does not work with Java <11: .... ./gradlew build diff --git a/build-logic/base/src/main/groovy/org/spockframework/gradle/SpockBasePlugin.groovy b/build-logic/base/src/main/groovy/org/spockframework/gradle/SpockBasePlugin.groovy index bd12d23283..42bb24345a 100644 --- a/build-logic/base/src/main/groovy/org/spockframework/gradle/SpockBasePlugin.groovy +++ b/build-logic/base/src/main/groovy/org/spockframework/gradle/SpockBasePlugin.groovy @@ -39,7 +39,8 @@ import java.time.Duration class SpockBasePlugin implements Plugin { @VisibleForTesting - public static final JavaLanguageVersion COMPILER_VERSION = JavaLanguageVersion.of(8) + public static final JavaLanguageVersion COMPILER_VERSION = JavaLanguageVersion.of(11) + public static final int COMPILER_RELEASE_VERSION = 8 void apply(Project project) { applyPlugins(project) @@ -65,6 +66,7 @@ class SpockBasePlugin implements Plugin { comp.javaCompiler.set(javaToolchains.compilerFor { it.languageVersion.set(COMPILER_VERSION) }) + comp.options.release.set(COMPILER_RELEASE_VERSION) } comp.options.encoding = 'UTF-8' } diff --git a/build.gradle b/build.gradle index 6b5f37f061..b9e84c58bd 100644 --- a/build.gradle +++ b/build.gradle @@ -44,6 +44,14 @@ ext { groovyVersion = libs.versions.groovy4.get() minGroovyVersion = "4.0.0" maxGroovyVersion = "4.9.99" + } else if (variant == 5.0) { + groovyGroup = "org.apache.groovy" + groovyVersion = libs.versions.groovy5.get() + minGroovyVersion = "5.0.0" + maxGroovyVersion = "5.9.99" + if (javaVersion < 11) { + throw new InvalidUserDataException("Groovy $variant is not compatible with Java $javaVersion") + } } else { throw new InvalidUserDataException("Unknown variant: $variant. Choose one of: $variants") } diff --git a/docs/release_notes.adoc b/docs/release_notes.adoc index cf12b69cf3..5ce805c74d 100644 --- a/docs/release_notes.adoc +++ b/docs/release_notes.adoc @@ -30,6 +30,7 @@ _This is a summary of the highlights of the milestone releases_ * Add <> spockPull:1844[] * Add `@Snapshot` extension for <> spockPull:1873[] * Add `!!` as <> spockPull:1532[] +* Add support for Groovy 5.0 spockIssue:2196[] === Breaking Changes diff --git a/gradle.properties b/gradle.properties index 0309bcb86c..4a5fc43dd6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,5 +22,5 @@ org.gradle.caching=true javaVersionsList=8, 11, 17, 21, 25 additionalJavaTestVersionsList= -variantsList=2.5, 3.0, 4.0 +variantsList=2.5, 3.0, 4.0, 5.0 kotlin.code.style=official diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9f3e93f24c..992c4af58d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,6 +3,7 @@ asciidoctorj = '3.0.0' groovy2 = '2.5.23' groovy3 = '3.0.25' groovy4 = '4.0.29' +groovy5 = '5.0.2' jacoco = '0.8.14' junit = '5.14.0' # The VersionRange used by OSGi to check compatibility with JUnit Platform. @@ -46,3 +47,4 @@ workflows-kotlin-scriptingCompiler = { module = "org.jetbrains.kotlin:kotlin-scr groovy-v2 = { module = "org.codehaus.groovy:groovy", version.ref="groovy2" } groovy-v3 = { module = "org.codehaus.groovy:groovy", version.ref="groovy3" } groovy-v4 = { module = "org.apache.groovy:groovy", version.ref="groovy4" } +groovy-v5 = { module = "org.apache.groovy:groovy", version.ref="groovy5" } 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 1098c08db2..16ec38c5bb 100644 --- a/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java +++ b/spock-core/src/main/java/org/spockframework/compiler/SpecRewriter.java @@ -824,8 +824,8 @@ private Expression transformRhsExpressionIfNecessary(DeclarationExpression declE MethodCallExpression methodCallExpression = (MethodCallExpression)rightExpression; if (methodCallExpression.isImplicitThis()) if (declExpr.isMultipleAssignmentDeclaration()) { - ArgumentListExpression argumentListExpression = (ArgumentListExpression)declExpr.getLeftExpression(); - argumentListExpression.getExpressions().stream() + TupleExpression tupleExpression = (TupleExpression)declExpr.getLeftExpression(); + tupleExpression.getExpressions().stream() .filter(VariableExpression.class::isInstance) .map(VariableExpression.class::cast) .map(VariableExpression::getName) diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyMockInterceptor.java b/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyMockInterceptor.java index 9e1fcfdd45..5280f482a5 100644 --- a/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyMockInterceptor.java +++ b/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyMockInterceptor.java @@ -67,10 +67,7 @@ public Object intercept(Object target, Method method, Object[] arguments, IRespo throw new MissingMethodException((String)args[0], mockConfiguration.getType(), new Object[]{args[1]}, false); } - if (isMethod(method, "propertyMissing", String.class)) { - throw new MissingPropertyException((String)args[0], mockConfiguration.getType()); - } - + IMockMethod mockMethod = new StaticMockMethod(method, mockConfiguration.getExactType()); IMockInvocation invocation = new MockInvocation(mockObject, mockMethod, asList(args), realMethodInvoker); IMockController controller = specification.getSpecificationContext().getMockController(); diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyMockMetaClass.java b/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyMockMetaClass.java index 1af4f4e1a7..ec051388f2 100644 --- a/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyMockMetaClass.java +++ b/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyMockMetaClass.java @@ -21,6 +21,7 @@ import java.lang.reflect.*; import java.util.*; +import java.util.function.Function; import groovy.lang.*; @@ -53,21 +54,47 @@ public Object invokeConstructor(Object[] arguments) { @Override public Object getProperty(Object target, String property) { + return getProperty(delegate.getTheClass(), target, property, false, false); + } + + @Override + public void setProperty(Object target, String property, Object newValue) { + setProperty(delegate.getTheClass(), target, property, newValue, false, false); + } + + + @Override + public Object getProperty(Class sender, Object target, String property, boolean useSuper, boolean fromInsideClass) { + final String methodName = propertyToGetterMethodName(property); + return doInvokeMethod(target, methodName, GroovyRuntimeUtil.EMPTY_ARGUMENTS, isTargetStatic(target), + metaClass -> new GroovyRealGetPropertyInvoker(metaClass, sender, property, useSuper, fromInsideClass)); + } + + @Override + public void setProperty(Class sender, Object target, String property, Object newValue, boolean useSuper, boolean fromInsideClass) { + String methodName = GroovyRuntimeUtil.propertyToSetterMethodName(property); + doInvokeMethod(target, methodName, new Object[]{newValue}, isTargetStatic(target), + metaClass -> new GroovyRealSetPropertyInvoker(metaClass, sender, property, useSuper, fromInsideClass)); + } + + private String propertyToGetterMethodName(String property) { String methodName = GroovyRuntimeUtil.propertyToBooleanGetterMethodName(property); MetaMethod metaMethod = delegate.getMetaMethod(methodName, GroovyRuntimeUtil.EMPTY_ARGUMENTS); if (metaMethod == null || metaMethod.getReturnType() != boolean.class) { methodName = GroovyRuntimeUtil.propertyToGetterMethodName(property); } - return invokeMethod(target, methodName, GroovyRuntimeUtil.EMPTY_ARGUMENTS); + return methodName; } - @Override - public void setProperty(Object target, String property, Object newValue) { - String methodName = GroovyRuntimeUtil.propertyToSetterMethodName(property); - invokeMethod(target, methodName, new Object[] {newValue}); + private boolean isTargetStatic(Object target) { + return target instanceof Class && delegate.getTheClass() != Class.class; } private Object doInvokeMethod(Object target, String methodName, Object[] arguments, boolean isStatic) { + return doInvokeMethod(target, methodName, arguments, isStatic, GroovyRealMethodInvoker::new); + } + + private Object doInvokeMethod(Object target, String methodName, Object[] arguments, boolean isStatic, Function invokerFactory) { Object[] args = GroovyRuntimeUtil.asArgumentArray(arguments); if (isGetMetaClassCallOnGroovyObject(target, methodName, args, isStatic)) { @@ -110,7 +137,8 @@ private Object doInvokeMethod(Object target, String methodName, Object[] argumen // getMetaClass was already handled earlier; setMetaClass isn't handled specially } - IMockInvocation invocation = createMockInvocation(metaMethod, target, methodName, args, isStatic); + final IResponseGenerator invoker = invokerFactory.apply(getAdaptee()); + IMockInvocation invocation = createMockInvocation(metaMethod, target, methodName, args, isStatic, invoker); IMockController controller = specification.getSpecificationContext().getMockController(); return controller.handle(invocation); } @@ -120,7 +148,8 @@ private boolean isGetMetaClassCallOnGroovyObject(Object target, String method, O } private IMockInvocation createMockInvocation(MetaMethod metaMethod, Object target, - String methodName, Object[] arguments, boolean isStatic) { + String methodName, Object[] arguments, boolean isStatic, + IResponseGenerator invoker) { IMockObject mockObject = new MockObject(configuration, target, specification, this); IMockMethod mockMethod; if (metaMethod != null) { @@ -129,7 +158,7 @@ private IMockInvocation createMockInvocation(MetaMethod metaMethod, Object targe } else { mockMethod = new DynamicMockMethod(methodName, arguments.length, isStatic); } - return new MockInvocation(mockObject, mockMethod, asList(arguments), new GroovyRealMethodInvoker(getAdaptee())); + return new MockInvocation(mockObject, mockMethod, asList(arguments), invoker); } @Override diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyRealGetPropertyInvoker.java b/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyRealGetPropertyInvoker.java new file mode 100644 index 0000000000..13c6f2c296 --- /dev/null +++ b/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyRealGetPropertyInvoker.java @@ -0,0 +1,62 @@ +/* + * Copyright 2025 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.mock.runtime; + +import groovy.lang.MetaClass; +import groovy.lang.MetaMethod; +import groovy.lang.MissingMethodException; +import groovy.lang.MissingPropertyException; +import org.codehaus.groovy.runtime.InvokerInvocationException; +import org.spockframework.mock.IMockInvocation; +import org.spockframework.util.ThreadSafe; + +/** + * A {@link GroovyRealMethodInvoker} which calls the real {@link MetaClass#getProperty(Class, Object, String, boolean, boolean)} as a fallback, if property was not found via the getter method. + * + * @author Andreas Turban + * @since 2.4 + */ +@ThreadSafe +public class GroovyRealGetPropertyInvoker extends GroovyRealMethodInvoker { + private final MetaClass metaClass; + private final Class sender; + private final String property; + private final boolean useSuper; + private final boolean fromInsideClass; + + public GroovyRealGetPropertyInvoker(MetaClass metaClass, Class sender, String property, boolean useSuper, boolean fromInsideClass) { + super(metaClass); + this.metaClass = metaClass; + this.sender = sender; + this.property = property; + this.useSuper = useSuper; + this.fromInsideClass = fromInsideClass; + } + + @Override + public Object respond(IMockInvocation invocation) { + try { + return super.respond(invocation); + } catch (InvokerInvocationException | MissingMethodException e1) { + Object receiver = invocation.getMockObject().getInstance(); + try { + return metaClass.getProperty(sender, receiver, property, useSuper, fromInsideClass); + } catch (InvokerInvocationException | MissingPropertyException e2) { + e2.addSuppressed(e1); + throw e2; + } + } + } +} diff --git a/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyRealSetPropertyInvoker.java b/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyRealSetPropertyInvoker.java new file mode 100644 index 0000000000..d1906b1b67 --- /dev/null +++ b/spock-core/src/main/java/org/spockframework/mock/runtime/GroovyRealSetPropertyInvoker.java @@ -0,0 +1,64 @@ +/* + * Copyright 2025 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.mock.runtime; + +import groovy.lang.MetaClass; +import groovy.lang.MetaMethod; +import groovy.lang.MissingMethodException; +import groovy.lang.MissingPropertyException; +import org.codehaus.groovy.runtime.InvokerInvocationException; +import org.spockframework.mock.IMockInvocation; +import org.spockframework.util.ThreadSafe; + +/** + * A {@link GroovyRealMethodInvoker} which calls the real {@link MetaClass#setProperty(Class, Object, String, Object, boolean, boolean)} as a fallback, if property was not found via the setter method. + * + * @author Andreas Turban + * @since 2.4 + */ +@ThreadSafe +public class GroovyRealSetPropertyInvoker extends GroovyRealMethodInvoker { + private final MetaClass metaClass; + private final Class sender; + private final String property; + private final boolean useSuper; + private final boolean fromInsideClass; + + public GroovyRealSetPropertyInvoker(MetaClass metaClass, Class sender, String property, boolean useSuper, boolean fromInsideClass) { + super(metaClass); + this.metaClass = metaClass; + this.sender = sender; + this.property = property; + this.useSuper = useSuper; + this.fromInsideClass = fromInsideClass; + } + + @Override + public Object respond(IMockInvocation invocation) { + try { + return super.respond(invocation); + } catch (InvokerInvocationException | MissingMethodException e1) { + Object receiver = invocation.getMockObject().getInstance(); + Object newValue = invocation.getArguments().get(0); + try { + metaClass.setProperty(sender, receiver, property, newValue, useSuper, fromInsideClass); + return null; + } catch (InvokerInvocationException | MissingPropertyException e2) { + e2.addSuppressed(e1); + throw e2; + } + } + } +} diff --git a/spock-specs/src/test/groovy/org/spockframework/mock/response/IterableResponseGeneratorSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/mock/response/IterableResponseGeneratorSpec.groovy index fe3658cdc9..e00c279a93 100644 --- a/spock-specs/src/test/groovy/org/spockframework/mock/response/IterableResponseGeneratorSpec.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/mock/response/IterableResponseGeneratorSpec.groovy @@ -39,7 +39,7 @@ class IterableResponseGeneratorSpec extends Specification { def "iterate over empty list"() { def gen = new IterableResponseGenerator([]) - def method = ReflectionUtil.getMethodByName(Object, "hashCode") + def method = ReflectionUtil.getMethodByName(Object, "toString") inv.getMethod() >> new StaticMockMethod(method, Object) expect: diff --git a/spock-specs/src/test/groovy/org/spockframework/runtime/EstimatedNumberOfIterations.groovy b/spock-specs/src/test/groovy/org/spockframework/runtime/EstimatedNumberOfIterations.groovy index 29ee314ab7..8bf0660868 100644 --- a/spock-specs/src/test/groovy/org/spockframework/runtime/EstimatedNumberOfIterations.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/runtime/EstimatedNumberOfIterations.groovy @@ -63,7 +63,7 @@ class EstimatedNumberOfIterations extends Specification { } - @DataProviderMetadata(dataVariables = ['a']) + @DataProviderMetadata(line = -1, dataVariables = ['a']) Iterator dataProvider2() { ['a'].iterator() } @@ -84,7 +84,7 @@ class EstimatedNumberOfIterations extends Specification { factory.createFeatureDataIterator(context.withCurrentFeature(featureInfo)).estimatedNumIterations == 3 } - @DataProviderMetadata(dataVariables = ['a']) + @DataProviderMetadata(line = -1, dataVariables = ['a']) List dataProvider3() { ['a', 'b', 'c'] } @@ -111,17 +111,17 @@ class EstimatedNumberOfIterations extends Specification { } - @DataProviderMetadata(dataVariables = ['a']) + @DataProviderMetadata(line = -1, dataVariables = ['a']) List dataProvider4_1() { ['a'] } - @DataProviderMetadata(dataVariables = ['b']) + @DataProviderMetadata(line = -1, dataVariables = ['b']) Range dataProvider4_2() { (1..3) } - @DataProviderMetadata(dataVariables = ['c']) + @DataProviderMetadata(line = -1, dataVariables = ['c']) Iterable dataProvider4_3() { [1, 2] } @@ -149,17 +149,17 @@ class EstimatedNumberOfIterations extends Specification { } - @DataProviderMetadata(dataVariables = ['a']) + @DataProviderMetadata(line = -1, dataVariables = ['a']) Iterator dataProvider5_1() { ['a'].iterator() } - @DataProviderMetadata(dataVariables = ['b']) + @DataProviderMetadata(line = -1, dataVariables = ['b']) Range dataProvider5_2() { (1..3) } - @DataProviderMetadata(dataVariables = ['c']) + @DataProviderMetadata(line = -1, dataVariables = ['c']) Iterable dataProvider5_3() { [1, 2] } diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/FinalFields.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/FinalFields.groovy index 5576102283..3511e833f6 100644 --- a/spock-specs/src/test/groovy/org/spockframework/smoke/FinalFields.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/FinalFields.groovy @@ -48,7 +48,7 @@ class FinalFields extends Specification { then: ReadOnlyPropertyException ex = thrown() - ex.message == 'Cannot set readonly property: finalField for class: org.spockframework.smoke.FinalFields' + ex.message ==~ /\QCannot set read\E-?\Qonly property: finalField for class: org.spockframework.smoke.FinalFields\E/ finalField == 'final field' } @@ -61,7 +61,7 @@ class FinalFields extends Specification { then: ReadOnlyPropertyException ex = thrown() - ex.message == 'Cannot set readonly property: finalShared for class: org.spockframework.smoke.FinalFields' + ex.message ==~ /\QCannot set read\E-?\Qonly property: finalShared for class: org.spockframework.smoke.FinalFields\E/ finalShared == 'final shared field' } } diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/ast/AstSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/AstSpec.groovy index f9bcce9486..b83f87817a 100644 --- a/spock-specs/src/test/groovy/org/spockframework/smoke/ast/AstSpec.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/AstSpec.groovy @@ -155,6 +155,9 @@ class Foo { } def "full feature exercise"() { + given: + def snapshotId = (GroovyRuntimeUtil.MAJOR_VERSION >= 5) ? "groovy5" : "" + when: def result = compiler.transpile(''' @Ann @@ -341,12 +344,23 @@ class Ext { ''') then: - snapshotter.assertThat(result.source).matchesSnapshot() + snapshotter.assertThat(result.source).matchesSnapshot(snapshotId) } def "Primitive types are used in AST transformation"() { given: - def snapshotId = (GroovyRuntimeUtil.MAJOR_VERSION >= 4) ? "groovy4" : "" + def snapshotId + switch (GroovyRuntimeUtil.MAJOR_VERSION) { + case 5..Integer.MAX_VALUE: + snapshotId = "groovy5" + break + case 4: + snapshotId = "groovy4" + break + default: + snapshotId = "" + break + } when: def result = compiler.transpileWithImports(''' diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/ast/CleanupBlocksAstSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/CleanupBlocksAstSpec.groovy index 538e1a32e8..6d66a477e7 100644 --- a/spock-specs/src/test/groovy/org/spockframework/smoke/ast/CleanupBlocksAstSpec.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/CleanupBlocksAstSpec.groovy @@ -16,6 +16,7 @@ package org.spockframework.smoke.ast import org.spockframework.EmbeddedSpecification +import org.spockframework.runtime.GroovyRuntimeUtil import org.spockframework.specs.extension.SpockSnapshotter import spock.lang.Snapshot import spock.lang.Issue @@ -55,6 +56,7 @@ def foobar() { def "cleanup rewrite keeps correct method reference for multi-assignments"() { given: snapshotter.specBody() + def snapshotId = (GroovyRuntimeUtil.MAJOR_VERSION >= 5) ? "groovy5" : "" when: def result = compiler.transpileSpecBody(''' @@ -74,6 +76,6 @@ def foobar() { }''', EnumSet.of(Show.METHODS)) then: - snapshotter.assertThat(result.source).matchesSnapshot() + snapshotter.assertThat(result.source).matchesSnapshot(snapshotId) } } diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec.groovy index 10019b8504..fd61d2b1a9 100644 --- a/spock-specs/src/test/groovy/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec.groovy @@ -16,6 +16,7 @@ package org.spockframework.smoke.ast.condition import org.spockframework.EmbeddedSpecification +import org.spockframework.runtime.GroovyRuntimeUtil import org.spockframework.specs.extension.SpockSnapshotter import spock.lang.Snapshot import spock.lang.Issue @@ -52,6 +53,7 @@ def foobar() { def "thrown rewrite keeps correct method reference for multi-assignments"() { given: snapshotter.specBody() + def snapshotId = (GroovyRuntimeUtil.MAJOR_VERSION >= 5) ? "groovy5" : "" when: def result = compiler.transpileSpecBody(''' @@ -68,6 +70,6 @@ def foobar() { }''', EnumSet.of(Show.METHODS)) then: - snapshotter.assertThat(result.source).matchesSnapshot() + snapshotter.assertThat(result.source).matchesSnapshot(snapshotId) } } diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/extension/RepeatableLocalExtensionsSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/extension/RepeatableLocalExtensionsSpec.groovy index c50bd9183a..6d3a1ce3e1 100644 --- a/spock-specs/src/test/groovy/org/spockframework/smoke/extension/RepeatableLocalExtensionsSpec.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/extension/RepeatableLocalExtensionsSpec.groovy @@ -75,9 +75,7 @@ class FooSpec extends Specification { '' || 0 '@Foo' || 1 '@Foo @Foo' || 2 - ' @FooContainer()' || 0 ' @FooContainer([])' || 0 - '@Foo @FooContainer()' || 1 '@Foo @FooContainer([])' || 1 ' @FooContainer(@Foo)' || 1 ' @FooContainer([@Foo])' || 1 @@ -107,9 +105,7 @@ def foo() { '' || 0 '@Foo' || 1 '@Foo @Foo' || 2 - ' @FooContainer()' || 0 ' @FooContainer([])' || 0 - '@Foo @FooContainer()' || 1 '@Foo @FooContainer([])' || 1 ' @FooContainer(@Foo)' || 1 ' @FooContainer([@Foo])' || 1 @@ -142,9 +138,7 @@ def foo() { '' || 0 '@Foo' || 1 '@Foo @Foo' || 2 - ' @FooContainer()' || 0 ' @FooContainer([])' || 0 - '@Foo @FooContainer()' || 1 '@Foo @FooContainer([])' || 1 ' @FooContainer(@Foo)' || 1 ' @FooContainer([@Foo])' || 1 @@ -176,9 +170,7 @@ def foo() { '' || 0 '@Foo' || 1 '@Foo @Foo' || 2 - ' @FooContainer()' || 0 ' @FooContainer([])' || 0 - '@Foo @FooContainer()' || 1 '@Foo @FooContainer([])' || 1 ' @FooContainer(@Foo)' || 1 ' @FooContainer([@Foo])' || 1 diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/mock/GroovyMockAbstractGlobalClass.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/mock/GroovyMockAbstractGlobalClass.groovy index 33b968e1d9..616b2b378b 100644 --- a/spock-specs/src/test/groovy/org/spockframework/smoke/mock/GroovyMockAbstractGlobalClass.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/mock/GroovyMockAbstractGlobalClass.groovy @@ -28,8 +28,11 @@ import spock.lang.* @SuppressWarnings('GroovyAssignabilityCheck') class GroovyMockAbstractGlobalClass extends Specification { private static final String NAME = "Name" + private static String MUTABLE_FIELD private static final String MOCKED_NAME = "MockedName" + private static String outerStaticMethod() { NAME } + @Issue('https://github.com/spockframework/spock/issues/464') def "Creating a Global GroovyMock from abstract class shall not fail"() { given: @@ -47,6 +50,47 @@ class GroovyMockAbstractGlobalClass extends Specification { AbstractClassA.staticName == null } + @Issue('https://github.com/spockframework/spock/issues/2196') + def "Static property access to staticName is working for the non-spied class"() { + expect: + AbstractClassA.staticName == NAME + } + + @Issue('https://github.com/spockframework/spock/issues/2196') + def "Static getProperty() access to staticName is working when a global spy was created"() { + given: + GroovySpy(AbstractClassA, global: true) + + expect: + AbstractClassA.staticName == NAME + } + + @Issue('https://github.com/spockframework/spock/issues/2196') + def "Static setProperty() to staticMutableField is working when a global spy was created"() { + given: + GroovySpy(AbstractClassA, global: true) + + expect: + AbstractClassA.staticMutableField == null + + when: + AbstractClassA.staticMutableField = NAME + then: + AbstractClassA.staticMutableField == NAME + + cleanup: + AbstractClassA.staticMutableField = null + } + + @Issue('https://github.com/spockframework/spock/issues/2196') + def "Static invokeMethod() to callsOuterStaticMethod is working when a global spy was created"() { + given: + GroovySpy(AbstractClassA, global: true) + + expect: + AbstractClassA.callsOuterStaticMethod() == NAME + } + @Issue('https://github.com/spockframework/spock/issues/464') def "Creating a Global GroovySpy from abstract class shall not fail"() { given: @@ -57,6 +101,7 @@ class GroovyMockAbstractGlobalClass extends Specification { then: 1 * AbstractClassA.getStaticName() >> MOCKED_NAME + 0 * _ result == MOCKED_NAME m instanceof AbstractClassA @@ -138,7 +183,21 @@ class GroovyMockAbstractGlobalClass extends Specification { } static abstract class AbstractClassA { - static String getStaticName() { NAME } + static String getStaticName() { + return NAME + } + + static String getStaticMutableField() { + MUTABLE_FIELD + } + + static void setStaticMutableField(String newValue) { + MUTABLE_FIELD = newValue + } + + static String callsOuterStaticMethod() { + return outerStaticMethod() + } @SuppressWarnings('GrMethodMayBeStatic') String getName() { NAME } @@ -149,4 +208,5 @@ class GroovyMockAbstractGlobalClass extends Specification { static abstract class ClassB { static String getStaticName() { NAME } } + } diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/mock/GroovyMockClassWithPropertyMissingSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/mock/GroovyMockClassWithPropertyMissingSpec.groovy new file mode 100644 index 0000000000..837bbae3ba --- /dev/null +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/mock/GroovyMockClassWithPropertyMissingSpec.groovy @@ -0,0 +1,111 @@ +/* + * Copyright 2025 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.mock + + +import org.spockframework.runtime.model.parallel.Resources +import spock.lang.Issue +import spock.lang.ResourceLock +import spock.lang.Specification +import spock.lang.Stepwise + +//We need to execute it sequentially, because we change global metaClass state +@Stepwise +@ResourceLock( + value = Resources.META_CLASS_REGISTRY, + reason = "Global Mocks" +) +@SuppressWarnings('GroovyAssignabilityCheck') +class GroovyMockClassWithPropertyMissingSpec extends Specification { + + @Issue('https://github.com/spockframework/spock/issues/2196') + def "Class with propertyMissing() works normally"() { + given: + def c = new ClassWithPropertyMissing() + expect: + c.prop1 == "prop1" + } + + @Issue('https://github.com/spockframework/spock/issues/2196') + def "Class with propertyMissing() setter works normally"() { + given: + def c = new ClassWithPropertyMissing() + + when: + c.prop1 = "value" + then: + def ex = thrown(IllegalStateException) + ex.message == "propertyMissing(prop1,value) called." + } + + @Issue('https://github.com/spockframework/spock/issues/2196') + def "Class with propertyMissing() as global spy"() { + given: + def c = GroovySpy(ClassWithPropertyMissing, global: true) + + expect: + c.prop1 == "prop1" + } + + @Issue('https://github.com/spockframework/spock/issues/2196') + def "Class with propertyMissing() setter as global spy"() { + given: + def c = GroovySpy(ClassWithPropertyMissing, global: true) + + when: + c.prop1 = "value" + then: + def ex = thrown(IllegalStateException) + ex.message == "propertyMissing(prop1,value) called." + } + + @Issue('https://github.com/spockframework/spock/issues/2196') + def "Spy on synthetic property of a Class with propertyMissing"() { + given: + def c = GroovySpy(ClassWithPropertyMissing, global: true) + + when: + def result = c.prop1 + then: + 1 * c.getProp1() >> "OtherValue" + 0 * _ + result == "OtherValue" + } + + @Issue('https://github.com/spockframework/spock/issues/2196') + def "Spy on synthetic property setter of a Class with propertyMissing"() { + given: + def c = GroovySpy(ClassWithPropertyMissing, global: true) + + when: + c.prop1 = null + then: + 1 * c.setProp1(null) >> "" + 0 * _ + } +} + +class ClassWithPropertyMissing { + def propertyMissing(String name) { + name + } + + def propertyMissing(String name, Object value) { + if (value != null) { + throw new IllegalStateException("propertyMissing($name,$value) called.") + } + } +} diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/mock/GroovyMockPropertySpec.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/mock/GroovyMockPropertySpec.groovy new file mode 100644 index 0000000000..72691eb51b --- /dev/null +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/mock/GroovyMockPropertySpec.groovy @@ -0,0 +1,108 @@ +/* + * Copyright 2025 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.mock + + +import org.spockframework.runtime.model.parallel.Resources +import spock.lang.Issue +import spock.lang.ResourceLock +import spock.lang.Specification +import spock.lang.Stepwise + +//We need to execute it sequentially, because we change global metaClass state +@Stepwise +@ResourceLock( + value = Resources.META_CLASS_REGISTRY, + reason = "Global Mocks" +) +@SuppressWarnings('GroovyAssignabilityCheck') +class GroovyMockPropertySpec extends Specification { + + @Issue('https://github.com/spockframework/spock/issues/2196') + def "Class with propertyMissing() works normally"() { + given: + def c = new ClassWithProperty() + expect: + c.prop1 == null + } + + @Issue('https://github.com/spockframework/spock/issues/2196') + def "Class with propertyMissing() setter works normally"() { + given: + def c = new ClassWithProperty() + + when: + c.prop1 = "value" + then: + c.prop1 == "value" + } + + @Issue('https://github.com/spockframework/spock/issues/2196') + def "Spy on property getter"() { + given: + def c = GroovySpy(ClassWithProperty, global: true) + + when: + def result = c.prop1 + then: + 1 * c.getProp1() >> "value" + 0 * _ + result == "value" + } + + @Issue('https://github.com/spockframework/spock/issues/2196') + def "Spy on property getter method"() { + given: + def c = GroovySpy(ClassWithProperty, global: true) + + when: + def result = c.getProp1() + then: + 1 * c.getProp1() >> "value" + 0 * _ + result == "value" + } + + @Issue('https://github.com/spockframework/spock/issues/2196') + def "Spy on property setter"() { + given: + def c = GroovySpy(ClassWithProperty, global: true) + + when: + c.prop1 = "value" + then: + 1 * c.setProp1("value") + 0 * _ + } + + @Issue('https://github.com/spockframework/spock/issues/2196') + def "Spy on property setter method"() { + given: + def c = GroovySpy(ClassWithProperty, global: true) + + when: + c.setProp1("value") + then: + 1 * c.setProp1("value") + 0 * _ + } + + class ClassWithProperty { + String prop1 + } +} + + diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/mock/GroovySpiesThatAreGlobal.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/mock/GroovySpiesThatAreGlobal.groovy index 6a73819ea4..c519d4fbc1 100644 --- a/spock-specs/src/test/groovy/org/spockframework/smoke/mock/GroovySpiesThatAreGlobal.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/mock/GroovySpiesThatAreGlobal.groovy @@ -14,6 +14,7 @@ package org.spockframework.smoke.mock +import org.spockframework.runtime.GroovyRuntimeUtil import spock.lang.* import spock.util.environment.Jvm @@ -246,6 +247,1418 @@ class GroovySpiesThatAreGlobal extends Specification { _ | { PersonWithOverloadedMethods.perform((Pattern) null) } } + def "Accessing non-existing property throws MissingPropertyException"() { + when: + Enclosing.Super.NON_EXISTING_FIELD + then: + def ex = thrown(MissingPropertyException) + ex.message.contains("NON_EXISTING_FIELD") + } + + def "Accessing non-existing property through global spy throws MissingPropertyException"() { + given: + GroovySpy(Enclosing.Super, global: true) + + when: + Enclosing.Super.NON_EXISTING_FIELD + then: + def ex = thrown(MissingPropertyException) + ex.message.contains(" NON_EXISTING_FIELD ") + def suppressed = ex.getSuppressed()[0].cause + suppressed instanceof MissingMethodException + suppressed.message.contains("getNON_EXISTING_FIELD") + } + + def "Accessing non-existing property setter throws MissingPropertyException"() { + when: + Enclosing.Super.NON_EXISTING_FIELD = "" + then: + thrown(MissingPropertyException) + } + + def "Accessing non-existing property setter through global spy throws MissingPropertyException"() { + given: + GroovySpy(Enclosing.Super, global: true) + + when: + Enclosing.Super.NON_EXISTING_FIELD = "" + then: + def ex = thrown(MissingPropertyException) + ex.message.contains(" NON_EXISTING_FIELD ") + def suppressed = ex.getSuppressed()[0].cause + suppressed instanceof MissingMethodException + suppressed.message.contains("setNON_EXISTING_FIELD") + } + + def "Accessing static field through global spy shall only log single invocation"() { + given: + GroovySpy(Enclosing.Super, global: true) + + when: + def result = Enclosing.Super.STATIC_FIELD + then: + 1 * _ + 0 * _ + result == result + } + + /** + * This test should verify that the resolution logic around fields and properties + * works identically when normal Groovy logic is used and when a global spy + * intercepts but no stubbing was done. + */ + def "Accessing real fields through global spy works like in reality"() { + given: + if (createSpies) { + GroovySpy(Enclosing.Super, global: true) + GroovySpy(Enclosing.Sub, global: true) + GroovySpy(Enclosing.ShadowingSub, global: true) + } + + and: + def supr = new Enclosing.Super() + def sub = new Enclosing.Sub() + def shadowingSub = new Enclosing.ShadowingSub() + + expect: + verifyAll { + Enclosing.Super.STATIC_FIELD == "super static" + Enclosing.Super.PUBLIC_STATIC_FIELD == "public super static" + Enclosing.Super.STATIC_PROPERTY == "super static property" + Enclosing.Super.getSTATIC_PROPERTY() == "super static property" + supr.STATIC_FIELD == "super static" + supr.PUBLIC_STATIC_FIELD == "public super static" + supr.STATIC_PROPERTY == "super static property" + supr.getSTATIC_PROPERTY() == "super static property" + supr.FIELD == "super" + supr.PUBLIC_FIELD == "public super" + supr.PROPERTY == "super property" + Enclosing.Super.retrieveEnclosingField() == "enclosing" + Enclosing.Super.retrievePublicEnclosingField() == "public enclosing" + Enclosing.Super.retrieveEnclosingProperty() == "enclosing property" + Enclosing.Super.retrieveStaticField() == "super static" + Enclosing.Super.retrievePublicStaticField() == "public super static" + Enclosing.Super.retrieveStaticProperty() == "super static property" + supr.retrieveEnclosingField() == "enclosing" + supr.retrievePublicEnclosingField() == "public enclosing" + supr.retrieveEnclosingProperty() == "enclosing property" + supr.retrieveStaticField() == "super static" + supr.retrievePublicStaticField() == "public super static" + supr.retrieveStaticProperty() == "super static property" + supr.retrieveEnclosingFieldFromInstance() == "enclosing" + supr.retrievePublicEnclosingFieldFromInstance() == "public enclosing" + supr.retrieveEnclosingPropertyFromInstance() == "enclosing property" + supr.retrieveStaticFieldFromInstance() == "super static" + supr.retrievePublicStaticFieldFromInstance() == "public super static" + supr.retrieveStaticPropertyFromInstance() == "super static property" + supr.retrieveField() == "super" + supr.retrievePublicField() == "public super" + supr.retrieveProperty() == "super property" + + Enclosing.Sub.PUBLIC_STATIC_FIELD == "public super static" + Enclosing.Sub.STATIC_PROPERTY == "super static property" + Enclosing.Sub.getSTATIC_PROPERTY() == "super static property" + sub.PUBLIC_STATIC_FIELD == "public super static" + sub.STATIC_PROPERTY == "super static property" + sub.getSTATIC_PROPERTY() == "super static property" + sub.PUBLIC_FIELD == "public super" + sub.PROPERTY == "super property" + Enclosing.Sub.retrieveEnclosingField() == "enclosing" + Enclosing.Sub.retrievePublicEnclosingField() == "public enclosing" + Enclosing.Sub.retrieveEnclosingProperty() == "enclosing property" + Enclosing.Sub.retrieveStaticField() == "super static" + Enclosing.Sub.retrievePublicStaticField() == "public super static" + Enclosing.Sub.retrieveStaticProperty() == "super static property" + sub.retrieveEnclosingField() == "enclosing" + sub.retrievePublicEnclosingField() == "public enclosing" + sub.retrieveEnclosingProperty() == "enclosing property" + sub.retrieveStaticField() == "super static" + sub.retrievePublicStaticField() == "public super static" + sub.retrieveStaticProperty() == "super static property" + sub.retrieveEnclosingFieldFromInstance() == "enclosing" + sub.retrievePublicEnclosingFieldFromInstance() == "public enclosing" + sub.retrieveEnclosingPropertyFromInstance() == "enclosing property" + sub.retrieveStaticFieldFromInstance() == "super static" + sub.retrievePublicStaticFieldFromInstance() == "public super static" + sub.retrieveStaticPropertyFromInstance() == "super static property" + sub.retrieveField() == "super" + sub.retrievePublicField() == "public super" + sub.retrieveProperty() == "super property" + Enclosing.Sub.SUB_STATIC_FIELD == "sub static" + Enclosing.Sub.PUBLIC_SUB_STATIC_FIELD == "public sub static" + Enclosing.Sub.SUB_STATIC_PROPERTY == "sub static property" + Enclosing.Sub.getSUB_STATIC_PROPERTY() == "sub static property" + sub.SUB_STATIC_FIELD == "sub static" + sub.PUBLIC_SUB_STATIC_FIELD == "public sub static" + sub.SUB_STATIC_PROPERTY == "sub static property" + sub.getSUB_STATIC_PROPERTY() == "sub static property" + sub.SUB_FIELD == "sub" + sub.PUBLIC_SUB_FIELD == "public sub" + sub.SUB_PROPERTY == "sub property" + sub.getSUB_PROPERTY() == "sub property" + Enclosing.Sub.retrieveSuperPublicStaticField() == "public super static" + Enclosing.Sub.retrieveSuperStaticProperty() == "super static property" + sub.retrieveSuperPublicStaticField() == "public super static" + sub.retrieveSuperStaticProperty() == "super static property" + Enclosing.Sub.retrieveSubStaticField() == "sub static" + Enclosing.Sub.retrievePublicSubStaticField() == "public sub static" + Enclosing.Sub.retrieveSubStaticProperty() == "sub static property" + sub.retrieveSubStaticField() == "sub static" + sub.retrievePublicSubStaticField() == "public sub static" + sub.retrieveSubStaticProperty() == "sub static property" + sub.retrieveSuperPublicStaticFieldFromInstance() == "public super static" + sub.retrieveSuperStaticPropertyFromInstance() == "super static property" + sub.retrieveSubStaticFieldFromInstance() == "sub static" + sub.retrievePublicSubStaticFieldFromInstance() == "public sub static" + sub.retrieveSubStaticPropertyFromInstance() == "sub static property" + sub.retrieveSuperPublicField() == "public super" + sub.retrieveSuperProperty() == "super property" + sub.retrieveSubField() == "sub" + sub.retrievePublicSubField() == "public sub" + sub.retrieveSubProperty() == "sub property" + + Enclosing.ShadowingSub.STATIC_FIELD == "shadowing sub static" + Enclosing.ShadowingSub.PUBLIC_STATIC_FIELD == "public shadowing sub static" + Enclosing.ShadowingSub.STATIC_PROPERTY == "shadowing sub static property" + Enclosing.ShadowingSub.getSTATIC_PROPERTY() == "shadowing sub static property" + shadowingSub.STATIC_FIELD == "shadowing sub static" + shadowingSub.PUBLIC_STATIC_FIELD == "public shadowing sub static" + shadowingSub.STATIC_PROPERTY == "shadowing sub static property" + shadowingSub.getSTATIC_PROPERTY() == "shadowing sub static property" + shadowingSub.FIELD == "shadowing sub" + shadowingSub.PUBLIC_FIELD == "public shadowing sub" + shadowingSub.PROPERTY == "shadowing sub property" + shadowingSub.getPROPERTY() == "shadowing sub property" + Enclosing.ShadowingSub.retrieveStaticField() == "super static" + Enclosing.ShadowingSub.retrievePublicStaticField() == "public super static" + Enclosing.ShadowingSub.retrieveStaticProperty() == "super static property" + shadowingSub.retrieveStaticField() == "super static" + shadowingSub.retrievePublicStaticField() == "public super static" + shadowingSub.retrieveStaticProperty() == "super static property" + shadowingSub.retrieveStaticFieldFromInstance() == "super static" + shadowingSub.retrievePublicStaticFieldFromInstance() == "public super static" + shadowingSub.retrieveStaticPropertyFromInstance() == "super static property" + shadowingSub.retrieveField() == "super" + shadowingSub.retrievePublicField() == "public super" + shadowingSub.retrieveProperty() == "super property" + Enclosing.ShadowingSub.retrieveSubStaticField() == "shadowing sub static" + Enclosing.ShadowingSub.retrievePublicSubStaticField() == "public shadowing sub static" + Enclosing.ShadowingSub.retrieveSubStaticProperty() == "shadowing sub static property" + shadowingSub.retrieveSubStaticField() == "shadowing sub static" + shadowingSub.retrievePublicSubStaticField() == "public shadowing sub static" + shadowingSub.retrieveSubStaticProperty() == "shadowing sub static property" + shadowingSub.retrieveSubStaticFieldFromInstance() == "shadowing sub static" + shadowingSub.retrievePublicSubStaticFieldFromInstance() == "public shadowing sub static" + shadowingSub.retrieveSubStaticPropertyFromInstance() == "shadowing sub static property" + shadowingSub.retrieveSuperPublicField() == "public super" + shadowingSub.retrieveSuperProperty() == "super property" + shadowingSub.retrieveSubField() == "shadowing sub" + shadowingSub.retrievePublicSubField() == "public shadowing sub" + shadowingSub.retrieveSubProperty() == "shadowing sub property" + } + + when: + Enclosing.Super.getSTATIC_FIELD() == "super static" + + then: + thrown(MissingMethodException) + + when: + Enclosing.Super.getPUBLIC_STATIC_FIELD() == "public super static" + + then: + thrown(MissingMethodException) + + when: + supr.getSTATIC_FIELD() == "super static" + + then: + thrown(MissingMethodException) + + when: + supr.getPUBLIC_STATIC_FIELD() == "public super static" + + then: + thrown(MissingMethodException) + + when: + supr.getFIELD() == "super" + + then: + thrown(MissingMethodException) + + when: + supr.getPUBLIC_FIELD() == "public super" + + then: + thrown(MissingMethodException) + + when: + supr.getPROPERTY() == "super property" + + then: + thrown(MissingMethodException) + + when: + Enclosing.Sub.STATIC_FIELD == "super static" + + then: + thrown(MissingPropertyException) + + when: + Enclosing.Sub.getSTATIC_FIELD() == "super static" + + then: + thrown(MissingMethodException) + + when: + Enclosing.Sub.getPUBLIC_STATIC_FIELD() == "public super static" + + then: + thrown(MissingMethodException) + + when: + sub.STATIC_FIELD == "super static" + + then: + thrown(MissingPropertyException) + + when: + sub.getSTATIC_FIELD() == "super static" + + then: + thrown(MissingMethodException) + + when: + sub.getPUBLIC_STATIC_FIELD() == "public super static" + + then: + thrown(MissingMethodException) + + when: + sub.FIELD == "super" + + then: + thrown(MissingPropertyException) + + when: + sub.getFIELD() == "super" + + then: + thrown(MissingMethodException) + + when: + sub.getPUBLIC_FIELD() == "public super" + + then: + thrown(MissingMethodException) + + when: + sub.getPROPERTY() == "super property" + + then: + thrown(MissingMethodException) + + when: + Enclosing.Sub.getSUB_STATIC_FIELD() == "sub static" + + then: + thrown(MissingMethodException) + + when: + Enclosing.Sub.getPUBLIC_SUB_STATIC_FIELD() == "public sub static" + + then: + thrown(MissingMethodException) + + when: + sub.getSUB_STATIC_FIELD() == "sub static" + + then: + thrown(MissingMethodException) + + when: + sub.getPUBLIC_SUB_STATIC_FIELD() == "public sub static" + + then: + thrown(MissingMethodException) + + when: + sub.getSUB_FIELD() == "sub" + + then: + thrown(MissingMethodException) + + when: + sub.getPUBLIC_SUB_FIELD() == "public sub" + + then: + thrown(MissingMethodException) + + when: + Enclosing.Sub.retrieveSuperStaticField() == "super static" + + then: + thrown(MissingPropertyException) + + when: + sub.retrieveSuperStaticField() == "super static" + + then: + thrown(MissingPropertyException) + + when: + sub.retrieveSuperStaticFieldFromInstance() == "super static" + + then: + thrown(MissingPropertyException) + + when: + sub.retrieveSuperField() == "super" + + then: + thrown(MissingPropertyException) + + when: + Enclosing.ShadowingSub.getSTATIC_FIELD() == "shadowing sub static" + + then: + thrown(MissingMethodException) + + when: + Enclosing.ShadowingSub.getPUBLIC_STATIC_FIELD() == "public shadowing sub static" + + then: + thrown(MissingMethodException) + + when: + shadowingSub.getSTATIC_FIELD() == "shadowing sub static" + + then: + thrown(MissingMethodException) + + when: + shadowingSub.getPUBLIC_STATIC_FIELD() == "public shadowing sub static" + + then: + thrown(MissingMethodException) + + when: + shadowingSub.getFIELD() == "shadowing sub" + + then: + thrown(MissingMethodException) + + when: + shadowingSub.getPUBLIC_FIELD() == "public shadowing sub" + + then: + thrown(MissingMethodException) + + when: + shadowingSub.retrieveSuperField() == "super" + + then: + thrown((GroovyRuntimeUtil.MAJOR_VERSION >= 4) ? MissingPropertyException : MissingMethodException) + + when: + def i = 0 + Enclosing.Super.STATIC_FIELD = "foo ${++i}" + + then: + Enclosing.Super.STATIC_FIELD == "foo $i" + + when: + Enclosing.Super.PUBLIC_STATIC_FIELD = "foo ${++i}" + + then: + Enclosing.Super.PUBLIC_STATIC_FIELD == "foo $i" + + when: + Enclosing.Super.STATIC_PROPERTY = "foo ${++i}" + + then: + Enclosing.Super.STATIC_PROPERTY == "foo $i" + + when: + Enclosing.Super.setSTATIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + Enclosing.Super.setPUBLIC_STATIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + Enclosing.Super.setSTATIC_PROPERTY("foo ${++i}") + + then: + Enclosing.Super.getSTATIC_PROPERTY() == "foo $i" + + when: + supr.STATIC_FIELD = "foo ${++i}" + + then: + supr.STATIC_FIELD == "foo $i" + + when: + supr.PUBLIC_STATIC_FIELD = "foo ${++i}" + + then: + supr.PUBLIC_STATIC_FIELD == "foo $i" + + when: + supr.STATIC_PROPERTY = "foo ${++i}" + + then: + supr.STATIC_PROPERTY == "foo $i" + + when: + supr.setSTATIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + supr.setPUBLIC_STATIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + supr.setSTATIC_PROPERTY("foo ${++i}") + + then: + supr.getSTATIC_PROPERTY() == "foo $i" + + when: + supr.FIELD = "foo ${++i}" + + then: + supr.FIELD == "foo $i" + + when: + supr.PUBLIC_FIELD = "foo ${++i}" + + then: + supr.PUBLIC_FIELD == "foo $i" + + when: + supr.PROPERTY = "foo ${++i}" + + then: + supr.PROPERTY == "foo $i" + + when: + supr.setFIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + supr.setPUBLIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + supr.setPROPERTY("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + Enclosing.Super.overwriteEnclosingField("foo ${++i}") + + then: + Enclosing.Super.retrieveEnclosingField() == "foo $i" + + when: + Enclosing.Super.overwritePublicEnclosingField("foo ${++i}") + + then: + Enclosing.Super.retrievePublicEnclosingField() == "foo $i" + + when: + Enclosing.Super.overwriteEnclosingProperty("foo ${++i}") + + then: + Enclosing.Super.retrieveEnclosingProperty() == "foo $i" + + when: + Enclosing.Super.overwriteStaticField("foo ${++i}") + + then: + Enclosing.Super.retrieveStaticField() == "foo $i" + + when: + Enclosing.Super.overwritePublicStaticField("foo ${++i}") + + then: + Enclosing.Super.retrievePublicStaticField() == "foo $i" + + when: + Enclosing.Super.overwriteStaticProperty("foo ${++i}") + + then: + Enclosing.Super.retrieveStaticProperty() == "foo $i" + + when: + supr.overwriteEnclosingField("foo ${++i}") + + then: + supr.retrieveEnclosingField() == "foo $i" + + when: + supr.overwritePublicEnclosingField("foo ${++i}") + + then: + supr.retrievePublicEnclosingField() == "foo $i" + + when: + supr.overwriteEnclosingProperty("foo ${++i}") + + then: + supr.retrieveEnclosingProperty() == "foo $i" + + when: + supr.overwriteStaticField("foo ${++i}") + + then: + supr.retrieveStaticField() == "foo $i" + + when: + supr.overwritePublicStaticField("foo ${++i}") + + then: + supr.retrievePublicStaticField() == "foo $i" + + when: + supr.overwriteStaticProperty("foo ${++i}") + + then: + supr.retrieveStaticProperty() == "foo $i" + + when: + supr.overwriteEnclosingFieldFromInstance("foo ${++i}") + + then: + supr.retrieveEnclosingFieldFromInstance() == "foo $i" + + when: + supr.overwritePublicEnclosingFieldFromInstance("foo ${++i}") + + then: + supr.retrievePublicEnclosingFieldFromInstance() == "foo $i" + + when: + supr.overwriteEnclosingPropertyFromInstance("foo ${++i}") + + then: + supr.retrieveEnclosingPropertyFromInstance() == "foo $i" + + when: + supr.overwriteStaticFieldFromInstance("foo ${++i}") + + then: + supr.retrieveStaticFieldFromInstance() == "foo $i" + + when: + supr.overwritePublicStaticFieldFromInstance("foo ${++i}") + + then: + supr.retrievePublicStaticFieldFromInstance() == "foo $i" + + when: + supr.overwriteStaticPropertyFromInstance("foo ${++i}") + + then: + supr.retrieveStaticPropertyFromInstance() == "foo $i" + + when: + supr.overwriteField("foo ${++i}") + + then: + supr.retrieveField() == "foo $i" + + when: + supr.overwritePublicField("foo ${++i}") + + then: + supr.retrievePublicField() == "foo $i" + + when: + supr.overwriteProperty("foo ${++i}") + + then: + supr.retrieveProperty() == "foo $i" + + when: + Enclosing.Sub.STATIC_FIELD = "foo ${++i}" + + then: + thrown(MissingPropertyException) + + when: + Enclosing.Sub.PUBLIC_STATIC_FIELD = "foo ${++i}" + + then: + Enclosing.Sub.PUBLIC_STATIC_FIELD == "foo $i" + + when: + Enclosing.Sub.STATIC_PROPERTY = "foo ${++i}" + + then: + Enclosing.Sub.STATIC_PROPERTY == "foo $i" + + when: + Enclosing.Sub.setSTATIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + Enclosing.Sub.setPUBLIC_STATIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + Enclosing.Sub.setSTATIC_PROPERTY("foo ${++i}") + + then: + Enclosing.Sub.getSTATIC_PROPERTY() == "foo $i" + + when: + sub.STATIC_FIELD = "foo ${++i}" + + then: + thrown(MissingPropertyException) + + when: + sub.PUBLIC_STATIC_FIELD = "foo ${++i}" + + then: + sub.PUBLIC_STATIC_FIELD == "foo $i" + + when: + sub.STATIC_PROPERTY = "foo ${++i}" + + then: + sub.STATIC_PROPERTY == "foo $i" + + when: + sub.setSTATIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + sub.setPUBLIC_STATIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + sub.setSTATIC_PROPERTY("foo ${++i}") + + then: + sub.getSTATIC_PROPERTY() == "foo $i" + + when: + sub.FIELD = "foo ${++i}" + + then: + thrown(MissingPropertyException) + + when: + sub.PUBLIC_FIELD = "foo ${++i}" + + then: + sub.PUBLIC_FIELD == "foo $i" + + when: + sub.setFIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + sub.setPUBLIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + sub.setPROPERTY("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + sub.PROPERTY = "foo ${++i}" + + then: + sub.PROPERTY == "foo $i" + + when: + Enclosing.Sub.overwriteEnclosingField("foo ${++i}") + + then: + Enclosing.Sub.retrieveEnclosingField() == "foo $i" + + when: + Enclosing.Sub.overwritePublicEnclosingField("foo ${++i}") + + then: + Enclosing.Sub.retrievePublicEnclosingField() == "foo $i" + + when: + Enclosing.Sub.overwriteEnclosingProperty("foo ${++i}") + + then: + Enclosing.Sub.retrieveEnclosingProperty() == "foo $i" + + when: + Enclosing.Sub.overwriteStaticField("foo ${++i}") + + then: + Enclosing.Sub.retrieveStaticField() == "foo $i" + + when: + Enclosing.Sub.overwritePublicStaticField("foo ${++i}") + + then: + Enclosing.Sub.retrievePublicStaticField() == "foo $i" + + when: + Enclosing.Sub.overwriteStaticProperty("foo ${++i}") + + then: + Enclosing.Sub.retrieveStaticProperty() == "foo $i" + + when: + sub.overwriteEnclosingField("foo ${++i}") + + then: + sub.retrieveEnclosingField() == "foo $i" + + when: + sub.overwritePublicEnclosingField("foo ${++i}") + + then: + sub.retrievePublicEnclosingField() == "foo $i" + + when: + sub.overwriteEnclosingProperty("foo ${++i}") + + then: + sub.retrieveEnclosingProperty() == "foo $i" + + when: + sub.overwriteStaticField("foo ${++i}") + + then: + sub.retrieveStaticField() == "foo $i" + + when: + sub.overwritePublicStaticField("foo ${++i}") + + then: + sub.retrievePublicStaticField() == "foo $i" + + when: + sub.overwriteStaticProperty("foo ${++i}") + + then: + sub.retrieveStaticProperty() == "foo $i" + + when: + sub.overwriteEnclosingFieldFromInstance("foo ${++i}") + + then: + sub.retrieveEnclosingFieldFromInstance() == "foo $i" + + when: + sub.overwritePublicEnclosingFieldFromInstance("foo ${++i}") + + then: + sub.retrievePublicEnclosingFieldFromInstance() == "foo $i" + + when: + sub.overwriteEnclosingPropertyFromInstance("foo ${++i}") + + then: + sub.retrieveEnclosingPropertyFromInstance() == "foo $i" + + when: + sub.overwriteStaticFieldFromInstance("foo ${++i}") + + then: + sub.retrieveStaticFieldFromInstance() == "foo $i" + + when: + sub.overwritePublicStaticFieldFromInstance("foo ${++i}") + + then: + sub.retrievePublicStaticFieldFromInstance() == "foo $i" + + when: + sub.overwriteStaticPropertyFromInstance("foo ${++i}") + + then: + sub.retrieveStaticPropertyFromInstance() == "foo $i" + + when: + sub.overwriteField("foo ${++i}") + + then: + sub.retrieveField() == "foo $i" + + when: + sub.overwritePublicField("foo ${++i}") + + then: + sub.retrievePublicField() == "foo $i" + + when: + sub.overwriteProperty("foo ${++i}") + + then: + sub.retrieveProperty() == "foo $i" + + when: + Enclosing.Sub.SUB_STATIC_FIELD = "foo ${++i}" + + then: + Enclosing.Sub.SUB_STATIC_FIELD == "foo $i" + + when: + Enclosing.Sub.PUBLIC_SUB_STATIC_FIELD = "foo ${++i}" + + then: + Enclosing.Sub.PUBLIC_SUB_STATIC_FIELD == "foo $i" + + when: + Enclosing.Sub.SUB_STATIC_PROPERTY = "foo ${++i}" + + then: + Enclosing.Sub.SUB_STATIC_PROPERTY == "foo $i" + + when: + Enclosing.Sub.setSUB_STATIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + Enclosing.Sub.setPUBLIC_SUB_STATIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + Enclosing.Sub.setSUB_STATIC_PROPERTY("foo ${++i}") + + then: + Enclosing.Sub.getSUB_STATIC_PROPERTY() == "foo $i" + + when: + sub.SUB_STATIC_FIELD = "foo ${++i}" + + then: + sub.SUB_STATIC_FIELD == "foo $i" + + when: + sub.PUBLIC_SUB_STATIC_FIELD = "foo ${++i}" + + then: + sub.PUBLIC_SUB_STATIC_FIELD == "foo $i" + + when: + sub.SUB_STATIC_PROPERTY = "foo ${++i}" + + then: + sub.SUB_STATIC_PROPERTY == "foo $i" + + when: + sub.setSUB_STATIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + sub.setPUBLIC_SUB_STATIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + sub.setSUB_STATIC_PROPERTY("foo ${++i}") + + then: + sub.getSUB_STATIC_PROPERTY() == "foo $i" + + when: + sub.SUB_FIELD = "foo ${++i}" + + then: + sub.SUB_FIELD == "foo $i" + + when: + sub.PUBLIC_SUB_FIELD = "foo ${++i}" + + then: + sub.PUBLIC_SUB_FIELD == "foo $i" + + when: + sub.SUB_PROPERTY = "foo ${++i}" + + then: + sub.SUB_PROPERTY == "foo $i" + + when: + sub.setSUB_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + sub.setPUBLIC_SUB_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + sub.setSUB_PROPERTY("foo ${++i}") + + then: + sub.getSUB_PROPERTY() == "foo $i" + + when: + Enclosing.Sub.overwriteSuperStaticField("foo ${++i}") + + then: + thrown(MissingPropertyException) + + when: + Enclosing.Sub.overwriteSuperPublicStaticField("foo ${++i}") + + then: + Enclosing.Sub.retrieveSuperPublicStaticField() == "foo $i" + + when: + Enclosing.Sub.overwriteSuperStaticProperty("foo ${++i}") + + then: + Enclosing.Sub.retrieveSuperStaticProperty() == "foo $i" + + when: + sub.overwriteSuperStaticField("foo ${++i}") + + then: + thrown(MissingPropertyException) + + when: + sub.overwriteSuperPublicStaticField("foo ${++i}") + + then: + sub.retrieveSuperPublicStaticField() == "foo $i" + + when: + sub.overwriteSuperStaticProperty("foo ${++i}") + + then: + sub.retrieveSuperStaticProperty() == "foo $i" + + when: + Enclosing.Sub.overwriteSubStaticField("foo ${++i}") + + then: + Enclosing.Sub.retrieveSubStaticField() == "foo $i" + + when: + Enclosing.Sub.overwritePublicSubStaticField("foo ${++i}") + + then: + Enclosing.Sub.retrievePublicSubStaticField() == "foo $i" + + when: + Enclosing.Sub.overwriteSubStaticProperty("foo ${++i}") + + then: + Enclosing.Sub.retrieveSubStaticProperty() == "foo $i" + + when: + sub.overwriteSubStaticField("foo ${++i}") + + then: + sub.retrieveSubStaticField() == "foo $i" + + when: + sub.overwritePublicSubStaticField("foo ${++i}") + + then: + sub.retrievePublicSubStaticField() == "foo $i" + + when: + sub.overwriteSubStaticProperty("foo ${++i}") + + then: + sub.retrieveSubStaticProperty() == "foo $i" + + when: + sub.overwriteSuperStaticFieldFromInstance("foo ${++i}") + + then: + thrown(MissingPropertyException) + + when: + sub.overwriteSuperPublicStaticFieldFromInstance("foo ${++i}") + + then: + sub.retrieveSuperPublicStaticFieldFromInstance() == "foo $i" + + when: + sub.overwriteSuperStaticPropertyFromInstance("foo ${++i}") + + then: + sub.retrieveSuperStaticPropertyFromInstance() == "foo $i" + + when: + sub.overwriteSubStaticFieldFromInstance("foo ${++i}") + + then: + sub.retrieveSubStaticFieldFromInstance() == "foo $i" + + when: + sub.overwritePublicSubStaticFieldFromInstance("foo ${++i}") + + then: + sub.retrievePublicSubStaticFieldFromInstance() == "foo $i" + + when: + sub.overwriteSubStaticPropertyFromInstance("foo ${++i}") + + then: + sub.retrieveSubStaticPropertyFromInstance() == "foo $i" + + when: + sub.overwriteSuperField("foo ${++i}") + + then: + thrown(MissingPropertyException) + + when: + sub.overwriteSuperPublicField("foo ${++i}") + + then: + sub.retrieveSuperPublicField() == "foo $i" + + when: + sub.overwriteSuperProperty("foo ${++i}") + + then: + sub.retrieveSuperProperty() == "foo $i" + + when: + sub.overwriteSubField("foo ${++i}") + + then: + sub.retrieveSubField() == "foo $i" + + when: + sub.overwritePublicSubField("foo ${++i}") + + then: + sub.retrievePublicSubField() == "foo $i" + + when: + sub.overwriteSubProperty("foo ${++i}") + + then: + sub.retrieveSubProperty() == "foo $i" + + when: + Enclosing.ShadowingSub.STATIC_FIELD = "foo ${++i}" + + then: + Enclosing.ShadowingSub.STATIC_FIELD == "foo $i" + + when: + Enclosing.ShadowingSub.PUBLIC_STATIC_FIELD = "foo ${++i}" + + then: + Enclosing.ShadowingSub.PUBLIC_STATIC_FIELD == "foo $i" + + when: + Enclosing.ShadowingSub.STATIC_PROPERTY = "foo ${++i}" + + then: + Enclosing.ShadowingSub.STATIC_PROPERTY == "foo $i" + + when: + Enclosing.ShadowingSub.setSTATIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + Enclosing.ShadowingSub.setPUBLIC_STATIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + Enclosing.ShadowingSub.setSTATIC_PROPERTY("foo ${++i}") + + then: + Enclosing.ShadowingSub.getSTATIC_PROPERTY() == "foo $i" + + when: + shadowingSub.STATIC_FIELD = "foo ${++i}" + + then: + shadowingSub.STATIC_FIELD == "foo $i" + + when: + shadowingSub.PUBLIC_STATIC_FIELD = "foo ${++i}" + + then: + shadowingSub.PUBLIC_STATIC_FIELD == "foo $i" + + when: + shadowingSub.STATIC_PROPERTY = "foo ${++i}" + + then: + shadowingSub.STATIC_PROPERTY == "foo $i" + + when: + shadowingSub.setSTATIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + shadowingSub.setPUBLIC_STATIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + shadowingSub.setSTATIC_PROPERTY("foo ${++i}") + + then: + shadowingSub.getSTATIC_PROPERTY() == "foo $i" + + when: + shadowingSub.FIELD = "foo ${++i}" + + then: + shadowingSub.FIELD == "foo $i" + + when: + shadowingSub.PUBLIC_FIELD = "foo ${++i}" + + then: + shadowingSub.PUBLIC_FIELD == "foo $i" + + when: + shadowingSub.PROPERTY = "foo ${++i}" + + then: + shadowingSub.PROPERTY == "foo $i" + + when: + shadowingSub.setFIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + shadowingSub.setPUBLIC_FIELD("foo ${++i}") + + then: + thrown(MissingMethodException) + + when: + shadowingSub.setPROPERTY("foo ${++i}") + + then: + shadowingSub.getPROPERTY() == "foo $i" + + when: + Enclosing.ShadowingSub.overwriteStaticField("foo ${++i}") + + then: + Enclosing.ShadowingSub.retrieveStaticField() == "foo $i" + + when: + Enclosing.ShadowingSub.overwritePublicStaticField("foo ${++i}") + + then: + Enclosing.ShadowingSub.retrievePublicStaticField() == "foo $i" + + when: + Enclosing.ShadowingSub.overwriteStaticProperty("foo ${++i}") + + then: + Enclosing.ShadowingSub.retrieveStaticProperty() == "foo $i" + + when: + shadowingSub.overwriteStaticField("foo ${++i}") + + then: + shadowingSub.retrieveStaticField() == "foo $i" + + when: + shadowingSub.overwritePublicStaticField("foo ${++i}") + + then: + shadowingSub.retrievePublicStaticField() == "foo $i" + + when: + shadowingSub.overwriteStaticProperty("foo ${++i}") + + then: + shadowingSub.retrieveStaticProperty() == "foo $i" + + when: + shadowingSub.overwriteStaticFieldFromInstance("foo ${++i}") + + then: + shadowingSub.retrieveStaticFieldFromInstance() == "foo $i" + + when: + shadowingSub.overwritePublicStaticFieldFromInstance("foo ${++i}") + + then: + shadowingSub.retrievePublicStaticFieldFromInstance() == "foo $i" + + when: + shadowingSub.overwriteStaticPropertyFromInstance("foo ${++i}") + + then: + shadowingSub.retrieveStaticPropertyFromInstance() == "foo $i" + + when: + shadowingSub.overwriteField("foo ${++i}") + + then: + shadowingSub.retrieveField() == "foo $i" + + when: + shadowingSub.overwritePublicField("foo ${++i}") + + then: + shadowingSub.retrievePublicField() == "foo $i" + + when: + shadowingSub.overwriteProperty("foo ${++i}") + + then: + shadowingSub.retrieveProperty() == "foo $i" + + when: + Enclosing.ShadowingSub.overwriteSubStaticField("foo ${++i}") + + then: + Enclosing.ShadowingSub.retrieveSubStaticField() == "foo $i" + + when: + Enclosing.ShadowingSub.overwritePublicSubStaticField("foo ${++i}") + + then: + Enclosing.ShadowingSub.retrievePublicSubStaticField() == "foo $i" + + when: + Enclosing.ShadowingSub.overwriteSubStaticProperty("foo ${++i}") + + then: + Enclosing.ShadowingSub.retrieveSubStaticProperty() == "foo $i" + + when: + shadowingSub.overwriteSubStaticField("foo ${++i}") + + then: + shadowingSub.retrieveSubStaticField() == "foo $i" + + when: + shadowingSub.overwritePublicSubStaticField("foo ${++i}") + + then: + shadowingSub.retrievePublicSubStaticField() == "foo $i" + + when: + shadowingSub.overwriteSubStaticProperty("foo ${++i}") + + then: + shadowingSub.retrieveSubStaticProperty() == "foo $i" + + when: + shadowingSub.overwriteSubStaticFieldFromInstance("foo ${++i}") + + then: + shadowingSub.retrieveSubStaticFieldFromInstance() == "foo $i" + + when: + shadowingSub.overwritePublicSubStaticFieldFromInstance("foo ${++i}") + + then: + shadowingSub.retrievePublicSubStaticFieldFromInstance() == "foo $i" + + when: + shadowingSub.overwriteSubStaticPropertyFromInstance("foo ${++i}") + + then: + shadowingSub.retrieveSubStaticPropertyFromInstance() == "foo $i" + + when: + shadowingSub.overwriteSuperField("foo ${++i}") + + then: + thrown(MissingPropertyException) + + when: + shadowingSub.overwriteSuperPublicField("foo ${++i}") + + then: + shadowingSub.retrieveSuperPublicField() == "foo $i" + + when: + shadowingSub.overwriteSuperProperty("foo ${++i}") + + then: + shadowingSub.retrieveSuperProperty() == "foo $i" + + when: + shadowingSub.overwriteSubField("foo ${++i}") + + then: + shadowingSub.retrieveSubField() == "foo $i" + + when: + shadowingSub.overwritePublicSubField("foo ${++i}") + + then: + shadowingSub.retrievePublicSubField() == "foo $i" + + when: + shadowingSub.overwriteSubProperty("foo ${++i}") + + then: + shadowingSub.retrieveSubProperty() == "foo $i" + + cleanup: + resetStatics() + + where: + createSpies << [false, true] + } + static class Person { String name int age @@ -280,4 +1693,451 @@ class GroovySpiesThatAreGlobal extends Specification { "Pattern" } } + + def resetStatics() { + Enclosing.resetStatics() + Enclosing.Super.resetStatics() + Enclosing.Sub.resetStatics() + Enclosing.ShadowingSub.resetStatics() + } + + static class Enclosing { + private static String ENCLOSING_FIELD + public static String PUBLIC_ENCLOSING_FIELD + static String ENCLOSING_PROPERTY + + static { + resetStatics() + } + + static void resetStatics() { + ENCLOSING_FIELD = "enclosing" + PUBLIC_ENCLOSING_FIELD = "public enclosing" + ENCLOSING_PROPERTY = "enclosing property" + } + + static class Super { + private static String STATIC_FIELD + public static String PUBLIC_STATIC_FIELD + static String STATIC_PROPERTY + private String FIELD = "super" + public String PUBLIC_FIELD = "public super" + public String PROPERTY = "super property" + + static { + resetStatics() + } + + static void resetStatics() { + STATIC_FIELD = "super static" + PUBLIC_STATIC_FIELD = "public super static" + STATIC_PROPERTY = "super static property" + } + + static retrieveEnclosingField() { + ENCLOSING_FIELD + } + + static overwriteEnclosingField(def value) { + ENCLOSING_FIELD = value + } + + static retrievePublicEnclosingField() { + PUBLIC_ENCLOSING_FIELD + } + + static overwritePublicEnclosingField(def value) { + PUBLIC_ENCLOSING_FIELD = value + } + + static retrieveEnclosingProperty() { + ENCLOSING_PROPERTY + } + + static overwriteEnclosingProperty(def value) { + ENCLOSING_PROPERTY = value + } + + static retrieveStaticField() { + STATIC_FIELD + } + + static overwriteStaticField(def value) { + STATIC_FIELD = value + } + + static retrievePublicStaticField() { + PUBLIC_STATIC_FIELD + } + + static overwritePublicStaticField(def value) { + PUBLIC_STATIC_FIELD = value + } + + static retrieveStaticProperty() { + STATIC_PROPERTY + } + + static overwriteStaticProperty(def value) { + STATIC_PROPERTY = value + } + + def retrieveEnclosingFieldFromInstance() { + ENCLOSING_FIELD + } + + def overwriteEnclosingFieldFromInstance(def value) { + ENCLOSING_FIELD = value + } + + def retrievePublicEnclosingFieldFromInstance() { + PUBLIC_ENCLOSING_FIELD + } + + def overwritePublicEnclosingFieldFromInstance(def value) { + PUBLIC_ENCLOSING_FIELD = value + } + + def retrieveEnclosingPropertyFromInstance() { + ENCLOSING_PROPERTY + } + + def overwriteEnclosingPropertyFromInstance(def value) { + ENCLOSING_PROPERTY = value + } + + def retrieveStaticFieldFromInstance() { + STATIC_FIELD + } + + def overwriteStaticFieldFromInstance(def value) { + STATIC_FIELD = value + } + + def retrievePublicStaticFieldFromInstance() { + PUBLIC_STATIC_FIELD + } + + def overwritePublicStaticFieldFromInstance(def value) { + PUBLIC_STATIC_FIELD = value + } + + def retrieveStaticPropertyFromInstance() { + STATIC_PROPERTY + } + + def overwriteStaticPropertyFromInstance(def value) { + STATIC_PROPERTY = value + } + + def retrieveField() { + FIELD + } + + def overwriteField(def value) { + FIELD = value + } + + def retrievePublicField() { + PUBLIC_FIELD + } + + def overwritePublicField(def value) { + PUBLIC_FIELD = value + } + + def retrieveProperty() { + PROPERTY + } + + def overwriteProperty(def value) { + PROPERTY = value + } + } + + static class Sub extends Super { + private static String SUB_STATIC_FIELD + public static String PUBLIC_SUB_STATIC_FIELD + static String SUB_STATIC_PROPERTY + private String SUB_FIELD = "sub" + public String PUBLIC_SUB_FIELD = "public sub" + String SUB_PROPERTY = "sub property" + + static { + resetStatics() + } + + static void resetStatics() { + SUB_STATIC_FIELD = "sub static" + PUBLIC_SUB_STATIC_FIELD = "public sub static" + SUB_STATIC_PROPERTY = "sub static property" + } + + static retrieveSuperStaticField() { + STATIC_FIELD + } + + static overwriteSuperStaticField(def value) { + STATIC_FIELD = value + } + + static retrieveSuperPublicStaticField() { + PUBLIC_STATIC_FIELD + } + + static overwriteSuperPublicStaticField(def value) { + PUBLIC_STATIC_FIELD = value + } + + static retrieveSuperStaticProperty() { + STATIC_PROPERTY + } + + static overwriteSuperStaticProperty(def value) { + STATIC_PROPERTY = value + } + + static retrieveSubStaticField() { + SUB_STATIC_FIELD + } + + static overwriteSubStaticField(def value) { + SUB_STATIC_FIELD = value + } + + static retrievePublicSubStaticField() { + PUBLIC_SUB_STATIC_FIELD + } + + static overwritePublicSubStaticField(def value) { + PUBLIC_SUB_STATIC_FIELD = value + } + + static retrieveSubStaticProperty() { + SUB_STATIC_PROPERTY + } + + static overwriteSubStaticProperty(def value) { + SUB_STATIC_PROPERTY = value + } + + def retrieveSuperStaticFieldFromInstance() { + STATIC_FIELD + } + + def overwriteSuperStaticFieldFromInstance(def value) { + STATIC_FIELD = value + } + + def retrieveSuperPublicStaticFieldFromInstance() { + PUBLIC_STATIC_FIELD + } + + def overwriteSuperPublicStaticFieldFromInstance(def value) { + PUBLIC_STATIC_FIELD = value + } + + def retrieveSuperStaticPropertyFromInstance() { + STATIC_PROPERTY + } + + def overwriteSuperStaticPropertyFromInstance(def value) { + STATIC_PROPERTY = value + } + + def retrieveSubStaticFieldFromInstance() { + SUB_STATIC_FIELD + } + + def overwriteSubStaticFieldFromInstance(def value) { + SUB_STATIC_FIELD = value + } + + def retrievePublicSubStaticFieldFromInstance() { + PUBLIC_SUB_STATIC_FIELD + } + + def overwritePublicSubStaticFieldFromInstance(def value) { + PUBLIC_SUB_STATIC_FIELD = value + } + + def retrieveSubStaticPropertyFromInstance() { + SUB_STATIC_PROPERTY + } + + def overwriteSubStaticPropertyFromInstance(def value) { + SUB_STATIC_PROPERTY = value + } + + def retrieveSuperField() { + FIELD + } + + def overwriteSuperField(def value) { + FIELD = value + } + + def retrieveSuperPublicField() { + PUBLIC_FIELD + } + + def overwriteSuperPublicField(def value) { + PUBLIC_FIELD = value + } + + def retrieveSuperProperty() { + PROPERTY + } + + def overwriteSuperProperty(def value) { + PROPERTY = value + } + + def retrieveSubField() { + SUB_FIELD + } + + def overwriteSubField(def value) { + SUB_FIELD = value + } + + def retrievePublicSubField() { + PUBLIC_SUB_FIELD + } + + def overwritePublicSubField(def value) { + PUBLIC_SUB_FIELD = value + } + + def retrieveSubProperty() { + SUB_PROPERTY + } + + def overwriteSubProperty(def value) { + SUB_PROPERTY = value + } + } + + static class ShadowingSub extends Super { + private static String STATIC_FIELD + public static String PUBLIC_STATIC_FIELD + static String STATIC_PROPERTY + private String FIELD = "shadowing sub" + public String PUBLIC_FIELD = "public shadowing sub" + String PROPERTY = "shadowing sub property" + + static { + resetStatics() + } + + static void resetStatics() { + STATIC_FIELD = "shadowing sub static" + PUBLIC_STATIC_FIELD = "public shadowing sub static" + STATIC_PROPERTY = "shadowing sub static property" + } + + static retrieveSubStaticField() { + STATIC_FIELD + } + + static overwriteSubStaticField(def value) { + STATIC_FIELD = value + } + + static retrievePublicSubStaticField() { + PUBLIC_STATIC_FIELD + } + + static overwritePublicSubStaticField(def value) { + PUBLIC_STATIC_FIELD = value + } + + static retrieveSubStaticProperty() { + STATIC_PROPERTY + } + + static overwriteSubStaticProperty(def value) { + STATIC_PROPERTY = value + } + + def retrieveSubStaticFieldFromInstance() { + STATIC_FIELD + } + + def overwriteSubStaticFieldFromInstance(def value) { + STATIC_FIELD = value + } + + def retrievePublicSubStaticFieldFromInstance() { + PUBLIC_STATIC_FIELD + } + + def overwritePublicSubStaticFieldFromInstance(def value) { + PUBLIC_STATIC_FIELD = value + } + + def retrieveSubStaticPropertyFromInstance() { + STATIC_PROPERTY + } + + def overwriteSubStaticPropertyFromInstance(def value) { + STATIC_PROPERTY = value + } + + def retrieveSuperField() { + super.FIELD + } + + def overwriteSuperField(def value) { + // `super.FIELD = value` is a compile error in Groovy <4 + // we work-around this by using `super."${"FIELD"}" = value` so that it is evaluated at runtime + // in Groovy <4 this though would result in the own FIELD being read, not the super FIELD + // hence we manually throw a MissingPropertyException here + if (GroovyRuntimeUtil.MAJOR_VERSION <= 3) { + throw new MissingPropertyException("FIELD", Super) + } + super."${"FIELD"}" = value + } + + def retrieveSuperPublicField() { + super.PUBLIC_FIELD + } + + def overwriteSuperPublicField(def value) { + super.PUBLIC_FIELD = value + } + + def retrieveSuperProperty() { + super.PROPERTY + } + + def overwriteSuperProperty(def value) { + super.PROPERTY = value + } + + def retrieveSubField() { + FIELD + } + + def overwriteSubField(def value) { + FIELD = value + } + + def retrievePublicSubField() { + PUBLIC_FIELD + } + + def overwritePublicSubField(def value) { + PUBLIC_FIELD = value + } + + def retrieveSubProperty() { + PROPERTY + } + + def overwriteSubProperty(def value) { + PROPERTY = value + } + } + } } diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/mock/MockingAndBridgeMethods.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/mock/MockingAndBridgeMethods.groovy index 5e036dd971..20f7711820 100644 --- a/spock-specs/src/test/groovy/org/spockframework/smoke/mock/MockingAndBridgeMethods.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/mock/MockingAndBridgeMethods.groovy @@ -30,6 +30,7 @@ class MockingAndBridgeMethods extends Specification { then: 1 * comparator.compare(0, 0) >> { IMockInvocation inv -> assert inv.getMethod().getParameterTypes() == [Integer, Integer] + 0 } } @@ -68,9 +69,11 @@ class MockingAndBridgeMethods extends Specification { then: 1 * di.getId() >> { IMockInvocation inv -> assert inv.method.method.bridge == isCglib + 0 } 1 * dpi.getId() >> { IMockInvocation inv -> assert inv.method.method.bridge == false + 0 } diId == 0 dpiId == 0 @@ -90,9 +93,11 @@ class MockingAndBridgeMethods extends Specification { then: 1 * di.getPrice() >> { IMockInvocation inv -> assert inv.method.method.bridge == false + 0 } 1 * dpi.getPrice() >> { IMockInvocation inv -> assert inv.method.method.bridge == false + 0 } diPrice == 0 dpiPrice == 0 diff --git a/spock-specs/src/test/groovy/org/spockframework/smoke/mock/TooFewInvocations.groovy b/spock-specs/src/test/groovy/org/spockframework/smoke/mock/TooFewInvocations.groovy index c54cf95886..cb020b0d31 100644 --- a/spock-specs/src/test/groovy/org/spockframework/smoke/mock/TooFewInvocations.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/smoke/mock/TooFewInvocations.groovy @@ -1154,7 +1154,8 @@ then: then: TooFewInvocationsError e = thrown() def exceptionMessage = normalize(e.message) - exceptionMessage =~ /(?m)^\Q1 * list.add(\)$/ + // work-around for https://issues.apache.org/jira/browse/GROOVY-11779 (the "|inaccessible") + exceptionMessage =~ /(?m)^\Q1 * list.add(\)$/ } def "correctly renders multidimensional arrays"() { diff --git a/spock-specs/src/test/groovy/org/spockframework/util/ReflectionUtilSpec.groovy b/spock-specs/src/test/groovy/org/spockframework/util/ReflectionUtilSpec.groovy index 18f718abb1..a4aaed749b 100644 --- a/spock-specs/src/test/groovy/org/spockframework/util/ReflectionUtilSpec.groovy +++ b/spock-specs/src/test/groovy/org/spockframework/util/ReflectionUtilSpec.groovy @@ -331,10 +331,10 @@ class ReflectionUtilSpec extends Specification { SubTypeWithAnno | Narrative | 2 } - @Narrative + @Narrative("") private static class TypeWithAnno {} - @Narrative + @Narrative("") private static class SubTypeWithAnno extends TypeWithAnno {} private static class SubTypeNoAnno extends TypeWithAnno {} diff --git a/spock-specs/src/test/groovy/spock/timeout/BaseTimeoutExtensionSpecification.groovy b/spock-specs/src/test/groovy/spock/timeout/BaseTimeoutExtensionSpecification.groovy index d2a5589a18..7817e67ec4 100644 --- a/spock-specs/src/test/groovy/spock/timeout/BaseTimeoutExtensionSpecification.groovy +++ b/spock-specs/src/test/groovy/spock/timeout/BaseTimeoutExtensionSpecification.groovy @@ -49,7 +49,7 @@ abstract class BaseTimeoutExtensionSpecification extends EmbeddedSpecification { } } - private class OutputListener implements IStandardStreamsListener { + protected class OutputListener implements IStandardStreamsListener { private final StringBuilder messages = new StringBuilder() diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation-groovy5.txt b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation-groovy5.txt new file mode 100644 index 0000000000..8c832f4e4c --- /dev/null +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/Primitive_types_are_used_in_AST_transformation-groovy5.txt @@ -0,0 +1,198 @@ +// class version 52.0 (52) +// access flags 0x21 +public class apackage/TestSpec extends spock/lang/Specification implements groovy/lang/GroovyObject { + + // compiled from: script.groovy + + // access flags 0x1 + public $spock_feature_0_0()V + TRYCATCHBLOCK L0 L1 L1 java/lang/Throwable + TRYCATCHBLOCK L0 L1 L2 null + TRYCATCHBLOCK L1 L3 L2 null + TRYCATCHBLOCK L4 L5 L5 java/lang/Throwable + TRYCATCHBLOCK L4 L5 L6 null + TRYCATCHBLOCK L5 L7 L6 null + L8 + LDC Lorg/spockframework/runtime/ErrorRethrower;.class + INVOKEDYNAMIC getProperty(Ljava/lang/Class;)Ljava/lang/Object; [ + // handle kind 0x6 : INVOKESTATIC + org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; + // arguments: + "INSTANCE", + 0 + ] + INVOKEDYNAMIC cast(Ljava/lang/Object;)Lorg/spockframework/runtime/ErrorCollector; [ + // handle kind 0x6 : INVOKESTATIC + org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; + // arguments: + "()", + 0 + ] + ASTORE 1 + L9 + LDC Lorg/spockframework/runtime/ValueRecorder;.class + INVOKEDYNAMIC init(Ljava/lang/Class;)Ljava/lang/Object; [ + // handle kind 0x6 : INVOKESTATIC + org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; + // arguments: + "", + 0 + ] + INVOKEDYNAMIC cast(Ljava/lang/Object;)Lorg/spockframework/runtime/ValueRecorder; [ + // handle kind 0x6 : INVOKESTATIC + org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; + // arguments: + "()", + 0 + ] + ASTORE 2 + L10 + ALOAD 0 + ICONST_0 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockEntered (Lspock/lang/Specification;I)V + L0 + LINENUMBER 4 L0 + ALOAD 1 + ALOAD 2 + INVOKEVIRTUAL org/spockframework/runtime/ValueRecorder.reset ()Lorg/spockframework/runtime/ValueRecorder; + LDC "true" + ICONST_4 + ICONST_5 + ACONST_NULL + ALOAD 2 + ALOAD 2 + ICONST_0 + INVOKEVIRTUAL org/spockframework/runtime/ValueRecorder.startRecordingValue (I)I + ICONST_1 + INVOKESTATIC java/lang/Boolean.valueOf (Z)Ljava/lang/Boolean; + INVOKEVIRTUAL org/spockframework/runtime/ValueRecorder.record (ILjava/lang/Object;)Ljava/lang/Object; + INVOKESTATIC org/spockframework/runtime/SpockRuntime.verifyCondition (Lorg/spockframework/runtime/ErrorCollector;Lorg/spockframework/runtime/ValueRecorder;Ljava/lang/String;IILjava/lang/Object;Ljava/lang/Object;)V + GOTO L11 + L1 + FRAME FULL [apackage/TestSpec org/spockframework/runtime/ErrorCollector org/spockframework/runtime/ValueRecorder] [java/lang/Throwable] + ASTORE 3 + L12 + ALOAD 1 + ALOAD 2 + LDC "true" + ICONST_4 + ICONST_5 + ACONST_NULL + ALOAD 3 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.conditionFailedWithException (Lorg/spockframework/runtime/ErrorCollector;Lorg/spockframework/runtime/ValueRecorder;Ljava/lang/String;IILjava/lang/Object;Ljava/lang/Throwable;)V + NOP + L3 + GOTO L11 + L11 + FRAME SAME + GOTO L13 + L2 + FRAME SAME1 java/lang/Throwable + ASTORE 4 + ALOAD 4 + ATHROW + L13 + FRAME SAME + ALOAD 0 + ICONST_0 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockExited (Lspock/lang/Specification;I)V + ALOAD 0 + ICONST_1 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockEntered (Lspock/lang/Specification;I)V + ALOAD 0 + INVOKEVIRTUAL spock/lang/Specification.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext; + INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [ + // handle kind 0x6 : INVOKESTATIC + org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; + // arguments: + "()", + 0 + ] + ACONST_NULL + INVOKEVIRTUAL org/spockframework/runtime/SpecificationContext.setThrownException (Ljava/lang/Throwable;)V + L4 + LINENUMBER 6 L4 + ICONST_1 + POP + GOTO L14 + L5 + FRAME SAME1 java/lang/Throwable + ASTORE 5 + L15 + ALOAD 0 + INVOKEVIRTUAL spock/lang/Specification.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext; + INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [ + // handle kind 0x6 : INVOKESTATIC + org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; + // arguments: + "()", + 0 + ] + ALOAD 5 + INVOKEVIRTUAL org/spockframework/runtime/SpecificationContext.setThrownException (Ljava/lang/Throwable;)V + NOP + L7 + GOTO L14 + L14 + FRAME SAME + GOTO L16 + L6 + FRAME SAME1 java/lang/Throwable + ASTORE 6 + ALOAD 6 + ATHROW + L16 + FRAME SAME + ALOAD 0 + ICONST_1 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockExited (Lspock/lang/Specification;I)V + ALOAD 0 + ICONST_2 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockEntered (Lspock/lang/Specification;I)V + L17 + LINENUMBER 8 L17 + LDC Lorg/spockframework/runtime/SpecInternals;.class + ALOAD 0 + ACONST_NULL + ACONST_NULL + LDC Ljava/lang/RuntimeException;.class + INVOKEDYNAMIC invoke(Ljava/lang/Class;Lapackage/TestSpec;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object; [ + // handle kind 0x6 : INVOKESTATIC + org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; + // arguments: + "thrownImpl", + 0 + ] + POP + ALOAD 0 + ICONST_2 + INVOKESTATIC org/spockframework/runtime/SpockRuntime.callBlockExited (Lspock/lang/Specification;I)V + ALOAD 0 + INVOKEVIRTUAL spock/lang/Specification.getSpecificationContext ()Lorg/spockframework/lang/ISpecificationContext; + INVOKEDYNAMIC cast(Lorg/spockframework/lang/ISpecificationContext;)Lorg/spockframework/runtime/SpecificationContext; [ + // handle kind 0x6 : INVOKESTATIC + org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; + // arguments: + "()", + 0 + ] + INVOKEVIRTUAL org/spockframework/runtime/SpecificationContext.getMockController ()Lorg/spockframework/mock/IMockController; + INVOKEDYNAMIC cast(Lorg/spockframework/mock/IMockController;)Lorg/spockframework/mock/runtime/MockController; [ + // handle kind 0x6 : INVOKESTATIC + org/codehaus/groovy/vmplugin/v8/IndyInterface.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; + // arguments: + "()", + 0 + ] + INVOKEVIRTUAL org/spockframework/mock/runtime/MockController.leaveScope ()V + L18 + LINENUMBER 9 L18 + RETURN + LOCALVARIABLE this Lapackage/TestSpec; L8 L18 0 + LOCALVARIABLE $spock_errorCollector Lorg/spockframework/runtime/ErrorCollector; L9 L18 1 + LOCALVARIABLE $spock_valueRecorder Lorg/spockframework/runtime/ValueRecorder; L10 L18 2 + LOCALVARIABLE $spock_condition_throwable Ljava/lang/Throwable; L12 L3 3 + LOCALVARIABLE $spock_ex Ljava/lang/Throwable; L15 L7 5 + MAXSTACK = 9 + MAXLOCALS = 7 +} \ No newline at end of file diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/full_feature_exercise-groovy5.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/full_feature_exercise-groovy5.groovy new file mode 100644 index 0000000000..e25e7b5443 --- /dev/null +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/AstSpec/full_feature_exercise-groovy5.groovy @@ -0,0 +1,230 @@ +@apackage.another.Ann +package apackage.another + +import static java.util.Collections.emptyList +import static java.nio.charset.StandardCharsets.* + +@apackage.another.Ann +import java.text.ParseException +import java.nio.file.* + +public abstract class apackage.another.Foo extends java.lang.Object implements java.lang.Comparable { + + private java.util.List x = new java.util.ArrayList<>() + + public apackage.another.Foo(java.lang.String initialValue) { + x << initialValue + } + +} +@apackage.another.Ann +package apackage.another + +import static java.util.Collections.emptyList +import static java.nio.charset.StandardCharsets.* + +@apackage.another.Ann +import java.text.ParseException +import java.nio.file.* + +public abstract interface apackage.another.Ex extends java.lang.Object { + + public abstract void ex() throws java.io.IOException, java.text.ParseException { + } + +} +@apackage.another.Ann +package apackage.another + +import static java.util.Collections.emptyList +import static java.nio.charset.StandardCharsets.* + +@apackage.another.Ann +import java.text.ParseException +import java.nio.file.* + +public abstract interface apackage.another.Ann extends java.lang.Object implements java.lang.annotation.Annotation { + +} +@apackage.another.Ann +package apackage.another + +import static java.util.Collections.emptyList +import static java.nio.charset.StandardCharsets.* + +@apackage.another.Ann +import java.text.ParseException +import java.nio.file.* + +@apackage.another.Ann +public class apackage.another.Bar extends apackage.another.Foo implements apackage.another.Ex, java.io.Serializable { + + private static final int[] ARR = (([1, 2, 3]) as int[]) + private static final java.lang.String STR = 'str' + private static final char CHR = ' ' + private static final int INT = 10 + private static int st + private int order + private java.lang.String someString = "String with ${STR}" + private java.lang.String plainString = 'plain' + + public apackage.another.Bar() { + super(apackage.another.Bar.STR) + } + + { + order = 1 + } + + static { + st = 42 + } + + public void loops() { + outer: + for (java.lang.Integer i = 0; i < INT ;( i )++) { + if ( i % 2 == 0) { + this.print('even') + continue outer + } else { + this.print('odd') + } + break outer + } + for (java.lang.String y : x ) { + this.print(y) + } + while (true) { + break + } + } + + @java.lang.Override + public void ex() throws java.lang.Exception { + try { + java.lang.System.getProperty('Foo', STR)?.stripIndent() + } + catch (java.lang.RuntimeException e) { + throw new java.lang.Exception(e) + } + finally { + this.println('executed ex') + } + } + + public java.lang.String convert(java.lang.String input) { + java.lang.String result = '' + switch ( input ) { + case 'Alpha.A': + result = 'a' + break + case 'Alpha.B': + case ~('Alpha.*') : + result = 'c' + break + default: + result = 'shrug' + } + return result + } + + public void operators() { + java.lang.Integer a = ~( INT ) + java.lang.Boolean b = !( a ) + java.lang.Integer c = -( a ) + java.lang.Integer d = +( c ) + java.lang.Object e = ['a': b , STR : c ] + java.lang.Object f = [*: e , 'foo': 'bar'] + java.lang.Object g = "a: ${a} and b: ${ e.a.compareTo(c)}" + java.lang.Integer h = 1 + java.lang.Integer i = 2 + java.lang.Object( j , k ) = x + java.lang.Object l = b ? c : d + java.lang.Object m = c ? c : d + java.lang.Object n = this.&'convert' + java.lang.Object o = this.order + java.lang.Object p = (1..5) + java.lang.Object q = (1..<5) + java.lang.Object r = x*.size()?.intersect([1]) + java.lang.Object s = { java.lang.Object x -> + x * x + } + java.lang.Object t = [:] + java.lang.Object u = ++( c ) + java.lang.Object v = this."${STR}"(a) + java.lang.Object w = { -> + this.println(g) + } + java.lang.Object x = (( c ) as long) + java.lang.Object y = ((long) c ) + java.lang.Object z = [1, 2, 3] [ 0] + assert c == d : null + } + + public void gstrings() { + java.lang.Object a = 'simple' + java.lang.Object b = 'normal string \$a ...' + java.lang.Object c = "gstring ${a} ..." + java.lang.Object d = "gstring with brackets ${a.size()} ..." + java.lang.Object e = "gstring with closure ${ -> + a + } ... " + java.lang.Object f = "with writer ${ java.lang.Object w -> + w << a + }" + java.lang.Object g = 'simple\nmulti\nline' + java.lang.Object h = "multi line gstring\n${a}\n..." + java.lang.Object i = "multi line gstring\n with brackets ${a.size()}\n with escaped brackets \${a.size()}\n ..." + java.lang.Object j = "multi line gstring\n with closure ${ -> + a + }\n with escaped closure \${-> a}\n ... " + java.lang.Object k = "multi line gstring\n with simple value ${a}\n with simple escaped value \$a\n with brackets ${a.size()}\n with escaped brackets \${a.size()}\n with closure ${ -> + a + }\n with escaped closure \${-> a}\n with writer ${ java.lang.Object w -> + w << a + }\n with writer escaped \${w -> w << a}\n..." + } + + @java.lang.Override + public int compareTo(java.lang.Object o) { + synchronized ( o ) { + return order <=> (( o ) as apackage.another.Bar).order + } + } + + public void multix(java.nio.file.Path a, @apackage.another.Ann int b, java.lang.String desc = '') { + } + + public void prop(java.util.List l, int[] a) { + java.lang.Object x = l*.foo + java.lang.Object y = a?.length + java.lang.Object z = a."${STR}" + } + + public static final void statMethod(java.lang.String a) { + } + +} +@apackage.another.Ann +package apackage.another + +import static java.util.Collections.emptyList +import static java.nio.charset.StandardCharsets.* + +@apackage.another.Ann +import java.text.ParseException +import java.nio.file.* + +public class apackage.another.Ext extends java.lang.Object { + + private T[] arr + private java.util.List lst = [] + + public V foo(java.util.List consumer) { + } + + @apackage.another.Ann + public > boolean saveCompare(X a, X b) { + } + +} \ No newline at end of file diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments-groovy5.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments-groovy5.groovy new file mode 100644 index 0000000000..feb37751f2 --- /dev/null +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/CleanupBlocksAstSpec/cleanup_rewrite_keeps_correct_method_reference_for_multi_assignments-groovy5.groovy @@ -0,0 +1,59 @@ +package aPackage +import spock.lang.* + +class ASpec extends Specification { +/*--------- tag::snapshot[] ---------*/ +public java.lang.Object foobar() { + return ['foo', 'bar'] +} + +public void $spock_feature_0_0() { + org.spockframework.runtime.ErrorCollector $spock_errorCollector = org.spockframework.runtime.ErrorRethrower.INSTANCE + org.spockframework.runtime.ValueRecorder $spock_valueRecorder = new org.spockframework.runtime.ValueRecorder() + java.lang.Object( foobar , b ) = [null, null] + java.lang.Throwable $spock_feature_throwable + try { + org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 0) + (foobar, b) = this.foobar() + 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(), 'println(foobar)', 6, 3, null, this, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(0), 'println'), new java.lang.Object[]{$spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(1), foobar)}, $spock_valueRecorder.realizeNas(4, false), false, 3) + } + catch (java.lang.Throwable $spock_condition_throwable) { + org.spockframework.runtime.SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, 'println(foobar)', 6, 3, null, $spock_condition_throwable)} + finally { + } + org.spockframework.runtime.SpockRuntime.callBlockExited(this, 1) + } + catch (java.lang.Throwable $spock_tmp_throwable) { + $spock_feature_throwable = $spock_tmp_throwable + throw $spock_tmp_throwable + } + finally { + org.spockframework.runtime.model.BlockInfo $spock_failedBlock = null + try { + if ( $spock_feature_throwable != null) { + $spock_failedBlock = this.getSpecificationContext().getCurrentBlock() + } + org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 2) + foobar.size() + org.spockframework.runtime.SpockRuntime.callBlockExited(this, 2) + } + catch (java.lang.Throwable $spock_tmp_throwable) { + if ( $spock_feature_throwable != null) { + $spock_feature_throwable.addSuppressed($spock_tmp_throwable) + } else { + throw $spock_tmp_throwable + } + } + finally { + if ( $spock_feature_throwable != null) { + ((org.spockframework.runtime.SpecificationContext) this.getSpecificationContext()).setCurrentBlock($spock_failedBlock) + } + } + } + this.getSpecificationContext().getMockController().leaveScope() +} +/*--------- end::snapshot[] ---------*/ +} diff --git a/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments-groovy5.groovy b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments-groovy5.groovy new file mode 100644 index 0000000000..6181b9e2ec --- /dev/null +++ b/spock-specs/src/test/resources/snapshots/org/spockframework/smoke/ast/condition/ExceptionConditionsAstSpec/thrown_rewrite_keeps_correct_method_reference_for_multi_assignments-groovy5.groovy @@ -0,0 +1,29 @@ +package aPackage +import spock.lang.* + +class ASpec extends Specification { +/*--------- tag::snapshot[] ---------*/ +public java.lang.Object foobar() { + throw new java.lang.IllegalStateException('foo') +} + +public void $spock_feature_0_0() { + java.lang.Object( foobar , b ) = [null, null] + org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 0) + this.getSpecificationContext().setThrownException(null) + try { + (foobar, b) = this.foobar() + } + catch (java.lang.Throwable $spock_ex) { + this.getSpecificationContext().setThrownException($spock_ex) + } + finally { + } + org.spockframework.runtime.SpockRuntime.callBlockExited(this, 0) + org.spockframework.runtime.SpockRuntime.callBlockEntered(this, 1) + org.spockframework.runtime.SpecInternals.thrownImpl(this, null, null, java.lang.IllegalStateException) + org.spockframework.runtime.SpockRuntime.callBlockExited(this, 1) + this.getSpecificationContext().getMockController().leaveScope() +} +/*--------- end::snapshot[] ---------*/ +}