run_loop 2.0.10.pre1 → 2.1.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7d2bb26dc2c4256fc698902883a2c1ab14e3fc64
4
- data.tar.gz: 1e52a50fa5717c9ad21afaaf51e4ae014b20ef27
3
+ metadata.gz: 7ea9c17ad6325b7381a689e16ea30c98b34e2765
4
+ data.tar.gz: a1f044d25c196213e310f2f70f096f35bb558c65
5
5
  SHA512:
6
- metadata.gz: e33dee1343def117171e6efc85d384400c9a67c9542cded949a856866c3411daaa053b435fc00031d14d04e7dcc0e89da223117ac863a7b17776973d5ce9069a
7
- data.tar.gz: 93e99e9e49920d2cf47fbd8021f2468e1cc74b653268708a95ee648626959de854cd2edb7ee2fb01db6d39aadd04b1d0ab5f1eac97c6525b3d82247c29ad6b18
6
+ metadata.gz: 49f63b7057fb4448850252365b840843b05a4ac39f81db367dd32a52d0b23dc0cdcb67c8de22e4838f02bfb81ef7f2bf3c189da4d4953a008f1812b6bd108326
7
+ data.tar.gz: 30aed60ffb8069e15260ea81f02fa8a2261a59e473fcd4beaeacfcec940030671c6eb730423d088f73864cfc66daf18972df60a0cfc671013ed61e7f3b8f293f
data/lib/run_loop/core.rb CHANGED
@@ -57,179 +57,24 @@ module RunLoop
57
57
  SCRIPTS[key]
58
58
  end
59
59
 
60
- def self.detect_connected_device
61
- begin
62
- Timeout::timeout(1, RunLoop::TimeoutError) do
63
- return `#{File.join(scripts_path, 'udidetect')}`.chomp
64
- end
65
- rescue RunLoop::TimeoutError => _
66
- `killall udidetect &> /dev/null`
67
- end
68
- nil
69
- end
70
-
71
- # @deprecated 1.5.2 No public replacement.
72
- #
73
- # Raise an error if the application binary is not compatible with the
74
- # target simulator.
75
- #
76
- # @note This method is implemented for CoreSimulator environments only;
77
- # for Xcode < 6.0 this method does nothing.
78
- #
79
- # @param [Hash] launch_options These options need to contain the app bundle
80
- # path and a udid that corresponds to a simulator name or simulator udid.
81
- # In practical terms: call this after merging the original launch
82
- # options with those options that are discovered.
83
- #
84
- # @param [RunLoop::SimControl] sim_control A simulator control object.
85
- # @raise [RuntimeError] Raises an error if the `launch_options[:udid]`
86
- # cannot be used to find a simulator.
87
- # @raise [RunLoop::IncompatibleArchitecture] Raises an error if the
88
- # application binary is not compatible with the target simulator.
89
- def self.expect_compatible_simulator_architecture(launch_options, sim_control)
90
- RunLoop.deprecated('1.5.2', 'No public replacement.')
91
- logger = launch_options[:logger]
92
- if sim_control.xcode_version_gte_6?
93
- sim_identifier = launch_options[:udid]
94
- simulator = sim_control.simulators.find do |simulator|
95
- [simulator.instruments_identifier(sim_control.xcode),
96
- simulator.udid].include?(sim_identifier)
97
- end
98
-
99
- if simulator.nil?
100
- raise "Could not find simulator with identifier '#{sim_identifier}'"
101
- end
102
-
103
- lipo = RunLoop::Lipo.new(launch_options[:bundle_dir_or_bundle_id])
104
- lipo.expect_compatible_arch(simulator)
105
- RunLoop::Logging.log_debug(logger, "Simulator instruction set '#{simulator.instruction_set}' is compatible with #{lipo.info}")
106
- true
107
- else
108
- RunLoop::Logging.log_debug(logger, "Xcode #{sim_control.xcode_version} detected; skipping simulator architecture check.")
109
- false
110
- end
111
- end
112
-
113
- # Raise an error if the application binary is not compatible with the
114
- # target simulator.
115
- #
116
- # @note This method is implemented for CoreSimulator environments only;
117
- # for Xcode < 6.0 this method does nothing.
118
- #
119
- # @param [RunLoop::Device] device The device to install on.
120
- # @param [RunLoop::App] app The app to install.
121
- # @param [RunLoop::Xcode] xcode The active Xcode.
122
- #
123
- # @raise [RunLoop::IncompatibleArchitecture] Raises an error if the
124
- # application binary is not compatible with the target simulator.
125
- def self.expect_simulator_compatible_arch(device, app, xcode)
126
- if !xcode.version_gte_6?
127
- RunLoop.log_warn("Checking for compatible arches is only available in CoreSimulator environments")
128
- return
129
- end
130
-
131
- lipo = RunLoop::Lipo.new(app.path)
132
- lipo.expect_compatible_arch(device)
133
-
134
- RunLoop.log_debug("Simulator instruction set '#{device.instruction_set}' is compatible with '#{lipo.info}'")
135
- end
136
-
137
- # Prepares the simulator for running.
138
- #
139
- # 1. enabling accessibility and software keyboard
140
- # 2. installing / uninstalling apps
141
- def self.prepare_simulator(launch_options, sim_control)
142
-
143
- xcode = sim_control.xcode
144
-
145
- # Respect option passed from Calabash
146
- if launch_options[:relaunch_simulator]
147
- sim_control.quit_sim
148
- end
149
-
150
- if !xcode.version_gte_6?
151
- # Xcode 5.1.1
152
-
153
- # Will quit the simulator!
154
- sim_control.enable_accessibility_on_sims({:verbose => false})
155
- else
156
-
157
- # CoreSimulator
158
-
159
- app_bundle_path = launch_options[:bundle_dir_or_bundle_id]
160
- app = RunLoop::App.new(app_bundle_path)
161
-
162
- unless app.valid?
163
- if !File.exist?(app.path)
164
- message = "App '#{app_bundle_path}' does not exist."
165
- else
166
- message = "App '#{app_bundle_path}' is not a valid .app bundle"
167
- end
168
- raise RuntimeError, message
169
- end
170
-
171
- udid = launch_options[:udid]
172
-
173
- device = sim_control.simulators.find do |sim|
174
- sim.udid == udid || sim.instruments_identifier(xcode) == udid
175
- end
176
-
177
- if device.nil?
178
- raise RuntimeError,
179
- "Could not find simulator with name or UDID that matches: '#{udid}'"
180
- end
181
-
182
- # Validate the architecture.
183
- self.expect_simulator_compatible_arch(device, app, xcode)
184
-
185
- # Quits the simulator.
186
- core_sim = RunLoop::CoreSimulator.new(device, app)
187
-
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
192
-
193
- # Will quit the simulator if it is running.
194
- # @todo fix accessibility_enabled? so we don't have to quit the sim
195
- # SimControl#accessibility_enabled? is always false during Core#prepare_simulator
196
- # https://github.com/calabash/run_loop/issues/167
197
- sim_control.ensure_accessibility(device)
198
-
199
- # Will quit the simulator if it is running.
200
- # @todo fix software_keyboard_enabled? so we don't have to quit the sim
201
- # SimControl#software_keyboard_enabled? is always false during Core#prepare_simulator
202
- # https://github.com/calabash/run_loop/issues/168
203
- sim_control.ensure_software_keyboard(device)
204
-
205
- # Launches the simulator if the app is not installed.
206
- core_sim.install
207
-
208
- # If CoreSimulator has already launched the simulator, it will not
209
- # launching it again.
210
- core_sim.launch_simulator
211
- end
212
- end
213
-
214
60
  def self.run_with_options(options)
