run_loop 1.5.6 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cbbee55fbd11dd5c1cd13fa461ae1be917adc6d3
4
- data.tar.gz: 22f0456c4949e71f6c8d2d951d7f0aa559448674
3
+ metadata.gz: 8d3ac00b7d27f57b3b75a5c968119b85e42106c5
4
+ data.tar.gz: 991d05cd919e9a9beab809a162a654c917404c64
5
5
  SHA512:
6
- metadata.gz: 434db36ae98ba6a3530cb164d9ac0379fbe59cfad22a2851901530abbe20393668ff3c8be03e046a180b3a168f4842376825bb13920fb0f3c9b9417e5b125902
7
- data.tar.gz: 351adc46d8f2849bb6c1b5f8b0772073f3608776fabe3ee324e16daed86e8d6b07fa58fc22f59881d86c60b979a27a2f050e2af6bc5759be833dc3c1e88f36e3
6
+ metadata.gz: b228aeeafd1f6a418deef62898a4001f8902c6566497b6d906da927356982bfc1b3b05f136ead14026135c0223d9ae32cfa9fc8c0f0c32598326d7a74657b466
7
+ data.tar.gz: c0786de6bf90282b0c9e9106a2893ceb8984b2df909aaa6d581b1a7fa7d58864a49066776caa780cefdabb21de960276d1d11ecc64acacbfabde40d2217e1aaf
data/lib/run_loop.rb CHANGED
@@ -13,7 +13,6 @@ require 'run_loop/dylib_injector'
13
13
  require 'run_loop/fifo'
14
14
  require 'run_loop/core'
15
15
  require 'run_loop/version'
16
- require 'run_loop/xctools'
17
16
  require 'run_loop/plist_buddy'
18
17
  require 'run_loop/app'
19
18
  require 'run_loop/ipa'
@@ -24,7 +23,6 @@ require 'run_loop/lipo'
24
23
  require 'run_loop/cache/cache'
25
24
  require 'run_loop/host_cache'
26
25
  require 'run_loop/patches/awesome_print'
27
- require 'run_loop/patches/retriable'
28
26
  require 'run_loop/core_simulator'
29
27
  require 'run_loop/simctl/plists'
30
28
 
@@ -37,11 +35,7 @@ module RunLoop
37
35
  # @return [void]
38
36
  def self.deprecated(version, msg)
39
37
 
40
- if RUBY_VERSION < '2.0'
41
- stack = Kernel.caller[1..6].join("\n")
42
- else
43
- stack = Kernel.caller(0, 6)[1..-1].join("\n")
44
- end
38
+ stack = Kernel.caller(0, 6)[1..-1].join("\n")
45
39
 
46
40
  msg = "deprecated '#{version}' - #{msg}\n#{stack}"
47
41
 
@@ -97,8 +97,12 @@ module RunLoop
97
97
  ENV['DEBUG'] = '1' if debug
98
98
 
99
99
  begin
100
- RunLoop::SimControl.terminate_all_sims
101
- RunLoop::LifeCycle::Simulator.new.terminate_core_simulator_processes
100
+ RunLoop::CoreSimulator.terminate_core_simulator_processes
101
+ process_name = "com.apple.CoreSimulator.CoreSimulatorService"
102
+ RunLoop::ProcessWaiter.new(process_name).pids.each do |pid|
103
+ kill_options = { :timeout => 0.5 }
104
+ RunLoop::ProcessTerminator.new(pid, 'KILL', process_name, kill_options)
105
+ end
102
106
  ensure
103
107
  ENV['DEBUG'] = original_value if debug
104
108
  end
data/lib/run_loop/core.rb CHANGED
@@ -185,7 +185,10 @@ module RunLoop
185
185
  # Quits the simulator.
186
186
  core_sim = RunLoop::CoreSimulator.new(device, app)
187
187
 
