Class: Calabash::Cucumber::Launcher

Inherits:
Object
  • Object
show all
Includes:
Logging, SimulatorAccessibility
Defined in:
lib/calabash-cucumber/launcher.rb

Defined Under Namespace

Classes: CalabashLauncherTimeoutErr, StartError

Constant Summary collapse

KNOWN_PRIVACY_SETTINGS =
{:photos => 'kTCCServicePhotos', :calendar => 'kTCCServiceCalendar', :address_book => 'kTCCServiceAddressBook'}
SERVER_VERSION_NOT_AVAILABLE =
'0.0.0'
@@launcher =
nil
@@server_version =
nil

Constants included from Logging

Calabash::Cucumber::Logging::CALABASH_NO_DEPRECATION

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from SimulatorAccessibility

#accessibility_properties_hash, #enable_accessibility_in_sdk_dir, #enable_accessibility_on_simulators, #existing_simulator_support_sdk_dirs, #launch_simulator, #plist_path_with_sdk_dir, #possible_simulator_sdks, #possible_simulator_support_sdk_dirs, #quit_simulator, #reset_simulator_content_and_settings, #simulator_app_support_dir

Methods included from PlistBuddy

#build_plist_cmd, #execute_plist_cmd, #plist_buddy, #plist_key_exists?, #plist_read, #plist_set

Methods included from XcodeTools

#installed_simulators, #instruments, #instruments_supports_hyphen_s?, #xcode_bin_dir, #xcode_developer_dir

Methods included from Logging

#_deprecated, #calabash_info, #calabash_warn, #debug_logging?, #full_console_logging?, #no_deprecation_warnings?

Constructor Details

#initializeLauncher

Returns a new instance of Launcher.



47
48
49
50
# File 'lib/calabash-cucumber/launcher.rb', line 47

def initialize
  @simulator_launcher = Calabash::Cucumber::SimulatorLauncher.new
  @@launcher = self
end

Instance Attribute Details

#actionsObject

Returns the value of attribute actions.



27
28
29
# File 'lib/calabash-cucumber/launcher.rb', line 27

def actions
  @actions
end

#deviceObject

Returns the value of attribute device.



26
27
28
# File 'lib/calabash-cucumber/launcher.rb', line 26

def device
  @device
end

#launch_argsObject

Returns the value of attribute launch_args.



28
29
30
# File 'lib/calabash-cucumber/launcher.rb', line 28

def launch_args
  @launch_args
end

#run_loopObject

Returns the value of attribute run_loop.



25
26
27
# File 'lib/calabash-cucumber/launcher.rb', line 25

def run_loop
  @run_loop
end

#simulator_launcherObject

Returns the value of attribute simulator_launcher.



29
30
31
# File 'lib/calabash-cucumber/launcher.rb', line 29

def simulator_launcher
  @simulator_launcher
end

Class Method Details

.attachObject



57
58
59
60
61
# File 'lib/calabash-cucumber/launcher.rb', line 57

def self.attach
  l = launcher
  return l if l && l.active?
  l.attach
end

.instruments?Boolean

Returns:

  • (Boolean)


98
99
100
101
102
# File 'lib/calabash-cucumber/launcher.rb', line 98

def self.instruments?
  l = launcher_if_used
  return false unless l
  l.instruments?
end

.launcherObject



104
105
106
# File 'lib/calabash-cucumber/launcher.rb', line 104

def self.launcher
  @@launcher ||= Calabash::Cucumber::Launcher.new
end

.launcher_if_usedObject



108
109
110
# File 'lib/calabash-cucumber/launcher.rb', line 108

def self.launcher_if_used
  @@launcher
end

Instance Method Details

#active?Boolean

Returns:

  • (Boolean)


579
580
581
# File 'lib/calabash-cucumber/launcher.rb', line 579

def active?
  not run_loop.nil?
end

#app_pathObject



571
572
573
# File 'lib/calabash-cucumber/launcher.rb', line 571

def app_path
  ENV['APP_BUNDLE_PATH'] || (defined?(APP_BUNDLE_PATH) && APP_BUNDLE_PATH) || ENV['APP']