215
61
  before = Time.now
216
62
 
217
63
  self.prepare(options)
218
64
 
219
65
  logger = options[:logger]
220
- sim_control ||= options[:sim_control] || RunLoop::SimControl.new
66
+ sim_control = options[:sim_control] || options[:simctl] || RunLoop::SimControl.new
67
+ xcode = options[:xcode] || RunLoop::Xcode.new
68
+ instruments = options[:instruments] || RunLoop::Instruments.new
221
69
 
222
- xcode ||= options[:xcode] || sim_control.xcode
70
+ # Find the Device under test, the App under test, UIA strategy, and reset options
71
+ device = RunLoop::Device.detect_device(options, xcode, sim_control, instruments)
72
+ app_details = RunLoop::DetectAUT.detect_app_under_test(options)
73
+ uia_strategy = self.detect_uia_strategy(options, device, xcode)
74
+ reset_options = self.detect_reset_options(options)
223
75
 
224
- instruments = RunLoop::Instruments.new
225
76
  instruments.kill_instruments(xcode)
226
77
 
227
- device_target = options[:udid] || options[:device_target] || detect_connected_device || 'simulator'
228
- if device_target && device_target.to_s.downcase == 'device'
229
- device_target = detect_connected_device
230
- end
231
-
232
- log_file = options[:log_path]
233
78
  timeout = options[:timeout] || 30
234
79
 
235
80
  results_dir = options[:results_dir] || RunLoop::DotDir.make_results_dir
@@ -253,8 +98,6 @@ module RunLoop
253
98
  repl_path = File.join(results_dir, 'repl-cmd.pipe')
254
99
  FileUtils.rm_f(repl_path)
255
100
 
256
- uia_strategy = options[:uia_strategy]
257
-
258
101
  if uia_strategy == :host
259
102
  create_uia_pipe(repl_path)
260
103
  else
@@ -271,39 +114,33 @@ module RunLoop
271
114
  file.puts code
272
115
  end
273
116
 
274
- udid = options[:udid]
275
- bundle_dir_or_bundle_id = options[:bundle_dir_or_bundle_id]
276
-
277
- if !(udid && bundle_dir_or_bundle_id)
278
- # Compute udid and bundle_dir / bundle_id from options and target depending on Xcode version
279
- udid, bundle_dir_or_bundle_id = self.udid_and_bundle_for_launcher(device_target, options, sim_control)
280
- end
281
-
282
117
  args = options.fetch(:args, [])
283
118
 
284
- log_file ||= File.join(results_dir, 'run_loop.out')
119
+ log_file = options[:log_path] || File.join(results_dir, 'run_loop.out')
285
120
 
286
121
  discovered_options =
287
- {
288
- :udid => udid,
289
- :results_dir_trace => results_dir_trace,
290
- :bundle_dir_or_bundle_id => bundle_dir_or_bundle_id,
291
- :results_dir => results_dir,
292
- :script => script,
293
- :log_file => log_file,
294
- :args => args
295
- }
122
+ {
123
+ :udid => device.udid,
124
+ :device => device,
125
+ :results_dir_trace => results_dir_trace,
126
+ :bundle_id => app_details[:bundle_id],
127
+ :app => app_details[:app] || app_details[:bundle_id],
128
+ :results_dir => results_dir,
129
+ :script => script,
130
+ :log_file => log_file,
131
+ :args => args
132
+ }
296
133
  merged_options = options.merge(discovered_options)
297
134
 
298
- if self.simulator_target?(merged_options)
299
- self.prepare_simulator(merged_options, sim_control)
135
+ if device.simulator?
136
+ self.prepare_simulator(app_details[:app], device, xcode, sim_control, reset_options)
300
137
  end
301
138
 
302
139
  self.log_run_loop_options(merged_options, xcode)
303
140
 
304
141
  automation_template = automation_template(instruments)
305
142
 
306
- RunLoop::Logging.log_header(logger, "Starting on #{device_target} App: #{bundle_dir_or_bundle_id}")
143
+ RunLoop::Logging.log_header(logger, "Starting on #{device.name} App: #{app_details[:bundle_id]}")
307
144
 
308
145
  pid = instruments.spawn(automation_template, merged_options, log_file)
309
146
 
@@ -311,14 +148,16 @@ module RunLoop
311
148
  f.write pid
312
149
  end
313
150
 
314
- run_loop = {:pid => pid,
315
- :index => 1,
316
- :uia_strategy => uia_strategy,
317
- :udid => udid,
318
- :app => bundle_dir_or_bundle_id,
319
- :repl_path => repl_path,
320
- :log_file => log_file,
321
- :results_dir => results_dir}
151
+ run_loop = {
152
+ :pid => pid,
153
+ :index => 1,
154
+ :uia_strategy => uia_strategy,
155
+ :udid => device.udid,
156
+ :app => app_details[:bundle_id],
157
+ :repl_path => repl_path,
158
+ :log_file => log_file,
159
+ :results_dir => results_dir
160
+ }
322
161
 
