Class: Calabash::Cucumber::Launcher
- Inherits:
-
Object
- Object
- Calabash::Cucumber::Launcher
- 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'}
- @@launcher =
nil
Instance Attribute Summary collapse
-
#actions ⇒ Object
Returns the value of attribute actions.
-
#device ⇒ Object
Returns the value of attribute device.
-
#launch_args ⇒ Object
Returns the value of attribute launch_args.
-
#run_loop ⇒ Object
Returns the value of attribute run_loop.
Class Method Summary collapse
Instance Method Summary collapse
- #active? ⇒ Boolean
- #app_path ⇒ Object
- #attach(max_retry = 1, timeout = 10) ⇒ Object
- #calabash_no_launch? ⇒ Boolean
- #calabash_no_stop? ⇒ Boolean
- #calabash_notify(world) ⇒ Object
- #default_launch_args ⇒ Object
- #default_launch_method ⇒ Object
- #detect_app_bundle_from_args(args) ⇒ Object
- #detect_bundle_id ⇒ Object
- #detect_bundle_id_from_app_bundle(args) ⇒ Object
- #detect_connected_device? ⇒ Boolean
- #detect_device_from_args(args) ⇒ Object
- #device_env ⇒ Object
- #device_target? ⇒ Boolean
- #directories_for_sdk_prefix(sdk) ⇒ Object
- #ensure_connectivity(max_retry = 10, timeout = 30) ⇒ Object
- #info_plist_as_hash(plist_path) ⇒ Object
- #info_plist_from_bundle_path(bundle_path) ⇒ Object
-
#initialize ⇒ Launcher
constructor
A new instance of Launcher.
- #inspect ⇒ Object
- #instruments? ⇒ Boolean
- #ios_major_version ⇒ Object
- #ios_version ⇒ Object
- #new_run_loop(args) ⇒ Object
- #ping_app ⇒ Object
- #privacy_setting(sdk_dir, bundle_id, setting_name) ⇒ Object
- #relaunch(args = {}) ⇒ Object
- #reset_app_jail(sdk = nil, path = nil) ⇒ Object
- #reset_between_scenarios? ⇒ Boolean
- #run_with_instruments?(args) ⇒ Boolean
- #sdk_version ⇒ Object
- #simulator_target? ⇒ Boolean
- #stop ⇒ Object
- #tcc_database_for_sdk_dir(dir) ⇒ Object
-
#update_privacy_settings(bundle_id, opts = {}) ⇒ Object
Call as update_privacy_settings(‘com.my.app’, => {:allow => true}).
- #use_instruments_env? ⇒ Boolean
Constructor Details
#initialize ⇒ Launcher
Returns a new instance of Launcher.
97 98 99 |
# File 'lib/calabash-cucumber/launcher.rb', line 97 def initialize @@launcher = self end |
Instance Attribute Details
#actions ⇒ Object
Returns the value of attribute actions.
15 16 17 |
# File 'lib/calabash-cucumber/launcher.rb', line 15 def actions @actions end |
#device ⇒ Object
Returns the value of attribute device.
14 15 16 |
# File 'lib/calabash-cucumber/launcher.rb', line 14 def device @device end |
#launch_args ⇒ Object
Returns the value of attribute launch_args.
16 17 18 |
# File 'lib/calabash-cucumber/launcher.rb', line 16 def launch_args @launch_args end |
#run_loop ⇒ Object
Returns the value of attribute run_loop.
13 14 15 |
# File 'lib/calabash-cucumber/launcher.rb', line 13 def run_loop @run_loop end |
Class Method Details
.attach ⇒ Object
41 42 43 44 45 46 |
# File 'lib/calabash-cucumber/launcher.rb', line 41 def self.attach l = launcher return l if l && l.active? l.attach end |
.instruments? ⇒ Boolean
83 84 85 86 87 |
# File 'lib/calabash-cucumber/launcher.rb', line 83 def self.instruments? l = launcher_if_used return false unless l l.instruments? end |
.launcher ⇒ Object
89 90 91 |
# File 'lib/calabash-cucumber/launcher.rb', line 89 def self.launcher @@launcher ||= Calabash::Cucumber::Launcher.new end |
.launcher_if_used ⇒ Object
93 94 95 |
# File 'lib/calabash-cucumber/launcher.rb', line 93 def self.launcher_if_used @@launcher end |
Instance Method Details
#active? ⇒ Boolean
518 519 520 |
# File 'lib/calabash-cucumber/launcher.rb', line 518 def active? not run_loop.nil? end |
#app_path ⇒ Object
510 511 512 |
# File 'lib/calabash-cucumber/launcher.rb', line 510 def app_path ENV['APP_BUNDLE_PATH'] || (defined?(APP_BUNDLE_PATH) && APP_BUNDLE_PATH) || ENV['APP'] end |
#attach(max_retry = 1, timeout = 10) ⇒ Object
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/calabash-cucumber/launcher.rb', line 48 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
482 483 484 |
# File 'lib/calabash-cucumber/launcher.rb', line 482 def calabash_no_launch? ENV['NO_LAUNCH']=='1' end |
#calabash_no_stop? ⇒ Boolean
478 479 480 |
# File 'lib/calabash-cucumber/launcher.rb', line 478 def calabash_no_stop? calabash_no_launch? or ENV['NO_STOP']=="1" end |
#calabash_notify(world) ⇒ Object
453 454 455 456 457 |
# File 'lib/calabash-cucumber/launcher.rb', line 453 def calabash_notify(world) if world.respond_to?(:on_launch) world.on_launch end end |
#default_launch_args ⇒ Object
196 197 198 199 200 201 202 203 204 205 206 207 208 209 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 |
# File 'lib/calabash-cucumber/launcher.rb', line 196 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 } #:device_target will be set if run_with_instruments?(args) && !simulator_target? device_tgt = ENV['DEVICE_TARGET'] 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 else args[:device_target] = 'simulator' end else args[:device_target] = 'simulator' end args end |
#default_launch_method ⇒ Object
255 256 257 258 259 260 261 262 263 264 265 |
# File 'lib/calabash-cucumber/launcher.rb', line 255 def default_launch_method return :instruments unless sdk_version || use_instruments_env? return :instruments if sdk_version.start_with?('7') # Only instruments supported for iOS7+ sim_detector = SimLauncher::SdkDetector.new() available = sim_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
348 349 350 351 352 353 354 355 |
# File 'lib/calabash-cucumber/launcher.rb', line 348 def detect_app_bundle_from_args(args) if args[:device_target]=='simulator' device_xamarin_build_dir = 'iPhoneSimulator' else device_xamarin_build_dir = 'iPhone' end Calabash::Cucumber::SimulatorHelper.detect_app_bundle(nil, device_xamarin_build_dir) end |
#detect_bundle_id ⇒ Object
468 469 470 471 472 473 474 475 476 |
# File 'lib/calabash-cucumber/launcher.rb', line 468 def detect_bundle_id begin bundle_path = Calabash::Cucumber::SimulatorHelper.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
357 358 359 360 361 362 363 364 365 366 367 |
# File 'lib/calabash-cucumber/launcher.rb', line 357 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
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/calabash-cucumber/launcher.rb', line 237 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
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
# File 'lib/calabash-cucumber/launcher.rb', line 326 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_env ⇒ Object
506 507 508 |
# File 'lib/calabash-cucumber/launcher.rb', line 506 def device_env ENV['DEVICE'] end |
#device_target? ⇒ Boolean
486 487 488 |
# File 'lib/calabash-cucumber/launcher.rb', line 486 def device_target? (ENV['DEVICE_TARGET'] != nil) && (not simulator_target?) end |
#directories_for_sdk_prefix(sdk) ⇒ Object
131 132 133 |
# File 'lib/calabash-cucumber/launcher.rb', line 131 def directories_for_sdk_prefix(sdk) Dir["#{ENV['HOME']}/Library/Application Support/iPhone Simulator/#{sdk}*"] end |
#ensure_connectivity(max_retry = 10, timeout = 30) ⇒ Object
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 |
# File 'lib/calabash-cucumber/launcher.rb', line 389 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 ENV['CALABASH_FULL_CONSOLE_OUTPUT'] == '1' 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
460 461 462 463 464 465 466 |
# File 'lib/calabash-cucumber/launcher.rb', line 460 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
369 370 371 372 |
# File 'lib/calabash-cucumber/launcher.rb', line 369 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 |
#inspect ⇒ Object
526 527 528 529 530 531 532 |
# File 'lib/calabash-cucumber/launcher.rb', line 526 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
522 523 524 |
# File 'lib/calabash-cucumber/launcher.rb', line 522 def instruments? !!(active? && run_loop[:pid]) end |
#ios_major_version ⇒ Object
101 102 103 104 |
# File 'lib/calabash-cucumber/launcher.rb', line 101 def ios_major_version return nil if device.nil? or device.ios_version.nil? device.ios_major_version end |
#ios_version ⇒ Object
106 107 108 109 |
# File 'lib/calabash-cucumber/launcher.rb', line 106 def ios_version return nil if device.nil? device.ios_version end |
#new_run_loop(args) ⇒ Object
374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
# File 'lib/calabash-cucumber/launcher.rb', line 374 def new_run_loop(args) last_err = nil 5.times do begin return RunLoop.run(args) rescue RunLoop::TimeoutError => e last_err = e if ENV['CALABASH_FULL_CONSOLE_OUTPUT'] == '1' puts "retrying run loop..." end end end raise StartError.new(last_err) end |
#ping_app ⇒ Object
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 |
# File 'lib/calabash-cucumber/launcher.rb', line 427 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
187 188 189 190 191 192 193 194 |
# File 'lib/calabash-cucumber/launcher.rb', line 187 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
267 268 269 270 271 272 273 274 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 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/calabash-cucumber/launcher.rb', line 267 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.(args[:app]) else # args[:app] is not a directory so must be a bundle id if args[:device_target] == 'simulator' ## bundle id set, but simulator target args[:app] = app_path || detect_app_bundle_from_args(args) end end end unless args[:app] if args[:device_target]=='simulator' device_xamarin_build_dir = 'iPhoneSimulator' else device_xamarin_build_dir = 'iPhone' end args[:app] = Calabash::Cucumber::SimulatorHelper.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 args[:device_target]=='simulator' 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 run_with_instruments?(args) self.run_loop = new_run_loop(args) self.actions= Calabash::Cucumber::InstrumentsActions.new else # run with sim launcher sdk = sdk_version || SimLauncher::SdkDetector.new().available_sdk_versions.reverse.find { |x| !x.start_with?('7') } path = Calabash::Cucumber::SimulatorHelper.app_bundle_or_raise(app_path) self.actions= Calabash::Cucumber::PlaybackActions.new Calabash::Cucumber::SimulatorHelper.relaunch(path, sdk, args[:device].to_s, args) end self.launch_args = args ensure_connectivity end |
#reset_app_jail(sdk = nil, path = nil) ⇒ Object
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/calabash-cucumber/launcher.rb', line 111 def reset_app_jail(sdk=nil, path=nil) sdk ||= sdk_version || SimLauncher::SdkDetector.new().latest_sdk_version path ||= Calabash::Cucumber::SimulatorHelper.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 ENV['DEBUG']=='1' 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
502 503 504 |
# File 'lib/calabash-cucumber/launcher.rb', line 502 def reset_between_scenarios? ENV['RESET_BETWEEN_SCENARIOS']=="1" end |
#run_with_instruments?(args) ⇒ Boolean
514 515 516 |
# File 'lib/calabash-cucumber/launcher.rb', line 514 def run_with_instruments?(args) args && args[:launch_method] == :instruments end |
#sdk_version ⇒ Object
494 495 496 |
# File 'lib/calabash-cucumber/launcher.rb', line 494 def sdk_version ENV['SDK_VERSION'] end |
#simulator_target? ⇒ Boolean
490 491 492 |
# File 'lib/calabash-cucumber/launcher.rb', line 490 def simulator_target? ENV['DEVICE_TARGET'] == 'simulator' end |
#stop ⇒ Object
449 450 451 |
# File 'lib/calabash-cucumber/launcher.rb', line 449 def stop RunLoop.stop(run_loop) if run_loop && run_loop[:pid] end |
#tcc_database_for_sdk_dir(dir) ⇒ Object
183 184 185 |
# File 'lib/calabash-cucumber/launcher.rb', line 183 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})
136 137 138 139 140 141 142 143 144 145 146 147 148 149 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 |
# File 'lib/calabash-cucumber/launcher.rb', line 136 def update_privacy_settings(bundle_id, opts={}) if ENV['DEBUG']=='1' 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_name = KNOWN_PRIVACY_SETTINGS[setting_name] || setting_name allow = [:allow] == false ? false : true sdk = [:sdk] || SimLauncher::SdkDetector.new().latest_sdk_version dirs = directories_for_sdk_prefix(sdk) if ENV['DEBUG']=='1' puts "About to update privacy setting #{setting_name} for #{bundle_id}, allow: #{allow} in sdk #{sdk}, #{dirs}" end dirs.each do |dir| if ENV['DEBUG']=='1' 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 ENV['DEBUG']=='1' 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
498 499 500 |
# File 'lib/calabash-cucumber/launcher.rb', line 498 def use_instruments_env? ENV['LAUNCH_VIA'] == 'instruments' end |