Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tests fail to run with Jenkins 2.357 and up #143

Open
mikedld opened this issue Mar 26, 2023 · 3 comments
Open

Tests fail to run with Jenkins 2.357 and up #143

mikedld opened this issue Mar 26, 2023 · 3 comments

Comments

@mikedld
Copy link

mikedld commented Mar 26, 2023

Expected Behavior

Test run with the most recent versions of org.jenkins-ci.main:jenkins-core package.

Actual Behavior

When using org.jenkins-ci.main:jenkins-core version 2.356 and below, tests run as expected.

When using org.jenkins-ci.main:jenkins-core version 2.357 up to 2.365, running tests produces an error for every test case:

  java.lang.UnsupportedOperationException
      at net.sf.cglib.asm.$ClassVisitor.visitNestMemberExperimental(ClassVisitor.java:248)
      at net.sf.cglib.asm.$ClassReader.accept(ClassReader.java:651)
      at net.sf.cglib.asm.$ClassReader.accept(ClassReader.java:391)
      at net.sf.cglib.proxy.BridgeMethodResolver.resolveAll(BridgeMethodResolver.java:70)
      at net.sf.cglib.proxy.Enhancer.emitMethods(Enhancer.java:1132)
      at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:630)
      at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
      at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:329)
      at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
      at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:93)
      at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:91)
      at net.sf.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)
      at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
      at net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)
      at net.sf.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
      at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:116)
      at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291)
      at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
      at net.sf.cglib.proxy.Enhancer.createClass(Enhancer.java:337)
      at org.spockframework.mock.runtime.CglibMockFactory.createMock(CglibMockFactory.java:32)
      at org.spockframework.mock.runtime.ProxyBasedMockFactory.create(ProxyBasedMockFactory.java:45)
      at org.spockframework.mock.runtime.JavaMockFactory.createInternal(JavaMockFactory.java:58)
      at org.spockframework.mock.runtime.JavaMockFactory.create(JavaMockFactory.java:38)
      at org.spockframework.mock.runtime.CompositeMockFactory.create(CompositeMockFactory.java:42)
      at org.spockframework.lang.SpecInternals.createMock(SpecInternals.java:46)
      at org.spockframework.lang.SpecInternals.createMockImpl(SpecInternals.java:294)
      at org.spockframework.lang.SpecInternals.createMockImpl(SpecInternals.java:284)
      at org.spockframework.lang.SpecInternals.MockImpl(SpecInternals.java:108)
      at com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification.makeStaticJenkins(JenkinsPipelineSpecification.groovy:1054)
      at com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification.getStaticJenkins(JenkinsPipelineSpecification.groovy:1070)
      at com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification$1.getInstance(JenkinsPipelineSpecification.groovy:1103)
      at jenkins.model.Jenkins.getInstanceOrNull(Jenkins.java:844)
      at hudson.cli.CLICommand.<clinit>(CLICommand.java:585)
      at java.base/java.lang.Class.forName(Class.java:315)
      at com.homeaway.devtools.jenkins.testing.WholeClasspathPipelineExtensionDetector.getClassesWithAnnotationOfTypeInPackage(WholeClasspathPipelineExtensionDetector.java:112)
      at com.homeaway.devtools.jenkins.testing.APipelineExtensionDetector.getPipelineSteps(APipelineExtensionDetector.java:81)
      at com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification.setupSpec(JenkinsPipelineSpecification.groovy:1109)

When using org.jenkins-ci.main:jenkins-core version 2.366 up to 2.395 (current latest), running tests produces an error for every test case:

  java.lang.NullPointerException: Cannot invoke method getMetaMethod() on null object
      at com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification.addPipelineMocksToObjects_closure1(JenkinsPipelineSpecification.groovy:609)
      at groovy.lang.Closure.call(Closure.java:420)
      at groovy.lang.Closure.call(Closure.java:436)
      at com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification.addPipelineMocksToObjects(JenkinsPipelineSpecification.groovy:607)
      at com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification.setup(JenkinsPipelineSpecification.groovy:1175)

Steps to Reproduce

Current working dependencies (reduced from full set required for my project), bumping the version of org.jenkins-ci.main:jenkins-core leads to failures:

repositories {
    mavenCentral()
    maven { url 'https://repo.jenkins-ci.org/releases' }
    maven { url 'https://repo.jenkins-ci.org/public' }
}

