Class: Evilution::Baseline

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

Defined Under Namespace

Classes: Result

Constant Summary collapse

GRACE_PERIOD =
0.5

Instance Method Summary collapse

Constructor Details

#initialize(spec_resolver: Evilution::SpecResolver.new, timeout: 30, runner: nil, fallback_dir: "spec", test_files: nil) ⇒ Baseline

Returns a new instance of Baseline.



18
19
20
21
22
23
24
25
# File 'lib/evilution/baseline.rb', line 18

def initialize(spec_resolver: Evilution::SpecResolver.new, timeout: 30, runner: nil,
               fallback_dir: "spec", test_files: nil)
  @spec_resolver = spec_resolver
  @timeout = timeout
  @runner = runner
  @fallback_dir = fallback_dir
  @test_files = test_files
end

Instance Method Details

#call(subjects) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/evilution/baseline.rb', line 27

def call(subjects)
  start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  spec_files = baseline_spec_files(subjects)
  failed = Set.new

  spec_files.each do |spec_file|
    failed.add(spec_file) unless run_spec_file(spec_file)
  end

  duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
  Result.new(failed_spec_files: failed, duration: duration)
end

#fork_spec_runner(spec_file, read_io, write_io) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/evilution/baseline.rb', line 56

def fork_spec_runner(spec_file, read_io, write_io)
  runner = @runner
  Process.fork do
    read_io.close
    $stdout.reopen(File::NULL, "w")
    $stderr.reopen(File::NULL, "w")

    passed = runner.call(spec_file)
    Marshal.dump({ passed: passed }, write_io)
    write_io.close
    exit!(passed ? 0 : 1)
  end
end

#read_result(read_io, pid) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/evilution/baseline.rb', line 72

def read_result(read_io, pid)
  if read_io.wait_readable(@timeout)
    data = read_io.read
    Process.wait(pid)
    return false if data.empty?

    result = Marshal.load(data)
    result[:passed]
  else
    terminate_child(pid)
    false
  end
end

#run_spec_file(spec_file) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/evilution/baseline.rb', line 40

def run_spec_file(spec_file)
  raise Evilution::Error, "no baseline runner configured" unless @runner

  read_io, write_io = IO.pipe
  pid = fork_spec_runner(spec_file, read_io, write_io)
  write_io.close
  read_result(read_io, pid)
rescue Evilution::Error
  raise
rescue StandardError
  false
ensure
  read_io&.close
  write_io&.close
end

#terminate_child(pid) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/evilution/baseline.rb', line 86

def terminate_child(pid)
  Evilution::ProcessCleanup.safe_kill("TERM", pid)
  _, status = Process.waitpid2(pid, Process::WNOHANG)
  return if status

  sleep(GRACE_PERIOD)
  _, status = Process.waitpid2(pid, Process::WNOHANG)
  return if status

  Evilution::ProcessCleanup.safe_kill("KILL", pid)
  Evilution::ProcessCleanup.safe_wait(pid)
end