188
- core_sim.install
188
+ # :reset is a legacy variable; has been replaced with :reset_app_sandbox
189
+ if launch_options[:reset] || launch_options[:reset_app_sandbox]
190
+ core_sim.reset_app_sandbox
191
+ end
189
192
 
190
193
  # Will quit the simulator if it is running.
191
194
  # @todo fix accessibility_enabled? so we don't have to quit the sim
@@ -196,17 +199,15 @@ module RunLoop
196
199
  # Will quit the simulator if it is running.
197
200
  # @todo fix software_keyboard_enabled? so we don't have to quit the sim
198
201
  # SimControl#software_keyboard_enabled? is always false during Core#prepare_simulator
199
- # https://github.com/calabash/run_loop/issues/167
202
+ # https://github.com/calabash/run_loop/issues/168
200
203
  sim_control.ensure_software_keyboard(device)
201
204
 
202
- # Xcode 6.3 instruments cannot launch an app that is already installed on
203
- # iOS 8.3 Simulators. See: https://github.com/calabash/calabash-ios/issues/744
204
- if xcode.version_gte_63?
205
+ # Launches the simulator if the app is not installed.
206
+ core_sim.install
205
207
 
206
- if core_sim.app_is_installed? && !sim_control.sim_is_running?
207
- core_sim.launch_simulator
208
- end
209
- end
208
+ # If CoreSimulator has already launched the simulator, it will not
209
+ # launching it again.
210
+ core_sim.launch_simulator
210
211
  end
211
212
  end
212
213
 
@@ -218,15 +219,6 @@ module RunLoop
218
219
  logger = options[:logger]
219
220
  sim_control ||= options[:sim_control] || RunLoop::SimControl.new
220
221
 
221
- if options[:xctools]
222
- if RunLoop::Environment.debug?
223
- RunLoop.deprecated('1.5.0', %q(
224
- RunLoop::XCTools has been replaced with RunLoop::Xcode.
225
- The :xctools key will be ignored. It has been replaced by the :xcode key.
226
- Please update your sources to pass an instance of RunLoop::Xcode))
227
- end
228
- end
229
-
230
222
  xcode ||= options[:xcode] || sim_control.xcode
231
223
 
232
224
  instruments = RunLoop::Instruments.new
@@ -476,34 +468,23 @@ Logfile: #{log_file}
476
468
  #
477
469
  # For historical reasons, the most recent non-64b SDK should be used.
478
470
  #
479
- # @param [RunLoop::XCTools, RunLoop::Xcode] xcode Used to detect the current xcode
471
+ # @param [RunLoop::Xcode] xcode Used to detect the current xcode
480
472
  # version.
481
473
  def self.default_simulator(xcode=RunLoop::Xcode.new)
482
- if xcode.is_a?(RunLoop::XCTools)
483
- if RunLoop::Environment.debug?
484
- RunLoop.deprecated('1.5.0',
485
- %q(
486
- RunLoop::XCTools has been replaced with RunLoop::Xcode.
487
- Please update your sources to pass an instance of RunLoop::Xcode))
488
- end
489
- ensured_xcode = RunLoop::Xcode.new
490
- else
491
- ensured_xcode = xcode
492
- end
493
474
 
494
- if ensured_xcode.version_gte_71?
475
+ if xcode.version_gte_71?
495
476
  'iPhone 6s (9.1)'
496
- elsif ensured_xcode.version_gte_7?
477
+ elsif xcode.version_gte_7?
497
478
  'iPhone 5s (9.0)'
498
- elsif ensured_xcode.version_gte_64?
479
+ elsif xcode.version_gte_64?
499
480
  'iPhone 5s (8.4 Simulator)'
500
- elsif ensured_xcode.version_gte_63?
481
+ elsif xcode.version_gte_63?
501
482
  'iPhone 5s (8.3 Simulator)'
502
- elsif ensured_xcode.version_gte_62?
483
+ elsif xcode.version_gte_62?
503
484
  'iPhone 5s (8.2 Simulator)'
