Running junit tests with gradle built classes dependent on signed jars


(Sean Van Buggenum) #1

Hi all,

I have a bit of a strange problem. I hope it can be solved.

There are two sets of jars being built;

  1. main application jars, built by ant, and signed 2. plugins to the main application, (will be signed, haven’t got around to it yet) built by gradle.

The gradle build (plugins) uses the result of the main application build (the signed jars) to build against.

That is, those jars are listed by path as compile time dependencies.

Cobertura instrumentation happens,

junit tests run with cobertura instrumentation first in the classpath,

and all is well.

At least, all WAS well, until they started signing the main application jars.

Now my plugin junit tests fail (in the gradle build) with a nasty error like this:

java.lang.SecurityException: class “com.mypackage.MyClass”'s signer information does not match signer information of other classes in the same package

Now, I can sign the plugin jars built by gradle, and this will ensure there are no problems in application run time.

However, how do I get my junit tests working…

In gradle, the junit tests are run with the raw classes built by gradle, correct, and not against any end product (i.e., signed jar) I have finished up with.

How do I get my junit tests working?

Do I really have to have two copies of the main application jars? Those that are signed (for release) and those that are not (for junit testing? )… this would be difficult.

Kind Regards,

sean


(Luke Daley) #2

This is all very dependent on how you have structured your build. Can you please share your build script.


(Sean Van Buggenum) #3

ok, sure. It is a little bit complicated, but here it is (or at least one of them). Included below is a build file of one of the projects, which is just a part of the overall build (all held together by a settings.gradle file which includes all the projects which should build together)

apply plugin: ‘java’

if (getProject().properties[‘codeCoverageProvider’]==null)

getProject().setProperty(‘codeCoverageProvider’, ‘/mnt/data/bin/cobertura-1.9.4.1’)

def buildTime = new java.util.Date()

configurations{

zkm

instrumentation }

dependencies {

instrumentation files("${project.buildDir}/cobertura-instrumentation")

testRuntime files("${codeCoverageProvider}/cobertura.jar")

testCompile files("${buildLibs}/junit.jar")

testCompile files("${buildHome}/mySignedJar.jar")

testCompile project(’:general/ModulesTestCaseBase’)

testCompile project(’:data_export/CustomMod2/CUShared’)

zkm files(zelix_lib)

compile files("${buildHome}/mySignedJar.jar") }

sourceSets {

main {

java {

srcDir ‘src’

}

resources {

srcDir ‘src’

}

}

test {

java {

srcDir ‘test’

}

resources {

srcDir ‘test’

}

runtimeClasspath = configurations.instrumentation + runtimeClasspath

} }

test{

workingDir = new File("${junitWorkingDir}")

ignoreFailures = true

systemProperties[‘net.sourceforge.cobertura.datafile’] = “${project.buildDir}/cobertura.ser” }

task obfuscate(dependsOn: classes) << {

javaexec {

main = ‘ZKM’

classpath = configurations.zkm + sourceSets.main.runtimeClasspath

args “$projectDir/script.txt”

jvmArgs “-DSaveAllDir=$buildDir/obfuscated-classes-extra”

} }

task jar(dependsOn: obfuscate, type: Jar, overwrite: true){ // we provide our own main jar task

manifest.from(“src/META-INF/MANIFEST.MF”)

// because our main jar should be obfuscated

manifest.attributes ‘Build-Time’: buildTime.toString()

from project.sourceSets.main.resources

from “$buildDir/obfuscated-classes-extra” }

task unobfuscatedJar(dependsOn: classes, type: Jar) { // we also provide an extra jar task, to provide our unobfuscated jar

baseName = “${project.name}-unobfuscated”

manifest.from(“src/META-INF/MANIFEST.MF”)

manifest.attributes ‘Build-Time’: buildTime.toString()

from “$buildDir/classes/main” }