end

#attach(max_retry = 1, timeout = 10) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/calabash-cucumber/launcher.rb', line 63

def attach(max_retry=1, timeout=10)
  if calabash_no_launch?
    self.actions= Calabash::Cucumber::PlaybackActions.new
    return
  end

  pids_str = `ps x -o pid,command | grep -v grep | grep "instruments" | awk '{printf "%s,", $1}'`
  pids = pids_str.split(',').map { |pid| pid.to_i }
  pid = pids.first
  rl = {}
  if pid
    rl[:pid] = pid
    self.actions= Calabash::Cucumber::InstrumentsActions.new
  else
    self.actions= Calabash::Cucumber::PlaybackActions.new
  end

  self.run_loop= rl

  ensure_connectivity(max_retry, timeout)

  major = self.device.ios_major_version
  if major.to_i >= 7 && self.actions.is_a?(Calabash::Cucumber::PlaybackActions)
    puts "\n\n WARNING \n\n"
    puts 'Warning Trying to connect to simulator that was not launched by Calabash/instruments.'
    puts 'To fix this you must let Calabash or instruments launch the app'
    puts 'Continuing... query et al will work.'
    puts "\n\n WARNING \n\n"
    puts "Please read: https://github.com/calabash/calabash-ios/wiki/A0-UIAutomation---instruments-problems"
  end


  self
end

#calabash_no_launch?Boolean

Returns:

  • (Boolean)


537
538
539
# File 'lib/calabash-cucumber/launcher.rb', line 537

def calabash_no_launch?
  ENV['NO_LAUNCH']=='1'
end

#calabash_no_stop?Boolean

Returns:

  • (Boolean)


533
534
535
# File 'lib/calabash-cucumber/launcher.rb', line 533

def calabash_no_stop?
  calabash_no_launch? or ENV['NO_STOP']=="1"
end

#calabash_notify(world) ⇒ Object



508
509
510
511
512
# File 'lib/calabash-cucumber/launcher.rb', line 508

def calabash_notify(world)
  if world.respond_to?(:on_launch)
    world.on_launch
  end
end

#check_server_gem_compatibilitynil

checks the server and gem version compatibility and generates a warning if the server and gem are not compatible.

WIP: this is a proof-of-concept implementation and requires strict equality. in the future we should allow minimum framework compatibility.

Returns:

  • (nil)

    nothing to return



664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
# File 'lib/calabash-cucumber/launcher.rb', line 664

def check_server_gem_compatibility
  app_bundle_path = self.launch_args[:app]
  if File.directory?(app_bundle_path)
    server_version = server_version_from_bundle(app_bundle_path)
  else
    server_version = server_version_from_server
  end

  if server_version == SERVER_VERSION_NOT_AVAILABLE
    calabash_warn('server version could not be found - skipping compatibility check')
    return nil
  end

  server_version = Calabash::Cucumber::Version.new(server_version)
  gem_version = Calabash::Cucumber::Version.new(Calabash::Cucumber::VERSION)
  min_server_version = Calabash::Cucumber::Version.new(Calabash::Cucumber::MIN_SERVER_VERSION)

  if server_version < min_server_version
    msgs = []
    msgs << 'server version is not compatible with gem version'
    msgs << 'please update your server and gem'
    msgs << "       gem version: '#{gem_version}'"
    msgs << "min server version: '#{min_server_version}'"
    msgs << "    server version: '#{server_version}'"

    calabash_warn("#{msgs.join("\n")}")
  else
    if full_console_logging?
      calabash_info("gem #{gem_version} is compat with '#{server_version}'")
    end
  end
  nil
end

#default_launch_argsObject



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/calabash-cucumber/launcher.rb', line 210