504
- elsif ensured_xcode.version_gte_61?
485
+ elsif xcode.version_gte_61?
505
486
  'iPhone 5s (8.1 Simulator)'
506
- elsif ensured_xcode.version_gte_6?
487
+ elsif xcode.version_gte_6?
507
488
  'iPhone 5s (8.0 Simulator)'
508
489
  else
509
490
  'iPhone Retina (4-inch) - Simulator - iOS 7.1'
@@ -731,18 +712,7 @@ Please update your sources to pass an instance of RunLoop::Xcode))
731
712
  candidate
732
713
  end
733
714
 
734
- def self.default_tracetemplate(instruments_arg=RunLoop::Instruments.new)
735
- if instruments_arg.is_a?(RunLoop::XCTools)
736
- if RunLoop::Environment.debug?
737
- RunLoop.deprecated('1.5.0',
738
- %q(
739
- RunLoop::XCTools has been replaced with RunLoop::Xcode.
740
- Please update your sources to pass an instance of RunLoop::Instruments))
741
- end
742
- instruments = RunLoop::Instruments.new
743
- else
744
- instruments = instruments_arg
745
- end
715
+ def self.default_tracetemplate(instruments=RunLoop::Instruments.new)
746
716
 
747
717
  templates = instruments.templates
748
718
 
@@ -1,6 +1,34 @@
1
1
  # A class to manage interactions with CoreSimulators.
2
2
  class RunLoop::CoreSimulator
3
3
 
4
+ # These options control various aspects of an app's life cycle on the iOS
5
+ # Simulator.
6
+ #
7
+ # You can override these values if they do not work in your environment.
8
+ #
9
+ # For cucumber users, the best place to override would be in your
10
+ # features/support/env.rb.
11
+ #
12
+ # For example:
13
+ #
14
+ # RunLoop::CoreSimulator::DEFAULT_OPTIONS[:install_app_timeout] = 60
15
+ DEFAULT_OPTIONS = {
16
+ # In most cases 30 seconds is a reasonable amount of time to wait for an
17
+ # install. When testing larger apps, on slow machines, or in CI, this
18
+ # value may need to be higher. 120 is the default for CI.
19
+ :install_app_timeout => RunLoop::Environment.ci? ? 120 : 30,
20
+ :uninstall_app_timeout => RunLoop::Environment.ci? ? 120 : 30,
21
+ :launch_app_timeout => RunLoop::Environment.ci? ? 120 : 30,
22
+ :wait_for_state_timeout => RunLoop::Environment.ci? ? 120 : 30
23
+ }
24
+
25
+ # @!visibility private
26
+ # This should not be overridden
27
+ WAIT_FOR_SIMULATOR_STATE_INTERVAL = 0.1
28
+
29
+ # @!visibility private
30
+ @@simulator_pid = nil
31
+
4
32
  # @!visibility private
5
33
  attr_reader :app
6
34
 
@@ -16,20 +44,12 @@ class RunLoop::CoreSimulator
16
44
  # @!visibility private
17
45
  attr_reader :xcrun
18
46
 
19
- # @!visibility private
20
- attr_reader :simulator_pid
21
-
22
47
  # @!visibility private
23
48
  METADATA_PLIST = '.com.apple.mobile_container_manager.metadata.plist'
24
49
 
25
50
  # @!visibility private
26
51
  CORE_SIMULATOR_DEVICE_DIR = File.expand_path('~/Library/Developer/CoreSimulator/Devices')
27
52
 
28
- # @!visibility private
29
- WAIT_FOR_DEVICE_STATE_OPTS = {
30
- interval: 0.1,
31
- timeout: 5
32
- }
33
53
 
34
54
  # @!visibility private
35
55
  MANAGED_PROCESSES =
@@ -109,6 +129,122 @@ class RunLoop::CoreSimulator
109
129
  send_term_first = process_details[1]
110
130
  self.term_or_kill(process_name, send_term_first)
111
131
  end
