run_loop 1.5.5 → 1.5.6.pre1

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.
@@ -1,5 +1,5 @@
1
1
  module RunLoop
2
- VERSION = '1.5.5'
2
+ VERSION = '1.5.6.pre1'
3
3
 
4
4
  # A model of a software release version that can be used to compare two versions.
5
5
  #
@@ -44,7 +44,7 @@ module RunLoop
44
44
  # version.minor => 10
45
45
  # version.patch => 1
46
46
  # version.pre => false
47
- # version.pre_release => nil
47
+ # version.pre_version => nil
48
48
  #
49
49
  # @example
50
50
  # version = Version.new(1.6.3.pre5)
@@ -52,7 +52,7 @@ module RunLoop
52
52
  # version.minor => 6
53
53
  # version.patch => 3
54
54
  # version.pre => true
55
- # version.pre_release => 5
55
+ # version.pre_version => 5
56
56
  #
57
57
  # @param [String] version the version string to parse.
58
58
  # @raise [ArgumentError] if version is not in the form 5, 6.1, 7.1.2, 8.2.3.pre1
@@ -88,6 +88,18 @@ module RunLoop
88
88
  "#<Version #{to_s}>"
89
89
  end
90
90
 
91
+ # Compare this version to another for _object_ equality. This allows
92
+ # Version instances to be used as Hash keys.
93
+ # @param [Version] other the version to compare against.
94
+ def eql?(other)
95
+ hash == other.hash
96
+ end
97
+
98
+ # The hash method for this instance.
99
+ def hash
100
+ to_s.hash
101
+ end
102
+
91
103
  # Compare this version to another for equality.
92
104
  # @param [Version] other the version to compare against
93
105
  # @return [Boolean] true if this Version is the same as `other`
data/lib/run_loop.rb CHANGED
@@ -2,6 +2,7 @@ require 'run_loop/regex'
2
2
  require 'run_loop/directory'
3
3
  require 'run_loop/environment'
4
4
  require 'run_loop/logging'
5
+ require 'run_loop/dot_dir'
5
6
  require 'run_loop/xcrun'
6
7
  require 'run_loop/xcode'
7
8
  require 'run_loop/l10n'
@@ -24,9 +25,7 @@ require 'run_loop/cache/cache'
24
25
  require 'run_loop/host_cache'
25
26
  require 'run_loop/patches/awesome_print'
26
27
  require 'run_loop/patches/retriable'
27
- require 'run_loop/life_cycle/simulator'
28
- require 'run_loop/life_cycle/core_simulator'
29
- require 'run_loop/simctl/bridge'
28
+ require 'run_loop/core_simulator'
30
29
  require 'run_loop/simctl/plists'
31
30
 
32
31
  module RunLoop
@@ -59,13 +58,13 @@ module RunLoop
59
58
  def self.run(options={})
60
59
 
61
60
  if RunLoop::Instruments.new.instruments_app_running?
62
- msg =
63
- [
64
- "Please quit the Instruments.app.",
65
- "If Instruments.app is open, the instruments command line",
66
- "tool cannot take control of your application."
67
- ]
68
- raise msg.join("\n")
61
+ raise %q(The Instruments.app is open.
62
+
63
+ If the Instruments.app is open, the instruments command line tool cannot take
64
+ control of your application.
65
+
66
+ Please quit the Instruments.app and try again.)
67
+
69
68
  end
70
69
 
71
70
  uia_strategy = options[:uia_strategy]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: run_loop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.5
4
+ version: 1.5.6.pre1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karl Krukow
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-25 00:00:00.000000000 Z
11
+ date: 2015-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -290,8 +290,10 @@ files:
290
290
  - lib/run_loop/cli/instruments.rb
291
291
  - lib/run_loop/cli/simctl.rb
292
292
  - lib/run_loop/core.rb
293
+ - lib/run_loop/core_simulator.rb
293
294
  - lib/run_loop/device.rb
