AKS
AKS

Reputation: 17336

Gradle - Jacoco - JMockit - Tests are hanging not progressing

I'm using Gradle (Gradle 1.6 -upto 1.9) to build a Java project. Tried with both Java 1.6 or 1.7.

src/java  - contains Java source code
test/java - contains test java code

Project compiles/builds successfully. During the build time, Junit UNIT test(s) runs successfully as well. I have only one test and it uses JMockit library. Please NOTE: This same jacoco code works fine in any other project where I don't have test which needs JMockit library.

JMockit groupid:artifactid:version is:

jmockit:jmockit:1.1

I wanted to have Jacoco code coverage enabled. Jacoco version that I have tried so far is shown in the code below, this code exists in my Gradle build script.

I added the following lines to my project's build.gradle file.

apply plugin: 'jacoco'

   jacoco {
                //toolVersion = "0.6.2.201302030002"
          toolVersion = "0.7.0.201403182114"
                //toolVersion = "0.7.1.201404171759" --- trying to find how to make version this working.
                // reportsDir = file("$buildDir/customJacocoReportDir")
   }

   test {
     ignoreFailures = true
     testReportDir = file("$buildDir/reports/tests/UT")
     testResultsDir = file("$buildDir/test-results/UT")

     // Uncomment the following if you need more detailed output.  
     //testLogging.showStandardStreams = true
     //onOutput { descriptor, event ->
     //  logger.lifecycle("Test: " + descriptor + " produced standard out/err: " + event.message )
     //}

     //Following Jacoco test section is required only in Jenkins instance extra common file
     jacoco {
       //The following vars works ONLY with 1.6 of Gradle
       destPath = file("$buildDir/jacoco/UT/jacocoUT.exec")
       classDumpPath = file("$buildDir/jacoco/UT/classpathdumps")

       //Following vars works only with versions >= 1.7 version of Gradle
       //destinationFile = file("$buildDir/jacoco/UT/jacocoUT.exec")
      //  classDumpFile = file("$buildDir/jacoco/UT/classpathdumps")
     }
   }

   task integrationTest( type: Test) {
     //Always run tests
     outputs.upToDateWhen { false }
     ignoreFailures = true

     testClassesDir = sourceSets.integrationTest.output.classesDir
     classpath = sourceSets.integrationTest.runtimeClasspath

     testReportDir = file("$buildDir/reports/tests/IT")
     testResultsDir = file("$buildDir/test-results/IT")

     //Following Jacoco test section is required only in Jenkins instance extra common file
     jacoco {
        //This works with 1.6
        destPath = file("$buildDir/jacoco/IT/jacocoIT.exec")
        classDumpPath = file("$buildDir/jacoco/IT/classpathdumps")

        //Following works only with versions >= 1.7 version of Gradle
        //destinationFile = file("$buildDir/jacoco/IT/jacocoIT.exec")
        //  classDumpFile = file("$buildDir/jacoco/IT/classpathdumps")
     }
  }

  jacocoTestReport {
      group = "Reporting"
      description = "Generate Jacoco coverage reports after running tests."
      ignoreFailures = true

      executionData = fileTree(dir: 'build/jacoco', include: '**/*.exec')

      reports {
             xml{
                 enabled true
                 //Following value is a file
                 destination "${buildDir}/reports/jacoco/xml/jacoco.xml"
             }
             csv.enabled false
             html{
                 enabled true
                 //Following value is a folder
                 destination "${buildDir}/reports/jacoco/html"
             }
      }

      //sourceDirectories = files(sourceSets.main.allJava.srcDirs)
      sourceDirectories = files('src/java')
      classDirectories =  files('build/classes/main')

      //------------------------------------------
      //additionalSourceDirs = files('test/java')
      //additionalSourceDirs += files('src/java-test')

      //additionalClassDirs = files('build/classes/test')
      //additionalClassDirs += files('build/classes/integrationTest')
      //additionalClassDirs += files('build/classes/acceptanceTest')
      //------------------------------------------
  }



My questions: 1. When I'm not using "apply plugin: 'jacoco'", then :test task runs successfully (I have only one test). BUT, when I enable apply plugin: 'jacoco', then during :test task, I see the following line during build output and the process just hangs there and sits for hours and doesn't proceed.

Starting process 'Gradle Worker 1'. Working directory: /production/jenkinsAKS/workspace/MyProjectSvc Command: /production/jdk1.6.0_03/bin/java -Djava.security.manager=jarjar.org.gradle.processinternal.child.BootstrapSecurityManager -javaagent:build/tmp/expandedArchives/org.jacoco.agent-0.7.0.201403182114.jar_2kiqpmj1hlqbuth11j0qnuarhs/jacocoagent.jar=destfile=build/jacoco/UT/jacocoUT.execappend=true,dumponexit=true,output=file,classdumpdir=build/jacoco/UT/classpathdumps,jmx=false -Dfile.encoding=UTF-8 -ea -cp /production/jenkins/.gradle/caches/1.6/workerMain/gradle-worker.jar jarjar.rg.gradle.process.internal.launcher.GradleWorkerMain
An attempt to initialize for well behaving parent process finished.
Successfully started process 'Gradle Worker 1'
Gradle Worker 1 executing tests.
> Building > :test