dependencies {
    Closure withoutIcu = { exclude group: 'com.ibm.icu', module: 'icu4j' }

    implementation 'org.codehaus.groovy:groovy-all:2.4.21'
    implementation 'org.jenkins-ci.main:jenkins-core:2.332.1', withoutIcu

    implementation 'com.cloudbees:groovy-cps:1.32@jar', withoutIcu
    implementation 'org.jenkinsci.plugins:pipeline-model-definition:2.2075.vce74e77b_ce40@jar'

    implementation 'org.jenkins-ci.plugins.workflow:workflow-api:1143.v2d42f1e9dea_5@jar'
    implementation 'org.jenkins-ci.plugins.workflow:workflow-cps:2686.v7c37e0578401@jar'
    implementation 'org.jenkins-ci.plugins.workflow:workflow-step-api:622.vb_8e7c15b_c95a_@jar'

    testImplementation 'com.homeaway.devtools.jenkins:jenkins-spock:2.1.5'
    testImplementation 'javax.servlet:javax.servlet-api:4.0.1'
    testImplementation 'org.spockframework:spock-core:1.3-groovy-2.4'
}

Minimal failing test:

package foo.bar

import com.homeaway.devtools.jenkins.testing.JenkinsPipelineSpecification

class FooBarSpec extends JenkinsPipelineSpecification {

    def 'foobar works'() {
    expect:
        1 == 1
    }
}

Additional Information

$ java -version
openjdk version "11.0.18" 2023-01-17
OpenJDK Runtime Environment Temurin-11.0.18+10 (build 11.0.18+10)
OpenJDK 64-Bit Server VM Temurin-11.0.18+10 (build 11.0.18+10, mixed mode)
$ ./gradlew -version

------------------------------------------------------------
Gradle 7.3.3
------------------------------------------------------------

Build time:   2021-12-22 12:37:54 UTC
Revision:     6f556c80f945dc54b50e0be633da6c62dbe8dc71

Kotlin:       1.5.31
Groovy:       3.0.9
Ant:          Apache Ant(TM) version 1.10.11 compiled on July 10 2021
JVM:          11.0.18 (Eclipse Adoptium 11.0.18+10)
OS:           Linux 6.2.7-[REDACTED] amd64
@mikedld
Copy link
Author

mikedld commented Mar 26, 2023

Worked around second error (NullPointerException) by applying the following patch (not sure how good it is, let me know if you want me to open a PR):

--- a/src/main/groovy/com/homeaway/devtools/jenkins/testing/JenkinsPipelineSpecification.groovy
+++ b/src/main/groovy/com/homeaway/devtools/jenkins/testing/JenkinsPipelineSpecification.groovy
@@ -606,6 +606,10 @@ public abstract class JenkinsPipelineSpecification extends Specification {
         
         _objects.each { object ->
             
+            if( object.metaClass == null ) {
+                return;
+            }
+
             final MetaMethod originalMethodMissing = object.metaClass.getMetaMethod("methodMissing", "string", new Object[0] )
             
             object.metaClass.methodMissing = { String _name, _args ->

With the above patch applied, still fails with the first error (UnsupportedOperationException), which could be worked around by bumping the cglib version from 3.2.7 to at least 3.2.9:

dependencies {
    // ...
    testImplementation 'cglib:cglib:3.2.9'
    // ...
}

@sdoeringNew
Copy link

In Groovy every class has a metaClass.

But a class can obfuscate the call to metaClass if it has a method with this signature: get(String key)
Like org.apache.log4j.MDC and org.slf4j.MDC have. 😉

The fix is not to check for a null, but to simply use object.getMetaClass() instead of object.metaClass.

@sdoeringNew
Copy link

I was able to workaround this issue by adding a Helper class to the test folder having a static initializer:

public class OutsmartJenkinsPipelineSpecification {
    static {
        def string = "metaClass"
        string.metaClass.getMetaMethod { String it1, String it2, Object[] it3 -> }
        string.metaClass.methodMissing = "methodMissing"
        string.metaClass.propertyMissing = "propertyMissing"
        org.slf4j.MDC.put("metaClass", string)
    }
}

That adds a fake metaClass to the org.slf4j.MDC class.
(Be aware if you are using MDC while running the tests.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants