class Calabash::Cucumber::Launcher

Launch apps on iOS Simulators and physical devices.

### Accessing the current launcher from ruby.

If you need a reference to the current launcher in your ruby code.

‘Calabash::Cucumber::Launcher.launcher`

This is usually not required, but might be useful in ‘support/01_launch.rb`.

### Attaching to the current launcher in a console

If Calabash already running and you want to attach to the current launcher, use ‘console_attach`. This is useful when a cucumber Scenario has failed and you want to query the current state of the app.

does not quit your application after a failed Scenario.

Constants

DEFAULTS

@!visibility private

Attributes

automator[R]

@!visibility private

launch_args[RW]

@!visibility private

run_loop[R]

@!visibility private

server_version[R]

The version of the embedded LPServer @return RunLoop::Version

usage_tracker[R]

@!visibility private

Public Class Methods

attach() click to toggle source

@!visibility private @see Calabash::Cucumber::Core#console_attach

# File lib/calabash-cucumber/launcher.rb, line 156
def self.attach
  l = launcher
  return l if l && l.attached_to_automator?
  l.attach
end
instruments?() click to toggle source

Are we running using instruments?

@return {Boolean} true if we’re using instruments to launch

# File lib/calabash-cucumber/launcher.rb, line 223
def self.instruments?
  launcher = Launcher::launcher_if_used
  if !launcher
    false
  else
    launcher.instruments?
  end
end
launcher() click to toggle source

A reference to the current launcher (instantiates a new one if needed). @return {Calabash::Cucumber::Launcher} the current launcher

# File lib/calabash-cucumber/launcher.rb, line 252
def self.launcher
  @@launcher ||= Calabash::Cucumber::Launcher.new
end
launcher_if_used() click to toggle source

Get a reference to the current launcher (does not instantiate a new one if unset). @return {Calabash::Cucumber::Launcher} the current launcher or nil

# File lib/calabash-cucumber/launcher.rb, line 258
def self.launcher_if_used
  @@launcher
end
new() click to toggle source

@!visibility private

# File lib/calabash-cucumber/launcher.rb, line 75
def initialize
  @@launcher = self
end

Public Instance Methods

active?() click to toggle source

TODO remove in 0.21.0 @!visibility private

# File lib/calabash-cucumber/launcher.rb, line 245
def active?
  RunLoop.deprecated("0.20.0", "replaced with attached_to_automator?")
  attached_to_automator?
end
app_path() click to toggle source

@!visibility private @deprecated 0.19.0 - no replacement TODO remove in 0.20.0

# File lib/calabash-cucumber/launcher.rb, line 541
def app_path
  RunLoop.deprecated("0.19.0", "No replacement")
  nil
end
attach(options={}) click to toggle source

@!visibility private @see Calabash::Cucumber::Core#console_attach

# File lib/calabash-cucumber/launcher.rb, line 164
      def attach(options={})
        if Calabash::Cucumber::Environment.xtc?
          raise "This method is not available on the Xamarin Test Cloud"
        end

        default_options = {:http_connection_retry => 1,
                           :http_connection_timeout => 10}
        merged_options = default_options.merge(options)

        begin
          Calabash::Cucumber::HTTP.ensure_connectivity(merged_options)
        rescue Calabash::Cucumber::ServerNotRespondingError => _
          device_endpoint = Calabash::Cucumber::Environment.device_endpoint
          RunLoop.log_warn(
%Q[

Could not connect to Calabash Server @ #{device_endpoint}.

If your app is running, check that you have set the DEVICE_ENDPOINT correctly.

If your app is not running, it was a mistake to call this method.

http://calabashapi.xamarin.com/ios/Calabash/Cucumber/Core.html#console_attach-instance_method

Try `start_test_server_in_background`

])

          # Nothing to do except log the problem and exit early.
          return false
        end

        # TODO check that the :pid is alive - no sense attaching if Automator
        # is not running.
        run_loop_cache = RunLoop::HostCache.default.read

        if run_loop_cache[:automator] == :device_agent
          # Sets the @run_loop variable to a new RunLoop::DeviceAgent::Client
          # instance.
          @automator = _attach_to_device_agent!(run_loop_cache)
        elsif run_loop_cache[:automator] == :instruments
          @run_loop = run_loop_cache
          @automator = Calabash::Cucumber::Automator::Instruments.new(run_loop_cache)
        else
          RunLoop.log_warn(
%Q[

Connected to an app that was not launched by Calabash using instruments or DeviceAgent.

Queries will work, but gestures and other automator actions will not.

])
        end
        self
      end
attached_to_automator?() click to toggle source

@!visibility private

# File lib/calabash-cucumber/launcher.rb, line 239
def attached_to_automator?
  automator != nil
end
calabash_no_launch?() click to toggle source

@!visibility private @deprecated 0.19.0 - no replacement TODO remove in 0.20.0

# File lib/calabash-cucumber/launcher.rb, line 497
      def calabash_no_launch?
        RunLoop.log_warn(%Q[
Calabash::Cucumber::Launcher #calabash_no_launch? and support for the NO_LAUNCH
environment variable has been removed from Calabash.  This always returns
false.  Please remove this method call from your hooks.
])
        false
      end
calabash_no_stop?() click to toggle source

@deprecated 0.19.0 - replaced with quit_app_after_scenario? TODO remove in 0.20.0 @!visibility private

# File lib/calabash-cucumber/launcher.rb, line 488
def calabash_no_stop?
  # Not yet.  Save for 0.20.0.
  # RunLoop.deprecated("0.19.0", "replaced with quit_app_after_scenario")
  !quit_app_after_scenario?
end
calabash_notify(_) click to toggle source

@!visibility private @deprecated 0.19.0 - no replacement - this method is a no op

relaunch will now send “:on_launch” to the Cucumber World if:

  • the Launcher is part of the World (it is not by default).

  • Cucumber responds to :on_launch.

TODO remove in 0.20.0

# File lib/calabash-cucumber/launcher.rb, line 569
def calabash_notify(_)
  false
end
check_server_gem_compatibility() click to toggle source

@!visibility private Checks the server and gem version compatibility and generates a warning if the server and gem are not compatible.

@note This is a proof-of-concept implementation and requires strict

equality.  in the future we should allow minimum framework compatibility.

@return [nil] nothing to return

# File lib/calabash-cucumber/launcher.rb, line 462
def check_server_gem_compatibility
  # Only check once.
  return server_version if server_version

  version_string = self.device.server_version

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

  if @server_version < min_server_version
    msgs = [
      "The server version is not compatible with gem version.",
      "Please update your server.",
      "https://github.com/calabash/calabash-ios/wiki/Updating-your-Calabash-iOS-version",
      "       gem version: '#{gem_version}'",
      "min server version: '#{min_server_version}'",
      "    server version: '#{@server_version}'"]
    RunLoop.log_warn("#{msgs.join("\n")}")
  end
  @server_version
end
default_launch_args() click to toggle source

@!visibility private @deprecated 0.19.0 - no replacement TODO remove in 0.20.0

# File lib/calabash-cucumber/launcher.rb, line 525
def default_launch_args
  RunLoop.deprecated("0.19.0", "No replacement")
  {}
end
default_uia_strategy(launch_args, sim_control, instruments) click to toggle source

@!visibility private @deprecated 0.19.0 - no replacement. TODO remove in 0.20.0

# File lib/calabash-cucumber/launcher.rb, line 509
def default_uia_strategy(launch_args, sim_control, instruments)
  RunLoop::deprecated("0.19.0", "This method has been removed.")
  :host
end
detect_connected_device?() click to toggle source

@!visibility private @deprecated 0.19.0 - no replacement TODO remove in 0.20.0

# File lib/calabash-cucumber/launcher.rb, line 517
def detect_connected_device?
  RunLoop.deprecated("0.19.0", "No replacement")
  false
end
device() click to toggle source

@!visibility private

This Calabash::Cucumber::Device instance is required because we cannot determine the iOS version of physical devices.

This device instance can only be created _if the server is running_.

We need this instance because we need to know at runtime whether or not to translate touch coordinates in the client or on the server. For iOS >= 8.0 translation is done on the server. Further, we need a Device instance for iOS < 8 so we can perform the necessary coordinate normalization - based on the device attributes.

We also need this instance to determine the default uia strategy.

+1 for tools to ask physical devices about attributes.

# File lib/calabash-cucumber/launcher.rb, line 134
def device
  @device ||= begin
    _, body = Calabash::Cucumber::HTTP.ensure_connectivity
    endpoint = Calabash::Cucumber::Environment.device_endpoint
    Calabash::Cucumber::Device.new(endpoint, body)
  end
end
device=(new_device) click to toggle source

@!visibility private

Legacy API. This is a required method. Do not remove

# File lib/calabash-cucumber/launcher.rb, line 145
def device=(new_device)
  @device = new_device
end
device_target?(options={}) click to toggle source

Is the current device under test a physical device?

Can be used before or after the application has been launched.

Maintainers, please do not call this method.

@param [Hash] options This argument is deprecated since 0.19.0.

@return [Boolean] True if the device under test a physical device.

# File lib/calabash-cucumber/launcher.rb, line 271
def device_target?(options={})
  if Calabash::Cucumber::Environment.xtc?
    true
  elsif @device
    @device.device?
  else
    detect_device(options).physical_device?
  end
end
discover_device_target(launch_args) click to toggle source

@!visibility private @deprecated 0.19.0 - no replacement TODO remove in 0.20.0

# File lib/calabash-cucumber/launcher.rb, line 533
def discover_device_target(launch_args)
  RunLoop.deprecated("0.19.0", "No replacement")
  nil
end
ensure_connectivity() click to toggle source

@!visibility private @deprecated 0.19.0 - no replacement TODO remove in 0.20.0

# File lib/calabash-cucumber/launcher.rb, line 557
def ensure_connectivity
  RunLoop.deprecated("0.19.0", "No replacement")
  Calabash::Cucumber::HTTP.ensure_connectivity
end
inspect() click to toggle source

@!visibility private

# File lib/calabash-cucumber/launcher.rb, line 104
def inspect
  to_s
end
instruments?() click to toggle source

@!visibility private

# File lib/calabash-cucumber/launcher.rb, line 233
def instruments?
  attached_to_automator? &&
    automator.name == :instruments
end
new_run_loop(args) click to toggle source

@!visibility private

# File lib/calabash-cucumber/launcher.rb, line 409
def new_run_loop(args)
  last_err = nil
  num_retries = args[:launch_retries] || DEFAULTS[:launch_retries]
  num_retries.times do
    begin
      return RunLoop.run(args)
    rescue RunLoop::TimeoutError => e
      last_err = e
    end
  end

  raise Calabash::Cucumber::LaunchError.new(last_err)
end
ping_app() click to toggle source

@!visibility private

Use this method to see if your app is already running. This is helpful if you have Scenarios that don’t require an app relaunch.

@raise Raises an error if the server does not respond.

# File lib/calabash-cucumber/launcher.rb, line 114
def ping_app
  Calabash::Cucumber::HTTP.ping_app
end
quit_app_after_scenario?() click to toggle source

Should Calabash quit the app under test after a Scenario?

Control this behavior using the QUIT_APP_AFTER_SCENARIO variable.

The default behavior is to quit after every Scenario.

# File lib/calabash-cucumber/launcher.rb, line 450
def quit_app_after_scenario?
  Calabash::Cucumber::Environment.quit_app_after_scenario?
end
relaunch(launch_options={}) click to toggle source

Launches your app on the connected device or simulator.

‘relaunch` does a lot of error detection and handling to reliably start the app and test. Instruments (particularly the cli) has stability issues which we workaround by restarting the simulator process and checking that UIAutomation is correctly attaching to your application.

