Search code examples
scalainfinite-loopcontrol-flowscoverage

Scoverage: enforce 100% branch coverage on "formally infinite" `while(true)` loops


The following simple code snippet contains a while-loop that looks as if it could be infinite:

  def findDivisor(n: Int): Int = {
    require(n >= 2)
    var i = 2
    while (true) {
      if (n % i == 0) {
        return i
      } else {
        // do-nothing branch
      }
      i += 1
    }
    // $COVERAGE-OFF$
    throw new Error("unreachable")
    // $COVERAGE-ON$
  }

Basic math guarantees that this method always terminates (even if it cannot find a proper divisor, it must stop at n).

Despite the $COVERAGE-OFF$ right after the while-loop, Scoverage (and maybe some other coverage tools) will complain, and compute only 75% branch coverage (because while counts as a branch point, and the false branch is never taken before return).

Moving the // $COVERAGE-OFF$ around, e.g. before the closing } of the while-body does not help either.

How do I force it to ignore the impossible branch?


Solution

  • Just wrap the while(true) { loop head into a separate pair of $COVERAGE-OFF$-$COVERAGE-ON$ comments:

      def findDivisor(n: Int): Int = {
        require(n >= 2)
        var i = 2
        // $COVERAGE-OFF$
        while (true) {
          // $COVERAGE-ON$
          if (n % i == 0) {
            return i
          } else {
            // do-nothing branch
          }
          i += 1
          // $COVERAGE-OFF$
        }
        throw new Error("unreachable")
        // $COVERAGE-ON$
      }
    

    Now Scoverage makes sure that each statement in the body of the while-loop is covered, but it ignores the false-branch, and reports 100% test coverage e.g. after the following simple test:

      "Whatever" should "do something" in {
        MyObjectName.findDivisor(57) should be(3)
      }