def default_launch_args
  # APP_BUNDLE_PATH
  # BUNDLE_ID
  # APP (unifies APP_BUNDLE_PATH, BUNDLE_ID)
  # DEVICE_TARGET
  # SDK_VERSION
  # RESET_BETWEEN_SCENARIOS
  # DEVICE
  # NO_LAUNCH
  # NO_STOP

  args = {
      :launch_method => default_launch_method,
      :reset => reset_between_scenarios?,
      :bundle_id => ENV['BUNDLE_ID'],
      :device => device_env,
      :no_stop => calabash_no_stop?,
      :no_launch => calabash_no_launch?,
      :sdk_version => sdk_version,
      # do not advertise this to users!
      # for example, don't include documentation about this
      # this is used to instrument internal testing
      :launch_retries => 5
  }

  device_tgt = ENV['DEVICE_TARGET']
  if run_with_instruments?(args)
    if simulator_target?
      args[:device_target] = device_tgt
      args[:udid] = nil
    else
      if detect_connected_device? && (device_tgt.nil? || device_tgt.downcase == 'device')
        device_tgt = RunLoop::Core.detect_connected_device
      end

      if device_tgt
        args[:device_target] = args[:udid] = device_tgt
      end
    end
  end

  if args[:device_target].nil?
    args[:device_target] = 'simulator'
  end
  args
end

#default_launch_methodObject



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/calabash-cucumber/launcher.rb', line 275

def default_launch_method
  sdk = sdk_version
  major = nil
  if sdk && !sdk.strip.empty?
    major = sdk.split('.')[0]
    begin
      major = major.to_i
    rescue
      calabash_warn("SDK_VERSION invalid #{sdk_version} - ignoring...")
    end
  end
  return :instruments if major && major >= 7 # Only instruments supported for iOS7+
  return :sim_launcher if major # and then we have <= 6

  if RunLoop::Core.above_or_eql_version?('5.1', RunLoop::Core.xcode_version)
    return use_sim_launcher_env? ? :sim_launcher : :instruments
  end

  available = self.simulator_launcher.sdk_detector.available_sdk_versions.reject { |v| v.start_with?('7') }
  if available.include?(sdk_version)
    :sim_launcher
  else
    :instruments
  end
end

#detect_app_bundle_from_args(args) ⇒ Object

todo this method should be migrated to the Simulator Launcher



387
388
389
390
391
392
393
394
395
# File 'lib/calabash-cucumber/launcher.rb', line 387

def detect_app_bundle_from_args(args)
  if simulator_target?(args)
    device_xamarin_build_dir = 'iPhoneSimulator'
  else
    device_xamarin_build_dir = 'iPhone'
  end
  # is this really only applicable to the Xamarin IDE?
  self.simulator_launcher.detect_app_bundle(nil, device_xamarin_build_dir)
end

#detect_bundle_idObject



523
524
525
526
527
528
529
530
531
# File 'lib/calabash-cucumber/launcher.rb', line 523

def detect_bundle_id
  begin
    bundle_path = self.simulator_launcher.app_bundle_or_raise(app_path)
    plist_path = File.join(bundle_path, 'Info.plist')
    info_plist_as_hash(plist_path)['CFBundleIdentifier']
  rescue => e
    raise "Unable to automatically find bundle id. Please set BUNDLE_ID environment variable. #{e}"
  end
end

#detect_bundle_id_from_app_bundle(args) ⇒ Object



397
398
399
400
401
402
403
404
405
406
407
# File 'lib/calabash-cucumber/launcher.rb', line 397

def detect_bundle_id_from_app_bundle(args)
  if args[:app] && File.directory?(args[:app])
    # Derive bundle id from bundle_dir
    plist_as_hash = info_plist_from_bundle_path(args[:app])
    if plist_as_hash
      plist_as_hash['CFBundleIdentifier']
    end
  else
    args[:app]
  end
end

#detect_connected_device?Boolean

Returns:

  • (Boolean)


257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/calabash-cucumber/launcher.rb', line 257

def detect_connected_device?
  if ENV['DETECT_CONNECTED_DEVICE'] == '1'
    return true
  end

  if ENV['BUNDLE_ID'].nil? && ENV['DETECT_CONNECTED_DEVICE'].nil?
    return false
  end
  if ENV['BUNDLE_ID'] && ENV['DETECT_CONNECTED_DEVICE'].nil?
    return true
  end
  if ENV['DETECT_CONNECTED_DEVICE']
    return ENV['DETECT_CONNECTED_DEVICE'] != '0'
  end

  return false
