Class: Evilution::Coverage::MapBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/evilution/coverage/map_builder.rb

Overview

Builds a per-example coverage Map by running the given spec files under ::Coverage (lines mode) with the Recorder installed as an around(:each) hook. The run happens in a FORKED CHILD so the global ::RSpec.configure hook, ::RSpec.world mutation, and ::Coverage state never leak into the calling process; the parent receives only the serialized map over a pipe.

Constant Summary collapse

LOCATION_LINE_SUFFIX =
/\A(.+?)(:\d+(?::\d+)*)\z/

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(spec_files:, target_files:, project_root: Evilution::PROJECT_ROOT) ⇒ MapBuilder

Returns a new instance of MapBuilder.



27
28
29
30
31
# File 'lib/evilution/coverage/map_builder.rb', line 27

def initialize(spec_files:, target_files:, project_root: Evilution::PROJECT_ROOT)
  @spec_files = spec_files
  @target_files = target_files
  @project_root = project_root.to_s
end

Class Method Details

.absolute_location(raw, root) ⇒ Object

RSpec reports example locations relative to its run dir as “./spec/x.rb:5”. Store them ABSOLUTE (path expanded against the project root, line suffix preserved) so they replay regardless of the per-mutation run’s CWD – Integration::RSpec#resolve_target passes absolute targets through unchanged.



21
22
23
24
25
# File 'lib/evilution/coverage/map_builder.rb', line 21

def self.absolute_location(raw, root)
  match = LOCATION_LINE_SUFFIX.match(raw)
  path, suffix = match ? [match[1], match[2]] : [raw, ""]
  "#{File.expand_path(path, root)}#{suffix}"
end

Instance Method Details

#callObject



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/evilution/coverage/map_builder.rb', line 33

def call
  read_io, write_io = IO.pipe
  read_io.binmode
  write_io.binmode
  pid = fork do
    read_io.close
    Marshal.dump(build_in_child.to_h, write_io)
    write_io.close
    exit!(0)
  end
  write_io.close
  payload = read_io.read
  read_io.close
  Process.wait(pid)
  # Trust boundary: payload is a Marshal dump this process's own forked child
  # produced over a private pipe -- same rationale as Channel::Frame.
  Evilution::Coverage::Map.from_h(Marshal.load(payload))
end