323
162
  uia_timeout = options[:uia_timeout] || RunLoop::Environment.uia_timeout || 10
324
163
 
@@ -326,8 +165,10 @@ module RunLoop
326
165
 
327
166
  before_instruments_launch = Time.now
328
167
 
329
- fifo_retry_on = [RunLoop::Fifo::NoReaderConfiguredError,
330
- RunLoop::Fifo::WriteTimedOut]
168
+ fifo_retry_on = [
169
+ RunLoop::Fifo::NoReaderConfiguredError,
170
+ RunLoop::Fifo::WriteTimedOut
171
+ ]
331
172
 
332
173
  begin
333
174
 
@@ -364,9 +205,9 @@ module RunLoop
364
205
 
365
206
  Logfile: #{log_file}
366
207
 
367
- #{File.read(log_file)}
208
+ #{File.read(log_file)}
368
209
 
369
- )
210
+ )
370
211
  raise RunLoop::TimeoutError, message
371
212
  end
372
213
 
@@ -375,8 +216,11 @@ Logfile: #{log_file}
375
216
  dylib_path = self.dylib_path_from_options(merged_options)
376
217
 
377
218
  if dylib_path
378
- RunLoop::LLDB.kill_lldb_processes
379
- app = RunLoop::App.new(options[:app])
219
+ if device.physical_device?
220
+ raise RuntimeError, "Injecting a dylib is not supported when targeting a device"
221
+ end
222
+
223
+ app = app_details[:app]
380
224
  lldb = RunLoop::DylibInjector.new(app.executable_name, dylib_path)
381
225
  lldb.retriable_inject_dylib
382
226
  end
@@ -395,7 +239,7 @@ Logfile: #{log_file}
395
239
  def self.include_calabash_script?(options)
396
240
 
397
241
  if (options[:include_calabash_script] == false) || options[:dismiss_immediate_dialogs]
398
- return false
242
+ return false
399
243
  end
400
244
  if Core.script_for_key(:run_loop_basic) == options[:script]
401
245
  return options[:include_calabash_script]
@@ -403,164 +247,71 @@ Logfile: #{log_file}
403
247
  true
404
248
  end
405
249
 
406
- # @!visibility private
407
- # Are we targeting a simulator?
408
- #
409
- # @note The behavior of this method is different than the corresponding
410
- # method in Calabash::Cucumber::Launcher method. If
411
- # `:device_target => {nil | ''}`, then the calabash-ios method returns
412
- # _false_. I am basing run-loop's behavior off the behavior in
413
- # `self.udid_and_bundle_for_launcher`
250
+ # Extracts the value of :inject_dylib from options Hash.
251
+ # @param options [Hash] arguments passed to {RunLoop.run}
252
+ # @return [String, nil] If the options contains :inject_dylibs and it is a
253
+ # path to a dylib that exists, return the path. Otherwise return nil or
254
+ # raise an error.
255
+ # @raise [RuntimeError] If :inject_dylib points to a path that does not exist.
256
+ # @raise [ArgumentError] If :inject_dylib is not a String.
257
+ def self.dylib_path_from_options(options)
258
+ inject_dylib = options.fetch(:inject_dylib, nil)
259
+ return nil if inject_dylib.nil?
260
+ unless inject_dylib.is_a? String
261
+ raise ArgumentError, "Expected :inject_dylib to be a path to a dylib, but found '#{inject_dylib}'"
262
+ end
263
+ dylib_path = File.expand_path(inject_dylib)
264
+ unless File.exist?(dylib_path)
265
+ raise "Cannot load dylib. The file '#{dylib_path}' does not exist."
266
+ end
267
+ dylib_path
268
+ end
269
+
270
+ # Returns the a default simulator to target. This default needs to be one
271
+ # that installed by default in the current Xcode version.
414
272
  #
415
- # @see {Core::RunLoop.udid_and_bundle_for_launcher}
273
+ # For historical reasons, the most recent non-64b SDK should be used.
416
274
  #
417
- # @todo sim_control argument is no longer necessary and can be removed.
418
- def self.simulator_target?(run_options, sim_control=nil)
419
- value = run_options[:device_target]
420
-
421
- # Match the behavior of udid_and_bundle_for_launcher.
422
- return true if value.nil? or value == ''
423
-
424
- # 5.1 <= Xcode < 7.0
425
- return true if value.downcase.include?('simulator')
426
-
427
- # Not a physical device.
428
- return false if value[DEVICE_UDID_REGEX, 0] != nil
429
-
430
- # Check for named simulators and Xcode >= 7.0 simulators.
431
- sim_control = run_options[:sim_control] || RunLoop::SimControl.new
432
- xcode = sim_control.xcode
433
- if xcode.version_gte_6?
434
- simulator = sim_control.simulators.find do |sim|
435
- [
436
- sim.instruments_identifier(xcode) == value,
437
- sim.udid == value,
438
- sim.name == value
439
- ].any?
440
- end
441
- !simulator.nil?
275
+ # @param [RunLoop::Xcode] xcode Used to detect the current xcode
276
+ # version.
277
+ def self.default_simulator(xcode=RunLoop::Xcode.new)
278
+
279
+ if xcode.version_gte_73?
280
+ "iPhone 6s (9.3)"
281
+ elsif xcode.version_gte_72?
282
+ "iPhone 6s (9.2)"
283
+ elsif xcode.version_gte_71?
284
+ "iPhone 6s (9.1)"
285
+ elsif xcode.version_gte_7?
286
+ "iPhone 5s (9.0)"
287
+ elsif xcode.version_gte_64?
288
+ "iPhone 5s (8.4 Simulator)"
289
+ elsif xcode.version_gte_63?
290
+ "iPhone 5s (8.3 Simulator)"
291
+ elsif xcode.version_gte_62?
292
+ "iPhone 5s (8.2 Simulator)"
293
+ elsif xcode.version_gte_61?
294
+ "iPhone 5s (8.1 Simulator)"
442
295
  else
443
- false
296
+ "iPhone 5s (8.0 Simulator)"
444
297
  end
445
298
  end
446
299
 
447
300
 
448
- # Extracts the value of :inject_dylib from options Hash.
449
- # @param options [Hash] arguments passed to {RunLoop.run}
450
- # @return [String, nil] If the options contains :inject_dylibs and it is a
451
- # path to a dylib that exists, return the path. Otherwise return nil or
452
- # raise an error.
453
- # @raise [RuntimeError] If :inject_dylib points to a path that does not exist.
454
- # @raise [ArgumentError] If :inject_dylib is not a String.
455
- def self.dylib_path_from_options(options)
456
- inject_dylib = options.fetch(:inject_dylib, nil)
457
- return nil if inject_dylib.nil?
458
- unless inject_dylib.is_a? String
459
- raise ArgumentError, "Expected :inject_dylib to be a path to a dylib, but found '#{inject_dylib}'"
460
- end
461
- dylib_path = File.expand_path(inject_dylib)
462
- unless File.exist?(dylib_path)
463
- raise "Cannot load dylib. The file '#{dylib_path}' does not exist."
464
- end
465
- dylib_path
466
- end
467
-
468
- # Returns the a default simulator to target. This default needs to be one
469
- # that installed by default in the current Xcode version.
470
- #
471
- # For historical reasons, the most recent non-64b SDK should be used.
472
- #
473
- # @param [RunLoop::Xcode] xcode Used to detect the current xcode
474
- # version.
475
- def self.default_simulator(xcode=RunLoop::Xcode.new)
476
-
477
- if xcode.version_gte_73?
478
- "iPhone 6s (9.3)"
479
- elsif xcode.version_gte_72?
480
- "iPhone 6s (9.2)"
481
- elsif xcode.version_gte_71?
482
- "iPhone 6s (9.1)"
483
- elsif xcode.version_gte_7?
484
- "iPhone 5s (9.0)"
485
- elsif xcode.version_gte_64?
486
- "iPhone 5s (8.4 Simulator)"
487
- elsif xcode.version_gte_63?
488
- "iPhone 5s (8.3 Simulator)"
489
- elsif xcode.version_gte_62?
490
- "iPhone 5s (8.2 Simulator)"
491
- elsif xcode.version_gte_61?
492
- "iPhone 5s (8.1 Simulator)"
493
- elsif xcode.version_gte_6?
494
- "iPhone 5s (8.0 Simulator)"
495
- else
496
- "iPhone Retina (4-inch) - Simulator - iOS 7.1"
497
- end
498
- end
499
-
500
- def self.udid_and_bundle_for_launcher(device_target, options, sim_control=RunLoop::SimControl.new)
501
- xcode = sim_control.xcode
502
-
503
- bundle_dir_or_bundle_id = options[:app] || RunLoop::Environment.bundle_id || RunLoop::Environment.path_to_app_bundle
504
-
505
- unless bundle_dir_or_bundle_id
506
- raise 'key :app or environment variable APP_BUNDLE_PATH, BUNDLE_ID or APP must be specified as path to app bundle (simulator) or bundle id (device)'
507
- end
508
-
509
- udid = nil
510
-
511
- if xcode.version_gte_51?
512
- if device_target.nil? || device_target.empty? || device_target == 'simulator'
513
- device_target = self.default_simulator(xcode)
514
- end
515
- udid = device_target
516
-
517
- unless self.simulator_target?(options)
518
- bundle_dir_or_bundle_id = options[:bundle_id] if options[:bundle_id]
519
- end
520
- else
521
- #TODO: this can be removed - Xcode < 5.1.1 not supported.
522
- if device_target == 'simulator'
523
-
524
- unless File.exist?(bundle_dir_or_bundle_id)
525
- raise "Unable to find app in directory #{bundle_dir_or_bundle_id} when trying to launch simulator"
526
- end
527
-
528
-
529
- device = options[:device] || :iphone
530
- device = device && device.to_sym
531
-
532
- plistbuddy='/usr/libexec/PlistBuddy'
533
- plistfile="#{bundle_dir_or_bundle_id}/Info.plist"
534
- if device == :iphone
535
- uidevicefamily=1
536
- else
537
- uidevicefamily=2
538
- end
539
- system("#{plistbuddy} -c 'Delete :UIDeviceFamily' '#{plistfile}'")
540
- system("#{plistbuddy} -c 'Add :UIDeviceFamily array' '#{plistfile}'")
541
- system("#{plistbuddy} -c 'Add :UIDeviceFamily:0 integer #{uidevicefamily}' '#{plistfile}'")
542
- else
543
- udid = device_target
544
- bundle_dir_or_bundle_id = options[:bundle_id] if options[:bundle_id]
545
- end
546
- end
547
- return udid, bundle_dir_or_bundle_id
548
- end
549
-
550
- def self.create_uia_pipe(repl_path)
551
- begin
552
- Timeout::timeout(5, RunLoop::TimeoutError) do
553
- loop do
554
- begin
555
- FileUtils.rm_f(repl_path)
556
- return repl_path if system(%Q[mkfifo "#{repl_path}"])
557
- rescue Errno::EINTR => e
558
- #retry
559
- sleep(0.1)
301
+ def self.create_uia_pipe(repl_path)
302
+ begin
303
+ Timeout::timeout(5, RunLoop::TimeoutError) do
304
+ loop do
305
+ begin
306
+ FileUtils.rm_f(repl_path)
307
+ return repl_path if system(%Q[mkfifo "#{repl_path}"])
308
+ rescue Errno::EINTR => e
309
+ #retry
310
+ sleep(0.1)
311
+ end
560
312
  end
561
313
  end
562
- end
563
- rescue RunLoop::TimeoutError => _
314
+ rescue RunLoop::TimeoutError => _
564
315
  raise RunLoop::TimeoutError, 'Unable to create pipe (mkfifo failed)'
565
316
  end
566
317
  end
@@ -581,7 +332,7 @@ Logfile: #{log_file}
581
332
  RunLoop::Fifo.write(repl_path, cmd_str)
582
333
  write_succeeded = validate_index_written(run_loop, index, logger)
583
334
  rescue RunLoop::Fifo::NoReaderConfiguredError,