294
295
  - lib/run_loop/directory.rb
296
+ - lib/run_loop/dot_dir.rb
295
297
  - lib/run_loop/dylib_injector.rb
296
298
  - lib/run_loop/environment.rb
297
299
  - lib/run_loop/fifo.rb
@@ -299,8 +301,6 @@ files:
299
301
  - lib/run_loop/instruments.rb
300
302
  - lib/run_loop/ipa.rb
301
303
  - lib/run_loop/l10n.rb
302
- - lib/run_loop/life_cycle/core_simulator.rb
303
- - lib/run_loop/life_cycle/simulator.rb
304
304
  - lib/run_loop/lipo.rb
305
305
  - lib/run_loop/lldb.rb
306
306
  - lib/run_loop/logging.rb
@@ -311,7 +311,6 @@ files:
311
311
  - lib/run_loop/process_waiter.rb
312
312
  - lib/run_loop/regex.rb
313
313
  - lib/run_loop/sim_control.rb
314
- - lib/run_loop/simctl/bridge.rb
315
314
  - lib/run_loop/simctl/plists.rb
316
315
  - lib/run_loop/version.rb
317
316
  - lib/run_loop/xcode.rb
@@ -345,9 +344,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
345
344
  version: '1.9'
346
345
  required_rubygems_version: !ruby/object:Gem::Requirement
347
346
  requirements:
348
- - - ">="
347
+ - - ">"
349
348
  - !ruby/object:Gem::Version
350
- version: '0'
349
+ version: 1.3.1
351
350
  requirements: []
352
351
  rubyforge_project:
353
352
  rubygems_version: 2.4.8
