run_loop 2.0.10.pre1 → 2.1.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
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