584
- RunLoop::Fifo::WriteTimedOut => e
335
+ RunLoop::Fifo::WriteTimedOut => e
585
336
  RunLoop::Logging.log_debug(logger, "Error while writing command (retry count #{i}). #{e}")
586
337
  end
587
338
  break if write_succeeded
@@ -744,13 +495,91 @@ Logfile: #{log_file}
744
495
  end
745
496
 
746
497
  message = ['Expected instruments to report an Automation tracetemplate.',
747
- 'Please report this as bug: https://github.com/calabash/run_loop/issues',
748
- "In the bug report, include the output of:\n",
749
- '$ xcrun xcodebuild -version',
750
- "$ xcrun instruments -s templates\n"]
498
+ 'Please report this as bug: https://github.com/calabash/run_loop/issues',
499
+ "In the bug report, include the output of:\n",
500
+ '$ xcrun xcodebuild -version',
501
+ "$ xcrun instruments -s templates\n"]
751
502
  raise message.join("\n")
752
503
  end
753
504
 
505
+ # @deprecated 2.1.0
506
+ # Replaced with Device.detect_physical_device_on_usb
507
+ def self.detect_connected_device
508
+ begin
509
+ Timeout::timeout(1, RunLoop::TimeoutError) do
510
+ return `#{File.join(scripts_path, 'udidetect')}`.chomp
511
+ end
512
+ rescue RunLoop::TimeoutError => _
513
+ `killall udidetect &> /dev/null`
514
+ end
515
+ nil
516
+ end
517
+
518
+ # @deprecated 2.1.0
519
+ # @!visibility private
520
+ # Are we targeting a simulator?
521
+ #
522
+ # @note The behavior of this method is different than the corresponding
523
+ # method in Calabash::Cucumber::Launcher method. If
524
+ # `:device_target => {nil | ''}`, then the calabash-ios method returns
525
+ # _false_. I am basing run-loop's behavior off the behavior in
526
+ # `self.udid_and_bundle_for_launcher`
527
+ #
528
+ # @see {Core::RunLoop.udid_and_bundle_for_launcher}
529
+ #
530
+ # @todo sim_control argument is no longer necessary and can be removed.
531
+ def self.simulator_target?(run_options, sim_control=nil)
532
+ # TODO Enable deprecation warning
533
+ # RunLoop.deprecated("2.1.0", "No replacement")
534
+ value = run_options[:device_target]
535
+
536
+ # Match the behavior of udid_and_bundle_for_launcher.
537
+ return true if value.nil? or value == ''
538
+
539
+ # 5.1 <= Xcode < 7.0
540
+ return true if value.downcase.include?('simulator')
541
+
542
+ # Not a physical device.
543
+ return false if value[DEVICE_UDID_REGEX, 0] != nil
544
+
545
+ # Check for named simulators and Xcode >= 7.0 simulators.
546
+ sim_control = run_options[:sim_control] || RunLoop::SimControl.new
547
+ xcode = sim_control.xcode
548
+ simulator = sim_control.simulators.find do |sim|
549
+ [
550
+ sim.instruments_identifier(xcode) == value,
551
+ sim.udid == value,
552
+ sim.name == value
553
+ ].any?
554
+ end
555
+ !simulator.nil?
556
+ end
557
+
558
+ # @!visibility private
559
+ # @deprecated 2.1.0
560
+ #
561
+ # Do not call this method.
562
+ def self.udid_and_bundle_for_launcher(device_target, options, sim_control=RunLoop::SimControl.new)
563
+ RunLoop.deprecated("2.1.0", "No replacement")
564
+ xcode = sim_control.xcode
565
+
566
+ bundle_dir_or_bundle_id = options[:app] || RunLoop::Environment.bundle_id || RunLoop::Environment.path_to_app_bundle
567
+
568
+ unless bundle_dir_or_bundle_id
569
+ raise 'key :app or environment variable APP_BUNDLE_PATH, BUNDLE_ID or APP must be specified as path to app bundle (simulator) or bundle id (device)'
570
+ end
571
+
572
+ if device_target.nil? || device_target.empty? || device_target == 'simulator'
573
+ device_target = self.default_simulator(xcode)
574
+ end
575
+ udid = device_target
576
+
577
+ unless self.simulator_target?(options)
578
+ bundle_dir_or_bundle_id = options[:bundle_id] if options[:bundle_id]
579
+ end
580
+ return udid, bundle_dir_or_bundle_id
581
+ end
582
+
754
583
  # @deprecated 1.0.5
755
584
  def self.ensure_instruments_not_running!
756
585
  RunLoop::Instruments.new.kill_instruments
@@ -794,10 +623,106 @@ Logfile: #{log_file}
794
623
 
795
624
  private
796
625
 
626
+ # @!visibility private
627
+ #
628
+ # @param [Hash] options The launch options passed to .run_with_options
797
629
  def self.prepare(run_options)
798
630
  RunLoop::DotDir.rotate_result_directories
799
631
  RunLoop::Instruments.rotate_cache_directories
800
632
  true
801
633
  end
634
+
635
+ # @!visibility private
636
+ #
637
+ # @param [RunLoop::Device] device The device under test.
638
+ # @param [RunLoop::Xcode] xcode The active Xcode
639
+ def self.default_uia_strategy(device, xcode)
640
+ if xcode.version_gte_7?
641
+ :host
642
+ elsif device.physical_device? && device.version >= RunLoop::Version.new("8.0")
643
+ :host
644
+ else
645
+ :preferences
646
+ end
647
+ end
648
+
649
+ # @!visibility private
650
+ #
651
+ # @param [Hash] options The launch options passed to .run_with_options
652
+ # @param [RunLoop::Device] device The device under test.
653
+ # @param [RunLoop::Xcode] xcode The active Xcode.
654
+ def self.detect_uia_strategy(options, device, xcode)
655
+ strategy = options[:uia_strategy] || self.default_uia_strategy(device, xcode)
656
+
657
+ if ![:host, :preferences, :shared_element].include?(strategy)
658
+ raise ArgumentError,
659
+ "Invalid strategy: expected '#{strategy}' to be :host, :preferences, or :shared_element"
660
+ end
661
+ strategy
662
+ end
663
+
664
+ # @!visibility private
665
+ #
666
+ # @param [Hash] options The launch options passed to .run_with_options
667
+ def self.detect_reset_options(options)
668
+ return options[:reset] if options.has_key?(:reset)
669
+
670
+ return options[:reset_app_sandbox] if options.has_key?(:reset_app_sandbox)
671
+
672
+ RunLoop::Environment.reset_between_scenarios?
673
+ end
674
+
675
+ # Prepares the simulator for running.
676
+ #
677
+ # 1. enabling accessibility and software keyboard
678
+ # 2. installing / uninstalling apps
679
+ def self.prepare_simulator(app, device, xcode, simctl, reset_options)
680
+
681
+ # Validate the architecture.
682
+ self.expect_simulator_compatible_arch(device, app)
683
+
684
+ # Quits the simulator.
685
+ core_sim = RunLoop::CoreSimulator.new(device, app, :xcode => xcode)
686
+
687
+ # Calabash 0.x can only reset the app sandbox (true/false).
688
+ # Calabash 2.x has advanced reset options.
689
+ if reset_options
690
+ core_sim.reset_app_sandbox
691
+ end
692
+
693
+ # Will quit the simulator if it is running.
694
+ # @todo fix accessibility_enabled? so we don't have to quit the sim
695
+ # SimControl#accessibility_enabled? is always false during Core#prepare_simulator
696
+ # https://github.com/calabash/run_loop/issues/167
697
+ simctl.ensure_accessibility(device)
698
+
699
+ # Will quit the simulator if it is running.
700
+ # @todo fix software_keyboard_enabled? so we don't have to quit the sim
701
+ # SimControl#software_keyboard_enabled? is always false during Core#prepare_simulator
702
+ # https://github.com/calabash/run_loop/issues/168
703
+ simctl.ensure_software_keyboard(device)
704
+
705
+ # Launches the simulator if the app is not installed.
706
+ core_sim.install
707
+
708
+ # If CoreSimulator has already launched the simulator, it will not launch it again.
709
+ core_sim.launch_simulator
710
+ end
711
+
712
+ # @!visibility private
713
+ # Raise an error if the application binary is not compatible with the
714
+ # target simulator.
715
+ #
716
+ # @param [RunLoop::Device] device The device to install on.
717
+ # @param [RunLoop::App] app The app to install.
718
+ #
719
+ # @raise [RunLoop::IncompatibleArchitecture] Raises an error if the
720
+ # application binary is not compatible with the target simulator.
721
+ def self.expect_simulator_compatible_arch(device, app)
722
+ lipo = RunLoop::Lipo.new(app.path)
723
+ lipo.expect_compatible_arch(device)
724
+
725
+ RunLoop.log_debug("Simulator instruction set '#{device.instruction_set}' is compatible with '#{lipo.info}'")
726
+ end
802
727
  end
803
728
  end
@@ -508,11 +508,9 @@ $ bundle exec run-loop simctl manage-processes
508
508
  def sim_name
509
509
  @sim_name ||= lambda {
510
510
  if xcode.version_gte_7?
511
- 'Simulator'
512
- elsif xcode.version_gte_6?
513
- 'iOS Simulator'
511
+ "Simulator"
514
512
  else
515
- 'iPhone Simulator'
513
+ "iOS Simulator"
516
514
  end
517
515
  }.call
518
516
  end
@@ -527,10 +525,8 @@ $ bundle exec run-loop simctl manage-processes
527
525
  dev_dir = xcode.developer_dir
528
526
  if xcode.version_gte_7?
529
527
  "#{dev_dir}/Applications/Simulator.app"
530
- elsif xcode.version_gte_6?
531
- "#{dev_dir}/Applications/iOS Simulator.app"
532
528
  else
533
- "#{dev_dir}/Platforms/iPhoneSimulator.platform/Developer/Applications/iPhone Simulator.app"
529
+ "#{dev_dir}/Applications/iOS Simulator.app"
534
530
  end
535
531
  }.call
536
532
  end
@@ -3,6 +3,24 @@ module RunLoop
3
3
  # @!visibility private
4
4
  module DetectAUT
5
5
 
6
+ # @!visibility private
7
+ def self.detect_app_under_test(options)
8
+ app = self.detect_app(options)
9
+ if app.is_a?(RunLoop::App) || app.is_a?(RunLoop::Ipa)
10
+ {
11
+ :app => app,
12
+ :bundle_id => app.bundle_identifier,
13
+ :is_ipa => app.is_a?(RunLoop::Ipa)
14
+ }
15
+ else
16
+ {
17
+ :app => nil,
18
+ :bundle_id => app,
19
+ :is_ipa => false
20
+ }
21
+ end
22
+ end
23
+
6
24
  # @!visibility private
7
25
  class Detect
8
26
  include RunLoop::DetectAUT::Errors
@@ -103,5 +121,59 @@ module RunLoop
103
121
  end
104
122
  end
105
123
  end
124
+
125
+ private
126
+
127
+ # @!visibility private
128
+ def self.app_from_options(options)
129
+ options[:app] || options[:bundle_id]
130
+ end
131
+
132
+ # @!visibility private
133
+ def self.app_from_environment
134
+ RunLoop::Environment.path_to_app_bundle ||
135
+ RunLoop::Environment.bundle_id
136
+ end
137
+
138
+ # @!visibility private
139
+ def self.app_from_constant
140
+ (defined?(APP_BUNDLE_PATH) && APP_BUNDLE_PATH) ||
141
+ (defined?(APP) && APP)
142
+ end
143
+
144
+ # @!visibility private
145
+ def self.app_from_opts_or_env_or_constant(options)
146
+ self.app_from_options(options) ||
147
+ self.app_from_environment ||
148
+ self.app_from_constant
149
+ end
150
+
151
+ # @!visibility private
152
+ def self.detector
153
+ RunLoop::DetectAUT::Detect.new
154
+ end
155
+
156
+ # @!visibility private
157
+ def self.detect_app(options)
158
+ app = self.app_from_opts_or_env_or_constant(options)
159
+
160
+ # Options or constant defined an instance of App or Ipa
161
+ return app if app && (app.is_a?(RunLoop::App) || app.is_a?(RunLoop::Ipa))
162
+
163
+ # User provided no information, so we attempt to auto detect
164
+ if app.nil? || app == ""
165
+ return self.detector.app_for_simulator
166
+ end
167
+
168
+ extension = File.extname(app)
169
+ if extension == ".ipa" && File.exist?(app)
170
+ RunLoop::Ipa.new(app)
171
+ elsif extension == ".app" && File.exist?(app)
172
+ RunLoop::App.new(app)
173
+ else
174
+ # Probably a bundle identifier.
175
+ app
176
+ end
177
+ end
106
178
  end
107
179
  end
@@ -128,6 +128,41 @@ Please update your sources.))
128
128
  raise ArgumentError, "Could not find a device with a UDID or name matching '#{udid_or_name}'"
129
129
  end
130
130
 
131
+ # @!visibility private
132
+ #
133
+ # Please don't call this method. It is for internal use only. The behavior
134
+ # may change at any time! You have been warned.
135
+ #
136
+ # @param [Hash] options The launch options passed to RunLoop::Core
137
+ # @param [RunLoop::Xcode] xcode An Xcode instance
138
+ # @param [RunLoop::SimControl] simctl A SimControl instance
139
+ # @param [RunLoop::Instruments] instruments An Instruments instance
140
+ #
141
+ # @raise [ArgumentError] If "device" is detected as the device target and
142
+ # there is no matching device.
143
+ # @raise [ArgumentError] If DEVICE_TARGET or options specify an identifier
144
+ # that does not match an iOS Simulator or phyiscal device.
145
+ def self.detect_device(options, xcode, simctl, instruments)
146
+ device = self.device_from_opts_or_env(options)
147
+
148
+ # Passed an instance of RunLoop::Device
149
+ return device if device && device.is_a?(RunLoop::Device)
150
+
151
+ # Need to infer what what the user wants from the environment and options.
152
+ if device == "device"
153
+ identifier = self.detect_physical_device_on_usb
154
+ self.ensure_physical_device_connected(identifier, options)
155
+ elsif device.nil? || device == "" || device == "simulator"
156
+ identifier = RunLoop::Core.default_simulator(xcode)
157
+ else
158
+ identifier = device
159
+ end
160
+
161
+ # Raises ArgumentError if no matching device can be found.
162
+ self.device_with_identifier(identifier, sim_control: simctl,
163
+ instruments: instruments)
164
+ end
165
+
131
166
  # @!visibility private
132
167
  def to_s
133
168
  if simulator?
@@ -162,10 +197,8 @@ Please update your sources.))
162
197
 
163
198
  if xcode.version_gte_7?
164
199
  "#{name} (#{version_part})"
165
- elsif xcode.version_gte_6?
166
- "#{name} (#{version_part} Simulator)"
167
200
  else
168
- udid
201
+ "#{name} (#{version_part} Simulator)"
169
202
  end
170
203
  end
171
204
  end
@@ -173,13 +206,13 @@ Please update your sources.))
173
206
  # Is this a physical device?
174
207
  # @return [Boolean] Returns true if this is a device.
175
208
  def physical_device?
176
- not udid[DEVICE_UDID_REGEX, 0].nil?
209
+ !udid[DEVICE_UDID_REGEX, 0].nil?
177
210
  end
178
211
 
179
212
  # Is this a simulator?
180
213
  # @return [Boolean] Returns true if this is a simulator.
181
214
  def simulator?
182
- not physical_device?
215
+ !physical_device?
183
216
  end
184
217
 
185
218
  # Return the instruction set for this device.
@@ -572,5 +605,78 @@ Please update your sources.))
572
605
 
573
606
  # TODO Is this a good idea? It speeds up rspec tests by a factor of ~2x...
574
607
  SIM_CONTROL = RunLoop::SimControl.new
608
+
609
+ # @!visibility private
610
+ def self.device_from_options(options)
611
+ options[:device] || options[:device_target] || options[:udid]
612
+ end
613
+
614
+ # @!visibility private
615
+ def self.device_from_environment
616
+ RunLoop::Environment.device_target
617
+ end
618
+
619
+ # @!visibility private
620
+ def self.device_from_opts_or_env(options)
621
+ self.device_from_options(options) || self.device_from_environment
622
+ end
623
+
624
+ # @!visibility private
625
+ UDID_DETECT = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", 'scripts', "udidetect"))
626
+
627
+ # @!visibility private
628
+ def self.detect_physical_device_on_usb
629
+ require "command_runner"
630
+
631
+ udid = nil
632
+ begin
633
+ hash = CommandRunner.run([UDID_DETECT], timeout: 1)
634
+ udid = hash[:out].chomp
635
+ if udid == ""
636
+ udid = nil
637
+ end
638
+ rescue => e
639
+ RunLoop.log_debug("Running `udidetect` raised: #{e}")
640
+ ensure
641
+ `killall udidetect &> /dev/null`
642
+ end
643
+ udid
644
+ end
645
+
646
+ # @!visibility private
647
+ def self.ensure_physical_device_connected(identifier, options)
648
+ if identifier.nil?
649
+ env = self.device_from_environment
650
+ if env == "device"
651
+ message = "DEVICE_TARGET=device means that you want to test on physical device"
652
+ elsif env && env[DEVICE_UDID_REGEX, 0]
653
+ message = "DEVICE_TARGET=#{env} did not match any connected device"
654
+ else
655
+ if options[:device]
656
+ key = ":device"
657
+ elsif options[:device_target]
658
+ key = ":device_target"
659
+ else
660
+ key = ":udid"
661
+ end
662
+ message = "#{key} => \"device\" means that you want to test on a physical device"
663
+ end
664
+
665
+ raise ArgumentError, %Q[Expected a physical device to be connected via USB.
666
+
667
+ #{message}
668
+
669
+ 1. Is your device connected?
670
+ 2. Does your device appear in the output of `xcrun instruments -s devices`?
671
+ 3. Does your device appear in Xcode > Windows > Devices without a warning message?
672
+
673
+ Please see the documentation about testing on physical devices.
674
+
675
+ https://github.com/calabash/calabash-ios/wiki/Testing-on-Physical-Devices
676
+ ]
677
+ end
678
+ true
679
+ end
575
680
  end
