Testng,Emma,Cobertura,coverage和JDK 7导致ClassFormatError和VerifyError的结果

55

我已经转换到最新的JDK 7,并且在使用由Emma覆盖工具处理的字节码运行TestNG单元测试时遇到了问题。我的所有测试用例都没有正确运行,对于大多数测试用例,我都收到了如下错误信息。

 java.lang.ClassFormatError: Illegal local variable table length 10 in method measurement.meter.AbstractSerialPortMeter.<init>(Lmeasurement/meter/SerialPort;)V at measurement.meter.Elc3133aTest.setUp(Elc3133aTest.java:42)
我在这里找到了一篇文章 JSR 292 Goodness Fast Code Coverage Tool Less 10k,它说“JSR 292引入了一个新的字节码指令invokedynamic,还有几种新的常量池常量。这意味着大多数解析字节码的工具(如ASM、BCEL、findbugs或EMMA)都需要更新以兼容Java 7。”
我查看了Emma主页,但看起来已经很久没有更新了。
是否有人解决了类似的问题?
我也尝试过Cobertura。它似乎工作得更好,但我得到了很多类型为VerifyError的异常。
java.lang.VerifyError: Expecting a stackmap frame at branch target 85 in method measurement.meter.AbstractSerialPortMeter.close()V at offset 26
at measurement.meter.AbstractSerialPortMeterTest.setUp(AbstractSerialPortMeterTest.java:27)

2
我认为在短时间内没有人会转移到Java7,因为目前还不知道它有多少漏洞和安全隐患,所以我建议你也这样做。同时,正如你提到的那样,EMMA已经很长时间没有更新了,足以被认为是一个几乎死亡的项目。我建议你在几个月后尝试Cobertura,因为它也存在一些与Java7相关的问题,但与EMMA不同的是,它仍在进行最小程度的开发(上次更改是2个月前)。 - Augusto
2
我在Eclipse 3.7.1中运行Java7项目时,遇到了与你完全相同的问题,即无法在EMMA中运行。 - user1037777
1
是的,我写这个问题已经有一段时间了,但Cobertura和Emma都没有更新。这有点令人沮丧,但好吧,我想我们也无能为力。 - Jagger
3
不确定cobertura是否可行,但eclemma团队似乎已经创建了符合Java 7标准的覆盖率引擎,名为JaCoCo http://www.eclemma.org/jacoco/trunk/index.html。 - JohnKlehm
Cobertura 的网站宣布现在支持 Java7。 - Vincent Cantin
显示剩余3条评论
7个回答

76
我使用maven cobertura插件时遇到了同样的问题。当从cobertura:report中运行时,所有测试都失败了。但是当直接从surefire插件中运行时,所有测试都成功了。正如你们中的一些人已经说过的那样,问题在于coberture字节码的仪器化与JDK7不兼容。
你可以在这里看到相关的异常:http://vikashazrati.wordpress.com/2011/10/09/quicktip-verifyerror-with-jdk-7/(参见:-X:+UseSplitVerifier JVM选项在http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html中)。
因此,我的解决方案是将surefire-plugin配置为始终使用JVM参数“-XX:-UseSplitVerifier”执行测试。它可以良好地工作,无论是否进行了cobertura仪器化。
我在maven中的surefire配置:
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.12</version>
    <configuration>
        <argLine>-XX:-UseSplitVerifier</argLine>
    </configuration>
</plugin>

5
在Eclipse的“覆盖率配置”中,将以下内容添加到“VM参数”。运行良好。 - dkantowitz
1
谢谢Pedro,对我来说完美无缺。相同的配置,相同的问题,相同的解决方案。 - Lorenzo Solano Martinez
@dkantowitz 你真是个天才!在Eclipse中它完美运行。如果你可以在代码编辑器中看到所有结果,为什么还要一直麻烦着Surefire呢?你应该把你的帖子置顶,因为它真的非常有帮助。另外,值得一提的是,在Kepler中使用Cobertura之前,你必须按照这些步骤进行操作:https://dev59.com/x2Ml5IYBdhLWcg3wK0SA - afrish
@nucleo 感谢美言。我很高兴我的便签对您有所帮助。说实话,我甚至不记得当时写下了什么评论或在做什么事情。整个过程可能是一件那种应付它 / 修复愚蠢的Java烂摊子的事情,并很快忘记;当时似乎不值得更多的快速评论。 - dkantowitz
@Pedro Ballesteros,使用上述配置解决了我的问题上周。但今天又出现了。 - Jagadeesh

5

点击您提供的链接后,我收到了XML响应“访问被拒绝”。但是根据链接的内容,我可以看出这是一个Emma Eclipse插件。您是否也将其作为Ant任务运行?Ant任务是我最需要的东西。 - Jagger
1
好的,已经想通了。我应该在Eclipse的“安装新软件…”中使用它。 - Jagger
你给我的提示让我找到了JaCoCo:http://www.eclemma.org/jacoco/trunk/index.html,它完美地满足了我的所有需求。因此我接受了这个答案。 - Jagger

2
我使用了这里建议的补丁,使用jvm参数成功地安装了Gradle 1.0M9、Java 7和EMMA 2.1。
详细信息请查看这里
configurations{
  emma
}

dependencies {
  // EMMS Code Coverage
  emma "emma:emma:2.1.5320"
  emma "emma:emma_ant:2.1.5320"
  ...
  testCompile group: 'junit', name: 'junit', version: '4.9'
}

