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,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