Description
Hello everyone. I was using this framework to create unit tests for newly created Jenkins shared library. I was pretty happy with this until I've encountered a problem that is outlined below - I would greatly appreciate any help as I'm currently at my wits ends.
High level description:
I'm experiencing a problem during test execution: I'm trying to create an instance of an object and then cast it to the interface type that this class implements. I'm getting GroovyCastException. The same code works correctly on Jenkins.
Details:
Environment:
Gradle 8.5
JVM: 11.0.24 (Ubuntu 11.0.24+8-post-Ubuntu-1ubuntu320.04)
I've distilled the problem to the smallest reproducible setup.
Let's assume following project structure:
├── build.gradle
├── gradle.properties
├── settings.gradle
├── src
│ └── com
│ └── example
│ └── pkg
│ ├── AFoo.groovy
│ └── IFoo.groovy
├── test
│ └── groovy
│ └── vars
│ └── HelloWorldTest.groovy
└── vars
└── helloWorld.groovy
AFoo.groovy:
package com.example.pkg
class AFoo implements IFoo {
void bar() {
println "test"
}
}
IFoo.groovy:
package com.example.pkg
interface IFoo {
void bar()
}
helloWorld.groovy:
import com.example.pkg.AFoo
import com.example.pkg.IFoo
void call() {
IFoo foo = getFoo()
echo "Hello world"
}
IFoo getFoo() {
AFoo af = new AFoo()
return af
}
I'm loading the current library during the tests and trying to execute helloWorld step in HelloWorldTest.groovy:
import static com.lesfurets.jenkins.unit.global.lib.LibraryConfiguration.library
import static com.lesfurets.jenkins.unit.global.lib.ProjectSource.projectSource
import com.lesfurets.jenkins.unit.BasePipelineTest
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
class HelloWorldTest extends BasePipelineTest {
@Override
@BeforeEach
void setUp() {
scriptRoots += 'vars'
Object library = library()
.name('solaris')
.defaultVersion('<notNeeded>')
.allowOverride(true)
.implicit(true)
.targetPath('<notNeeded>')
.retriever(projectSource())
.build()
helper.registerSharedLibrary(library)
super.setUp()
}
@Test
void testHello() {
String expectedEcho = "Hello world"
Script script = loadScript('helloWorld.groovy')
script.call()
printCallStack()
assertCallStackContains(expectedEcho)
assertJobStatusSuccess()
}
}
My build.gradle:
plugins {
// Apply the groovy Plugin to add support for Groovy.
id 'groovy'
// Apply the java-library plugin for API and implementation separation.
id 'java-library'
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
maven { url 'https://repo.jenkins-ci.org/releases/' }
}
dependencies {
implementation 'org.codehaus.groovy:groovy:2.4.21'
implementation 'org.codehaus.groovy:groovy-all:2.4.21'
testImplementation platform("org.junit:junit-bom:5.11.0")
testImplementation 'org.junit.jupiter:junit-jupiter-api'
testImplementation 'org.junit.jupiter:junit-jupiter-params'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
testImplementation 'com.lesfurets:jenkins-pipeline-unit:1.20'
}
sourceSets {
// Location where gradle should look for files to build
main {
groovy {
srcDirs = ['src', 'vars']
}
}
test {
groovy {
srcDirs = ['test']
}
}
}
test {
useJUnitPlatform()
maxHeapSize = '1G'
testLogging {
events 'passed'
exceptionFormat 'full'
}
// delete old test reports
dependsOn 'cleanTest'
afterSuite { desc, result ->
if(!desc.parent) {
println "\nRESULTS > ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)"
}
}
}
buildDir = new File("${getLayout().getProjectDirectory()}/../${project.name}_build")
If I run gradle :test
I'm getting following error:
gradle :test
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/home/wbrozyn/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.4.21/abbb8d83268f3243a57d9f7768e159373a4e378/groovy-2.4.21.jar) to method java.lang.Object.finalize()
WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedClass
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
> Task :test
HelloWorldTest > testHello() FAILED
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'com.example.pkg.AFoo@4e2916c3' with class 'com.example.pkg.AFoo' to class 'com.example.pkg.IFoo'
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.continueCastOnSAM(DefaultTypeTransformation.java:418)
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.continueCastOnNumber(DefaultTypeTransformation.java:332)
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.castToType(DefaultTypeTransformation.java:245)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.castToType(ScriptBytecodeAdapter.java:616)
at helloWorld.call(helloWorld.groovy:5)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:98)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at groovy.lang.MetaMethod$doMethodInvoke.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:136)
at com.lesfurets.jenkins.unit.PipelineTestHelper.callMethod(PipelineTestHelper.groovy:329)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:98)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1225)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1034)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:68)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:51)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:157)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:185)
at com.lesfurets.jenkins.unit.PipelineTestHelper$_closure3.doCall(PipelineTestHelper.groovy:316)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:98)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod.invoke(ClosureMetaMethod.java:84)
at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:1123)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1034)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:41)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
at HelloWorldTest.testHello(HelloWorldTest.groovy:30)
RESULTS > FAILURE (1 tests, 0 passed, 1 failed, 0 skipped)
1 test completed, 1 failed
> Task :test FAILED
I've tried many things - none of which worked: using normal inheritance (instead of using interfaces), moving interface to a different package, trying few different versions of groovy or this library (in a few combinations), and few others that I can't recall right now.
Conversely this happens on groovy 2.*, on Groovy 3.0.13 which I initially used to compile and run tests with it doesn't error - it hangs on parseClass in GroovyClassLoader.
I would really appreciate any help - either in solving this error outlined above or in getting this to run with Groovy 3.0.13 (which I would prefer but I know that you have no reason to support that).
Thank you in advance.