I googled around and it seems like there's some incompatibility between Jacoco and JMockit libraries in the current latest version and there's a fix coming to get this issue resolved. The new version of Jacoco 0.7.1.xxxxx has the fix but I don't know when it'll be available in Maven repository.

Any idea, how can I set the javaagent to ignore the test/test class file for JACOCO and still apply jacoco plugin. In my case, apply plugin: 'jacoco' will later exist in a global file i.e. inside /init.d/global-common.gradle file within allProjects { .... } section.

I tried the following but still, the build process hangs at :test task until I uncomment exclude below. If I comment out the whole jacoco subsection within test section, build process still hangs at :test task (seems like as apply plugin: 'jacoco' is there).

If I uncomment exlude, then I don't see error but then no test runs i.e. index.html for test reports shows nothing ran.

test {
     include "**/*"
     jacoco {
        //exclude "**/util/Test*"
     }
}

2. How can I use the jacoco 0.7.1.xxxx version (non-release aka nightly release which has the fix for this issue) in my build.gradle file. When I used 0.7.1.xxx version after uncommenting it (as shown above), it errored out saying can't find dependency jacoco:0.7.1.xxxx

3. To get rid of this issue, I think I can set a jacoco agent parameter i.e. when it runs, it'll ignore the JMOckit/JUnit .jar library or something. See/Found the following links:
http://javaee.ch/2012/10/09/jmockit-with-maven-sonar-jacoco-and-jenkinshudson/
https://github.com/jacoco/jacoco/pull/35

Acc. to the second link:

A workaround which avoids the problem is to exclude the JUnit classes from JaCoCo's consideration. I used the following JVM initialization parameter in my testing, excluding both JUnit and TestNG classes: -javaagent:/jacoco-0.7.1/lib/jacocoagent.jar=excludes=junit.:org.junit.:org.testng. The good news is that the fix I described in my previous comment also solves this deadlock problem, because then the JUnit classes will be ignored by JaCoCo as they get instrumented by JMockit.

I'm trying to find what variable in jacoco { ... } or within test { ... } I can set to do the same until I get the new version of Jacoco or JMockit (which has the fix).

Seems like it's within test section i.e. test { ..here jvmArgs '...'will be set. jacoco { ... } .. } http://stevendick.github.io/blog/2012/01/22/jacoco-and-gradle/

4. If I exclude the test class file by using "exclude "com/xxx/yyy/a/b/c/util/Testname.class", then it works and I don't see an error but then I found that due the this exclude, my test never run!!! that's why it didn't hang. Well, I want the test to run and don't want Jacoco to process it.

Just by having apply plugin: 'jacoco' in build.gradle is hanging the build at :test task. I need jacoco as Development team would like to see the code coverage details as well.

At this time, I'm trying to find answers to the above ?s, appreciate your inputs.

Upvotes: 3

Views: 3795

Answers (3)

AKS
AKS

Reputation: 17336

Final answer:

Both Jmockit and Jacoco instruments the class files. Jmockit does it first during the build process and when jacoco tries the same (later in the process) it says "oh oh, can't instrument an already instrumented class file". This issue happened with older versions of jacoco and jmockit. To see this error, enable --stacktrace option during Gradle build or --debug.

Now, using the latest jacoco and jmockit versions, we can solve this issue easily.

If you use jacoco:

toolVersion="0.7.1.201405082137"

or

toolVersion="0.7.2.201409121644"

See this: changes that went in 0.7.1 version http://www.eclemma.org/jacoco/trunk/doc/changes.html

For JMockit, you have to use jmockit version: 1.8 at least or later (1.9 to 1.13).

org.jmockit:jmockit:1.8

See this: changes that went under 1.8 version: http://jmockit.github.io/changes.html

Upvotes: 4

dcsohl
dcsohl

Reputation: 7406

I'm not too familiar with gradle, so I'm not sure I can help with questions 1, 3, or 4...

But question 2, I can help - the version string for JaCoCo 0.7.1 is not 0.7.1.201404171759 but rather 0.7.1.201405082137. This version has been formally released, in case you hadn't noticed.

If you want the latest nightly build, the convention is to simply refer as 0.7.2-SNAPSHOT (make sure you are pointing at the snapshot repository at https://oss.sonatype.org/content/repositories/snapshots/).

Upvotes: 0

AKS
AKS

Reputation: 17336

Answer 1: Setting jacoco subsection (within test section) -- enabled field to "false" did the trick. I'm not getting the code coverage (which I can live with until I get the new versions out for both JMockit / Jacoco) but now I see valid test report index.html file (i.e. test ran successfully) and still apply plugin: 'jacoco' can stay effective in /init.d/global-common.gradle file ...

test {
     jacoco {
        enabled false
     }
}

For Answer 2: NOT found so far, will share.

Answer 3: Didn't resolve the error - but how you set it up is given at the link: http://stevendick.github.io/blog/2012/01/22/jacoco-and-gradle/

I tried giving, may be I didn't use it correctly. jvmArgs '....: ,....:.. ,....:....,exclude="com.:org.gradle.;jmockit.:mockit.:junit.*"

Answer 4: Answer 1 will suffice.

Upvotes: 0

Related Questions