run_loop 1.5.5 → 1.5.6.pre1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,73 +0,0 @@
1
- module RunLoop
2
-
3
- # @!visibility private
4
- module LifeCycle
5
-
6
- # @!visibility private
7
- #
8
- # Defines a Life Cycle interface for Simulators.
9
- class Simulator
10
-
11
- # @!visibility private
12
- # Pattern.
13
- # [ '< process name >', < send term first > ]
14
- MANAGED_PROCESSES =
15
- [
16
- # This process is a daemon, and requires 'KILL' to terminate.
17
- # Killing the process is fast, but it takes a long time to
18
- # restart.
19
- # ['com.apple.CoreSimulator.CoreSimulatorService', false],
20
-
21
- # Probably do not need to quit this, but it is tempting to do so.
22
- #['com.apple.CoreSimulator.SimVerificationService', false],
23
-
24
- # Started by Xamarin Studio, this is the parent process of the
25
- # processes launched by Xamarin's interaction with
26
- # CoreSimulatorBridge.
27
- ['csproxy', true],
28
-
29
- # Yes.
30
- ['SimulatorBridge', true],
31
- ['configd_sim', true],
32
- ['launchd_sim', true],
33
-
34
- # Does not always appear.
35
- ['CoreSimulatorBridge', true],
36
-
37
- # assetsd instances clobber each other and are not properly
38
- # killed when quiting the simulator.
39
- ['assetsd', true],
40
-
41
- # iproxy is started by UITest. It is not necessary to send
42
- # TERM first.
43
- ['iproxy', false],
44
-
45
- # Xcode 7
46
- ['ids_simd', true]
47
- ]
48
-
49
- # @!visibility private
50
- def terminate_core_simulator_processes
51
- MANAGED_PROCESSES.each do |pair|
52
- name = pair[0]
53
- send_term = pair[1]
54
- pids = RunLoop::ProcessWaiter.new(name).pids
55
- pids.each do |pid|
56
-
57
- if send_term
58
- term = RunLoop::ProcessTerminator.new(pid, 'TERM', name)
59
- killed = term.kill_process
60
- else
61
- killed = false
62
- end
63
-
64
- unless killed
65
- term = RunLoop::ProcessTerminator.new(pid, 'KILL', name)
66
- term.kill_process
67
- end
68
- end
69
- end
70
- end
71
- end
72
- end
73
- end
@@ -1,499 +0,0 @@
1
- require 'fileutils'
2
- require 'open3'
3
- #require 'retriable'
4
-
5
- module RunLoop::Simctl
6
-
7
- class SimctlError < StandardError
8
-
9
- end
10
-
11
- # @!visibility private
12
- # This is not a public API. You have been warned.
13
- #
14
- # TODO Some code is duplicated from sim_control.rb
15
- # TODO Reinstall if checksum does not match.
16
- # TODO Analyze terminate_core_simulator_processes.
17
- # TODO Figure out when CoreSimulator appears and does not appear.
18
- class Bridge < RunLoop::LifeCycle::CoreSimulator
19
-
20
- attr_reader :device
21
- attr_reader :app
22
- attr_reader :sim_control
23
- attr_reader :pbuddy
24
-
25
- def initialize(device, app_bundle_path)
26
-
27
- @pbuddy = RunLoop::PlistBuddy.new
28
-
29
- @sim_control = RunLoop::SimControl.new
30
- @path_to_ios_sim_app_bundle = @sim_control.send(:sim_app_path)
31
-
32
- @app = RunLoop::App.new(app_bundle_path)
33
-
34
- unless @app.valid?
35
- raise "Could not recreate a valid app from '#{app_bundle_path}'"
36
- end
37
-
38
- @device = device
39
-
40
- # It may seem weird to do this in the initialize, but you cannot make
41
- # simctl calls successfully unless the simulator is:
42
- # 1. closed
43
- # 2. the device you are trying to operate on is Shutdown
44
- # 3. the CoreSimulator processes are terminated
45
- RunLoop::SimControl.terminate_all_sims
46
- shutdown
47
- terminate_core_simulator_processes
48
- end
49
-
50
- # The sha1 of the installed app.
51
- def installed_app_sha1
52
- installed_bundle = fetch_app_dir
53
- if installed_bundle
54
- RunLoop::Directory.directory_digest(installed_bundle)
55
- else
56
- nil
57
- end
58
- end
59
-
60
- # Is the app that is install the same as the one we have in hand?
61
- def same_sha1_as_installed?
62
- app.sha1 == installed_app_sha1
63
- end
64
-
65
- # @!visibility private
66
- def is_sdk_8?
67
- @is_sdk_8 ||= device.version >= RunLoop::Version.new('8.0')
68
- end
69
-
70
- def device_data_dir
71
- @device_data_dir ||= File.join(CORE_SIMULATOR_DEVICE_DIR, device.udid, 'data')
72
- end
73
-
74
- def device_applications_dir
75
- @simulator_app_dir ||= lambda {
76
- if is_sdk_8?
77
- File.join(device_data_dir, 'Containers', 'Bundle', 'Application')
78
- else
79
- File.join(device_data_dir, 'Applications')
80
- end
81
- }.call
82
- end
83
-
84
- def app_data_dir
85
- app_install_dir = fetch_app_dir
86
- return nil if app_install_dir.nil?
87
- if is_sdk_8?
88
- containers_data_dir = File.join(device_data_dir, 'Containers', 'Data', 'Application')
89
- apps = Dir.glob("#{containers_data_dir}/**/#{METADATA_PLIST}")
90
- match = apps.detect do |metadata_plist|
91
- pbuddy.plist_read('MCMMetadataIdentifier', metadata_plist) == app.bundle_identifier
92
- end
93
- if match
94
- File.dirname(match)
95
- else
96
- nil
97
- end
98
- else
99
- app_install_dir
100
- end
101
- end
102
-
103
- def app_library_dir
104
- base_dir = app_data_dir
105
- if base_dir.nil?
106
- nil
107
- else
108
- File.join(base_dir, 'Library')
109
- end
110
- end
111
-
112
- def app_library_preferences_dir
113
- base_dir = app_library_dir
114
- if base_dir.nil?
115
- nil
116
- else
117
- File.join(base_dir, 'Preferences')
118
- end
119
- end
120
-
121
- def app_documents_dir
122
- base_dir = app_data_dir
123
- if base_dir.nil?
124
- nil
125
- else
126
- File.join(base_dir, 'Documents')
127
- end
128
- end
129
-
130
- def app_tmp_dir
131
- base_dir = app_data_dir
132
- if base_dir.nil?
133
- nil
134
- else
135
- File.join(base_dir, 'tmp')
136
- end
137
- end
138
-
139
- def reset_app_sandbox
140
- return true if !app_is_installed?
141
-
142
- shutdown
143
-
144
- reset_app_sandbox_internal
145
- end
146
-
147
- def update_device_state(options={})
148
- merged_options = UPDATE_DEVICE_STATE_OPTS.merge(options)
149
-
150
- interval = merged_options[:interval]
151
- tries = merged_options[:tries]
152
-
153
- debug_logging = RunLoop::Environment.debug?
154
-
155
- on_retry = Proc.new do |_, try, elapsed_time, next_interval|
156
- if debug_logging
157
- # Retriable 2.0
158
- if elapsed_time && next_interval
159
- puts "Updating device state attempt #{try} failed in '#{elapsed_time}'; will retry in '#{next_interval}'"
160
- else
161
- puts "Updating device state attempt #{try} failed; will retry in #{interval}"
162
- end
163
- end
164
- end
165
-
166
- retry_opts = RunLoop::RetryOpts.tries_and_interval(tries, interval,
167
- {:on_retry => on_retry,
168
- :on => [SimctlError]
169
- })
170
- matching_device = nil
171
-
172
- Retriable.retriable(retry_opts) do
173
- matching_device = fetch_matching_device
174
-
175
- unless matching_device
176
- raise "simctl could not find device with '#{device.udid}'"
177
- end
178
-
179
- if matching_device.state == nil || matching_device.state == ''
180
- raise SimctlError, "Could not find the state of the device with #{device.udid}"
181
- end
182
- end
183
-
184
- # Device#state is immutable
185
- @device = matching_device
186
- @device.state
187
- end
188
-
189
- def wait_for_device_state(target_state)
190
- return true if update_device_state == target_state
191
-
192
- now = Time.now
193
- timeout = WAIT_FOR_DEVICE_STATE_OPTS[:timeout]
194
- poll_until = now + WAIT_FOR_DEVICE_STATE_OPTS[:timeout]
195
- delay = WAIT_FOR_DEVICE_STATE_OPTS[:interval]
196
- in_state = false
197
- while Time.now < poll_until
198
- in_state = update_device_state == target_state
199
- break if in_state
200
- sleep delay
201
- end
202
-
203
- if RunLoop::Environment.debug?
204
- puts "Waited for #{timeout} seconds for device to have state: '#{target_state}'."
205
- end
206
-
207
- unless in_state
208
- raise "Expected '#{target_state} but found '#{device.state}' after waiting."
209
- end
210
- in_state
211
- end
212
-
213
- def app_is_installed?
214
- !fetch_app_dir.nil?
215
- end
216
-
217
- # @!visibility private
218
- def fetch_app_dir
219
- sim_app_dir = device_applications_dir
220
- return nil if !File.exist?(sim_app_dir)
221
- Dir.glob("#{sim_app_dir}/**/*.app").detect(nil) do |path|
222
- RunLoop::App.new(path).bundle_identifier == app.bundle_identifier
223
- end
224
- end
225
-
226
- def wait_for_app_install
227
- return true if app_is_installed?
228
-
229
- now = Time.now
230
- timeout = WAIT_FOR_APP_INSTALL_OPTS[:timeout]
231
- poll_until = now + timeout
232
- delay = WAIT_FOR_APP_INSTALL_OPTS[:interval]
233
- is_installed = false
234
- while Time.now < poll_until
235
- is_installed = app_is_installed?
236
- break if is_installed
237
- sleep delay
238
- end
239
-
240
- if RunLoop::Environment.debug?
241
- puts "Waited for #{timeout} seconds for '#{app.bundle_identifier}' to install."
242
- end
243
-
244
- unless is_installed
245
- raise "Expected app to be installed on #{device.instruments_identifier}"
246
- end
247
-
248
- true
249
- end
250
-
251
- def wait_for_app_uninstall
252
- return true unless app_is_installed?
253
-
254
- now = Time.now
255
- timeout = WAIT_FOR_APP_INSTALL_OPTS[:timeout]
256
- poll_until = now + timeout
257
- delay = WAIT_FOR_APP_INSTALL_OPTS[:interval]
258
- not_installed = false
259
- while Time.now < poll_until
260
- not_installed = !app_is_installed?
261
- break if not_installed
262
- sleep delay
263
- end
264
-
265
- if RunLoop::Environment.debug?
266
- puts "Waited for #{timeout} seconds for '#{app.bundle_identifier}' to uninstall."
267
- end
268
-
269
- unless not_installed
270
- raise "Expected app to be installed on #{device.instruments_identifier}"
271
- end
272
-
273
- true
274
- end
275
-
276
- def shutdown
277
- return true if update_device_state == 'Shutdown'
278
-
279
- if device.state != 'Booted'
280
- raise "Cannot handle state '#{device.state}' for #{device.instruments_identifier}"
281
- end
282
-
283
- args = "simctl shutdown #{device.udid}".split(' ')
284
- Open3.popen3('xcrun', *args) do |_, _, stderr, status|
285
- err = stderr.read.strip
286
- exit_status = status.value.exitstatus
287
- if exit_status != 0
288
- raise "Could not shutdown #{device.instruments_identifier}: #{exit_status} => '#{err}'"
289
- end
290
- end
291
- wait_for_device_state('Shutdown')
292
- end
293
-
294
- def boot
295
- return true if update_device_state == 'Booted'
296
-
297
- if device.state != 'Shutdown'
298
- raise "Cannot handle state '#{device.state}' for #{device.instruments_identifier}"
299
- end
300
-
301
- args = "simctl boot #{device.udid}".split(' ')
302
- Open3.popen3('xcrun', *args) do |_, _, stderr, status|
303
- err = stderr.read.strip
304
- exit_status = status.value.exitstatus
305
- if exit_status != 0
306
- raise "Could not boot #{device.instruments_identifier}: #{exit_status} => '#{err}'"
307
- end
308
- end
309
- wait_for_device_state('Booted')
310
- end
311
-
312
- def install
313
- return true if app_is_installed?
314
-
315
- boot
316
-
317
- args = "simctl install #{device.udid} #{app.path}".split(' ')
318
- Open3.popen3('xcrun', *args) do |_, _, stderr, process_status|
319
- err = stderr.read.strip
320
- exit_status = process_status.value.exitstatus
321
- if exit_status != 0
322
- raise "Could not install '#{app.bundle_identifier}': #{exit_status} => '#{err}'."
323
- end
324
- end
325
-
326
- wait_for_app_install
327
- shutdown
328
- end
329
-
330
- def uninstall
331
- return true unless app_is_installed?
332
-
333
- boot
334
-
335
- args = "simctl uninstall #{device.udid} #{app.bundle_identifier}".split(' ')
336
- Open3.popen3('xcrun', *args) do |_, _, stderr, process_status|
337
- err = stderr.read.strip
338
- exit_status = process_status.value.exitstatus
339
- if exit_status != 0
340
- raise "Could not uninstall '#{app.bundle_identifier}': #{exit_status} => '#{err}'."
341
- end
342
- end
343
-
344
- wait_for_app_uninstall
345
- shutdown
346
- end
347
-
348
- def launch_simulator
349
- args = ['open', '-g', '-a', @path_to_ios_sim_app_bundle, '--args', '-CurrentDeviceUDID', device.udid]
350
- pid = spawn('xcrun', *args)
351
- Process.detach(pid)
352
-
353
- # @todo Does not always appear?
354
- # RunLoop::ProcessWaiter.new('CoreSimulatorBridge', WAIT_FOR_APP_LAUNCH_OPTS).wait_for_any
355
- sim_name = @sim_control.send(:sim_name)
356
- RunLoop::ProcessWaiter.new(sim_name, WAIT_FOR_APP_LAUNCH_OPTS).wait_for_any
357
- RunLoop::ProcessWaiter.new('SimulatorBridge', WAIT_FOR_APP_LAUNCH_OPTS).wait_for_any
358
- wait_for_device_state 'Booted'
359
- sleep(SIM_POST_LAUNCH_WAIT)
360
- end
361
-
362
- def launch
363
-
364
- install
365
- launch_simulator
366
-
367
- args = "simctl launch #{device.udid} #{app.bundle_identifier}".split(' ')
368
- Open3.popen3('xcrun', *args) do |_, _, stderr, process_status|
369
- err = stderr.read.strip
370
- exit_status = process_status.value.exitstatus
371
- unless exit_status == 0
372
- raise "Could not simctl launch '#{app.bundle_identifier}' on '#{device.instruments_identifier}': #{exit_status} => '#{err}'"
373
- end
374
- end
375
-
376
- RunLoop::ProcessWaiter.new(app.executable_name, WAIT_FOR_APP_LAUNCH_OPTS).wait_for_any
377
- true
378
- end
379
-
380
- private
381
-
382
- WAIT_FOR_DEVICE_STATE_OPTS =
383
- {
384
- interval: 0.1,
385
- timeout: 5
386
- }
387
-
388
- WAIT_FOR_APP_INSTALL_OPTS =
389
- {
390
- interval: 0.1,
391
- timeout: 20
392
- }
393
-
394
- WAIT_FOR_APP_LAUNCH_OPTS =
395
- {
396
- timeout: 10,
397
- raise_on_timeout: true
398
- }
399
-
400
- UPDATE_DEVICE_STATE_OPTS =
401
- {
402
- :tries => 100,
403
- :interval => 0.1
404
- }
405
-
406
- SIM_POST_LAUNCH_WAIT = RunLoop::Environment.sim_post_launch_wait || 1.0
407
-
408
- METADATA_PLIST = '.com.apple.mobile_container_manager.metadata.plist'
409
- CORE_SIMULATOR_DEVICE_DIR = File.expand_path('~/Library/Developer/CoreSimulator/Devices')
410
-
411
- # @!visibility private
412
- def fetch_matching_device
413
- sim_control.simulators.detect do |sim|
414
- sim.udid == device.udid
415
- end
416
- end
417
-
418
- # @!visibility private
419
- def reset_app_sandbox_internal_shared
420
- [app_documents_dir, app_tmp_dir].each do |dir|
421
- FileUtils.rm_rf dir
422
- FileUtils.mkdir dir
423
- end
424
- end
425
-
426
- # @!visibility private
427
- def reset_app_sandbox_internal_sdk_gte_8
428
- lib_dir = app_library_dir
429
- RunLoop::Directory.recursive_glob_for_entries(lib_dir).each do |entry|
430
- if entry.include?('Preferences')
431
- # nop
432
- else
433
- if File.exist?(entry)
434
- FileUtils.rm_rf(entry)
435
- end
436
- end
437
- end
438
-
439
- prefs_dir = app_library_preferences_dir
440
- protected = ['com.apple.UIAutomation.plist',
441
- 'com.apple.UIAutomationPlugIn.plist']
442
- RunLoop::Directory.recursive_glob_for_entries(prefs_dir).each do |entry|
443
- unless protected.include?(File.basename(entry))
444
- if File.exist?(entry)
445
- FileUtils.rm_rf entry
446
- end
447
- end
448
- end
449
- end
450
-
451
- # @!visibility private
452
- def reset_app_sandbox_internal_sdk_lt_8
453
- prefs_dir = app_library_preferences_dir
454
- RunLoop::Directory.recursive_glob_for_entries(prefs_dir).each do |entry|
455
- if entry.end_with?('.GlobalPreferences.plist') ||
456
- entry.end_with?('com.apple.PeoplePicker.plist')
457
- # nop
458
- else
459
- if File.exist?(entry)
460
- FileUtils.rm_rf entry
461
- end
462
- end
463
- end
464
-
465
- # app preferences lives in device Library/Preferences
466
- device_prefs_dir = File.join(app_data_dir, 'Library', 'Preferences')
467
- app_prefs_plist = File.join(device_prefs_dir, "#{app.bundle_identifier}.plist")
468
- if File.exist?(app_prefs_plist)
469
- FileUtils.rm_rf(app_prefs_plist)
470
- end
471
- end
472
-
473
- # @!visibility private
474
- def reset_app_sandbox_internal
475
- reset_app_sandbox_internal_shared
476
-
477
- if is_sdk_8?
478
- reset_app_sandbox_internal_sdk_gte_8
479
- else
480
- reset_app_sandbox_internal_sdk_lt_8
481
- end
482
- end
483
-
484
- # @!visibility private
485
- def app_uia_crash_logs
486
- base_dir = app_data_dir
487
- if base_dir.nil?
488
- nil
489
- else
490
- dir = File.join(base_dir, 'Library', 'CrashReporter', 'UIALogs')
491
- if Dir.exist?(dir)
492
- Dir.glob("#{dir}/*.plist")
493
- else
494
- nil
495
- end
496
- end
497
- end
498
- end
499
- end