razvn / jacoco-to-cobertura-gradle-plugin

MIT License
13 stars 5 forks source link

FileNotFoundException for ~/.gradle/daemon/7.2/report.dtd #11

Open fadookie opened 1 year ago

fadookie commented 1 year ago

I have integrated the plugin following the instructions in the README and set an input and output path. I have confirmed that the input file exists, but when I try to run the jacocoToCobertura task I get a FileNotFoundException. It seems the plugin is looking for a file called ~/.gradle/daemon/7.2/report.dtd but this file does not exist on my system.

% ./gradlew --version 

------------------------------------------------------------
Gradle 7.2
------------------------------------------------------------

Build time:   2021-08-17 09:59:03 UTC
Revision:     a773786b58bb28710e3dc96c4d1a7063628952ad

Kotlin:       1.5.21
Groovy:       3.0.8
Ant:          Apache Ant(TM) version 1.10.9 compiled on September 27 2020
JVM:          19.0.1 (Oracle Corporation 19.0.1+10-21)
OS:           Mac OS X 12.6.1 aarch64
12:26:04 PM: Executing task 'app:jacocoToCobertura'...

Executing tasks: [app:jacocoToCobertura] in project <REDACTED>

> Configure project :app
inputPath: '<REDACTED>/android/app/build/reports/jacoco/testDevelopEnvironmentDebugUnitTestCoverage/testDevelopEnvironmentDebugUnitTestCoverage.xml'