576
681
  end
682
+
@@ -48,6 +48,11 @@ module RunLoop
48
48
  end
49
49
  end
50
50
 
51
+ # Should the app data be reset between Scenarios?
52
+ def self.reset_between_scenarios?
53
+ ENV["RESET_BETWEEN_SCENARIOS"] == "1"
54
+ end
55
+
51
56
  # Returns the value of XCODEPROJ which can be used to specify an Xcode
52
57
  # project directory (my.xcodeproj).
53
58
  #
@@ -198,20 +198,14 @@ module RunLoop
198
198
  @instruments_templates ||= lambda do
199
199
  args = ['instruments', '-s', 'templates']
200
200
  hash = xcrun.exec(args, log_cmd: true)
201
- if xcode.version_gte_6?
202
- hash[:out].chomp.split("\n").map do |elm|
203
- stripped = elm.strip.tr('"', '')
204
- if stripped == '' || stripped == 'Known Templates:'
205
- nil
206
- else
207
- stripped
208
- end
209
- end.compact
210
- else
211
- hash[:out].strip.split("\n").delete_if do |path|
212
- not path =~ /tracetemplate/
213
- end.map { |elm| elm.strip }
214
- end
201
+ hash[:out].chomp.split("\n").map do |elm|
202
+ stripped = elm.strip.tr('"', '')
203
+ if stripped == '' || stripped == 'Known Templates:'
204
+ nil
205
+ else
206
+ stripped
207
+ end
208
+ end.compact
215
209
  end.call
216
210
  end
217
211
 
@@ -306,29 +300,7 @@ module RunLoop
306
300
  array = ['instruments']
307
301
  array << '-w'
308
302
 
309
- # Xcode 7 simulators must be launched with UDID to avoid
310
- # Ambiguous device name/identifier errors (from instruments)
311
- if xcode.version_gte_7?
312
- udid = options[:udid]
313
-
314
- if udid[DEVICE_UDID_REGEX, 0]
315
- array << udid
316
- else
317
- match = simulators.find do |simulator|
318
- [simulator.name == udid,
319
- simulator.udid == udid,
320
- simulator.instruments_identifier(xcode) == udid].any?
321
- end
322
-
323
- if match
324
- array << match.udid
325
- else
326
- array << udid
327
- end
328
- end
329
- else
330
- array << options[:udid]
331
- end
303
+ array << options[:udid]
332
304
 
333
305
  trace = options[:results_dir_trace]
334
306
  if trace
@@ -339,7 +311,7 @@ module RunLoop
339
311
  array << '-t'
340
312
  array << automation_template
341
313
 
342
- array << options[:bundle_dir_or_bundle_id]
314
+ array << options[:bundle_id]
343
315
 
344
316
  {
345
317
  'UIARESULTSPATH' => options[:results_dir],
data/lib/run_loop/l10n.rb CHANGED
@@ -70,31 +70,7 @@ module RunLoop
70
70
  end
71
71
 
72
72
  def uikit_bundle_l10n_path
73
- developer_dir = xcode.developer_dir
74
- if !developer_dir
75
- nil
76
- else
77
- if xcode.version_gte_6?
78
- File.join(developer_dir, UIKIT_AXBUNDLE_PATH_CORE_SIM)
79
- else
80
- ['7.1', '7.0', '6.1'].map do |sdk|
81
- path = axbundle_path_for_sdk(developer_dir, sdk)
82
- if File.exist?(path)
83
- path
84
- else
85
- nil
86
- end
87
- end.compact.first
88
- end
89
- end
90
- end
91
-
92
- # Xcode 5.1.1 path.
93
- def axbundle_path_for_sdk(developer_dir, sdk)
94
- File.join(developer_dir,
95
- 'Platforms/iPhoneSimulator.platform/Developer/SDKs',
96
- "iPhoneSimulator#{sdk}.sdk",
97
- 'System/Library/AccessibilityBundles/UIKit.axbundle')
73
+ File.join(xcode.developer_dir, UIKIT_AXBUNDLE_PATH_CORE_SIM)
98
74
  end
99
75
 
100
76
  def is_full_name?(two_letter_country_code)
@@ -62,7 +62,7 @@ module RunLoop
62
62
  # .log_info is already taken by the XTC logger. (>_O)
63
63
  # green
64
64
  def self.log_info2(msg)
65
- puts self.green(" INFO: #{msg}") if msg
65
+ puts self.green("INFO: #{msg}") if msg
66
66
  end
67
67
 
68
68
  # red
@@ -37,7 +37,9 @@ module RunLoop
37
37
  end
38
38
 
39
39
  # @!visibility private
40
+ # @deprecated 2.1.0
40
41
  def xcode_version_gte_51?
42
+ RunLoop.deprecated("2.1.0", "No replacement.")
41
43
  xcode.version_gte_51?
42
44
  end
43
45
 
@@ -401,10 +403,6 @@ module RunLoop
401
403
  end
402
404
 
403
405
  def software_keyboard_enabled?(device)
404
- unless xcode_version_gte_51?
405
- raise RuntimeError, 'Keyboard enabling is only available on Xcode >= 6'
406
- end
407
-
408
406
  plist = device.simulator_preferences_plist_path
409
407
  return false unless File.exist?(plist)
410
408
 
@@ -1,5 +1,5 @@
1
1
  module RunLoop
2
- VERSION = "2.0.10.pre1"
2
+ VERSION = "2.1.0.pre1"
3
3
 
4
4
  # A model of a software release version that can be used to compare two versions.
5
5
  #
@@ -175,10 +175,12 @@ module RunLoop
175
175
  version >= v60
176
176
  end
177
177
 
178
+ # @deprecated 2.1.0
178
179
  # Is the active Xcode version 5.1 or above?
179
180
  #
180
181
  # @return [Boolean] `true` if the current Xcode version is >= 5.1
181
182
  def version_gte_51?
183
+ RunLoop.deprecated("2.1.0", "No replacement")
182
184
  version >= v51
183
185
  end
184
186
 
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: 2.0.10.pre1
4
+ version: 2.1.0.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: 2016-03-17 00:00:00.000000000 Z
11
+ date: 2016-03-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json