end

#detect_device_from_args(args) ⇒ Object



364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/calabash-cucumber/launcher.rb', line 364

def detect_device_from_args(args)
  if args[:app] && File.directory?(args[:app])
    # Derive bundle id from bundle_dir
    plist_as_hash = info_plist_from_bundle_path(args[:app])
    if plist_as_hash
      device_family = plist_as_hash['UIDeviceFamily']
      if device_family
        first_device = device_family.first
        if first_device == 2
          return 'ipad'
        else
          return 'iphone'
        end
      end
    end
  else
    args[:app]
  end


end

#device_envObject



567
568
569
# File 'lib/calabash-cucumber/launcher.rb', line 567

def device_env
  ENV['DEVICE']
end

#device_target?Boolean

Returns:

  • (Boolean)


541
542
543
# File 'lib/calabash-cucumber/launcher.rb', line 541

def device_target?
  (ENV['DEVICE_TARGET'] != nil) && (not simulator_target?)
end

#directories_for_sdk_prefix(sdk) ⇒ Object



145
146
147
# File 'lib/calabash-cucumber/launcher.rb', line 145

def directories_for_sdk_prefix(sdk)
  Dir["#{ENV['HOME']}/Library/Application Support/iPhone Simulator/#{sdk}*"]
end

#ensure_connectivity(max_retry = 10, timeout = 30) ⇒ Object



444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
# File 'lib/calabash-cucumber/launcher.rb', line 444

def ensure_connectivity(max_retry=10, timeout=30)
  begin
    max_retry_count = (ENV['MAX_CONNECT_RETRY'] || max_retry).to_i
    timeout = (ENV['CONNECT_TIMEOUT'] || timeout).to_i
    retry_count = 0
    connected = false
    if full_console_logging?
      puts 'Waiting for App to be ready'
    end
    until connected do
      raise "MAX_RETRIES" if retry_count == max_retry_count
      retry_count += 1
      begin
        Timeout::timeout(timeout, CalabashLauncherTimeoutErr) do
          until connected
            begin
              connected = (ping_app == '200')
              break if connected
            rescue Exception => e
              #p e
              #retry
            ensure
              sleep 1 unless connected
            end
          end
        end
      rescue CalabashLauncherTimeoutErr => e
        puts "Timed out...Retry.."
      end
    end
  rescue RuntimeError => e
    p e
    msg = "Unable to make connection to Calabash Server at #{ENV['DEVICE_ENDPOINT']|| "http://localhost:37265/"}\n"
    msg << "Make sure you don't have a firewall blocking traffic to #{ENV['DEVICE_ENDPOINT']|| "http://localhost:37265/"}.\n"
    raise msg
  end
end

#info_plist_as_hash(plist_path) ⇒ Object



515
516
517
518
519
520
521
# File 'lib/calabash-cucumber/launcher.rb', line 515

def info_plist_as_hash(plist_path)
  unless File.exist?(plist_path)
    raise "Unable to find Info.plist: #{plist_path}"
  end
  parsedplist = CFPropertyList::List.new(:file => plist_path)
  CFPropertyList.native_types(parsedplist.value)
end

#info_plist_from_bundle_path(bundle_path) ⇒ Object



409
410
411
412
# File 'lib/calabash-cucumber/launcher.rb', line 409

def info_plist_from_bundle_path(bundle_path)
  plist_path = File.join(bundle_path, 'Info.plist')
  info_plist_as_hash(plist_path) if File.exist?(plist_path)
end

#inspectObject



587
588
589
590
591
592
593
# File 'lib/calabash-cucumber/launcher.rb', line 587

def inspect
  msg = ["#{self.class}: Launch Method #{launch_args && launch_args[:launch_method]}"]
  if run_with_instruments?(self.launch_args) && self.run_loop
    msg << "Log file: #{self.run_loop[:log_file]}"
  end
  msg.join("\n")
end

#instruments?Boolean

Returns:

  • (Boolean)


583
584
585
# File 'lib/calabash-cucumber/launcher.rb', line 583