132
+
133
+ self.simulator_pid = nil
134
+ end
135
+
136
+ # @!visibility private
137
+ #
138
+ # Some operations, like erase, require that the simulator be
139
+ # 'Shutdown'.
140
+ #
141
+ # @param [RunLoop::Device] simulator the sim to wait for
142
+ # @param [String] target_state the state to wait for
143
+ def self.wait_for_simulator_state(simulator, target_state)
144
+ now = Time.now
145
+ timeout = DEFAULT_OPTIONS[:wait_for_state_timeout]
146
+ poll_until = now + timeout
147
+ delay = WAIT_FOR_SIMULATOR_STATE_INTERVAL
148
+ in_state = false
149
+ while Time.now < poll_until
150
+ in_state = simulator.update_simulator_state == target_state
151
+ break if in_state
152
+ sleep delay if delay != 0
153
+ end
154
+
155
+ elapsed = Time.now - now
156
+ RunLoop.log_debug("Waited for #{elapsed} seconds for device to have state: '#{target_state}'.")
157
+
158
+ unless in_state
159
+ raise "Expected '#{target_state} but found '#{simulator.state}' after waiting."
160
+ end
161
+ in_state
162
+ end
163
+
164
+ # @!visibility private
165
+ # Erase a simulator. This is the same as touching the Simulator
166
+ # "Reset Content & Settings" menu item.
167
+ #
168
+ # @param [RunLoop::Device] simulator The simulator to erase
169
+ # @param [Hash] options Control the behavior of the method.
170
+ # @option options [Numeric] :timout (180) How long tow wait for simctl to
171
+ # shutdown and erase the simulator. The timeout is apply separately to
172
+ # each command.
173
+ #
174
+ # @raise RuntimeError If the simulator cannot be shutdown
175
+ # @raise RuntimeError If the simulator cannot be erased
176
+ # @raise ArgumentError If the simulator is a physical device
177
+ def self.erase(simulator, options={})
178
+ if simulator.physical_device?
179
+ raise ArgumentError,
180
+ "#{simulator} is a physical device. This method is only for Simulators"
181
+ end
182
+
183
+ default_options = {
184
+ :timeout => 60*3
185
+ }
186
+
187
+ merged_options = default_options.merge(options)
188
+
189
+ self.quit_simulator
190
+
191
+ xcrun = merged_options[:xcrun] || RunLoop::Xcrun.new
192
+ timeout = merged_options[:timeout]
193
+ xcrun_opts = {
194
+ :log_cmd => true,
195
+ :timeout => timeout
196
+ }
197
+
198
+ if simulator.update_simulator_state != "Shutdown"
199
+ args = ["simctl", "shutdown", simulator.udid]
200
+ xcrun.exec(args, xcrun_opts)
201
+ begin
202
+ self.wait_for_simulator_state(simulator, "Shutdown")
203
+ rescue RuntimeError => _
204
+ raise RuntimeError, %Q{
205
+ Could not erase simulator because it could not be Shutdown.
206
+
207
+ This usually means your CoreSimulator processes need to be restarted.
208
+
209
+ You can restart the CoreSimulator processes with this command:
210
+
211
+ $ bundle exec run-loop simctl manage-processes
212
+
213
+ }
214
+
215
+ end
216
+ end
217
+
218
+ args = ["simctl", "erase", simulator.udid]
219
+ hash = xcrun.exec(args, xcrun_opts)
220
+
221
+ if hash[:exit_status] != 0
222
+ raise RuntimeError, %Q{
223
+ Could not erase simulator because simctl returned this error:
224
+
225
+ #{hash[:out]}
226
+
227
+ This usually means your CoreSimulator processes need to be restarted.
228
+
229
+ You can restart the CoreSimulator processes with this command:
230
+
231
+ $ bundle exec run-loop simctl manage-processes
232
+
233
+ }
234
+
235
+ end
236
+
237
+ hash
238
+ end
239
+
240
+ # @!visibility private
241
+ def self.simulator_pid
242
+ @@simulator_pid
243
+ end
244
+
245
+ # @!visibility private
246
+ def self.simulator_pid=(pid)
247
+ @@simulator_pid = pid
112
248
  end