Use the ‘args` parameter to to control:

  • ‘:app` - which app to launch.

  • ‘:device` - simulator or device to target.

  • ‘:reset_app_sandbox - reset the app’s data (sandbox) before testing

and many other behaviors.

Many of these behaviors can be be controlled by environment variables. The most important environment variables are ‘APP`, `DEVICE_TARGET`, and `DEVICE_ENDPOINT`.

@param {Hash} launch_options optional arguments to control the how the app is launched

# File lib/calabash-cucumber/launcher.rb, line 350
      def relaunch(launch_options={})
        simctl = launch_options[:simctl] || launch_options[:sim_control]
        instruments = launch_options[:instruments]
        xcode = launch_options[:xcode]

        options = launch_options.clone

        # Reusing Simctl, Instruments, and Xcode can speed up launches.
        options[:simctl] = simctl || Calabash::Cucumber::Environment.simctl
        options[:instruments] = instruments || Calabash::Cucumber::Environment.instruments
        options[:xcode] = xcode || Calabash::Cucumber::Environment.xcode
        options[:inject_dylib] = detect_inject_dylib_option(launch_options)

        @launch_args = options

        @run_loop = new_run_loop(options)
        if @run_loop.is_a?(Hash)
          @automator = Calabash::Cucumber::Automator::Instruments.new(@run_loop)
        elsif @run_loop.is_a?(RunLoop::DeviceAgent::Client)
          @automator = Calabash::Cucumber::Automator::DeviceAgent.new(@run_loop)
        else
          raise ArgumentError, %Q[

Could not determine which automator to use based on the launch arguments:

#{@launch_args.join("$-0")}

RunLoop.run returned:

#{@run_loop}

]
        end

        Calabash::Cucumber::UIA.redefine_instance_methods_if_necessary(options[:xcode],
                                                                       automator)

        if !options[:calabash_lite]
          Calabash::Cucumber::HTTP.ensure_connectivity
          check_server_gem_compatibility
        end

        # What was Calabash tracking? Read this post for information
        # No private data (like ip addresses) were collected
        # https://github.com/calabash/calabash-android/issues/655
        #
        # Removing usage tracking to avoid problems with EU General Data
        # Protection Regulation which takes effect in 2018.
        # usage_tracker.post_usage_async

        # :on_launch to the Cucumber World if:
        # * the Launcher is part of the World (it is not by default).
        # * Cucumber responds to :on_launch.
        self.send(:on_launch) if self.respond_to?(:on_launch)

        self
      end
