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

Issue #613 closed
Nick Williams created an issue

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. :-)

Comments (5)

  1. Ned Batchelder repo owner

    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?

  2. Ned Batchelder repo owner

    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.

  3. Ned Batchelder repo owner

    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">
    $
    
  4. Justin Nesselrotte

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

    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:

    [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.

  5. Log in to comment