test.doFirst { // coverage

println “Setting up cobertura code coverage instrumentation”

ant.taskdef(resource:‘tasks.properties’) {

classpath {

pathelement(location:"${codeCoverageProvider}/cobertura.jar")

fileset(dir:"${codeCoverageProvider}/lib", includes:’*.jar’)

}

}

println “instrumentation to: ${project.buildDir}/cobertura-instrumentation”

ant.‘cobertura-instrument’(toDir: “${project.buildDir}/cobertura-instrumentation”, datafile: “${project.buildDir}/cobertura.ser”){

fileset(dir: “${sourceSets.main.classesDir}”){

include(name: “**/*.class”)

}

} }

test.doLast{

println “Writing cobertura report”

ant.‘cobertura-report’(destdir: “${project.buildDir}/cobertura-report”, datafile: “${project.buildDir}/cobertura.ser”){

sourceSets.main.java.srcDirs.each { dir ->

if (dir.isDirectory()){

fileset(dir: “${dir}”){

include(name: “**/*.java”)

}

}

}

} }


(Peter Niederwieser) #4

Please format your code with code tags.


(Sean Van Buggenum) #5

yes, sorry, I realized what I had done after I posted; but there seems no way to edit a previous posted reply.


(Peter Niederwieser) #6

You should be able to edit it as long as it’s the last reply in the topic. I hope it’s not my comment that turned it uneditable. Could you try once more whether you can edit your last post (the one I’m just commenting on)?


(Sean Van Buggenum) #7

I don’t see any link to do so. Here now, with this post, I will try to edit, before you reply to it. Yes, Ok, I can edit a post I only now just posted (no reply yet made). I didn’t see that before. But the older ones remain uneditable.

And also undeletable.

If someone (you perhaps?) can delete my older posts (or the whole thread) I can re-post correctly… sorry


(Peter Niederwieser) #8

A page refresh sometimes helps to make the edit button appear.


(Sean Van Buggenum) #9

And I should also learn to reply too the comments, and not add to the muck of the thread. … please delete it!!! :frowning:


(Peter Niederwieser) #10

Can you still edit it after I made this comment?


(Sean Van Buggenum) #11

No, there appears to be no ‘edit’ or delete’ link to the right (or anywhere) of the post anymore.


(Peter Niederwieser) #12

OK, thanks a lot for the information. Hopefully this can be fixed at some point.


(Sean Van Buggenum) #13

It is curious, that after having posted your reply to my comment "No, there appears to be no …’ I am still able to edit my comment. I am not able to edit a previous top-level post though (being somehow handled different from comment)


(Peter Niederwieser) #14

Good point. Comments seem to stay editable forever (for the author himself).


(Peter Niederwieser) #15

Can you post your code again with proper formatting? I’ll see if I can remove some of the earlier posts.


(Sean Van Buggenum) #16

Here’s my previous post, correctly (hopefully) marked

ok, sure. It is a little bit complicated, but here it is (or at least one of them).

Included below is a build file of one of the projects, which is just a part of the overall build

(all held together by a settings.gradle file which includes all the projects which should build together)