reset_simulator(device=nil) click to toggle source

Erases a simulator. This is the same as touching the Simulator “Reset Content & Settings” menu item.

@param [RunLoop::Device, String] device The simulator to erase. Can be a

RunLoop::Device instance, a simulator UUID, or a human readable simulator
name.

@raise ArgumentError If the simulator is a physical device @raise RuntimeError If the simulator cannot be shutdown @raise RuntimeError If the simulator cannot be erased

# File lib/calabash-cucumber/launcher.rb, line 310
      def reset_simulator(device=nil)
        if device.is_a?(RunLoop::Device)
          device_target = device
        else
          device_target = detect_device(:device => device)
        end

        if device_target.physical_device?
          raise ArgumentError,
%Q{
Cannot reset: #{device_target}.

Resetting physical devices is not supported.
}
        end

        RunLoop::CoreSimulator.erase(device_target)
        device_target
      end
server_version_from_bundle(app_bundle_path) click to toggle source

@!visibility private @deprecated 0.19.0 - no replacement TODO remove in 0.20.0

# File lib/calabash-cucumber/launcher.rb, line 584
def server_version_from_bundle(app_bundle_path)
  RunLoop.deprecated("0.19.0", "No replacement")
  options = {:app => app_bundle_path }
  app_details = RunLoop::DetectAUT.detect_app_under_test(options)
  app = app_details[:app]

  if app.respond_to?(:calabash_server_version)
    app.calabash_server_version
  else
    nil
  end