@@ -1,515 +0,0 @@
1
- module RunLoop
2
- module LifeCycle
3
-
4
- class CoreSimulator < Simulator
5
-
6
- require 'securerandom'
7
-
8
- # @!visibility private
9
- METADATA_PLIST = '.com.apple.mobile_container_manager.metadata.plist'
10
-
11
- # @!visibility private
12
- CORE_SIMULATOR_DEVICE_DIR = File.expand_path('~/Library/Developer/CoreSimulator/Devices')
13
-
14
- # @!visibility private
15
- # How long to wait after the simulator has launched.
16
- SIM_POST_LAUNCH_WAIT = RunLoop::Environment.sim_post_launch_wait || 1.0
17
-
18
- # @!visibility private
19
- # How long to wait for for a device to reach a state.
20
- WAIT_FOR_DEVICE_STATE_OPTS =
21
- {
22
- interval: 0.1,
23
- timeout: 5
24
- }
25
-
26
- # @!visibility private
27
- # How long to wait for the CoreSimulator processes to start.
28
- WAIT_FOR_SIMULATOR_PROCESSES_OPTS =
29
- {
30
- timeout: 5,
31
- raise_on_timeout: true
32
- }
33
-
34
- attr_reader :app
35
- attr_reader :device
36
- attr_reader :sim_control
37
- attr_reader :pbuddy
38
-
39
- # @param [RunLoop::App] app The application.
40
- # @param [RunLoop::Device] device The device.
41
- def initialize(app, device, sim_control=RunLoop::SimControl.new)
42
- @app = app
43
- @device = device
44
- @sim_control = sim_control
45
-
46
- # In order to manage the app on the device, we need to manage the
47
- # CoreSimulator processes.
48
- RunLoop::SimControl.terminate_all_sims
49
- terminate_core_simulator_processes
50
- end
51
-
52
- # Launch simulator without specifying an app.
53
- def launch_simulator
54
- sim_path = sim_control.send(:sim_app_path)
55
- args = ['open', '-g', '-a', sim_path, '--args', '-CurrentDeviceUDID', device.udid]
56
-
57
- RunLoop.log_debug("Launching #{device} with:")
58
- RunLoop.log_unix_cmd("xcrun #{args.join(' ')}")
59
-
60
- start_time = Time.now
61
-
62
- pid = spawn('xcrun', *args)
63
- Process.detach(pid)
64
-
65
- sim_name = sim_control.send(:sim_name)
66
-
67
- RunLoop::ProcessWaiter.new(sim_name, WAIT_FOR_SIMULATOR_PROCESSES_OPTS).wait_for_any
68
-
69
- device.simulator_wait_for_stable_state
70
-
71
- elapsed = Time.now - start_time
72
- RunLoop.log_debug("Took #{elapsed} seconds to launch the simulator")
73
-
74
- true
75
- end
76
-
77
- # @!visibility private
78
- def pbuddy
79
- @pbuddy ||= RunLoop::PlistBuddy.new
80
- end
81
-
82
- # @!visibility private
83
- def sdk_gte_8?
84
- device.version >= RunLoop::Version.new('8.0')
85
- end
86
-
87
- # The data directory for the the device.
88
- #
89
- # ~/Library/Developer/CoreSimulator/Devices/<UDID>/data
90
- def device_data_dir
91
- @device_data_dir ||= File.join(CORE_SIMULATOR_DEVICE_DIR, device.udid, 'data')
92
- end
93
-
94
- # The applications directory for the device.
95
- #
96
- # ~/Library/Developer/CoreSimulator/Devices/<UDID>/Containers/Bundle/Application
97
- def device_applications_dir
98
- @device_app_dir ||= lambda do
99
- if sdk_gte_8?
100
- File.join(device_data_dir, 'Containers', 'Bundle', 'Application')
101
- else
102
- File.join(device_data_dir, 'Applications')
103
- end
104
- end.call
105
- end
106
-
107
- # The sandbox directory for the app.
108
- #
109
- # ~/Library/Developer/CoreSimulator/Devices/<UDID>/Containers/Data/Application
110
- #
111
- # Contains Library, Documents, and tmp directories.
112
- def app_sandbox_dir
113
- app_install_dir = installed_app_bundle_dir
114
- return nil if app_install_dir.nil?
115
- if sdk_gte_8?
116
- app_sandbox_dir_sdk_gte_8
117
- else
118
- app_install_dir
119
- end
120
- end
121
-
122
- # The Library directory in the sandbox.
123
- def app_library_dir
124
- base_dir = app_sandbox_dir
125
- if base_dir.nil?
126
- nil
127
- else
128
- File.join(base_dir, 'Library')
129
- end
130
- end
131
-
132
- # The Library/Preferences directory in the sandbox.
133
- def app_library_preferences_dir
134
- base_dir = app_library_dir
135
- if base_dir.nil?
136
- nil
137
- else
138
- File.join(base_dir, 'Preferences')
139
- end
140
- end
141
-
142
- # The Documents directory in the sandbox.
143
- def app_documents_dir
144
- base_dir = app_sandbox_dir
145
- if base_dir.nil?
146
- nil
147
- else
148
- File.join(base_dir, 'Documents')
149
- end
150
- end
151
-
152
- # The tmp directory in the sandbox.
153
- def app_tmp_dir
154
- base_dir = app_sandbox_dir
155
- if base_dir.nil?
156
- nil
157
- else
158
- File.join(base_dir, 'tmp')
159
- end
160
- end
161
-
162
- # Is this app installed?
163
- def app_is_installed?
164
- !installed_app_bundle_dir.nil?
165
- end
166
-
167
- # The sha1 of the installed app.
168
- def installed_app_sha1
169
- installed_bundle = installed_app_bundle_dir
170
- if installed_bundle
171
- RunLoop::Directory.directory_digest(installed_bundle)
172
- else
173
- nil
174
- end
175
- end
176
-
177
- # Is the app that is install the same as the one we have in hand?
178
- def same_sha1_as_installed?
179
- app.sha1 == installed_app_sha1
180
- end
181
-
182
- # @!visibility private
183
- #
184
- # Returns the path to the installed app bundle directory (.app).
185
- #
186
- # If this method returns nil, the app is not installed.
187
- def installed_app_bundle_dir
188
- sim_app_dir = device_applications_dir
189
- return nil if !File.exist?(sim_app_dir)
190
- Dir.glob("#{sim_app_dir}/**/*.app").find do |path|
191
- RunLoop::App.new(path).bundle_identifier == app.bundle_identifier
192
- end
193
- end
194
-
195
- # Uninstall the app on the device.
196
- def uninstall
197
- installed_app_bundle = installed_app_bundle_dir
198
- if installed_app_bundle
199
- uninstall_app_and_sandbox(installed_app_bundle)
200
- :uninstalled
201
- else
202
- RunLoop.log_debug('App was not installed. Nothing to do')
203
- :not_installed
204
- end
205
- end
206
-
207
- # Install the app on the device.
208
- def install
209
- installed_app_bundle = installed_app_bundle_dir
210
-
211
- # App is not installed.
212
- return install_new_app if installed_app_bundle.nil?
213
-
214
- # App is installed but sha1 is different.
215
- if !same_sha1_as_installed?
216
- return reinstall_existing_app_and_clear_sandbox(installed_app_bundle)
217
- end
218
-
219
- RunLoop.log_debug('The installed app is the same as the app we are trying to install; skipping installation')
220
- installed_app_bundle
221
- end
222
-
223
- # @!visibility private
224
- #
225
- # 1. Does nothing if the app is not installed.
226
- # 2. Does nothing if the app the same as the app that is installed
227
- # 3. Installs app if it is different from the installed app
228
- #
229
- # TODO needs unit tests and a better name?
230
- def ensure_app_same
231
- installed_app_bundle = installed_app_bundle_dir
232
-
233
- if !installed_app_bundle
234
- RunLoop.log_debug("App: #{app} is not installed")
235
- return true
236
- end
237
-
238
- installed_sha = installed_app_sha1
239
- app_sha = app.sha1
240
-
241
- if installed_sha == app_sha
242
- RunLoop.log_debug("Installed app is the same as #{app}")
243
- return true
244
- end
245
-
246
- RunLoop.log_debug("The app you are trying to launch is not the same as the app that is installed.")
247
- RunLoop.log_debug(" Installed app SHA: #{installed_sha}")
248
- RunLoop.log_debug(" App to launch SHA: #{app_sha}")
249
- RunLoop.log_debug("Will install #{app}")
250
-
251
-
252
- FileUtils.rm_rf installed_app_bundle
253
- RunLoop.log_debug('Deleted the existing app')
254
-
255
- directory = File.expand_path(File.join(installed_app_bundle, '..'))
256
- bundle_name = File.basename(app.path)
257
- target = File.join(directory, bundle_name)
258
-
259
- args = ['ditto', app.path, target]
260
- RunLoop::Xcrun.new.exec(args, log_cmd: true)
261
-
262
- RunLoop.log_debug("Installed #{app} on CoreSimulator #{device.udid}")
263
-
264
- clear_device_launch_csstore
265
-
266
- true
267
- end
268
-
269
- # Reset app sandbox.
270
- def reset_app_sandbox
271
- return true if !app_is_installed?
272
-
273
- wait_for_device_state('Shutdown')
274
-
275
- reset_app_sandbox_internal
276
- end
277
-
278
- private
279
-
280
- def generate_uuid
281
- SecureRandom.uuid.upcase!
282
- end
283
-
284
- def existing_app_container_uuids
285
- if File.exist?(device_applications_dir)
286
- Dir.entries(device_applications_dir)
287
- else
288
- []
289
- end
290
- end
291
-
292
- def generate_unique_uuid(existing, timeout=1.0)
293
- begin
294
- Timeout::timeout(timeout, Timeout::Error) do
295
- uuid = generate_uuid
296
- loop do
297
- break if !existing.include?(uuid)
298
- uuid = generate_uuid
299
- end
300
- uuid
301
- end
302
- rescue Timeout::Error => _
303
- raise RuntimeError,
304
- "Expected to be able to generate a unique uuid in #{timeout} seconds"
305
- end
306
- end
307
-
308
- def install_new_app
309
- wait_for_device_state('Shutdown')
310
-
311
- existing = existing_app_container_uuids
312
- udid = generate_unique_uuid(existing)
313
- directory = File.join(device_applications_dir, udid)
314
-
315
- bundle_name = File.basename(app.path)
316
- target = File.join(directory, bundle_name)
317
-
318
- args = ['ditto', app.path, target]
319
- RunLoop::Xcrun.new.exec(args, log_cmd: true)
320
- target
321
- end
322
-
323
- def reinstall_existing_app_and_clear_sandbox(installed_app_bundle)
324
- wait_for_device_state('Shutdown')
325
-
326
- reset_app_sandbox_internal
327
-
328
- if File.exist?(installed_app_bundle)
329
- FileUtils.rm_rf(installed_app_bundle)
330
- RunLoop.log_debug("Deleted app bundle: #{installed_app_bundle}")
331
- end
332
-
333
- directory = File.dirname(installed_app_bundle)
334
- bundle_name = File.basename(app.path)
335
- target = File.join(directory, bundle_name)
336
-
337
- args = ['ditto', app.path, target]
338
- RunLoop::Xcrun.new.exec(args, log_cmd: true)
339
- installed_app_bundle
340
- end
341
-
342
- def uninstall_app_and_sandbox(installed_app_bundle)
343
- wait_for_device_state('Shutdown')
344
-
345
- if sdk_gte_8?
346
- # Must delete the sandbox first.
347
- directory = app_sandbox_dir
348
- if File.exist?(directory)
349
- FileUtils.rm_rf(directory)
350
- RunLoop.log_debug("Deleted app sandbox: #{directory}")
351
- end
352
-
353
- directory = File.dirname(installed_app_bundle)
354
- if File.exist?(directory)
355
- FileUtils.rm_rf(directory)
356
- RunLoop.log_debug("Deleted app container: #{directory}")
357
- end
358
- else
359
- # Sandbox _is_ in the container.
360
- directory = File.dirname(installed_app_bundle)
361
- if File.exist?(directory)
362
- FileUtils.rm_rf(directory)
363
- RunLoop.log_debug("Deleted app container: #{directory}")
364
- end
365
- end
366
- end
367
-
368
- # @!visibility private
369
- def app_sandbox_dir_sdk_gte_8
370
- containers_data_dir = File.join(device_data_dir, 'Containers', 'Data', 'Application')
371
- apps = Dir.glob("#{containers_data_dir}/**/#{METADATA_PLIST}")
372
- match = apps.find do |metadata_plist|
373
- pbuddy.plist_read('MCMMetadataIdentifier', metadata_plist) == app.bundle_identifier
374
- end
375
- if match
376
- File.dirname(match)
377
- else
378
- nil
379
- end
380
- end
381
-
382
- # @!visibility private
383
- def wait_for_device_state(target_state)
384
- now = Time.now
385
- timeout = WAIT_FOR_DEVICE_STATE_OPTS[:timeout]
386
- poll_until = now + timeout
387
- delay = WAIT_FOR_DEVICE_STATE_OPTS[:interval]
388
- in_state = false
389
- while Time.now < poll_until
390
- in_state = device.update_simulator_state == target_state
391
- break if in_state
392
- sleep delay
393
- end
394
-
395
- elapsed = Time.now - now
396
- RunLoop.log_debug("Waited for #{elapsed} seconds for device to have state: '#{target_state}'.")
397
-
398
- unless in_state
399
- raise "Expected '#{target_state} but found '#{device.state}' after waiting."
400
- end
401
- in_state
402
- end
403
-
404
- # @!visibility private
405
- def device_caches_dir
406
- @device_caches_dir ||= File.join(device_data_dir, 'Library', 'Caches')
407
- end
408
-
409
- # @!visibility private
410
- def clear_device_launch_csstore
411
- glob = File.join(device_caches_dir, "com.apple.LaunchServices-*.csstore")
412
- Dir.glob(glob) do | ccstore |
413
- FileUtils.rm_f ccstore
414
- end
415
- end
416
-
417
- # @!visibility private
418
- def reset_app_sandbox_internal_shared
419
- [app_documents_dir, app_tmp_dir].each do |dir|
420
- FileUtils.rm_rf dir
421
- FileUtils.mkdir dir
422
- end
423
- end
424
-
425
- # @!visibility private
426
- def reset_app_sandbox_internal_sdk_gte_8
427
- lib_dir = app_library_dir
428
- RunLoop::Directory.recursive_glob_for_entries(lib_dir).each do |entry|
429
- if entry.include?('Preferences')
430
- # nop
431
- else
432
- if File.exist?(entry)
433
- FileUtils.rm_rf(entry)
434
- end
435
- end
436
- end
437
-
438
- prefs_dir = app_library_preferences_dir
439
- protected = ['com.apple.UIAutomation.plist',
440
- 'com.apple.UIAutomationPlugIn.plist']
441
- RunLoop::Directory.recursive_glob_for_entries(prefs_dir).each do |entry|
442
- unless protected.include?(File.basename(entry))
443
- if File.exist?(entry)
444
- FileUtils.rm_rf entry
445
- end
446
- end
447
- end
448
- end
449
-
450
- # @!visibility private
451
- def reset_app_sandbox_internal_sdk_lt_8
452
- prefs_dir = app_library_preferences_dir
453
- RunLoop::Directory.recursive_glob_for_entries(prefs_dir).each do |entry|
454
- if entry.end_with?('.GlobalPreferences.plist') ||
455
- entry.end_with?('com.apple.PeoplePicker.plist')
456
- # nop
457
- else
458
- if File.exist?(entry)
459
- FileUtils.rm_rf entry
460
- end
461
- end
462
- end
463
-
464
- # app preferences lives in device Library/Preferences
465
- device_prefs_dir = File.join(app_sandbox_dir, 'Library', 'Preferences')
466
- app_prefs_plist = File.join(device_prefs_dir, "#{app.bundle_identifier}.plist")
467
- if File.exist?(app_prefs_plist)
468
- FileUtils.rm_rf(app_prefs_plist)
469
- end
470
- end
471
-
472
- # @!visibility private
473
- def reset_app_sandbox_internal
474
- reset_app_sandbox_internal_shared
475
-
476
- if sdk_gte_8?
477
- reset_app_sandbox_internal_sdk_gte_8
478
- else
479
- reset_app_sandbox_internal_sdk_lt_8
480
- end
481
- end
482
-
483
- # @!visibility private
484
- # For testing.
485
- def launch
486
-
487
- launch_simulator
488
-
489
- args = ['simctl', 'install', device.udid, app.path]
490
- RunLoop::Xcrun.new.exec(args, log_cmd: true, timeout: 10)
491
-
492
- device.simulator_wait_for_stable_state
493
-
494
- args = ['simctl', 'launch', device.udid, app.bundle_identifier]
495
- hash = RunLoop::Xcrun.new.exec(args, log_cmd: true, timeout: 20)
496
-
497
- exit_status = hash[:exit_status]
498
-
499
- if exit_status != 0
500
- err = hash[:err]
501
- RunLoop.log_error(err)
502
- raise RuntimeError, "Could not launch #{app.bundle_identifier} on #{device}"
503
- end
504
-
505
- RunLoop::ProcessWaiter.new(app.executable_name, {:timeout => 10,
506
- :raise_on_timeout => true}).wait_for_any
507
-
508
-
509
- device.simulator_wait_for_stable_state
510
-
511
- true
512
- end
513
- end
514
- end
515
- end