apply plugin: 'java'
      if (getProject().properties['codeCoverageProvider']==null)
  getProject().setProperty('codeCoverageProvider', '/mnt/data/bin/cobertura-1.9.4.1')
  def buildTime = new java.util.Date()
      configurations{
  zkm
  instrumentation
 }
      dependencies {
  instrumentation files("${project.buildDir}/cobertura-instrumentation")
  testRuntime files("${codeCoverageProvider}/cobertura.jar")
  testCompile files("${buildLibs}/junit.jar")
  testCompile files("${buildHome}/mySignedJar.jar")
  testCompile project(':general/ModulesTestCaseBase')
  testCompile project(':data_export/CustomMod2/CUShared')
  zkm files(zelix_lib)
  compile files("${buildHome}/mySignedJar.jar")
 }
  sourceSets {
  main {
   java {
    srcDir 'src'
   }
   resources {
    srcDir 'src'
   }
 }
   test {
   java {
        srcDir 'test'
    }
    resources {
        srcDir 'test'
    }
    runtimeClasspath = configurations.instrumentation + runtimeClasspath
   }
  }
  test{
 workingDir = new File("${junitWorkingDir}")
  ignoreFailures = true
  systemProperties['net.sourceforge.cobertura.datafile'] = "${project.buildDir}/cobertura.ser"
 }
  task obfuscate(dependsOn: classes) << {
  javaexec {
   main = 'ZKM'
   classpath = configurations.zkm + sourceSets.main.runtimeClasspath
   args "$projectDir/script.txt"
   jvmArgs "-DSaveAllDir=$buildDir/obfuscated-classes-extra"
  }
 }
  task jar(dependsOn: obfuscate, type: Jar, overwrite: true){ // we provide our own main jar task
  manifest.from("src/META-INF/MANIFEST.MF") // because our main jar should be obfuscated
  manifest.attributes 'Build-Time': buildTime.toString()
  from project.sourceSets.main.resources
  from "$buildDir/obfuscated-classes-extra"
 }
  task unobfuscatedJar(dependsOn: classes, type: Jar) { // we also provide an extra jar task, to provide our unobfuscated jar
  baseName = "${project.name}-unobfuscated"
  manifest.from("src/META-INF/MANIFEST.MF")
  manifest.attributes 'Build-Time': buildTime.toString()
  from "$buildDir/classes/main"
 }
  test.doFirst { // coverage
   println "Setting up cobertura code coverage instrumentation"
   ant.taskdef(resource:'tasks.properties') {
    classpath {
        pathelement(location:"${codeCoverageProvider}/cobertura.jar")
        fileset(dir:"${codeCoverageProvider}/lib", includes:'*.jar')
    }
   }
   println "instrumentation to: ${project.buildDir}/cobertura-instrumentation"
   ant.'cobertura-instrument'(toDir: "${project.buildDir}/cobertura-instrumentation", datafile: "${project.buildDir}/cobertura.ser"){
    fileset(dir: "${sourceSets.main.classesDir}"){
        include(name: "**/*.class")
    }
   }
  }
  test.doLast{
   println "Writing cobertura report"
   ant.'cobertura-report'(destdir: "${project.buildDir}/cobertura-report", datafile: "${project.buildDir}/cobertura.ser"){
    sourceSets.main.java.srcDirs.each { dir ->
        if (dir.isDirectory()){
            fileset(dir: "${dir}"){
                include(name: "**/*.java")
            }
        }
    }
   }
  }

(Peter Niederwieser) #17
  1. Is the class that you are getting a SecurityException for a main class, plugin class, or plugin test class?

  2. Do some main classes and plugin classes live in the same package?

  3. Do the plugin test classes live in the same package as the plugin classes they are testing?


(Sean Van Buggenum) #18

Hi Peter, thanks for the reply: Here is, I hope, the answer to your questions:

1 The Junit test class, in package ‘com.test’, calls a function of a test-helper class in package ‘com.mypackage’ (all still existing in the plugin project) which attempts to instantiate a class with protected constructor, from the signed application jar, also located at package ‘com.mypackage’.

It is at attempted construction that we get the error: java.lang.SecurityException: class “com.mypackage.MyClass”'s signer information does not match signer information of other classes in the same package

So I guess the answer to your question is ‘main class’.

The test-helper class does live in the same package (not the plugin classes) so it can get access to the constructor of the application class it is trying to instantiate for the tests. This particular class does some database work that is necessary for the junit tests to run.

Yes, they do, all plugin test classes live in the same package as the plugin classes they are testing, except for those test-helper classes which need to be in application packages to gain access to certain classes or functions.

Think there is any hope for this setup?

Thanks in advance for any suggestion.


(Peter Niederwieser) #19

It seems that the JVM security manager insists that all classes in the same package are signed in the same way, and that your test helper class (which is in the same package as the signed application code) violates this. This isn’t a Gradle-related problem. I’d try one of the following:

  1. Move the test helper class to a different package and make the constructor public 2. Move the test helper class to a different package and access the protected constructor by some other means (e.g. reflection or by writing the test helper class in Groovy) 3. Sign the test helper class 4. Tweak the security manager/security policy

(Peter Niederwieser) #20

Since the constructor is protected, you can also just create a subclass that makes the constructor public. No need to put anything in the same package.