List comprehension branch coverage relevence

Issue #574 closed
Steve Peak created an issue

Issue

Coverage for list comprehension is returned as a branch with seemingly irrelevant information.

Proof

Below is source/test/result to show the issue.

def never():
    return [_ for _ in []]

def full():
    a = [1]
    return [_ for _ in a]

def empty():
    a = []
    return [_ for _ in a]

empty()
full()
Python 3.6.1
Coverage.py, version 4.3.4 with C extension

$ coverage run --branch test.py && coverage xml && cat coverage.xml
<?xml version="1.0" ?>
<coverage branch-rate="0.6667" line-rate="0.9" timestamp="1493301584473" version="4.3.4">
    <!-- Generated by coverage.py: https://coverage.readthedocs.io -->
    <!-- Based on https://raw.githubusercontent.com/cobertura/web/f0366e5e2cf18f111cbd61fc34ef720a6584ba02/htdocs/xml/coverage-03.dtd -->
    <sources>
        <source>/home/ryan/Code/aspiredu/codecov-test</source>
    </sources>
    <packages>
        <package branch-rate="0.6667" complexity="0" line-rate="0.9" name=".">
            <classes>
                <class branch-rate="0.6667" complexity="0" filename="test.py" line-rate="0.9" name="test.py">
                    <methods/>
                    <lines>
                        <line hits="1" number="1"/>
                        <line branch="true" condition-coverage="0% (0/2)" hits="0" missing-branches="exit,exit" number="2"/>
                        <line hits="1" number="4"/>
                        <line hits="1" number="5"/>
                        <line branch="true" condition-coverage="100% (2/2)" hits="1" number="6"/>
                        <line hits="1" number="8"/>
                        <line hits="1" number="9"/>
                        <line branch="true" condition-coverage="100% (2/2)" hits="1" number="10"/>
                        <line hits="1" number="12"/>
                        <line hits="1" number="13"/>
                    </lines>
                </class>
            </classes>
        </package>
    </packages>
</coverage>

Suggested fix options

(A) Remove branch coverage for lists

- <line branch="true" condition-coverage="100% (2/2)" hits="1" number="10"/>
+ <line branch="false" hits="1" number="10"/>

(B) Provide partial branch coverage when iterable was empty

- <line branch="true" condition-coverage="100% (2/2)" hits="1" number="10"/>
+ <line branch="true" condition-coverage="50% (1/2)" hits="1" number="10"/>

Credit @Ryan Hiebert with discovery

Comments (5)

  1. Ned Batchelder repo owner

    I thought the issue was the "missing-branches" attribute on the "never" case. But neither of your suggested fixes is about that.

  2. Steve Peak reporter

    Oh, yes you are right. The condition-coverage and missing-branches need attention.

    missing-branches="exit,exit" should be (for example) missing-branches="enter,exit"

    Thanks

  3. Jim G

    I think I have the same bug to

        def liste(cls) -> Callable:
            def liste(patient_id: int) -> List:
                return [
                    acte.dico 
                    for acte in select(a for a in cls.model if a.patient.id == patient_id)
                ]
            return liste
    

    is not covered but:

        def liste(cls) -> Callable:
            def liste(patient_id: int) -> List:
                return [
                    acte.dico  for acte in select(a for a in cls.model if a.patient.id == patient_id)
                ]
            return liste
    

    is covered

  4. Log in to comment