end
server_version_from_server() click to toggle source

@!visibility private @deprecated 0.19.0 - no replacement. TODO remove in 0.20.0

# File lib/calabash-cucumber/launcher.rb, line 576
def server_version_from_server
  RunLoop.deprecated("0.19.0", "No replacement")
  server_version
end
simulator_target?(options={}) click to toggle source

Is the current device under test a simulator?

Can be used before or after the application has been launched.

Maintainers, please do not call this method.

@param [Hash] options This argument is deprecated since 0.19.0.

@return [Boolean] True if the device under test a simulator.

# File lib/calabash-cucumber/launcher.rb, line 290
def simulator_target?(options={})
  if Calabash::Cucumber::Environment.xtc?
    false
  elsif @device
    @device.simulator?
  else
    detect_device(options).simulator?
  end
end
stop() click to toggle source

@!visibility private TODO Should call calabash exit route to shutdown the server.

# File lib/calabash-cucumber/launcher.rb, line 425
def stop
  return :no_automator if !automator

  if !automator.respond_to?(:name)
    RunLoop.log_warn("Unknown automator: #{automator}")
    RunLoop.log_warn("Calabash does not know how to stop this automator")
    return :unknown_automator
  end

  case automator.name
    when :instruments, :device_agent
      automator.stop
      :stopped
    else
      RunLoop.log_warn("Unknown automator: #{automator}")
      RunLoop.log_warn("Calabash does not know how to stop this automator")
      :unknown_automator
  end
