Class: Calabash::Cucumber::SimulatorLauncher
- Inherits:
-
Object
- Object
- Calabash::Cucumber::SimulatorLauncher
- Includes:
- Logging
- Defined in:
- lib/calabash-cucumber/launch/simulator_launcher.rb
Overview
acts as a bridge to the sim_launcher SimLauncher and SdkDetector classes
Defined Under Namespace
Classes: TimeoutErr
Constant Summary collapse
- DERIVED_DATA =
the file path to the default Xcode DerivedData directory
File.('~/Library/Developer/Xcode/DerivedData')
- DEFAULT_DERIVED_DATA_INFO =
REGEX for finding application Info.plist
File.("#{DERIVED_DATA}/*/info.plist")
- DEFAULT_SIM_WAIT =
if CONNECT_TIMEOUT is not set, wait this long for the app to launch in the simulator before retrying
30
- DEFAULT_SIM_RETRY =
if MAX_CONNECT_RETRY is not set, try to launch the app this many times in the simulator before giving up
2
Constants included from Logging
Logging::CALABASH_NO_DEPRECATION
Instance Attribute Summary collapse
-
#device ⇒ Object
an instance of Calabash::Cucumber::Device.
-
#launch_args ⇒ Object
the launch args passed from Calabash::Cucumber::Launcher to the launch and relaunch methods.
-
#sdk_detector ⇒ Object
an instance of SimLauncher::SdkDetector.
-
#simulator ⇒ Object
an instance of SimLauncher::Simulator.
Instance Method Summary collapse
-
#app_bundle_or_raise(path = nil, device_build_dir = 'iPhoneSimulator') ⇒ String
attempts to deduce the path the to the app bundle (.app) todo methods should not use 2 optional arguments.
-
#bundle_path_from_xamarin_project(device_build_dir = 'iPhoneSimulator') ⇒ String?
attempts to deduce the path to the app bundle path (*.app) using heuristics and checking for executables linked with the Calabash server.
-
#derived_data_dir_for_project ⇒ String
uses heuristics to deduce the derived data directory for the project so the path to the app bundle (.app) can be detected.
-
#detect_app_bundle(path = nil, device_build_dir = 'iPhoneSimulator') ⇒ String?
attempts to deduce the app bundle path todo methods should not use 2 optional arguments.
-
#ensure_connectivity(app_bundle_path, sdk, device_family, args = nil) ⇒ Object
attempts to connect to launch the app and connect to the embedded calabash server.
-
#find_preferred_dir(sim_dirs) ⇒ String?
todo find_preferred_dir is a bad name - preferred for what? todo sim_dirs arg is a bad name - we can be iterating over any directory.
-
#get_version ⇒ Object
deprecated
Deprecated.
Calabash::Cucumber::Launcher.launcher.device instance methods
-
#initialize ⇒ SimulatorLauncher
constructor
creates a new instance an sets the :simulator and :sdk_detector attributes.
-
#ios_major_version ⇒ Object
deprecated
Deprecated.
use Calabash::Cucumber::Launcher.launcher.ios_major_version
-
#ios_version ⇒ Object
deprecated
Deprecated.
Calabash::Cucumber::Launcher.launcher.device instance methods
-
#launch(app_bundle_path, sdk, device_family, args = nil) ⇒ Object
launches the app todo args was originally intended to be the args passed to the application @ launch.
-
#linked_with_calabash?(d) ⇒ Boolean
searches
d
for a file linked with Calabash server todo why are we not grep’ing for executable files? see server_version_from_bundle. -
#ping_app ⇒ String
ping the version route of the calabash server embedded in the app.
-
#project_dir ⇒ String
returns the absolute path to the project directory unless PROJECT_DIR is defined, returns the absolute path to the current directory todo migrate PROJECT_DIR to environment_helpers.rb.
-
#relaunch(app_path, sdk, args) ⇒ Object
relaunches the app at
app_path
in the simulator usingsdk
andargs
. -
#stop ⇒ Object
stops (quits) the simulator.
-
#version_check(version) ⇒ Object
deprecated
Deprecated.
version checking is done in Launcher
-
#xamarin_ios_bin_dir?(bin_dir) ⇒ Boolean
is this the Xamarin iOS bin directory?.
-
#xamarin_ios_csproj_path ⇒ String
path to the Xamarin IDE project.
-
#xamarin_project? ⇒ Boolean
is this a Xamarin IDE project?.
Methods included from Logging
#_deprecated, #calabash_info, #calabash_warn, #debug_logging?, #full_console_logging?, #no_deprecation_warnings?
Constructor Details
#initialize ⇒ SimulatorLauncher
creates a new instance an sets the :simulator and :sdk_detector attributes
48 49 50 51 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 48 def initialize @simulator = SimLauncher::Simulator.new @sdk_detector = SimLauncher::SdkDetector.new() end |
Instance Attribute Details
#device ⇒ Object
an instance of Calabash::Cucumber::Device
35 36 37 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 35 def device @device end |
#launch_args ⇒ Object
the launch args passed from Calabash::Cucumber::Launcher to the launch and relaunch methods.
45 46 47 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 45 def launch_args @launch_args end |
#sdk_detector ⇒ Object
an instance of SimLauncher::SdkDetector
41 42 43 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 41 def sdk_detector @sdk_detector end |
#simulator ⇒ Object
an instance of SimLauncher::Simulator
38 39 40 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 38 def simulator @simulator end |
Instance Method Details
#app_bundle_or_raise(path = nil, device_build_dir = 'iPhoneSimulator') ⇒ String
attempts to deduce the path the to the app bundle (.app) todo methods should not use 2 optional arguments
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 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 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 166 def app_bundle_or_raise(path=nil, device_build_dir='iPhoneSimulator') path = File.(path) if path if path and not File.directory?(path) raise "Unable to find .app bundle at #{path}. It should be an .app directory." elsif path bundle_path = path elsif xamarin_project? bundle_path = bundle_path_from_xamarin_project(device_build_dir) unless bundle_path msg = ['Detected Xamarin project, but did not detect built app linked with Calabash'] msg << 'You should build your project from Xamarin Studio' msg << "Make sure you build for Simulator and that you're using the Calabash components" raise msg.join("\n") end if full_console_logging? puts('-'*37) puts "Auto detected APP_BUNDLE_PATH:\n\n" puts "APP_BUNDLE_PATH= '#{bundle_path}'\n\n" puts 'Please verify!' puts "If this is wrong please set it as APP_BUNDLE_PATH in features/support/01_launch.rb\n" puts('-'*37) end else dd_dir = derived_data_dir_for_project sim_dirs = Dir.glob(File.join(dd_dir, 'Build', 'Products', '*-iphonesimulator', '*.app')) if sim_dirs.empty? msg = ['Unable to auto detect APP_BUNDLE_PATH.'] msg << 'Have you built your app for simulator?' msg << "Searched dir: #{dd_dir}/Build/Products" msg << 'Please build your app from Xcode' msg << 'You should build the -cal target.' msg << '' msg << 'Alternatively, specify APP_BUNDLE_PATH in features/support/01_launch.rb' msg << "This should point to the location of your built app linked with calabash.\n" raise msg.join("\n") end preferred_dir = find_preferred_dir(sim_dirs) if preferred_dir.nil? msg = ['Error... Unable to find APP_BUNDLE_PATH.'] msg << 'Cannot find a built app that is linked with calabash.framework' msg << 'Please build your app from Xcode' msg << 'You should build your calabash target.' msg << '' msg << 'Alternatively, specify APP_BUNDLE_PATH in features/support/01_launch.rb' msg << "This should point to the location of your built app linked with calabash.\n" raise msg.join("\n") end if full_console_logging? puts('-'*37) puts "Auto detected APP_BUNDLE_PATH:\n\n" puts "APP_BUNDLE_PATH=#{preferred_dir || sim_dirs[0]}\n\n" puts 'Please verify!' puts "If this is wrong please set it as APP_BUNDLE_PATH in features/support/01_launch.rb\n" puts('-'*37) end bundle_path = sim_dirs[0] end bundle_path end |
#bundle_path_from_xamarin_project(device_build_dir = 'iPhoneSimulator') ⇒ String?
attempts to deduce the path to the app bundle path (*.app) using heuristics and checking for executables linked with the Calabash server
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 285 def bundle_path_from_xamarin_project(device_build_dir='iPhoneSimulator') ios_project_path = xamarin_ios_csproj_path conf_glob = File.join(ios_project_path,'bin',device_build_dir,'*') built_confs = Dir[conf_glob] calabash_build = built_confs.find {|path| File.basename(path) == 'Calabash'} debug_build = built_confs.find {|path| File.basename(path) == 'Debug'} bundle_path = [calabash_build, debug_build, *built_confs].find do |path| next unless path && File.directory?(path) app_dir = Dir[File.join(path,'*.app')].first app_dir && linked_with_calabash?(app_dir) end Dir[File.join(bundle_path,'*.app')].first if bundle_path end |
#derived_data_dir_for_project ⇒ String
uses heuristics to deduce the derived data directory for the project so the path to the app bundle (.app) can be detected.
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 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 57 def derived_data_dir_for_project dir = project_dir xcode_workspace_name = '' info_plist = Dir.glob(DEFAULT_DERIVED_DATA_INFO).find { |plist_file| begin plist = CFPropertyList::List.new(:file => plist_file) hash = CFPropertyList.native_types(plist.value) ws_dir = File.dirname(hash['WorkspacePath']).downcase p_dir = dir.downcase if p_dir.include? ws_dir xcode_workspace_name = ws_dir.split('/').last end ws_dir == p_dir rescue false end } return File.dirname(info_plist) unless info_plist.nil? res = Dir.glob("#{dir}/*.xcodeproj") if res.empty? raise "Unable to find *.xcodeproj in #{dir}" elsif res.count > 1 raise "Unable to found several *.xcodeproj in #{dir}: #{res}" end xcode_proj_name = res.first.split('.xcodeproj')[0] xcode_proj_name = File.basename(xcode_proj_name) build_dirs = Dir.glob("#{DERIVED_DATA}/*").find_all do |xc_proj| File.basename(xc_proj).start_with?(xcode_proj_name) end if build_dirs.count == 0 && !xcode_workspace_name.empty? # check for directory named "workspace-{deriveddirectoryrandomcharacters}" build_dirs = Dir.glob("#{DERIVED_DATA}/*").find_all do |xc_proj| File.basename(xc_proj).downcase.start_with?(xcode_workspace_name) end end # todo analyze `derived_data_dir_for_project` to see if it contains dead code # todo assuming this is not dead code, the documentation around derived data for project needs to be updated if build_dirs.count == 0 msg = ['Unable to find your built app.'] msg << "This means that Calabash can't automatically launch iOS simulator." msg << "Searched in Xcode 4.x default: #{DEFAULT_DERIVED_DATA_INFO}" msg << '' msg << "To fix there are a couple of options:\n" msg << 'Option 1) Make sure you are running this command from your project directory, ' msg << 'i.e., the directory containing your .xcodeproj file.' msg << 'In Xcode, build your calabash target for simulator.' msg << "Check that your app can be found in\n #{DERIVED_DATA}" msg << "\n\nOption 2). In features/support/01_launch.rb set APP_BUNDLE_PATH to" msg << 'the path where Xcode has built your Calabash target.' msg << "Alternatively you can use the environment variable APP_BUNDLE_PATH.\n" raise msg.join("\n") elsif build_dirs.count > 1 msg = ['Unable to auto detect APP_BUNDLE_PATH.'] msg << "You have several projects with the same name: #{xcode_proj_name} in #{DERIVED_DATA}:\n" msg << build_dirs.join("\n") msg << "\nThis means that Calabash can't automatically launch iOS simulator." msg << "Searched in Xcode 4.x default: #{DEFAULT_DERIVED_DATA_INFO}" msg << "\nIn features/support/01_launch.rb set APP_BUNDLE_PATH to" msg << 'the path where Xcode has built your Calabash target.' msg << "Alternatively you can use the environment variable APP_BUNDLE_PATH.\n" raise msg.join("\n") else if full_console_logging? puts "Found potential build dir: #{build_dirs.first}" puts 'Checking...' end build_dirs.first end end |
#detect_app_bundle(path = nil, device_build_dir = 'iPhoneSimulator') ⇒ String?
attempts to deduce the app bundle path todo methods should not use 2 optional arguments
152 153 154 155 156 157 158 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 152 def detect_app_bundle(path=nil,device_build_dir='iPhoneSimulator') begin app_bundle_or_raise(path,device_build_dir) rescue nil end end |
#ensure_connectivity(app_bundle_path, sdk, device_family, args = nil) ⇒ Object
attempts to connect to launch the app and connect to the embedded calabash server.
to change the number of times launching is attempted set MAX_CONNECT_RETRY
to change the relaunch timeout set CONNECT_TIMEOUT
todo nearly a duplicate of Launcher ensure_connectivity todo args was originally intended to be the args passed to the application @ launch
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 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 392 def ensure_connectivity(app_bundle_path, sdk, device_family, args = nil) begin # todo should get the retry could from the args # todo should migrate MAX_CONNECT_RETRY to environment_helpers max_retry_count = (ENV['MAX_CONNECT_RETRY'] || DEFAULT_SIM_RETRY).to_i # todo should get the timeout from the args # todo should migrate CONNECT_TIMEOUT to environment helpers timeout = (ENV['CONNECT_TIMEOUT'] || DEFAULT_SIM_WAIT).to_i retry_count = 0 connected = false if full_console_logging? puts "Waiting at most #{timeout} seconds for simulator (CONNECT_TIMEOUT)" puts "Retrying at most #{max_retry_count} times (MAX_CONNECT_RETRY)" end until connected do raise 'MAX_RETRIES' if retry_count == max_retry_count retry_count += 1 if full_console_logging? puts "(#{retry_count}.) Start Simulator #{sdk}, #{device_family}, for #{app_bundle_path}" end begin Timeout::timeout(timeout, TimeoutErr) do launch(app_bundle_path, sdk, device_family, args) until connected begin connected = (ping_app == '200') break if connected rescue Exception => e # nop ensure sleep 1 unless connected end end end rescue TimeoutErr => e puts 'Timed out... Retrying' stop 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've' linked correctly with calabash.framework and set Other Linker Flags.\n" msg << "Make sure you don't have a firewall blocking traffic to #{ENV['DEVICE_ENDPOINT']|| 'http://localhost:37265/'}.\n" raise msg end end |
#find_preferred_dir(sim_dirs) ⇒ String?
todo find_preferred_dir is a bad name - preferred for what? todo sim_dirs arg is a bad name - we can be iterating over any directory
332 333 334 335 336 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 332 def find_preferred_dir(sim_dirs) sim_dirs.find do |d| linked_with_calabash?(d) end end |
#get_version ⇒ Object
Calabash::Cucumber::Launcher.launcher.device instance methods
500 501 502 503 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 500 def get_version _deprecated('0.9.169', 'use an instance Device class instead', :warn) raise(NotImplementedError, 'this method has been deprecated') end |
#ios_major_version ⇒ Object
use Calabash::Cucumber::Launcher.launcher.ios_major_version
516 517 518 519 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 516 def ios_major_version _deprecated('0.9.169', 'use an instance Device class instead', :warn) raise(NotImplementedError, 'this method has been deprecated') end |
#ios_version ⇒ Object
Calabash::Cucumber::Launcher.launcher.device instance methods
508 509 510 511 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 508 def ios_version _deprecated('0.9.169', 'use an instance Device class instead', :warn) raise(NotImplementedError, 'this method has been deprecated') end |
#launch(app_bundle_path, sdk, device_family, args = nil) ⇒ Object
launches the app todo args was originally intended to be the args passed to the application @ launch
448 449 450 451 452 453 454 455 456 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 448 def launch(app_bundle_path, sdk, device_family, args = nil) # cached but not used self.launch_args = args # launch arguments (eg. -NSShowNonLocalizedStrings) are not being passed # to sim_launcher # https://github.com/calabash/calabash-ios/issues/363 self.simulator.launch_ios_app(app_bundle_path, sdk, device_family) simulator end |
#linked_with_calabash?(d) ⇒ Boolean
searches d
for a file linked with Calabash server todo why are we not grep’ing for executable files? see server_version_from_bundle
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 307 def linked_with_calabash?(d) skipped_formats = ['.png', '.jpg', '.jpeg', '.plist', '.nib', '.lproj'] dir = File.(d) # For every file on that .app directory Dir.entries(d).each do |file| # If this is an asset or any of those skipped formats, skip it. next if skipped_formats.include? File.extname(file) # If its not, try to run otool against that file, check whether we are linked against calabash framework. out = `otool "#{dir}/#{file}" -o 2> /dev/null | grep CalabashServer` return true if /CalabashServer/.match(out) end # Defaulted to false false end |
#ping_app ⇒ String
ping the version route of the calabash server embedded in the app
has the side effect of setting self.device attribute if successful
todo migrate DEVICE_ENDPOINT to environment_helpers todo migrate CALABASH_VERSION_PATH to environment_helpers todo this is an exact duplicate of Launcher ping method
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 347 def ping_app url = URI.parse(ENV['DEVICE_ENDPOINT']|| 'http://localhost:37265/') if full_console_logging? puts "Ping #{url}..." end http = Net::HTTP.new(url.host, url.port) res = http.start do |sess| # noinspection RubyResolve sess.request Net::HTTP::Get.new(ENV['CALABASH_VERSION_PATH'] || 'version') end status = res.code begin http.finish if http and http.started? rescue # nop end if status == '200' version_body = JSON.parse(res.body) self.device = Calabash::Cucumber::Device.new(url, version_body) end if full_console_logging? puts "ping status = '#{status}'" end status end |
#project_dir ⇒ String
returns the absolute path to the project directory unless PROJECT_DIR is defined, returns the absolute path to the current directory todo migrate PROJECT_DIR to environment_helpers.rb
142 143 144 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 142 def project_dir File.(ENV['PROJECT_DIR'] || Dir.pwd) end |
#relaunch(app_path, sdk, args) ⇒ Object
relaunches the app at app_path
in the simulator using sdk
and args
todo args was originally intended to be the args passed to the application @ launch todo it is very likely that args == app_path so we might be able to eliminate an argument
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 467 def relaunch(app_path, sdk, args) app_bundle_path = app_bundle_or_raise(app_path) if sdk.nil? # iOS 7 requires launching with instruments, so we _must_ launch with # the first SDK that is _not_ iOS 7 _sdk = self.sdk_detector.available_sdk_versions.reverse.find { |x| !x.start_with?('7') } else # use SDK_VERSION to specify a different version # as of Xcode 5.0.2, the min supported simulator version is iOS 6 _sdk = sdk end if args[:device] device_family = args[:device].to_s else device_family = 'iphone' end self.launch_args = args ensure_connectivity(app_bundle_path, _sdk, device_family, args) end |
#stop ⇒ Object
stops (quits) the simulator
492 493 494 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 492 def stop self.simulator.quit_simulator end |
#version_check(version) ⇒ Object
version checking is done in Launcher
527 528 529 530 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 527 def version_check(version) _deprecated('0.9.169', 'check is now done in Launcher', :warn) raise(NotImplementedError, 'this method has been deprecated and will be removed') end |
#xamarin_ios_bin_dir?(bin_dir) ⇒ Boolean
is this the Xamarin iOS bin directory?
273 274 275 276 277 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 273 def xamarin_ios_bin_dir?(bin_dir) File.directory?(bin_dir) && (File.directory?(File.join(bin_dir,'iPhoneSimulator')) || File.directory?(File.join(bin_dir,'iPhone'))) end |
#xamarin_ios_csproj_path ⇒ String
path to the Xamarin IDE project
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 237 def xamarin_ios_csproj_path solution_path = Dir['*.sln'].first project_dir = nil if solution_path project_dir = Dir.pwd else solution_path = Dir[File.join('..','*.sln')].first if solution_path project_dir = File.('..') end end return nil unless project_dir ios_project_dir = Dir[File.join(project_dir,'*.iOS')].first return ios_project_dir if ios_project_dir && File.directory?(ios_project_dir) # ios_project_dir does not exist # Detect case where there is no such sub directory # (i.e. iOS only Xamarin project) bin_dir = File.join(project_dir, 'bin') if xamarin_ios_bin_dir?(bin_dir) return project_dir ## Looks like iOS bin dir is here end sub_dirs = Dir[File.join(project_dir,'*')].select {|dir| File.directory?(dir)} sub_dirs.find do |sub_dir| contains_csproj = Dir[File.join(sub_dir,'*.csproj')].first contains_csproj && xamarin_ios_bin_dir?(File.join(sub_dir,'bin')) end end |
#xamarin_project? ⇒ Boolean
is this a Xamarin IDE project?
231 232 233 |
# File 'lib/calabash-cucumber/launch/simulator_launcher.rb', line 231 def xamarin_project? xamarin_ios_csproj_path != nil end |