JacocoPlugin makes tests fail, not finding JUnit Runner

I’m currently struggling with getting Jacoco plugin to run. My build is fine and all tests are working. Now I applied Jacoco plugin, set the jacoco.toolVersion to 0.7.1.201405082137 and tried to run one of my subprojects test task. Now every test fails with

{code}

java.lang.NoClassDefFoundError: org.junit.runner.Runner
 at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:61)
 at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:26)
 at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:80)
 at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:49)
 at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:69)
 at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:50)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:601)
 at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
 at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
 at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
 at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
 at $Proxy2.processTestClass(Unknown Source)
 at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:105)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:601)
 at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
 at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
 at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:355)
 at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
 at java.lang.Thread.run(Thread.java:722)

{code}

I then removed the jacoco.toolVersion definition to use the version of Jacoco the plugin was developed against. Now the tests do not fail anymore, but it hangs forever trying to execute the first test.

Actually if I run with any but the latest version of Jacoco it hangs forever and when using the latest version as originally mentioned, there is the following on stderr:

{code}

java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)
        at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:382)
        at sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:407)
    Caused by: java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields)
        at sun.instrument.InstrumentationImpl.redefineClasses0(Native Method)
        at sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:170)
        at mockit.internal.startup.Startup.redefineMethods(Startup.java:235)
        at mockit.internal.startup.Startup.redefineMethods(Startup.java:227)
        at mockit.internal.annotations.MockClassSetup.applyClassModifications(MockClassSetup.java:214)
        at mockit.internal.annotations.MockClassSetup.redefineMethodsInClassHierarchy(MockClassSetup.java:177)
        at mockit.internal.annotations.MockClassSetup.redefineMethods(MockClassSetup.java:164)
        at mockit.internal.annotations.MockClassSetup.setUpStartupMock(MockClassSetup.java:158)
        at mockit.internal.startup.JMockitInitialization.applyMockClass(JMockitInitialization.java:77)
        at mockit.internal.startup.JMockitInitialization.setUpInternalStartupMock(JMockitInitialization.java:57)
        at mockit.internal.startup.JMockitInitialization.loadInternalStartupMocksForJUnitIntegration(JMockitInitialization.java:39)
        at mockit.internal.startup.JMockitInitialization.initialize(JMockitInitialization.java:26)
        at mockit.internal.startup.Startup.initialize(Startup.java:74)
        at mockit.internal.startup.Startup.initialize(Startup.java:63)
        at mockit.internal.startup.Startup.agentmain(Startup.java:84)
        ... 6 more
    java.lang.RuntimeException: com.sun.tools.attach.AgentInitializationException: Agent JAR loaded but agent failed to initialize
        at mockit.internal.startup.JDK6AgentLoader.loadAgentAndDetachFromThisVM(JDK6AgentLoader.java:124)
        at mockit.internal.startup.JDK6AgentLoader.loadAgent(JDK6AgentLoader.java:61)
        at mockit.internal.startup.AgentInitialization.initializeAccordingToJDKVersion(AgentInitialization.java:21)
        at mockit.internal.startup.Startup.initializeIfNeeded(Startup.java:187)
        at mockit.internal.startup.Startup.initializeIfPossible(Startup.java:220)
        at org.junit.runner.Runner.<clinit>(Runner.java:22)
        at org.junit.internal.builders.JUnit4Builder.runnerForClass(JUnit4Builder.java:10)
        at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
        at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
        at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
        at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:26)
        at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:80)
        at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:49)
        at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:69)
        at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:50)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)
        at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
        at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
        at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
        at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
        at $Proxy2.processTestClass(Unknown Source)
        at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:105)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)
        at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
        at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
        at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:355)
        at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
        at java.lang.Thread.run(Thread.java:722)
    Caused by: com.sun.tools.attach.AgentInitializationException: Agent JAR loaded but agent failed to initialize
        at sun.tools.attach.HotSpotVirtualMachine.loadAgent(HotSpotVirtualMachine.java:121)
        at mockit.internal.startup.JDK6AgentLoader.loadAgentAndDetachFromThisVM(JDK6AgentLoader.java:117)
        ... 35 more

{code}

Ah forget it, please mark this as self-answered.

The problem were incompatibilities between JaCoCo and JMockit. You need at least JaCoCo 0.7.1.201405082137 and JMockit 1.8 for the two to play nicely together. Otherwise you will have all sorts of trouble from infinite hangs up to strange exceptions about unsupported class modifications.

Bjorn,

I tried to reproduce this but couldn’t. Could you provide a project that we can use to reproduce?

We probably can’t make it work, but we shouldn’t hang indefinitely. That is, we should have better error handling here.

I doubt you can do much but to set up some deadlock recognition that kills the VM or similar. As far as I remember it was a deadlock inside JaCoCo and / or JMockit. I think you should be able to reproduce the issue if you apply the JaCoCo plugin with default toolVersion, add JMockit in version 1.4 (‘com.googlecode.jmockit:jmockit:1.4’) to testCompile and add a test that uses JMockit. Then the deadlock should occur and if you upgrade JaCoCo to toolVersion 0.7.1.201405082137, then the NoClassDefFoundError for org.junit.runner.Runner should occur. With JaCoCo at least toolVersion 0.7.1.201405082137 and JMockit at least version 1.8 no problems should occur as in those versions the incompatibilites were fixed.

Here you are: https://github.com/Vampire/jacoco-jmockit-hang-showcase

Right you are, nothing we can really do here.

Thanks for the test case. Good to be sure.

Well, you could increase the default toolVersion of the JaCoCo plugin to 0.7.1.201405082137 and make sure that if JaCoCo plugin and JMockit plugin are both applied that JaCoCo is at least of version 0.7.1.201405082137 and JMockit is at least of version 1.8, because if either of the two tools is of older version, one of the various errors may / will appear.

I’m not keen on adding that specific check, but bumping the default Jacoco version is not a bad idea.

Yeah, sounds a bit cumbersome. It just would help preventing the issues happening, but maybe it is not a too good idea.