end
to_s() click to toggle source

@!visibility private

# File lib/calabash-cucumber/launcher.rb, line 80
def to_s
  class_name = "Launcher"

  if !automator
    "#<#{class_name}: not attached to an automator>"
  else
    if automator.respond_to?(:name)
      case automator.name
        when :instruments
          log_file =  automator.run_loop[:log_file]
          "#<#{class_name}: UIAutomation/instruments - #{log_file}>"
        when :device_agent
          launcher_name = automator.client.cbx_launcher.name
          "#<#{class_name}: DeviceAgent/#{launcher_name}>"
        else
          "#<#{class_name}: attached to #{automator.name}>"
      end
    else
      "#<#{class_name}: attached to #{automator}>"
    end
  end
end
xcode() click to toggle source

@!visibility private @deprecated 0.19.0 - no replacement TODO remove in 0.20.0

# File lib/calabash-cucumber/launcher.rb, line 549
def xcode
  RunLoop.deprecated("0.19.0", "Use Calabash::Cucumber::Environment.xcode")
  Calabash::Cucumber::Environment.xcode
end

Private Instance Methods

_attach_to_device_agent!(hash) click to toggle source

@!visibility private

# File lib/calabash-cucumber/launcher.rb, line 633
def _attach_to_device_agent!(hash)
  simctl = Calabash::Cucumber::Environment.simctl
  instruments = Calabash::Cucumber::Environment.instruments
  xcode = Calabash::Cucumber::Environment.xcode

  options = { simctl: simctl, instruments: instruments, xcode: xcode}
  device = RunLoop::Device.device_with_identifier(hash[:udid], options)
  bundle_id = hash[:app]

  options = { cbx_launcher: hash[:launcher] }
  cbx_launcher = RunLoop::DeviceAgent::Client.detect_cbx_launcher(options, device)
  launcher_options = hash[:launcher_options]

  device_agent_client = RunLoop::DeviceAgent::Client.new(bundle_id,
                                                         device,
                                                         cbx_launcher,
                                                         launcher_options)
  @run_loop = device_agent_client
  Calabash::Cucumber::Automator::DeviceAgent.new(@run_loop)
end
detect_device(options) click to toggle source

@!visibility private

A convenience wrapper around RunLoop::Device.detect_device

# File lib/calabash-cucumber/launcher.rb, line 602
def detect_device(options)
  xcode = Calabash::Cucumber::Environment.xcode
  simctl = Calabash::Cucumber::Environment.simctl
  instruments = Calabash::Cucumber::Environment.instruments
  RunLoop::Device.detect_device(options, xcode, simctl, instruments)
end
detect_inject_dylib_option(options) click to toggle source

@!visibility private

@param [Hash] options the launch options passed by the user

# File lib/calabash-cucumber/launcher.rb, line 616
def detect_inject_dylib_option(options)
  return nil if !options[:inject_dylib]

  value = options[:inject_dylib]

  # Test for boolean true.
  if [true].include?(value)
    # Injection is only supported on simulators, so this cool for now.
    # Depend on run-loop to raise an error.
    Calabash::Cucumber::Dylibs.path_to_sim_dylib
  else
    # User supplied a path
    value
  end
end