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.



33
34
35
36
37
38
39
# File 'lib/rspec/multiprocess_runner/worker.rb', line 33

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]
  self.options = options
  @example_results = []
end

Instance Attribute Details

#current_fileObject (readonly)

Returns the value of attribute current_file.



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

def current_file
  @current_file
end

#deactivation_reasonObject

Returns the value of attribute deactivation_reason.



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

def deactivation_reason
  @deactivation_reason
end

#environment_numberObject (readonly)

Returns the value of attribute environment_number.



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

def environment_number
  @environment_number
end

#example_resultsObject (readonly)

Returns the value of attribute example_results.



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

def example_results
  @example_results
end

#optionsObject

Returns the value of attribute options.



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

def options
  @options
end

#pidObject (readonly)

Returns the value of attribute pid.



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

def pid
  @pid
end

Instance Method Details

#==(other) ⇒ Object

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



43
44
45
46
47
48
49
50
# File 'lib/rspec/multiprocess_runner/worker.rb', line 43

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

#kill_nowObject



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

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

#quit_when_idle_and_wait_for_quitObject



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

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

#reapObject



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

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

#receive_and_act_on_message_from_workerObject



144
145
146
# File 'lib/rspec/multiprocess_runner/worker.rb', line 144

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



215
216
217
218
219
220
221
222
223
224
# File 'lib/rspec/multiprocess_runner/worker.rb', line 215

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,
    filename: @current_file
  )
end

#run_file(filename) ⇒ Object



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

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



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

def shutdown_now
  terminate_then_kill(5)
end

#socketObject



90
91
92
93
94
95
96
# File 'lib/rspec/multiprocess_runner/worker.rb', line 90

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

#stalled?Boolean

Returns:

  • (Boolean)


119
120
121
122
123
124
125
126
127
128
129
# File 'lib/rspec/multiprocess_runner/worker.rb', line 119

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

#startObject

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



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

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

    # 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 }

    # Disable RSpec's at_exit hook that would try to run whatever is in ARGV
    ::RSpec::Core::Runner.disable_autorun!

    set_process_name
    run_loop
  end
end

#test_env_numberObject



52
53
54
55
56
57
58
# File 'lib/rspec/multiprocess_runner/worker.rb', line 52

def test_env_number
  if environment_number == 1 && !options.first_is_1
    ""
  else
    environment_number.to_s
  end
end

#to_json(options = nil) ⇒ Object



148
149
150
# File 'lib/rspec/multiprocess_runner/worker.rb', line 148

def to_json(options = nil)
  { "pid" => @pid, "environment_number" => @environment_number, "current_file" => @current_file, "deactivation_reason" => @deactivation_reason }.to_json
end

#working?Boolean

Returns:

  • (Boolean)


115
116
117
# File 'lib/rspec/multiprocess_runner/worker.rb', line 115

def working?
  @current_file
end