danielpalme / ReportGenerator

ReportGenerator converts coverage reports generated by coverlet, OpenCover, dotCover, Visual Studio, NCover, Cobertura, JaCoCo, Clover, gcov or lcov into human readable reports in various formats.
https://reportgenerator.io
Apache License 2.0
2.62k stars 283 forks source link

Cobertura: Line coverage is incomplete for Jest-generated coverages #603

Closed RoadTrain closed 1 year ago

RoadTrain commented 1 year ago

Describe the bug When parsing Cobertura files generated with JestJS some line coverage information is not processed by ReportGenerator. Apparently, ReportGenerator only considers line coverage information attached to <class> element while ignoring information attached to <method> elements.

It works for other cobertura producers because they usually duplicate line coverage: so a line is attached to both a class and a method. In case with JestJS it's not duplicated for some unknown reason, so a line is attached to either a class or a method.

To Reproduce Try to generate a report from this example:

<?xml version="1.0" ?>
<!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">
<coverage lines-valid="7" lines-covered="2" line-rate="0.28" branches-valid="0" branches-covered="0" branch-rate="0" timestamp="1681280730343" complexity="0" version="0.1">
  <sources>
    <source>C:\coverage</source>
  </sources>
  <packages>
    <package name="some.package.name" line-rate="0.5" branch-rate="0">
      <classes>
        <class name="code-file.js" filename="src\code-file.js" line-rate="0.5" branch-rate="0">
          <methods>
            <method name="(anonymous_0)" hits="0" signature="()V">
              <lines>
                <line number="4" hits="0"/>
              </lines>
            </method>
            <method name="(anonymous_1)" hits="0" signature="()V">
              <lines>
                <line number="8" hits="0"/>
              </lines>
            </method>
            <method name="(anonymous_2)" hits="0" signature="()V">
              <lines>
                <line number="11" hits="0"/>
              </lines>
            </method>
          </methods>
          <lines>
            <line number="1" hits="3" branch="false"/>
            <line number="3" hits="3" branch="false"/>
            <line number="5" hits="0" branch="false"/>
            <line number="9" hits="0" branch="false"/>
          </lines>
        </class>
      </classes>
    </package>
  </packages>
</coverage>

Expected result: Covered = 2 Uncovered = 5 Coverable = 7 Actual result: image

RoadTrain commented 1 year ago

The code in question seems to be located here: https://github.com/danielpalme/ReportGenerator/blob/3364c9602c315f9aaba9e238d61021ceaed6144e/src/ReportGenerator.Core/Parser/CoberturaParser.cs#L231-L233

A straightforward solution would be to parse lines data for both classes and methods but then make sure to filter out duplicates in the resulting line set.

RoadTrain commented 1 year ago

Here's an chunk of cobertura produced by coverlet:

        <class name="SomeClass" filename="SomeClass.cs" line-rate="1" branch-rate="1" complexity="2">
          <methods>
            <method name="get_RepoPath" signature="()" line-rate="1" branch-rate="1" complexity="1">
              <lines>
                <line number="5" hits="5" branch="False" />
              </lines>
            </method>
            <method name="get_CovPath" signature="()" line-rate="1" branch-rate="1" complexity="1">
              <lines>
                <line number="7" hits="3" branch="False" />
              </lines>
            </method>
          </methods>
          <lines>
            <line number="5" hits="5" branch="False" />
            <line number="7" hits="3" branch="False" />
          </lines>
        </class>

Notice how line data is duplicated, so only looking at class-level data would be enough here. But it's not enough for Jest-produced coverages.

danielpalme commented 1 year ago

Thanks for your detailed description.

I will try to add a workaround in ReportGenerator with the next days.

Why does Jest do it differently as everyone else ...

RoadTrain commented 1 year ago

Yeah, it's annoying. I think it's probably in part due to a lack of a formal cobertura standard.

RoadTrain commented 1 year ago

I took another look at jest cobertura, and apparently there's always one <line> element for a <method> -- it's the line where method name is declared (like function name(...) {). And the lines for the method body are at <class>. This makes it even weirder.

The bad side effect of this is that ReportGenerator cannot correctly determine first and last line of a method.

danielpalme commented 1 year ago

I made the necessary changes in d472d5d.

The only problem that remains is, that "ReportGenerator cannot correctly determine first and last line of a method". But there's nothing I can do about that.

Next release will contain the changes. I guess a new version will be published within the next 1-2 weeks.

danielpalme commented 1 year ago

Release 5.1.20 is now available!

RoadTrain commented 1 year ago

@danielpalme Thanks, will try soon!

Regarding

ReportGenerator cannot correctly determine first and last line of a method

I have found the exact place where this incomplete cobertura is generated, for future reference: https://github.com/istanbuljs/istanbuljs/blob/master/packages/istanbul-reports/lib/cobertura/index.js#L102

I am planning to file an issue at istanbuljs, but apparently that project has been inactive since Nov 2022.