113
249
 
114
250
  # @param [RunLoop::Device] device The device.
@@ -130,7 +266,7 @@ class RunLoop::CoreSimulator
130
266
  RunLoop::CoreSimulator.quit_simulator
131
267
  end
132
268
 
133
- # stdio.pipe - can cause problems finding the SHA or size data dir
269
+ # stdio.pipe - can cause problems finding the SHA of a simulator
134
270
  rm_instruments_pipe
135
271
  end
136
272
 
@@ -149,19 +285,14 @@ class RunLoop::CoreSimulator
149
285
  @xcrun ||= RunLoop::Xcrun.new
150
286
  end
151
287
 
152
- # @!visibility private
153
- def simulator_pid
154
- @simulator_pid
155
- end
156
-
157
288
  # Launch the simulator indicated by device.
158
289
  def launch_simulator
159
290
 
160
- if sim_pid != nil
291
+ if running_simulator_pid != nil
161
292
  # There is a running simulator.
162
293
 
163
294
  # Did we launch it?
164
- if sim_pid == simulator_pid
295
+ if running_simulator_pid == RunLoop::CoreSimulator.simulator_pid
165
296
  # Nothing to do, we already launched the simulator.
166
297
  return
167
298
  else
@@ -177,12 +308,9 @@ class RunLoop::CoreSimulator
177
308
 
178
309
  start_time = Time.now
179
310
 
180
- pid = spawn('xcrun', *args)
311
+ pid = Process.spawn('xcrun', *args)
181
312
  Process.detach(pid)
182
313
 
183
- # Keep track of the pid so we can know if we have already launched this sim.
184
- @simulator_pid = pid
185
-
186
314
  options = { :timeout => 5, :raise_on_timeout => true }
187
315
  RunLoop::ProcessWaiter.new(sim_name, options).wait_for_any
188
316
 
@@ -191,6 +319,9 @@ class RunLoop::CoreSimulator
191
319
  elapsed = Time.now - start_time
192
320
  RunLoop.log_debug("Took #{elapsed} seconds to launch the simulator")
193
321
 
322
+ # Keep track of the pid so we can know if we have already launched this sim.
323
+ RunLoop::CoreSimulator.simulator_pid = running_simulator_pid
324
+
194
325
  true
195
326
  end
196
327
 
@@ -201,14 +332,20 @@ class RunLoop::CoreSimulator
201
332
  def launch
202
333
  install
203
334
 
335
+ # If the app is the same, install will not launch the simulator.
336
+ # In order to launch the app, the simulator needs to be running.
337
+ # launch_simulator ensures that the sim is launched and will not
338
+ # relaunch it.
339
+ launch_simulator
340
+
204
341
  args = ['simctl', 'launch', device.udid, app.bundle_identifier]
205
- hash = xcrun.exec(args, log_cmd: true, timeout: 20)
342
+ timeout = DEFAULT_OPTIONS[:launch_app_timeout]
343
+ hash = xcrun.exec(args, log_cmd: true, timeout: timeout)
206
344
 
207
345
  exit_status = hash[:exit_status]
208
346
 
209
347
  if exit_status != 0
210
- err = hash[:err]
211
- RunLoop.log_error(err)
348
+ RunLoop.log_error(hash[:out])
212
349
  raise RuntimeError, "Could not launch #{app.bundle_identifier} on #{device}"
213
350
  end
214
351
 
@@ -254,7 +391,7 @@ class RunLoop::CoreSimulator
254
391
  def reset_app_sandbox
255
392
  return true if !app_is_installed?
256
393
 
257
- wait_for_device_state('Shutdown')
394
+ RunLoop::CoreSimulator.wait_for_simulator_state(device, "Shutdown")
258
395
 
