Module: SimpleCov::Combine::FilesCombiner

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

Overview

Handle combining two coverage results for same file

Should be called through SimpleCov.combine.

Constant Summary collapse

NO_SYNTHESIZED =

Branch/method tuples drawn from a simulated (never-loaded) file when the other side of the merge was actually executed.

{"branches" => EMPTY_TABLE, "methods" => EMPTY_TABLE}.freeze

Class Method Summary collapse

Class Method Details

.combine(coverage_a, coverage_b) ⇒ Hash

Combines the results for 2 coverages of a file.

Returns:

  • (Hash)


25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/simplecov/combine/files_combiner.rb', line 25

def combine(coverage_a, coverage_b)
  source_a, source_b = reconcile_synthesized(coverage_a, coverage_b)

  combination = {"lines" => Combine.combine(LinesCombiner, coverage_a["lines"], coverage_b["lines"])}
  if SimpleCov.branch_coverage?
    combined_branches = Combine.combine(BranchesCombiner, source_a["branches"], source_b["branches"])
    combination["branches"] = combined_branches || {}
  end
  if SimpleCov.method_coverage?
    combination["methods"] = Combine.combine(MethodsCombiner, source_a["methods"], source_b["methods"])
  end
  combination
end

.executed?(coverage) ⇒ Boolean

A file some process actually loaded has at least one executed line; a simulated (never-loaded) file's lines are all nil or 0.

Returns:

  • (Boolean)


64
65
66
67
# File 'lib/simplecov/combine/files_combiner.rb', line 64

def executed?(coverage)
  lines = Array(coverage["lines"]) #: Array[Integer?]
  lines.any? { |count| count&.positive? }
end

.reconcile_synthesized(coverage_a, coverage_b) ⇒ Object

When exactly one side of the merge was actually executed, its branch and method tuples are authoritative and the other side's are dropped. A simulated entry (SimulateCoverage backfills tracked-but-unloaded files) synthesizes those tuples statically, so a location that drifts from what Coverage emits would otherwise be unioned in by position and survive as a phantom, permanently-missed branch (see #1233). This contains any such drift to denominator inflation for files no process loaded, rather than a false miss on a covered file. Lines are never dropped: a simulated file's line shape is correct and carries the unloaded-file denominator (#1059).

Returns the two coverages to draw branch/method tuples from, blanking the non-executed side only when the other side was executed. When both sides were executed (two real runs) or neither was (all simulated), both are returned unchanged and combine normally.



54
55
56
57
58
59
60
# File 'lib/simplecov/combine/files_combiner.rb', line 54

def reconcile_synthesized(coverage_a, coverage_b)
  executed_a = executed?(coverage_a)
  executed_b = executed?(coverage_b)
  return [coverage_a, coverage_b] if executed_a == executed_b

  executed_a ? [coverage_a, NO_SYNTHESIZED] : [NO_SYNTHESIZED, coverage_b]
end