test {
    // add EMMA related JVM args to our tests
    jvmArgs "-XX:-UseSplitVerifier", "-Demma.coverage.out.file=$buildDir/tmp/emma/metadata.emma", "-Demma.coverage.out.merge=true"

    doFirst {
       println "Instrumenting the classes at " + sourceSets.main.output.classesDir.absolutePath
       // define the custom EMMA ant tasks
       ant.taskdef( resource:"emma_ant.properties", classpath: configurations.emma.asPath)

       ant.path(id:"run.classpath") {
          pathelement(location:sourceSets.main.output.classesDir.absolutePath)
       }
       def emmaInstDir = new File(sourceSets.main.output.classesDir.parentFile.parentFile, "tmp/emma/instr")
       emmaInstDir.mkdirs()
       println "Creating $emmaInstDir to instrument from " +       sourceSets.main.output.classesDir.absolutePath
       // instruct our compiled classes and store them at $buildDir/tmp/emma/instr
       ant.emma(enabled: 'true', verbosity:'info'){
          instr(merge:"true", destdir: emmaInstDir.absolutePath, instrpathref:"run.classpath",
                metadatafile: new File(emmaInstDir, '/metadata.emma').absolutePath) {
             instrpath {
             fileset(dir:sourceSets.main.output.classesDir.absolutePath, includes:"**/*.class")
             }
          }
       }
       setClasspath(files("$buildDir/tmp/emma/instr") + configurations.emma +    getClasspath())
    }

    // The report should be generated directly after the tests are done.
    // We create three types (txt, html, xml) of reports here. Running your build script now should
    // result in output like that:
    doLast {
       def srcDir = sourceSets.main.java.srcDirs.toArray()[0]
       println "Creating test coverage reports for classes " + srcDir
       def emmaInstDir = new File(sourceSets.main.output.classesDir.parentFile.parentFile, "tmp/emma")
       ant.emma(enabled:"true"){
          new File("$buildDir/reports/emma").mkdirs()
          report(sourcepath: srcDir){
             fileset(dir: emmaInstDir.absolutePath){
                include(name:"**/*.emma")
             }
             txt(outfile:"$buildDir/reports/emma/coverage.txt")
             html(outfile:"$buildDir/reports/emma/coverage.html")
             xml(outfile:"$buildDir/reports/emma/coverage.xml")
          }
       }
       println "Test coverage reports available at $buildDir/reports/emma."
       println "txt: $buildDir/reports/emma/coverage.txt"
       println "Test $buildDir/reports/emma/coverage.html"
       println "Test $buildDir/reports/emma/coverage.xml"
    }
}

运行 "gradle test" 将会得到以下结果:

marcello@hawaii:/u1/development/workspaces/open-source/interviews/vmware$ gradle test
:compileJava
:processResources UP-TO-DATE
:classes
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
Instrumenting the classes at /u1/development/workspaces/open-source/interviews/vmware/build/classes/main
Creating /u1/development/workspaces/open-source/interviews/vmware/build/tmp/emma/instr to instrument from /u1/development/workspaces/open-source/interviews/vmware/build/classes/main
Creating test coverage reports for classes /u1/development/workspaces/open-source/interviews/vmware/src/main/java
Test coverage reports available at /u1/development/workspaces/open-source/interviews/vmware/build/reports/emma.
txt: /u1/development/workspaces/open-source/interviews/vmware/build/reports/emma/coverage.txt
Test /u1/development/workspaces/open-source/interviews/vmware/build/reports/emma/coverage.html
Test /u1/development/workspaces/open-source/interviews/vmware/build/reports/emma/coverage.xml

BUILD SUCCESSFUL

1
jvmArgs“-XX:-UseSplitVerifier”对我有用。谢谢! - Pescuma

1

我曾经遇到过这个问题。通过Eclipse市场升级到2.0.1.201112281951版本对我有用。


1

如果您不使用新的语言特性(例如try-with-resources等),Emma可以工作。您可以使用Java 7并使用新库(例如Paths、DirectoryStream等)。 我知道这不是解决您问题的方法,但如果您只想检查“JDK 7的表现如何”,那么可能会有所帮助...


在切换到Java 7之后,我对我的旧代码进行了重构,因此回退不是一个选项,但还是谢谢你的提示。由于我的代码符合最新版本的Java,我无法检查它是否仍然可以使用Java 6语法。 - Jagger
这似乎不是真的。我也遇到了同样的问题,而且我没有做任何源代码更改。 - Samuel Edwin Ward

1

IntelliJ IDEA 11的内部覆盖率工具对于使用try-with-resources、diamond operator的项目运行良好,但我们没有使用invokedynamic。我认为覆盖率工具不包含在社区版中,只有Ultimate版。

我还没有尝试过jacoco - 这是大多数emma的前开发人员似乎去了的地方。


JaCoCo看起来非常完美,与其他工具相比,它不使用字节码仪器(Emma,Cobertura)或源代码仪器(Clover)。我不知道细节,但它似乎重用调试信息并作为Java代理工作,在构建期间简化了许多任务。 - Jagger
1
事实上,JaCoCo执行字节码插装,但与Emma和Cobertura相比,它是即时的。 - Godin

1
Java 8+ 的等价答案是 Pedro Ballesteros 的答案:
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.12.4</version>
  <configuration>
    <argLine>-noverify</argLine>
  </configuration>
</plugin>

(调整版本号以匹配您正在使用的Surefire版本。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接