nedbat / coveragepy

The code coverage tool for Python
https://coverage.readthedocs.io
Apache License 2.0
3.01k stars 432 forks source link

Coverage XML report with sources argument turns root package name into period #613

Open nedbat opened 7 years ago

nedbat commented 7 years ago

Originally reported by Nick Williams (Bitbucket: beamerblvd, GitHub: beamerblvd)


I filed this bug against pytest-cov, but I believe there is also a bug in Coverage that was revealed in my debugging of this code.

I won't copy the lengthy details and IPDB debugging output into here that can be read there. I'll just post the Coverage-specific problem here.

Essentially, calling coverage xml from the command line doesn't allow you to pass in sources, so this problem doesn't reveal itself through normal usage. However, when you call Coverage.xml_report from code (such as when integrating with pytest-cov, etc.), you are allowed to supply sources, and when doing so, the top-level package name in the XML report becomes a period (.) instead of the actual package name. I'm not sure what the correct behavior is if sources is passed in (or if that should be an error / not even an option in the XML reporter), but surely turning the top level package name into a period is not the correct behavior. :-)


nedbat commented 6 years ago

Original comment by Justin Nesselrotte (Bitbucket: jnesselr, GitHub: jnesselr)


If you do something like this, you'll be able to reproduce the error (getting . in the package name instead of bug613)

#!python

import coverage

cov = coverage.Coverage(source=["."])
cov.load()
cov.xml_report()

What I did to alleviate this issue and give us the behaviour we desired was set the coverage source to "." and use the [report] section of .coveragerc to limit what I wanted to be covered. By setting the coverage source to ".", it would include the test directory and the setup.py file. I then set the coveragerc up like this:

#!ini

[report]
include=*/my_pkg/*

That way, it would only actually report coverage metrics for my my_pkg package and would have them under the package name of "my_pkg" not under the package name of ".". Really, we should have a "src" directory, and then my_pkg under that, but we don't so... this is the workaround we found.

nedbat commented 6 years ago

I tried an arrangement more similar to the pytest-cov bug report, and still don't see the problem:

$ tree bug613/
bug613/
├── __init__.py
└── doit.py

0 directories, 2 files
$ cat bug613/__init__.py
$ cat bug613/doit.py
print("Hello")
$
$ coverage run --source=bug613 bug613/doit.py
Hello
$ coverage xml
$ cat coverage.xml
<?xml version="1.0" ?>
<coverage branch-rate="0" branches-covered="0" branches-valid="0" complexity="0" line-rate="1" lines-covered="1" lines-valid="1" timestamp="1516969674907" version="4.5a0">
    <!-- Generated by coverage.py: https://coverage.readthedocs.io/en/coverage-4.5a0 -->
    <!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
    <sources>
        <source>/private/tmp</source>
    </sources>
    <packages>
        <package branch-rate="0" complexity="0" line-rate="1" name="bug613">
            <classes>
                <class branch-rate="0" complexity="0" filename="bug613/__init__.py" line-rate="1" name="__init__.py">
                    <methods/>
                    <lines/>
                </class>
                <class branch-rate="0" complexity="0" filename="bug613/doit.py" line-rate="1" name="doit.py">
                    <methods/>
                    <lines>
                        <line hits="1" number="1"/>
                    </lines>
                </class>
            </classes>
        </package>
    </packages>
</coverage>
$
$
$ coverage run --source=bug613 bug613/doit.py
Hello
$ cat bug613.py
import coverage

cov = coverage.Coverage(source=["."])
cov.load()
cov.xml_report(outfile="bug613.xml")
$ python bug613.py
$ more bug613.xml
<?xml version="1.0" ?>
<coverage branch-rate="0" branches-covered="0" branches-valid="0" complexity="0" line-rate="1" lines-covered="1" lines-valid="1" timestamp="1516969775453" version="4.5a0">
        <!-- Generated by coverage.py: https://coverage.readthedocs.io/en/coverage-4.5a0 -->
        <!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
        <sources>
                <source>/private/tmp</source>
        </sources>
        <packages>
                <package branch-rate="0" complexity="0" line-rate="1" name="bug613">
                        <classes>
                                <class branch-rate="0" complexity="0" filename="bug613/__init__.py" line-rate="1" name="__init__.py">
                                        <methods/>
                                        <lines/>
                                </class>
                                <class branch-rate="0" complexity="0" filename="bug613/doit.py" line-rate="1" name="doit.py">
                                        <methods/>
                                        <lines>
                                                <line hits="1" number="1"/>
                                        </lines>
                                </class>
                        </classes>
                </package>
        </packages>
</coverage>
$ diff coverage.xml bug613.xml
2c2
< <coverage branch-rate="0" branches-covered="0" branches-valid="0" complexity="0" line-rate="1" lines-covered="1" lines-valid="1" timestamp="1516969703433" version="4.5a0">
---
> <coverage branch-rate="0" branches-covered="0" branches-valid="0" complexity="0" line-rate="1" lines-covered="1" lines-valid="1" timestamp="1516969775453" version="4.5a0">
$
nedbat commented 6 years ago

I tried using this:

import coverage

cov = coverage.Coverage(source=["."])
cov.load()
cov.xml_report()

But it produces the same XML report as the command line "coverage xml". In both cases, the coverage.xml file has a line like this:

<package branch-rate="0" complexity="0" line-rate="1" name=".">

I don't understand what problem you are seeing.

nedbat commented 6 years ago

I'm trying to understand the reproduction scenario. xml_report has no sources argument, are you talking about Coverage(source=...)? Do you have a short sample that demonstrates the problem?