def instruments?
  !!(active? && run_loop[:pid])
end

#ios_major_versionObject



112
113
114
115
116
117
118
# File 'lib/calabash-cucumber/launcher.rb', line 112

def ios_major_version
  # pinging the app will set self.device
  ping_app if self.device.nil?
  # guard against Runtime errors
  return nil if device.nil? or device.ios_version.nil?
  device.ios_major_version
end

#ios_versionObject



120
121
122
123
# File 'lib/calabash-cucumber/launcher.rb', line 120

def ios_version
  return nil if device.nil?
  device.ios_version
end

#new_run_loop(args) ⇒ Object

Raises:



414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/calabash-cucumber/launcher.rb', line 414

def new_run_loop(args)

  # for stability, quit the simulator if Xcode version is > 5.1 and the
  # target device is the simulator
  target_is_sim = simulator_target?(args)
  xcode_gte_51 = RunLoop::Core.above_or_eql_version?('5.1', RunLoop::Core.xcode_version)
  if target_is_sim and xcode_gte_51
    self.simulator_launcher.stop
  end

  last_err = nil

  num_retries = args[:launch_retries] || 5

  num_retries.times do
    begin
      return RunLoop.run(args)
    rescue RunLoop::TimeoutError => e
      last_err = e
      if full_console_logging?
        puts 'retrying run loop...'
      end
      self.simulator_launcher.stop
    end
  end
  self.simulator_launcher.stop
  puts "Unable to start. Make sure you've set APP_BUNDLE_PATH to a build supported by this simulator version"
  raise StartError.new(last_err)
end

#ping_appObject



482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
# File 'lib/calabash-cucumber/launcher.rb', line 482

def ping_app
  url = URI.parse(ENV['DEVICE_ENDPOINT']|| "http://localhost:37265/")

  http = Net::HTTP.new(url.host, url.port)
  res = http.start do |sess|
    sess.request Net::HTTP::Get.new(ENV['CALABASH_VERSION_PATH'] || "version")
  end
  status = res.code
  begin
    http.finish if http and http.started?
  rescue Exception => e

  end

  if status == '200'
    version_body = JSON.parse(res.body)
    self.device = Calabash::Cucumber::Device.new(url, version_body)
  end

  status
end

#privacy_setting(sdk_dir, bundle_id, setting_name) ⇒ Object



201
202
203
204
205
206
207
208
# File 'lib/calabash-cucumber/launcher.rb', line 201

def privacy_setting(sdk_dir, bundle_id, setting_name)
  setting_name = KNOWN_PRIVACY_SETTINGS[setting_name] || setting_name
  path_to_tcc_db = tcc_database_for_sdk_dir(sdk_dir)
  sql = %Q['SELECT allowed FROM access WHERE client="#{bundle_id}" and service="#{setting_name}";']
  output = `sqlite3 "#{path_to_tcc_db}" #{sql}`.strip

  (output == '0' || output == '1') ? output.to_i : nil
end

#relaunch(args = {}) ⇒ Object



301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/calabash-cucumber/launcher.rb', line 301

def relaunch(args={})
  RunLoop.stop(run_loop) if run_loop

  args = default_launch_args.merge(args)

  args[:app] = args[:app] || args[:bundle_id] || app_path || detect_app_bundle_from_args(args)


  if args[:app]
    if File.directory?(args[:app])
      args[:app] = File.expand_path(args[:app])
    else
      # args[:app] is not a directory so must be a bundle id
      if simulator_target?(args) ## bundle id set, but simulator target
        args[:app] = app_path || detect_app_bundle_from_args(args)
      end
    end
  end

  unless args[:app]
    if simulator_target?(args)
      device_xamarin_build_dir = 'iPhoneSimulator'
    else
      device_xamarin_build_dir = 'iPhone'
    end
    args[:app] = self.simulator_launcher.app_bundle_or_raise(app_path, device_xamarin_build_dir)
  end

  args[:bundle_id] ||= detect_bundle_id_from_app_bundle(args)

  args[:device] ||= detect_device_from_args(args)


  reset_app_jail if args[:reset]


  if args[:privacy_settings]
    if simulator_target?(args)
      update_privacy_settings(args[:bundle_id], args[:privacy_settings])
    else
      #Not supported on device
      puts 'Warning: :privacy_settings not supported on device'
    end
  end

  if simulator_target?(args)
    enable_accessibility_on_simulators
  end

  if run_with_instruments?(args)
    self.run_loop = new_run_loop(args)
    self.actions= Calabash::Cucumber::InstrumentsActions.new
  else
    # run with sim launcher
    self.actions= Calabash::Cucumber::PlaybackActions.new
    # why not just pass args - AFAICT args[:app] == app_path?
    self.simulator_launcher.relaunch(app_path, sdk_version(), args)
  end
  self.launch_args = args
  ensure_connectivity
  check_server_gem_compatibility
