www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 17613] New: Inconsistent behaviour in code coverage

https://issues.dlang.org/show_bug.cgi?id=17613

          Issue ID: 17613
           Summary: Inconsistent behaviour in code coverage
           Product: D
           Version: D2
          Hardware: x86_64
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: P1
         Component: dmd
          Assignee: nobody puremagic.com
          Reporter: hsteoh quickfur.ath.cx

Reduced code:
------------
import std.stdio;
struct MyFile
{
    this(string filename) { }
    auto byLine() { return 0; }
}
struct Wrapper
{
    this(Args...)(Args args) {}
}
auto func(F = std.stdio.File)(bool[] args)
{
    import std.algorithm.iteration : map;
    foreach (x; args.map!(arg => arg ?  Wrapper(
                                            F("A").byLine) :
                                        Wrapper(
                                            0)))
    { }
}
void main()
{
    import std.algorithm : each;
    func!MyFile([ true, false ]);
    func([ false ]);
}
------------

Compile with dmd -unittest -cov, and run the program.  Here is the obtained
.lst file:

------------
       |import std.stdio;
       |struct MyFile
       |{
      1|    this(string filename) { }
      1|    auto byLine() { return 0; }
       |}
       |struct Wrapper
       |{
      3|    this(Args...)(Args args) {}
       |}
       |auto func(F = std.stdio.File)(bool[] args)
       |{
       |    import std.algorithm.iteration : map;
     17|    foreach (x; args.map!(arg => arg ?  Wrapper(
0000000|                                            F("A").byLine) :
      2|                                        Wrapper(
       |                                            0)))
       |    { }
       |}
       |void main()
       |{
       |    import std.algorithm : each;
      1|    func!MyFile([ true, false ]);
      1|    func([ false ]);
       |}
util.d is 87% covered
------------

Note the line with 0000000.  This is clearly wrong, because the first call to
func!MyFile ought to have traversed both branches of the ?: operator, so
F("A").byLine must have been invoked at least once.

Now comment out the second call to func (func([ false ])), recompile, and run
again. Here is the obtained .lst file:

------------
       |import std.stdio;
       |struct MyFile
       |{
      1|    this(string filename) { }
      1|    auto byLine() { return 0; }
       |}
       |struct Wrapper
       |{
      2|    this(Args...)(Args args) {}
       |}
       |auto func(F = std.stdio.File)(bool[] args)
       |{
       |    import std.algorithm.iteration : map;
     11|    foreach (x; args.map!(arg => arg ?  Wrapper(
       |                                            F("A").byLine) :
      1|                                        Wrapper(
       |                                            0)))
       |    { }
       |}
       |void main()
       |{
       |    import std.algorithm : each;
      1|    func!MyFile([ true, false ]);
       |    //func([ false ]);
       |}
util.d is 100% covered
------------

Note that the call to F("A").byLine is now *no longer part of the coverage
count*.  So here we have a paradoxical situation where decreasing code coverage
(by not instantiating func with the default parameter for F) increases the
reported code coverage to 100%.

Note that defaulting F to std.stdio.File seems to be an essential part of this
bug; I could not get rid of the reference to std.stdio.File without making the
bug also vanish.  (I tried declaring my own version of File in a different
module and importing that, but that made the bug disappear.)

Also, the odd formatting of the Wrapper lines are necessary to expose this bug;
in the original, unreduced code, the lines were wrapped this way because the
original code had longer lines that needed to be wrapped. However, wrapping
lines should not affect the code coverage percentage(!).

--
Jul 06 2017