Skip to content

Commit

Permalink
Merge branch 'master' into OSGi
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreasTu authored Sep 24, 2024
2 parents cc6f548 + cdc792b commit ac47acd
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 5 deletions.
2 changes: 2 additions & 0 deletions docs/release_notes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ include::include.adoc[]
* Add new <<extensions.adoc#default-value-provider,`IDefaultValueProviderExtension`>> extension point to add support for special classes in the Stub's default `EmptyOrDummyResponse` spockPull:1994[]
* Improve `@Timeout` extension will now use virtual threads if available spockPull:1986[]
* Improve mock argument matching, types constraints or arguments in interactions can now handle primitive types like `_ as int` spockIssue:1974[]
* `EmbeddedSpecRunner` and `EmbeddedSpecCompiler` now support the construction with a custom `ClassLoader` spockPull:1988[]
** This allows the use of these classes in an OSGi environment, where the class imports in the embedded spec are not visible to the Spock OSGi bundle ClassLoader
* Support ContextClassLoader when loading optional classes via `ReflectionUtil` spockPull:1995[]
** This enables loading of optional classes in e.g. OSGi environments

Expand Down
7 changes: 5 additions & 2 deletions spock-core/core.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ dependencies {
if (variant == 2.5) {
api projects.spockGroovy2Compat
}

implementation libs.geantyref

compileOnly libs.jetbrains.annotations
Expand Down Expand Up @@ -69,7 +69,10 @@ tasks.named("jar", Jar) {
'net.bytebuddy.*;resolution:=optional',
'net.sf.cglib.*;resolution:=optional',
'org.objectweb.asm.*;resolution:=optional',
'org.mockito.*;resolution:=optional',
/* We need to override the OSGi version for Mockito here, otherwise the version range would be [4.11,5)
* which would exclude Mockito 5 in the OSGi case.
*/
'org.mockito.*;resolution:=optional;version="[4.11,6)"',
'*'
].join(','),
'-noclassforname': 'true',
Expand Down
29 changes: 27 additions & 2 deletions spock-core/src/main/groovy/spock/util/EmbeddedSpecCompiler.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,36 @@ class EmbeddedSpecCompiler {
final CompilerConfiguration compilerConfigurationWithImports = new CompilerConfiguration().tap {
it.addCompilationCustomizers(importCustomizer)
}
final GroovyClassLoader loaderWithImports = new GroovyClassLoader(getClass().classLoader, compilerConfigurationWithImports)
final GroovyClassLoader loader = new GroovyClassLoader(getClass().classLoader)
final GroovyClassLoader loaderWithImports
final GroovyClassLoader loader

boolean unwrapCompileException = true

/**
* Creates a new {@link EmbeddedSpecCompiler} with default settings using the Spock {@link ClassLoader} for resolving imports.
*/
EmbeddedSpecCompiler() {
//We can't use the new constructor EmbeddedSpecCompiler(ClassLoader) here, because we can't call this before super
//and someone could have subclassed the EmbeddedSpecCompiler with a different classloader,
//which would break the existing code, if we use "EmbeddedSpecCompiler.class.classLoader"
def classLoader = this.class.classLoader
this.loaderWithImports = new GroovyClassLoader(classLoader, compilerConfigurationWithImports)
this.loader = new GroovyClassLoader(classLoader)
}

/**
* Creates a new {@link EmbeddedSpecCompiler} with a custom classloader to load imports of the spec.
* Note: The classloader need to be able to load Spock classes.
*
* @param classLoader the ClassLoader to use to load the spec imports
* @since 2.4
*/
@Beta
EmbeddedSpecCompiler(ClassLoader classLoader) {
Objects.requireNonNull(classLoader)
this.loaderWithImports = new GroovyClassLoader(classLoader, compilerConfigurationWithImports)
this.loader = new GroovyClassLoader(classLoader)
}

void addPackageImport(String pkg) {
importCustomizer.addStarImports(pkg)
Expand Down
34 changes: 33 additions & 1 deletion spock-core/src/main/groovy/spock/util/EmbeddedSpecRunner.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass
*/
@NotThreadSafe
class EmbeddedSpecRunner {
private final EmbeddedSpecCompiler compiler = new EmbeddedSpecCompiler(unwrapCompileException: false)
private final EmbeddedSpecCompiler compiler

boolean throwFailure = true

Expand All @@ -50,6 +50,38 @@ class EmbeddedSpecRunner {
List<Class> configClasses = []
boolean inheritParentExtensions = true

/**
* Creates a new {@link EmbeddedSpecRunner} with a default {@link EmbeddedSpecCompiler}.
*/
EmbeddedSpecRunner() {
this(new EmbeddedSpecCompiler())
this.compiler.unwrapCompileException = false
}

/**
* Creates a new {@link EmbeddedSpecRunner} with a custom classloader to load imports of the spec.
* Note: The classloader need to be able to load Spock classes.
*
* @param classLoader the ClassLoader to use to load the spec imports
* @since 2.4
*/
@Beta
EmbeddedSpecRunner(ClassLoader classLoader) {
this(new EmbeddedSpecCompiler(classLoader))
this.compiler.unwrapCompileException = false
}

/**
* Creates a new {@link EmbeddedSpecRunner} with a custom {@link EmbeddedSpecCompiler}.
*
* @param compiler the compiler to use
* @since 2.4
*/
@Beta
EmbeddedSpecRunner(EmbeddedSpecCompiler compiler) {
this.compiler = Objects.requireNonNull(compiler)
}

void configurationScript(Closure configurationScript) {
this.configurationScript = configurationScript
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2024 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 spock.util

import org.codehaus.groovy.control.MultipleCompilationErrorsException
import org.spockframework.mock.runtime.ByteBuddyTestClassLoader
import spock.lang.Specification

class EmbeddedSpecRunnerClassLoaderSpec extends Specification {

def "EmbeddedSpecRunner with different ClassLoader"() {
given:
def cl = new ByteBuddyTestClassLoader()
def testInterfaceName = "test.TestIf"
cl.defineInterface(testInterfaceName)
def runner = new EmbeddedSpecRunner(cl)

expect:
//noinspection GroovyAccessibility
!runner.compiler.unwrapCompileException

when:
def result = runner.runFeatureBody("""
expect:
${testInterfaceName}.class != null
""")
then:
result.testsSucceededCount == 1
}

def "EmbeddedSpecRunner with different ClassLoader - unable to see Spock"() {
given:
def cl = new URLClassLoader([] as URL[], null as ClassLoader)
def runner = new EmbeddedSpecRunner(cl)

when: "This should fail, because the Specification class is not visible in the ClassLoader"
runner.runFeatureBody("""
expect:
true
""")
then:
def ex = thrown(MultipleCompilationErrorsException)
ex.message.contains("unable to resolve class Specification")

cleanup:
cl?.close()
}
}

0 comments on commit ac47acd

Please sign in to comment.