end

#reset_app_jail(sdk = nil, path = nil) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/calabash-cucumber/launcher.rb', line 125

def reset_app_jail(sdk=nil, path=nil)
  sdk ||= sdk_version || self.simulator_launcher.sdk_detector.latest_sdk_version
  path ||= self.simulator_launcher.app_bundle_or_raise(app_path)

  app = File.basename(path)
  directories_for_sdk_prefix(sdk).each do |dir|
    bundle = `find "#{dir}/Applications" -type d -depth 2 -name "#{app}" | head -n 1`
    next if bundle.empty? # Assuming we're already clean
    if debug_logging?
      puts "Reset app state for #{bundle}"
    end
    sandbox = File.dirname(bundle)
    ['Library', 'Documents', 'tmp'].each do |dir|
      FileUtils.rm_rf(File.join(sandbox, dir))
    end
  end


end

#reset_between_scenarios?Boolean

Returns:

  • (Boolean)


563
564
565
# File 'lib/calabash-cucumber/launcher.rb', line 563

def reset_between_scenarios?
  ENV['RESET_BETWEEN_SCENARIOS']=="1"
end

#run_with_instruments?(args) ⇒ Boolean

Returns:

  • (Boolean)


575
576
577
# File 'lib/calabash-cucumber/launcher.rb', line 575

def run_with_instruments?(args)
  args && args[:launch_method] == :instruments
end

#sdk_versionObject



551
552
553
# File 'lib/calabash-cucumber/launcher.rb', line 551

def sdk_version
  ENV['SDK_VERSION']
end

#server_version_from_bundle(app_bundle_path) ⇒ String

extracts server version from the app binary at app_bundle_path by inspecting the binary’s strings table.

SPECIAL: sets the @@server_version class variable to cache the server version because the server version will never change during runtime.

Parameters:

  • app_bundle_path (String)

    file path (usually) to the application bundle

Returns:

  • (String)

    the server version

Raises:

  • (RuntimeError)

    if there is no executable at app_bundle_path

  • (RuntimeError)

    if the server version cannot be extracted from any binary at app_bundle_path



606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
# File 'lib/calabash-cucumber/launcher.rb', line 606

def server_version_from_bundle(app_bundle_path)
  return @@server_version unless @@server_version.nil?
  exe_paths = []
  Dir.foreach(app_bundle_path) do |item|
    next if item == '.' or item == '..'

    full_path = File.join(app_bundle_path, item)
    if File.executable?(full_path) and not File.directory?(full_path)
      exe_paths << full_path
    end
  end

  if exe_paths.empty?
    calabash_warn "could not find executable in '#{app_bundle_path}'"

    @@server_version = SERVER_VERSION_NOT_AVAILABLE
    return @@server_version
  end

  server_version = nil
  exe_paths.each do |path|
    server_version_string = `strings #{path} | grep -E 'CALABASH VERSION'`.chomp!
    if server_version_string
      server_version = server_version_string.split(' ').last
      break
    end
  end

  unless server_version
    calabash_warn('could not find server version by inspecting the binary strings table')

    @@server_version = SERVER_VERSION_NOT_AVAILABLE
    return @@server_version
  end

  @@server_version = server_version
end

#server_version_from_serverString

queries the server for its version.

SPECIAL: sets the @@server_version class variable to cache the server version because the server version will never change during runtime.