259
396
  reset_app_sandbox_internal
260
397
  end
@@ -266,7 +403,9 @@ class RunLoop::CoreSimulator
266
403
  launch_simulator
267
404
 
268
405
  args = ['simctl', 'uninstall', device.udid, app.bundle_identifier]
269
- xcrun.exec(args, log_cmd: true, timeout: 20)
406
+
407
+ timeout = DEFAULT_OPTIONS[:uninstall_app_timeout]
408
+ xcrun.exec(args, log_cmd: true, timeout: timeout)
270
409
 
271
410
  device.simulator_wait_for_stable_state
272
411
  true
@@ -345,12 +484,46 @@ class RunLoop::CoreSimulator
345
484
  #
346
485
  # @note Will only search for the current Xcode simulator.
347
486
  #
348
- # @return [String, nil] The pid as a String or nil if no process is found.
487
+ # @return [Integer, nil] The pid as a String or nil if no process is found.
349
488
  #
350
489
  # @todo Convert this to force UTF8
351
- def sim_pid
490
+ def running_simulator_pid
352
491
  process_name = "MacOS/#{sim_name}"
353
- `xcrun ps x -o pid,command | grep "#{process_name}" | grep -v grep`.strip.split(' ').first
492
+
493
+ args = ["xcrun", "ps", "x", "-o", "pid,command"]
494
+ hash = xcrun.exec(args)
495
+
496
+ exit_status = hash[:exit_status]
497
+ if exit_status != 0
498
+ raise RuntimeError,
499
+ %Q{Could not find the pid of #{sim_name} with:
500
+
501
+ #{args.join(" ")}
502
+
503
+ Command exited with status #{exit_status}
504
+ Message: '#{hash[:out]}'
505
+ }
506
+ end
507
+
508
+ if hash[:out].nil? || hash[:out] == ""
509
+ raise RuntimeError,
510
+ %Q{Could not find the pid of #{sim_name} with:
511
+
512
+ #{args.join(" ")}
513
+
514
+ Command had no output
515
+ }
516
+ end
517
+
518
+ lines = hash[:out].split("\n")
519
+
520
+ match = lines.detect do |line|
521
+ line[/#{process_name}/, 0]
522
+ end
523
+
524
+ return nil if match.nil?
525
+
526
+ match.split(" ").first.to_i
354
527
  end
355
528
 
356
529
  # @!visibility private
@@ -358,34 +531,13 @@ class RunLoop::CoreSimulator
358
531
  launch_simulator
359
532
 
360
533
  args = ['simctl', 'install', device.udid, app.path]
361
- xcrun.exec(args, log_cmd: true, timeout: 20)
534
+ timeout = DEFAULT_OPTIONS[:install_app_timeout]
535
+ xcrun.exec(args, log_cmd: true, timeout: timeout)
362
536
 
363
537
  device.simulator_wait_for_stable_state
364
538
  installed_app_bundle_dir
365
539
  end
366
540
 
367
- # @!visibility private
368
- def wait_for_device_state(target_state)
369
- now = Time.now
370
- timeout = WAIT_FOR_DEVICE_STATE_OPTS[:timeout]
371
- poll_until = now + timeout
372
- delay = WAIT_FOR_DEVICE_STATE_OPTS[:interval]
373
- in_state = false
374
- while Time.now < poll_until
375
- in_state = device.update_simulator_state == target_state
376
- break if in_state
377
- sleep delay
378
- end
379
-
380
- elapsed = Time.now - now
381
- RunLoop.log_debug("Waited for #{elapsed} seconds for device to have state: '#{target_state}'.")
382
-
383
- unless in_state
384
- raise "Expected '#{target_state} but found '#{device.state}' after waiting."
385
- end
386
- in_state
387
- end
388
-
389
541
  # Required for support of iOS 7 CoreSimulators. Can be removed when
390
542
  # Xcode support is dropped.
391
543
  def sdk_gte_8?