> Task :app:jacocoToCobertura
Conversion jacoco report to cobertura
Error while running JacocoToCobertura conversion: `Loading Jacoco report error: `(was java.io.FileNotFoundException) /Users/eliotlash/.gradle/daemon/7.2/report.dtd (No such file or directory)
 at [row,col {unknown-source}]: [1,124]`

BUILD SUCCESSFUL in 147ms
1 actionable task: 1 executed

Build Analyzer results available
12:26:04 PM: Task execution finished 'app:jacocoToCobertura'.

(This is occurring in a work project under NDA so I have redacted some of the build details). I added a println statement to log the input path.

razvn commented 1 year ago

Not sure what it can be. The only place I see report.dtd is in the jacoco report. In my jacoco report xml I see something like:

<!DOCTYPE report PUBLIC "-//JACOCO//DTD Report 1.1//EN" "report.dtd">

but is not something I really care about... maybe the xml parser lib but I did nothing to change or don't know how it's modified. are you using jacoco 0.8.8? any special jacoco xml report configuration?

fadookie commented 1 year ago

Thanks for the reply. I was using the configuration from https://blog.canopas.com/android-code-coverage-using-jacoco-6639a1fc4293

I was using jacoco 0.8.7. I was blocked by this so I decided to deintegrate this gradle plugin and use cover2cover instead. So if you want I can close the issue, but I'm happy to keep it open and continue to troubleshoot to help others who might run into the same issue in the future.

fadookie commented 1 year ago

OK, so this is pretty hacky but I am trying out this plugin again (cover2cover is generating invalid XML) and I figured out a workaround... I just downloaded https://github.com/jacoco/jacoco/blob/master/org.jacoco.report/src/org/jacoco/report/xml/report.dtd into ~/.gradle/daemon/7.2/report.dtd 🤷‍♂️ It looks like it also works to save this file next to the jacoco XML file.

I suppose it might also work to edit the JaCoCo XML to have the fully qualified URL of https://raw.githubusercontent.com/jacoco/jacoco/master/org.jacoco.report/src/org/jacoco/report/xml/report.dtd

razvn commented 1 year ago

I don't see why it really looks for the dtd... I tried with jacoco 0.8.7 but no matter how hard I try to screw up the DOCTYPE tag <!DOCTYPE report PUBLIC "-//JACOCO//DTD Report 1.1//EN" "report.dtd"> (using a bad path, bad version, wrong file name, without the tag) I get no error.

fadookie commented 1 year ago

That's bizarre. I can reproduce it 100% of the time even on JaCoCo 0.8.8. I tried changing the dtd to that raw URL to see if that would fix it but it didn't seem to work. What is your JDK version? Maybe something changed in the underlying XML parsing lib, if you're using one from the java standard library?

razvn commented 1 year ago

Hello, I tried on macOS & Linux jdk 11 &. 17. The xml library I use is SimpleXML I can't see where or why it cares about the dtd, I couldn't manage to make it care.

I may try to switch to another one when I'll have some time as this one is quite old, but wanted something easy and fast to implement.

Kotlin-Native commented 1 year ago

had the same problem in a simply spring-kotlin-multimodule project with open-jdk-17, gradle 8.0.2 and the default used jacoco plugin. I also use macOS. I switched back to kover and with that it works fine.

fadookie commented 1 year ago

So this is the solution I eventually landed at that's compatible with running in CI. In the gradle task, I download the DTD to the working directory:

jacocoToCobertura {
    def inputDir = "$buildDir/reports/jacoco/testDevelopEnvironmentDebugUnitTestCoverage"
    def inputFilePath = "$inputDir/testDevelopEnvironmentDebugUnitTestCoverage.xml"

    // Workaround for parse error: Download JaCoCo DTD to working directory
    def workingDir = new File(".").getAbsoluteFile().getParent()
    def f = new File("$workingDir/report.dtd")
    if (!f.exists()) {
        new URL('https://raw.githubusercontent.com/jacoco/jacoco/master/org.jacoco.report/src/org/jacoco/report/xml/report.dtd')
                .withInputStream{ i -> f.withOutputStream{ it << i }}
    }

    def outDir = "${buildDir}/reports/cobertura"
    file(outDir).mkdirs()
    inputFile.set(file(inputFilePath))
    outputFile.set(file("$outDir/cobertura.xml"))
}
psibre commented 1 year ago

This vexing issue has been annoying me also for the past year, but FWIW, the solution above (thanks @fadookie!) led me to adopt this workaround:

tasks.named('jacocoToCobertura').configure {
    dependsOn jacocoTestReport
    doFirst {
        def jacocoClasspathUrls = jacocoTestReport.jacocoClasspath.collect { it.toURI().toURL() }
        def classloader = new URLClassLoader(jacocoClasspathUrls as URL[])
        def jacocoReportsDir = jacocoTestReport.reports.xml.outputLocation.get().asFile.parent
        new File(jacocoReportsDir, 'report.dtd').withWriter {
            it << classloader.getResourceAsStream('org/jacoco/report/xml/report.dtd')
        }
    }
}

The key difference to the previous solution is that I'm unpacking the report.dtd directly from Gradle's JaCoCo classpath instead of downloading it from the internet, and dropping it into build/reports/jacoco instead of the Gradle daemon's global working directory.

psibre commented 1 year ago

P.S. For some reason, in CI/CD, the local build dir destination for the "missing" report.dtd isn't working after all, but using

// [...]
        def workingDir = new File('').absolutePath
        new File(workingDir, 'report.dtd').withWriter {
// [...]

like in @fadookie's solution works fine.

tjerkw commented 1 year ago

My workaround is just to remove the doctype:


jacocoToCobertura {

    // Remove the DOCTYPE otherwise the conversion fails with
    // Error while running JacocoToCobertura conversion: `Loading Jacoco report error: `(was java.io.FileNotFoundException) .gradle/daemon/8.0.2/report.dtd (No such file or directory)
    // See https://github.com/razvn/jacoco-to-cobertura-gradle-plugin/issues/11

    val reportFile = file("$buildDir/reports/kover/report.xml")
    val inputWithoutDoctypeFile = file("$buildDir/reports/kover/report-without-doctype.xml")
    val docTypeToRemove = "<!DOCTYPE report PUBLIC \"-//JACOCO//DTD Report 1.1//EN\" \"report.dtd\">"
    inputWithoutDoctypeFile.writeText(
        reportFile.readText().replace(docTypeToRemove, "")
    )

    inputFile.set(inputWithoutDoctypeFile)
    outputFile.set(file("$buildDir/reports/kover/cobertura.xml"))
}
tlusk commented 1 year ago

Similar to the above ^

tasks.jacocoToCobertura {
    val jacocoReport = file("$buildDir/reports/jacoco/test/jacocoTestReport.xml")
    onlyIf { jacocoReport.exists() }

    doFirst {
        // This is a hack to remove the DOCTYPE from the jacoco report.
        // Resolves https://github.com/razvn/jacoco-to-cobertura-gradle-plugin/issues/11
        jacocoReport.readText().replace("<!DOCTYPE[^>]*>".toRegex(), "").let {
            jacocoReport.writeText(it)
        }
    }
}
bddckr commented 4 months ago

Thanks, everyone. I now am getting this all of a sudden as well. It must the due to a new dependency I've added recently, but I did neither touch Gradle nor this plugin 🤷

Anyway, if you adapt the above solutions a little bit, you reach this one that is generic and works by just utilizing the plugin's task's input:

// https://github.com/razvn/jacoco-to-cobertura-gradle-plugin/issues/11
tasks.named<JacocoToCoberturaTask>(JacocoToCoberturaPlugin.TASK_NAME) {
    doFirst {
        val inputFile = inputFile.asFile.get()
        inputFile.writeText(
            inputFile.readText().replace("<!DOCTYPE[^>]*>".toRegex(), "")
        )
    }
}