Returns:

  • (String)

    the server version

Raises:

  • (RuntimeError)

    if the server cannot be reached



651
652
653
654
655
# File 'lib/calabash-cucumber/launcher.rb', line 651

def server_version_from_server
  return @@server_version unless @@server_version.nil?
  ensure_connectivity if self.device == nil
  @@server_version = self.device.server_version
end

#simulator_target?(launch_args = {}) ⇒ Boolean

Returns:

  • (Boolean)


545
546
547
548
549
# File 'lib/calabash-cucumber/launcher.rb', line 545

def simulator_target?(launch_args={})
  value = ENV['DEVICE_TARGET'] || launch_args[:device_target]
  return false if value.nil?
  value.downcase.include?('simulator')
end

#stopObject



504
505
506
# File 'lib/calabash-cucumber/launcher.rb', line 504

def stop
  RunLoop.stop(run_loop) if run_loop && run_loop[:pid]
end

#tcc_database_for_sdk_dir(dir) ⇒ Object



197
198
199
# File 'lib/calabash-cucumber/launcher.rb', line 197

def tcc_database_for_sdk_dir(dir)
  File.join(dir,'Library', 'TCC', 'TCC.db')
end

#update_privacy_settings(bundle_id, opts = {}) ⇒ Object

Call as update_privacy_settings(‘com.my.app’, => {:allow => true})



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/calabash-cucumber/launcher.rb', line 150

def update_privacy_settings(bundle_id, opts={})
  if debug_logging?
    puts "Update privacy settings #{bundle_id}, #{opts}"
  end
  unless File.exist?(`which sqlite3`.strip)
    raise 'Error: Unable to find sqlite3. The binary sqlite3 must be installed and on path.'
  end
  opts.each do |setting_name, setting_options|

    setting_name = KNOWN_PRIVACY_SETTINGS[setting_name] || setting_name
    allow = setting_options[:allow] == false ? false : true
    sdk = setting_options[:sdk] || self.simulator_launcher.sdk_detector.latest_sdk_version

    dirs = directories_for_sdk_prefix(sdk)
    if debug_logging?
      puts "About to update privacy setting #{setting_name} for #{bundle_id}, allow: #{allow} in sdk #{sdk}, #{dirs}"
    end

    dirs.each do |dir|
      if debug_logging?
        puts "Setting access for #{bundle_id} for permission #{setting_name} to allow: #{allow}"
      end
      path_to_tcc_db = tcc_database_for_sdk_dir(dir)
      unless File.exist?(path_to_tcc_db)
        puts "Warning: No TCC.db in location #{path_to_tcc_db}"
        next
      end
      allowed_as_i = allow ? 1 : 0
      if privacy_setting(dir, bundle_id,setting_name).nil?
        sql = %Q['INSERT INTO access (service, client, client_type, allowed, prompt_count) VALUES ("#{setting_name}","#{bundle_id}",0,#{allowed_as_i},1);']
      else
        sql = %Q['UPDATE access SET allowed=#{allowed_as_i} where client="#{bundle_id}" AND service="#{setting_name}";']
      end

      if debug_logging?
        puts "Executing sql #{sql} on #{path_to_tcc_db}"
      end

      unless system(%Q[sqlite3 "#{path_to_tcc_db}" #{sql}]) && privacy_setting(dir,bundle_id,setting_name) == allowed_as_i
        puts "Warning: Error executing sql: #{sql} against #{path_to_tcc_db} (Setting is #{privacy_setting(dir,bundle_id,setting_name)}). Continuing..."
        next
      end
    end
  end

end

#use_instruments_env?Boolean

Returns:

  • (Boolean)


555
556
557
# File 'lib/calabash-cucumber/launcher.rb', line 555

def use_instruments_env?
  ENV['LAUNCH_VIA'] == 'instruments'
end

#use_sim_launcher_env?Boolean

Returns:

  • (Boolean)


559
560
561
# File 'lib/calabash-cucumber/launcher.rb', line 559

def use_sim_launcher_env?
  ENV['LAUNCH_VIA'] == 'sim_launcher'
end