Class: RSpec::MultiprocessRunner::Worker

Inherits:
Object
  • Object
show all
Defined in:
lib/rspec/multiprocess_runner/worker.rb

Overview

This object has several roles:

  • It forks the worker process

  • In the coordinator process, it is used to send messages to the worker and track the worker’s status, completed specs, and example results.

  • In the worker process, it is used to send messages to the coordinator and actually run specs.

Constant Summary collapse

COMMAND_QUIT =
"quit"
COMMAND_RUN_FILE =
"run_file"
STATUS_EXAMPLE_COMPLETE =
"example_complete"
STATUS_RUN_COMPLETE =
"run_complete"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(environment_number, options) ⇒ Worker

Returns a new instance of Worker.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/rspec/multiprocess_runner/worker.rb', line 32

def initialize(environment_number, options)
  @environment_number = environment_number
  @worker_socket, @coordinator_socket = Socket.pair(:UNIX, :STREAM)
  @rspec_arguments = (options[:rspec_options] || []) + ["--format", ReportingFormatter.to_s]
  @example_timeout_seconds = options[:example_timeout_seconds]
  @file_timeout_seconds = options[:file_timeout_seconds]
  @example_results = []

  # Use a single configuration and world across all individual runs
  # This will not be necessary to do manually in RSpec 3 — it does not
  # reset the globals after each run.
  @rspec_configuration = RSpec.configuration
  @rspec_world = RSpec.world
end

Instance Attribute Details

#current_fileObject (readonly)

Returns the value of attribute current_file.



23
24
25
# File 'lib/rspec/multiprocess_runner/worker.rb', line 23

def current_file
  @current_file
end

#deactivation_reasonObject

Returns the value of attribute deactivation_reason.



24
25
26
# File 'lib/rspec/multiprocess_runner/worker.rb', line 24

def deactivation_reason
  @deactivation_reason
end

#environment_numberObject (readonly)

Returns the value of attribute environment_number.



23
24
25
# File 'lib/rspec/multiprocess_runner/worker.rb', line 23

def environment_number
  @environment_number
end

#example_resultsObject (readonly)

Returns the value of attribute example_results.



23
24
25
# File 'lib/rspec/multiprocess_runner/worker.rb', line 23

def example_results
  @example_results
end

#pidObject (readonly)

Returns the value of attribute pid.



23
24
25
# File 'lib/rspec/multiprocess_runner/worker.rb', line 23

def pid
  @pid
end

Instance Method Details

#==(other) ⇒ Object

Workers can be found in the coordinator process by their coordinator socket.



49
50
51
52
53
54
55
56
# File 'lib/rspec/multiprocess_runner/worker.rb', line 49

def ==(other)
  case other
  when Socket
    other == @coordinator_socket
  else
    super
  end
end

#kill_nowObject



130
131
132
133
# File 'lib/rspec/multiprocess_runner/worker.rb', line 130

def kill_now
  Process.kill(:KILL, pid)
  Process.detach(pid)
end

#quit_when_idle_and_wait_for_quitObject



99
100
101
102
# File 'lib/rspec/multiprocess_runner/worker.rb', line 99

def quit_when_idle_and_wait_for_quit
  send_message_to_worker(command: COMMAND_QUIT)
  Process.wait(self.pid)
end

#reapObject



135
136
137
# File 'lib/rspec/multiprocess_runner/worker.rb', line 135

def reap
  terminate_then_kill(3, "Reaping troubled process #{environment_number} (#{pid}; #{@current_file})")
end

#receive_and_act_on_message_from_workerObject



139
140
141
# File 'lib/rspec/multiprocess_runner/worker.rb', line 139

def receive_and_act_on_message_from_worker
  act_on_message_from_worker(receive_message_from_worker)
end

#report_example_result(example_status, description, line_number, details) ⇒ Object



203
204
205
206
207
208
209
210
211
212
# File 'lib/rspec/multiprocess_runner/worker.rb', line 203

def report_example_result(example_status, description, line_number, details)
  send_message_to_coordinator(
    status: STATUS_EXAMPLE_COMPLETE,
    example_status: example_status,
    description: description,
    line_number: line_number,
    details: details,
    file_path: @current_file
  )
end

#run_file(filename) ⇒ Object



104
105
106
107
108
# File 'lib/rspec/multiprocess_runner/worker.rb', line 104

def run_file(filename)
  send_message_to_worker(command: COMMAND_RUN_FILE, filename: filename)
  @current_file = filename
  @current_file_started_at = @current_example_started_at = Time.now
end

#shutdown_nowObject



126
127
128
# File 'lib/rspec/multiprocess_runner/worker.rb', line 126

def shutdown_now
  terminate_then_kill(5)
end

#socketObject



85
86
87
88
89
90
91
# File 'lib/rspec/multiprocess_runner/worker.rb', line 85

def socket
  if self.pid == Process.pid
    @worker_socket
  else
    @coordinator_socket
  end
end

#stalled?Boolean

Returns:

  • (Boolean)


114
115
116
117
118
119
120
121
122
123
124
# File 'lib/rspec/multiprocess_runner/worker.rb', line 114

def stalled?
  file_stalled =
    if @file_timeout_seconds
      working? && (Time.now - @current_file_started_at > @file_timeout_seconds)
    end
  example_stalled =
    if @example_timeout_seconds
      working? && (Time.now - @current_example_started_at > @example_timeout_seconds)
    end
  file_stalled || example_stalled
end

#startObject

Forks the worker process. In the parent, returns the PID.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/rspec/multiprocess_runner/worker.rb', line 60

def start
  pid = fork
  if pid
    @worker_socket.close
    @pid = pid
  else
    @coordinator_socket.close
    @pid = Process.pid
    ENV["TEST_ENV_NUMBER"] = environment_number.to_s

    # reset TERM handler so that
    # - the coordinator's version (if any) is not executed twice
    # - it actually terminates the process, instead of doing the ruby
    #   default (throw an exception, which gets caught by RSpec)
    Kernel.trap("TERM", "SYSTEM_DEFAULT")
    # rely on the coordinator to handle INT
    Kernel.trap("INT", "IGNORE")
    # prevent RSpec from trapping INT, also
    ::RSpec::Core::Runner.instance_eval { def self.trap_interrupt; end }

    set_process_name
    run_loop
  end
end

#working?Boolean

Returns:

  • (Boolean)


110
111
112
# File 'lib/rspec/multiprocess_runner/worker.rb', line 110

def working?
  @current_file
end