Module: SimpleCov::Combine::LinesCombiner

Defined in:
lib/simplecov/combine/lines_combiner.rb

Overview

Combine two different lines coverage results on same file

Should be called through CoverageAccumulator.

Class Method Summary collapse

Class Method Details

.coerce_add(existing, value) ⇒ Object

The rare arm: existing is nil (line not yet relevant in the target) or malformed external input.



68
69
70
# File 'lib/simplecov/combine/lines_combiner.rb', line 68

def coerce_add(existing, value)
  existing.nil? ? value : existing.to_i + value
end

.merge_into(target, source) ⇒ Array

Folds source into target rather than building a third array. Only for a target the caller owns outright: a caller holding a reference to it (e.g. the parsed coverage key of a resultset hash being passed into a second merge) would see it change. source is never touched.

Two runs of the same source file should agree on which lines are coverage-relevant (nil for comments / whitespace, 0+ for executable). When they don't, treat "relevant on either side" as relevant rather than masking a real 0 as nil, which would silently drop an uncovered line from the denominator and inflate the percentage:

=> nil + nil = nil => nil + int = int (preserves a relevant-but-uncovered 0) => int + int = int (sum)

The loop is written out rather than dispatching a block per element because this is the innermost loop of a merge: a 160-worker run over ~1,800 files folds tens of millions of line counts through it, and a call per count is a measurable share of that.

Returns:

  • (Array)

    the array to keep — target itself once there is one



36
37
38
39
40
41
42
43
# File 'lib/simplecov/combine/lines_combiner.rb', line 36

def merge_into(target, source)
  return target unless source
  return source.dup unless target

  size = source.size
  target.concat(Array.new(size - target.size)) if target.size < size
  sum_into(target, source, size)
end

.sum_into(target, source, size) ⇒ Object

Split out only to keep merge_into short; it is the same loop.

The Integer tests coerce malformed counts (a "3" written by a hand-edited or foreign resultset, a JSON float) with to_i, so external input merges to a wrong answer instead of raising out of the middle of a merge. An is_a? per count is far cheaper than the block dispatch this loop exists to avoid, and the well-formed fast path stays branch-for-branch what it was.



53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/simplecov/combine/lines_combiner.rb', line 53

def sum_into(target, source, size)
  index = 0
  while index < size
    if (value = source[index])
      value = value.to_i unless value.is_a?(Integer)
      existing = target[index]
      target[index] = existing.is_a?(Integer) ? existing + value : coerce_add(existing, value)
    end
    index += 1
  end
  target
end