run_loop 1.2.6 → 1.2.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/run_loop.rb +7 -0
- data/lib/run_loop/app.rb +67 -0
- data/lib/run_loop/core.rb +17 -128
- data/lib/run_loop/device.rb +15 -3
- data/lib/run_loop/dylib_injector.rb +125 -0
- data/lib/run_loop/environment.rb +15 -0
- data/lib/run_loop/host_cache.rb +2 -1
- data/lib/run_loop/instruments.rb +59 -65
- data/lib/run_loop/lldb.rb +55 -0
- data/lib/run_loop/plist_buddy.rb +18 -2
- data/lib/run_loop/process_terminator.rb +140 -0
- data/lib/run_loop/process_waiter.rb +85 -0
- data/lib/run_loop/sim_control.rb +4 -4
- data/lib/run_loop/simctl/bridge.rb +242 -0
- data/lib/run_loop/version.rb +1 -1
- data/lib/run_loop/xctools.rb +16 -0
- data/scripts/calabash_script_uia.js +3667 -3590
- metadata +48 -24
- data/bin/run-loop +0 -9
- data/lib/run_loop/cli.rb +0 -21
@@ -0,0 +1,15 @@
|
|
1
|
+
module RunLoop
|
2
|
+
class Environment
|
3
|
+
|
4
|
+
# Returns the user's Unix uid.
|
5
|
+
# @return [Integer] The user's Unix uid as an integer.
|
6
|
+
def self.uid
|
7
|
+
`id -u`.strip.to_i
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns true if debugging is enabled.
|
11
|
+
def self.debug?
|
12
|
+
ENV['DEBUG'] == '1'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/run_loop/host_cache.rb
CHANGED
@@ -23,7 +23,8 @@ module RunLoop
|
|
23
23
|
# The directory where the cache is stored.
|
24
24
|
# @return [String] Expanded path to the default cache directory.
|
25
25
|
def self.default_directory
|
26
|
-
|
26
|
+
uid = RunLoop::Environment.uid
|
27
|
+
File.expand_path("/tmp/com.xamarin.calabash.run-loop/host-cache/#{uid}")
|
27
28
|
end
|
28
29
|
|
29
30
|
# The default cache.
|
data/lib/run_loop/instruments.rb
CHANGED
@@ -3,8 +3,6 @@ module RunLoop
|
|
3
3
|
# A class for interacting with the instruments command-line tool
|
4
4
|
#
|
5
5
|
# @note All instruments commands are run in the context of `xcrun`.
|
6
|
-
#
|
7
|
-
# @todo Detect Instruments.app is running and pop an alert.
|
8
6
|
class Instruments
|
9
7
|
|
10
8
|
# Returns an Array of instruments process ids.
|
@@ -38,31 +36,11 @@ module RunLoop
|
|
38
36
|
# what version of Xcode is active.
|
39
37
|
def kill_instruments(xcode_tools = RunLoop::XCTools.new)
|
40
38
|
kill_signal = kill_signal xcode_tools
|
41
|
-
# It is difficult to test using a block.
|
42
39
|
instruments_pids.each do |pid|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
Process.kill(kill_signal, pid.to_i)
|
48
|
-
Process.wait(pid, Process::WNOHANG)
|
49
|
-
rescue Exception => e
|
50
|
-
if ENV['DEBUG'] == '1' or ENV['DEBUG_UNIX'] == '1'
|
51
|
-
puts "Could not kill and wait for process '#{pid.to_i}' - ignoring exception '#{e}'"
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
# Process.wait or `wait` here is pointless. The pid may or may not be
|
56
|
-
# a child of this Process.
|
57
|
-
begin
|
58
|
-
if ENV['DEBUG'] == '1' or ENV['DEBUG_UNIX_CALLS'] == '1'
|
59
|
-
puts "Waiting for instruments '#{pid}' to terminate"
|
60
|
-
end
|
61
|
-
wait_for_process_to_terminate(pid, {:timeout => 2.0})
|
62
|
-
rescue Exception => e
|
63
|
-
if ENV['DEBUG'] == '1' or ENV['DEBUG_UNIX_CALLS'] == '1'
|
64
|
-
puts "Ignoring #{e.message}"
|
65
|
-
end
|
40
|
+
terminator = RunLoop::ProcessTerminator.new(pid, kill_signal, 'instruments')
|
41
|
+
unless terminator.kill_process
|
42
|
+
terminator = RunLoop::ProcessTerminator.new(pid, 'KILL', 'instruments')
|
43
|
+
terminator.kill_process
|
66
44
|
end
|
67
45
|
end
|
68
46
|
end
|
@@ -80,8 +58,59 @@ module RunLoop
|
|
80
58
|
end
|
81
59
|
end
|
82
60
|
|
61
|
+
# Spawn a new instruments process in the context of `xcrun` and detach.
|
62
|
+
#
|
63
|
+
# @param [String] automation_template The template instruments will use when
|
64
|
+
# launching the application.
|
65
|
+
# @param [Hash] options The launch options.
|
66
|
+
# @param [String] log_file The file to log to.
|
67
|
+
# @return [Integer] Returns the process id of the instruments process.
|
68
|
+
# @todo Do I need to enumerate the launch options in the docs?
|
69
|
+
# @todo Should this raise errors?
|
70
|
+
# @todo Is this jruby compatible?
|
71
|
+
def spawn(automation_template, options, log_file)
|
72
|
+
splat_args = spawn_arguments(automation_template, options)
|
73
|
+
if ENV['DEBUG'] == '1'
|
74
|
+
puts "#{Time.now} xcrun #{splat_args.join(' ')} >& #{log_file}"
|
75
|
+
$stdout.flush
|
76
|
+
end
|
77
|
+
pid = Process.spawn('xcrun', *splat_args, {:out => log_file, :err => log_file})
|
78
|
+
Process.detach(pid)
|
79
|
+
pid.to_i
|
80
|
+
end
|
81
|
+
|
83
82
|
private
|
84
83
|
|
84
|
+
# @!visibility private
|
85
|
+
# Parses the run-loop options hash into an array of arguments that can be
|
86
|
+
# passed to `Process.spawn` to launch instruments.
|
87
|
+
def spawn_arguments(automation_template, options)
|
88
|
+
array = ['instruments']
|
89
|
+
array << '-w'
|
90
|
+
array << options[:udid]
|
91
|
+
|
92
|
+
trace = options[:results_dir_trace]
|
93
|
+
if trace
|
94
|
+
array << '-D'
|
95
|
+
array << trace
|
96
|
+
end
|
97
|
+
|
98
|
+
array << '-t'
|
99
|
+
array << automation_template
|
100
|
+
|
101
|
+
array << options[:bundle_dir_or_bundle_id]
|
102
|
+
|
103
|
+
{
|
104
|
+
'UIARESULTSPATH' => options[:results_dir],
|
105
|
+
'UIASCRIPT' => options[:script]
|
106
|
+
}.each do |key, value|
|
107
|
+
array << '-e'
|
108
|
+
array << key
|
109
|
+
array << value
|
110
|
+
end
|
111
|
+
array + options.fetch(:args, [])
|
112
|
+
end
|
113
|
+
|
85
114
|
# @!visibility private
|
86
115
|
#
|
87
116
|
# ```
|
@@ -96,7 +125,7 @@ module RunLoop
|
|
96
125
|
# $ ps x -o pid,command | grep -v grep | grep instruments
|
97
126
|
# 98082 /Xcode/6.0.1/Xcode.app/Contents/Developer/usr/bin/instruments -w < args >
|
98
127
|
# ```
|
99
|
-
|
128
|
+
INSTRUMENTS_FIND_PIDS_CMD = 'ps x -o pid,command | grep -v grep | grep instruments'
|
100
129
|
|
101
130
|
# @!visibility private
|
102
131
|
#
|
@@ -106,7 +135,7 @@ module RunLoop
|
|
106
135
|
# processes.
|
107
136
|
# @return [String] A ps-style list of process details. The details returned
|
108
137
|
# are controlled by the `ps_cmd`.
|
109
|
-
def ps_for_instruments(ps_cmd=
|
138
|
+
def ps_for_instruments(ps_cmd=INSTRUMENTS_FIND_PIDS_CMD)
|
110
139
|
`#{ps_cmd}`.strip
|
111
140
|
end
|
112
141
|
|
@@ -117,8 +146,7 @@ module RunLoop
|
|
117
146
|
# @return [Boolean] True if the details describe an instruments process.
|
118
147
|
def is_instruments_process?(ps_details)
|
119
148
|
return false if ps_details.nil?
|
120
|
-
|
121
|
-
ps_details[/sh -c xcrun instruments/, 0]) != nil
|
149
|
+
ps_details[/\/usr\/bin\/instruments/, 0] != nil
|
122
150
|
end
|
123
151
|
|
124
152
|
# @!visibility private
|
@@ -129,7 +157,7 @@ module RunLoop
|
|
129
157
|
# processes.
|
130
158
|
# @return [Array<Integer>] An array of integer pids for instruments
|
131
159
|
# processes. Returns an empty list if no instruments process are found.
|
132
|
-
def pids_from_ps_output(ps_cmd=
|
160
|
+
def pids_from_ps_output(ps_cmd=INSTRUMENTS_FIND_PIDS_CMD)
|
133
161
|
ps_output = ps_for_instruments(ps_cmd)
|
134
162
|
lines = ps_output.lines("\n").map { |line| line.strip }
|
135
163
|
lines.map do |line|
|
@@ -166,39 +194,5 @@ module RunLoop
|
|
166
194
|
def kill_signal(xcode_tools = RunLoop::XCTools.new)
|
167
195
|
xcode_tools.xcode_version_gte_6? ? 'QUIT' : 'TERM'
|
168
196
|
end
|
169
|
-
|
170
|
-
# @!visibility private
|
171
|
-
# Wait for Unix process with id `pid` to terminate.
|
172
|
-
#
|
173
|
-
# @param [Integer] pid The id of the process we are waiting on.
|
174
|
-
# @param [Hash] options Values to control the behavior of this method.
|
175
|
-
# @option options [Float] :timeout (2.0) How long to wait for the process to
|
176
|
-
# terminate.
|
177
|
-
# @option options [Float] :interval (0.1) The polling interval.
|
178
|
-
# @option options [Boolean] :raise_on_no_terminate (false) Should an error
|
179
|
-
# be raised if process does not terminate.
|
180
|
-
# @raise [RuntimeError] If process does not terminate and
|
181
|
-
# options[:raise_on_no_terminate] is truthy.
|
182
|
-
def wait_for_process_to_terminate(pid, options={})
|
183
|
-
default_opts = {:timeout => 2.0,
|
184
|
-
:interval => 0.1,
|
185
|
-
:raise_on_no_terminate => false}
|
186
|
-
merged_opts = default_opts.merge(options)
|
187
|
-
|
188
|
-
cmd = "ps #{pid} -o pid | grep #{pid}"
|
189
|
-
poll_until = Time.now + merged_opts[:timeout]
|
190
|
-
delay = merged_opts[:interval]
|
191
|
-
has_terminated = false
|
192
|
-
while Time.now < poll_until
|
193
|
-
has_terminated = `#{cmd}`.strip == ''
|
194
|
-
break if has_terminated
|
195
|
-
sleep delay
|
196
|
-
end
|
197
|
-
|
198
|
-
if merged_opts[:raise_on_no_terminate] and not has_terminated
|
199
|
-
details = `ps -p #{pid} -o pid,comm | grep #{pid}`.strip
|
200
|
-
raise RuntimeError, "Waited #{merged_opts[:timeout]} s for process '#{details}' to terminate"
|
201
|
-
end
|
202
|
-
end
|
203
197
|
end
|
204
198
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module RunLoop
|
2
|
+
|
3
|
+
# A class for interacting with the lldb command-line tool
|
4
|
+
class LLDB
|
5
|
+
|
6
|
+
# Returns a list of lldb pids.
|
7
|
+
# @return [Array<Integer>] An array of integer pids.
|
8
|
+
def self.lldb_pids
|
9
|
+
ps_output = `#{LLDB_FIND_PIDS_CMD}`.strip
|
10
|
+
lines = ps_output.lines("\n").map { |line| line.strip }
|
11
|
+
lldb_processes = lines.select { |line| self.is_lldb_process?(line) }
|
12
|
+
lldb_processes.map do |ps_description|
|
13
|
+
tokens = ps_description.strip.split(' ').map { |token| token.strip }
|
14
|
+
pid = tokens.fetch(0, nil)
|
15
|
+
if pid.nil?
|
16
|
+
nil
|
17
|
+
else
|
18
|
+
pid.to_i
|
19
|
+
end
|
20
|
+
end.compact.sort
|
21
|
+
end
|
22
|
+
|
23
|
+
# @!visibility private
|
24
|
+
# Is the process described an lldb process?
|
25
|
+
#
|
26
|
+
# @param [String] ps_details Details about a process as returned by `ps`
|
27
|
+
# @return [Boolean] True if the details describe an lldb process.
|
28
|
+
def self.is_lldb_process?(ps_details)
|
29
|
+
return false if ps_details.nil?
|
30
|
+
ps_details[/Contents\/Developer\/usr\/bin\/lldb/, 0] != nil
|
31
|
+
end
|
32
|
+
|
33
|
+
# Attempts to gracefully kill all running lldb processes.
|
34
|
+
def self.kill_lldb_processes
|
35
|
+
self.lldb_pids.each do |pid|
|
36
|
+
unless self.kill_with_signal(pid, 'TERM')
|
37
|
+
unless self.kill_with_signal(pid, 'QUIT')
|
38
|
+
self.kill_with_signal(pid, 'KILL')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# @!visibility private
|
47
|
+
LLDB_FIND_PIDS_CMD = 'ps x -o pid,command | grep -v grep | grep lldb'
|
48
|
+
|
49
|
+
# @!visibility private
|
50
|
+
def self.kill_with_signal(pid, signal)
|
51
|
+
RunLoop::ProcessTerminator.new(pid, signal, 'lldb').kill_process
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
data/lib/run_loop/plist_buddy.rb
CHANGED
@@ -68,6 +68,23 @@ module RunLoop
|
|
68
68
|
res == ''
|
69
69
|
end
|
70
70
|
|
71
|
+
# Creates an new empty plist at `path`.
|
72
|
+
#
|
73
|
+
# Is not responsible for creating directories or ensuring write permissions.
|
74
|
+
#
|
75
|
+
# @param [String] path Where to create the new plist.
|
76
|
+
def create_plist(path)
|
77
|
+
File.open(path, 'w') do |file|
|
78
|
+
file.puts "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
|
79
|
+
file.puts "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
|
80
|
+
file.puts "<plist version=\"1.0\">"
|
81
|
+
file.puts '<dict>'
|
82
|
+
file.puts '</dict>'
|
83
|
+
file.puts '</plist>'
|
84
|
+
end
|
85
|
+
path
|
86
|
+
end
|
87
|
+
|
71
88
|
private
|
72
89
|
|
73
90
|
# returns the path to the PlistBuddy executable
|
@@ -168,6 +185,5 @@ module RunLoop
|
|
168
185
|
|
169
186
|
"#{plist_buddy} -c #{cmd_part} \"#{file}\""
|
170
187
|
end
|
171
|
-
|
172
188
|
end
|
173
|
-
end
|
189
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module RunLoop
|
2
|
+
|
3
|
+
# A class for terminating processes and waiting for them to die.
|
4
|
+
class ProcessTerminator
|
5
|
+
|
6
|
+
# @!attribute [r] pid
|
7
|
+
# The process id of the process.
|
8
|
+
# @return [Integer] The pid.
|
9
|
+
attr_reader :pid
|
10
|
+
|
11
|
+
# @!attribute [r] kill_signal
|
12
|
+
# The kill signal to send to the process. Can be a Unix signal name or an
|
13
|
+
# Integer.
|
14
|
+
# @return [Integer, String] The kill signal.
|
15
|
+
attr_reader :kill_signal
|
16
|
+
|
17
|
+
# @!attribute [r] display_name
|
18
|
+
# The process name to use log messages and exceptions. Not used to find
|
19
|
+
# or otherwise interact with the process.
|
20
|
+
# @return [String] The display name.
|
21
|
+
attr_reader :display_name
|
22
|
+
|
23
|
+
# @!attribute [r] options
|
24
|
+
# Options to control the behavior of `kill_process`.
|
25
|
+
# @return [Hash] A hash of options.
|
26
|
+
attr_reader :options
|
27
|
+
|
28
|
+
# Create a new process terminator.
|
29
|
+
#
|
30
|
+
# @param[String,Integer] pid The process pid.
|
31
|
+
# @param[String, Integer] kill_signal The kill signal to send to the process.
|
32
|
+
# @param[String] display_name The name of the process to kill. Used only
|
33
|
+
# in log messages and exceptions.
|
34
|
+
# @option options [Float] :timeout (2.0) How long to wait for the process to
|
35
|
+
# terminate.
|
36
|
+
# @option options [Float] :interval (0.1) The polling interval.
|
37
|
+
# @option options [Boolean] :raise_on_no_terminate (false) Should an error
|
38
|
+
# be raised if process does not terminate.
|
39
|
+
def initialize(pid, kill_signal, display_name, options={})
|
40
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
41
|
+
@pid = pid.to_i
|
42
|
+
@kill_signal = kill_signal
|
43
|
+
@display_name = display_name
|
44
|
+
end
|
45
|
+
|
46
|
+
# Try to kill the process identified by `pid`.
|
47
|
+
#
|
48
|
+
# After sending `kill_signal` to `pid`, wait for the process to terminate.
|
49
|
+
#
|
50
|
+
# @return [Boolean] Returns true if the process was terminated or is no
|
51
|
+
# longer alive.
|
52
|
+
# @raise [SignalException] Raised on an unhandled `Process.kill` exception.
|
53
|
+
# Errno:ESRCH and Errno:EPERM are _handled_ exceptions; all others will
|
54
|
+
# be raised.
|
55
|
+
def kill_process
|
56
|
+
return true unless process_alive?
|
57
|
+
|
58
|
+
debug_logging = RunLoop::Environment.debug?
|
59
|
+
begin
|
60
|
+
if debug_logging
|
61
|
+
puts "Sending '#{kill_signal}' to #{display_name} process '#{pid}'"
|
62
|
+
end
|
63
|
+
Process.kill(kill_signal, pid.to_i)
|
64
|
+
# Don't wait.
|
65
|
+
# We might not own this process and a WNOHANG would be a nop.
|
66
|
+
# Process.wait(pid, Process::WNOHANG)
|
67
|
+
rescue Errno::ESRCH
|
68
|
+
if debug_logging
|
69
|
+
puts "Process with pid '#{pid}' does not exist; nothing to do."
|
70
|
+
end
|
71
|
+
# Return early; there is no need to wait if the process does not exist.
|
72
|
+
return true
|
73
|
+
rescue Errno::EPERM
|
74
|
+
if debug_logging
|
75
|
+
puts "Cannot kill process '#{pid}' with '#{kill_signal}'; not a child of this process"
|
76
|
+
end
|
77
|
+
rescue SignalException => e
|
78
|
+
raise e.message
|
79
|
+
end
|
80
|
+
|
81
|
+
if debug_logging
|
82
|
+
puts "Waiting for #{display_name} '#{pid}' to terminate"
|
83
|
+
end
|
84
|
+
wait_for_process_to_terminate
|
85
|
+
end
|
86
|
+
|
87
|
+
# Is the process `pid` alive?
|
88
|
+
# @return [Boolean] Returns true if the process is still alive.
|
89
|
+
def process_alive?
|
90
|
+
begin
|
91
|
+
Process.kill(0, pid.to_i)
|
92
|
+
true
|
93
|
+
rescue Errno::ESRCH
|
94
|
+
false
|
95
|
+
rescue Errno::EPERM
|
96
|
+
true
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
# @!visibility private
|
103
|
+
# The default options for waiting on a process to terminate.
|
104
|
+
DEFAULT_OPTIONS =
|
105
|
+
{
|
106
|
+
:timeout => 2.0,
|
107
|
+
:interval => 0.1,
|
108
|
+
:raise_on_no_terminate => false
|
109
|
+
}
|
110
|
+
|
111
|
+
# @!visibility private
|
112
|
+
# The details of the process reported by `ps`.
|
113
|
+
def ps_details
|
114
|
+
`xcrun ps -p #{pid} -o pid,comm | grep #{pid}`.strip
|
115
|
+
end
|
116
|
+
|
117
|
+
# @!visibility private
|
118
|
+
# Wait for the process to terminate by polling.
|
119
|
+
def wait_for_process_to_terminate
|
120
|
+
now = Time.now
|
121
|
+
poll_until = now + options[:timeout]
|
122
|
+
delay = options[:interval]
|
123
|
+
has_terminated = false
|
124
|
+
while Time.now < poll_until
|
125
|
+
has_terminated = !process_alive?
|
126
|
+
break if has_terminated
|
127
|
+
sleep delay
|
128
|
+
end
|
129
|
+
|
130
|
+
if RunLoop::Environment.debug?
|
131
|
+
puts "Waited for #{Time.now - now} seconds for #{display_name} with '#{pid}' to terminate"
|
132
|
+
end
|
133
|
+
|
134
|
+
if @options[:raise_on_no_terminate] and !has_terminated
|
135
|
+
raise "Waited #{options[:timeout]} seconds for #{display_name} (#{ps_details}) to terminate"
|
136
|
+
end
|
137
|
+
has_terminated
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module RunLoop
|
2
|
+
|
3
|
+
# A class for waiting on processes.
|
4
|
+
class ProcessWaiter
|
5
|
+
|
6
|
+
attr_reader :process_name
|
7
|
+
|
8
|
+
def initialize(process_name, options={})
|
9
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
10
|
+
@process_name = process_name
|
11
|
+
end
|
12
|
+
|
13
|
+
# Collect a list of Integer pids.
|
14
|
+
# @return [Array<Integer>] An array of integer pids for the `process_name`
|
15
|
+
def pids
|
16
|
+
process_info = `ps x -o pid,comm | grep -v grep | grep #{process_name}`
|
17
|
+
process_array = process_info.split("\n")
|
18
|
+
process_array.map { |process| process.split(' ').first.strip.to_i }
|
19
|
+
end
|
20
|
+
|
21
|
+
# Is the `process_name` a running?
|
22
|
+
def running_process?
|
23
|
+
!pids.empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
# Wait for `process_name` to start.
|
27
|
+
def wait_for_any
|
28
|
+
return true if running_process?
|
29
|
+
|
30
|
+
now = Time.now
|
31
|
+
poll_until = now + @options[:timeout]
|
32
|
+
delay = @options[:interval]
|
33
|
+
is_alive = false
|
34
|
+
while Time.now < poll_until
|
35
|
+
is_alive = running_process?
|
36
|
+
break if is_alive
|
37
|
+
sleep delay
|
38
|
+
end
|
39
|
+
|
40
|
+
if RunLoop::Environment.debug?
|
41
|
+
puts "Waited for #{Time.now - now} seconds for '#{process_name}' to start."
|
42
|
+
end
|
43
|
+
|
44
|
+
if @options[:raise_on_timeout] and !is_alive
|
45
|
+
raise "Waited #{@options[:timeout]} seconds for '#{process_name}' to start."
|
46
|
+
end
|
47
|
+
is_alive
|
48
|
+
end
|
49
|
+
|
50
|
+
# Wait for all `process_name` to finish.
|
51
|
+
def wait_for_none
|
52
|
+
return true if !running_process?
|
53
|
+
|
54
|
+
now = Time.now
|
55
|
+
poll_until = now + @options[:timeout]
|
56
|
+
delay = @options[:interval]
|
57
|
+
has_terminated = false
|
58
|
+
while Time.now < poll_until
|
59
|
+
has_terminated = !self.running_process?
|
60
|
+
break if has_terminated
|
61
|
+
sleep delay
|
62
|
+
end
|
63
|
+
|
64
|
+
if RunLoop::Environment.debug?
|
65
|
+
puts "Waited for #{Time.now - now} seconds for '#{process_name}' to die."
|
66
|
+
end
|
67
|
+
|
68
|
+
if @options[:raise_on_timeout] and !has_terminated
|
69
|
+
raise "Waited #{@options[:timeout]} seconds for '#{process_name}' to die."
|
70
|
+
end
|
71
|
+
has_terminated
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# @!visibility private
|
77
|
+
DEFAULT_OPTIONS =
|
78
|
+
{
|
79
|
+
:timeout => 10.0,
|
80
|
+
:interval => 0.1,
|
81
|
+
:raise_on_timeout => false
|
82
|
+
}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|