Class: RunLoop::Device

Inherits:
Object
  • Object
show all
Defined in:
lib/run_loop/device.rb

Constant Summary collapse

SIM_STABLE_STATE_OPTIONS =

Starting in Xcode 7, iOS 9 simulators have a new “booting” state.

The simulator must completely boot before run-loop tries to do things like installing an app or clearing an app sandbox. Run-loop tries to wait for a the simulator stabilize by watching the checksum of the simulator directory and the simulator log.

On resource constrained devices or CI systems, the default settings may not work.

You can override these values if they do not work in your environment.

For cucumber users, the best place to override would be in your features/support/env.rb.

For example:

RunLoop::Device::SIM_STABLE_STATE_OPTIONS = 60

{
  # The maximum amount of time to wait for the simulator
  # to stabilize.  No errors are raised if this timeout is
  # exceeded - if the default 30 seconds has passed, the
  # simulator is probably stable enough for subsequent
  # operations.
  :timeout => RunLoop::Environment.ci? ? 120 : 30
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, version, udid, state = nil) ⇒ Device

Create a new device.

Parameters:

  • name (String)

    The name of the device. For sims this should be ‘iPhone 5s’ and for physical devices it will be the name the user gave to the device.

  • version (String, RunLoop::Version)

    The iOS version that is running on the device. Can be a string or a Version instance.

  • udid (String)

    The device identifier.

  • state (String) (defaults to: nil)

    (nil) This a simulator only value. It refers to the Booted/Shutdown/Creating state of the simulator. For pre-Xcode 6 simulators, this value should be nil.



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/run_loop/device.rb', line 56

def initialize(name, version, udid, state=nil)
  @name = name
  @udid = udid
  @state = state

  if version.is_a? String
    @version = RunLoop::Version.new version
  else
    @version = version
  end
end

Instance Attribute Details

#nameObject (readonly)

Returns the value of attribute name.



34
35
36
# File 'lib/run_loop/device.rb', line 34

def name
  @name
end

#stateObject (readonly)

Returns the value of attribute state.



37
38
39
# File 'lib/run_loop/device.rb', line 37

def state
  @state
end

#udidObject (readonly)

Returns the value of attribute udid.



36
37
38
# File 'lib/run_loop/device.rb', line 36

def udid
  @udid
end

#versionObject (readonly)

Returns the value of attribute version.



35
36
37
# File 'lib/run_loop/device.rb', line 35

def version
  @version
end

Class Method Details

.device_with_identifier(udid_or_name, options = {}) ⇒ RunLoop::Device

Returns a device given a udid or name. In the case of a physical device, the udid is the device identifier. In the case of a simulator the name is the _instruments identifier_ as reported by ‘$ xcrun instruments -s devices` - this is the identifier that can be passed to instruments.

Note that if you have a device and simulator with the same name, the simulator will always be selected.

Examples:

RunLoop::Device.device_with_identifier('iPhone 4s (8.3 Simulator'))
RunLoop::Device.device_with_identifier('6E43E3CF-25F5-41CC-A833-588F043AE749')
RunLoop::Device.device_with_identifier('denis') # Simulator or device named 'denis'
RunLoop::Device.device_with_identifier('893688959205dc7eb48d603c558ede919ad8dd0c')

Parameters:

  • udid_or_name (String)

    A name or udid that identifies the device you are looking for.

  • options (Hash) (defaults to: {})

    Allows callers to pass runtime models that might optimize performance (via memoization).

Options Hash (options):

  • :simctl (RunLoop::Simctl)

    An instance of Simctl.

  • :instruments (RunLoop::Instruments)

    An instance of Instruments.

  • :xcode (RunLoop::Xcode)

    An instance of Xcode

Returns:

Raises:

  • (ArgumentError)

    If no matching device can be found.



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
# File 'lib/run_loop/device.rb', line 95

def self.device_with_identifier(udid_or_name, options={})
  if options.is_a?(RunLoop::SimControl)
    raise ArgumentError, %q[Support for the 'sim_control' argument has been
removed (1.5.0).  It has been replaced by an options hash with two keys:
:simctl and :instruments. Please update your sources.))]
  end

  default_options = {
    :simctl => RunLoop::Simctl.new,
    :instruments => RunLoop::Instruments.new,
    :xcode => RunLoop::Xcode.new
  }

  merged_options = default_options.merge(options)

  instruments = merged_options[:instruments]
  simctl = merged_options[:simctl]

  xcode = RunLoop::Xcode.new
  simulator = simctl.simulators.detect do |sim|
    sim.instruments_identifier(xcode) == udid_or_name ||
          sim.udid == udid_or_name
  end

  return simulator if !simulator.nil?

  physical_device = instruments.physical_devices.detect do |device|
    device.name == udid_or_name ||
          device.udid == udid_or_name
  end

  return physical_device if !physical_device.nil?

  raise ArgumentError, "Could not find a device with a UDID or name matching '#{udid_or_name}'"
end

Instance Method Details

#instruction_setString

Note:

Finding the instruction set of a device requires a third-party tool like ideviceinfo. Example: ‘$ ideviceinfo -u 89b59 < snip > ab7ba –key ’CPUArchitecture’ => arm64`

Return the instruction set for this device.

Simulator The simulator instruction set will be i386 or x86_64 depending on the the (marketing) name of the device.

Returns:

  • (String)

    An instruction set.

Raises:

  • (RuntimeError)

    Raises an error if this device is a physical device.



242
243
244
245
246
247
248
249
250
251
252
# File 'lib/run_loop/device.rb', line 242

def instruction_set
  if simulator?
    if ['iPhone 4s', 'iPhone 5', 'iPad 2', 'iPad Retina'].include?(self.name)
      'i386'
    else
      'x86_64'
    end
  else
    raise 'Finding the instruction set of a device requires a third-party tool like ideviceinfo'
  end
end

#instruments_identifier(xcode) ⇒ String

Returns and instruments-ready device identifier that is a suitable value for DEVICE_TARGET environment variable.

Parameters:

Returns:

  • (String)

    An instruments-ready device identifier.

Raises:

  • (RuntimeError)

    If trying to obtain a instruments-ready identifier for a simulator when Xcode < 6.



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/run_loop/device.rb', line 189

def instruments_identifier(xcode)
  if physical_device?
    udid
  else
    if version == RunLoop::Version.new('7.0.3')
      version_part = version.to_s
    else
      version_part = "#{version.major}.#{version.minor}"
    end

    if xcode.version_gte_7?
      "#{name} (#{version_part})"
    else
      "#{name} (#{version_part} Simulator)"
    end
  end
end

#physical_device?Boolean

Is this a physical device?

Returns:

  • (Boolean)

    Returns true if this is a device.



209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/run_loop/device.rb', line 209

def physical_device?
  if udid.nil?
    stack = Kernel.caller(0, 6)[0..-1].join("\n")
    raise RuntimeError,
      %Q[udid is nil

#{stack}

   name: #{name}
version: #{version}
]
  end
  !udid[DEVICE_UDID_REGEX, 0].nil?
end

#simulator?Boolean

Is this a simulator?

Returns:

  • (Boolean)

    Returns true if this is a simulator.



226
227
228
# File 'lib/run_loop/device.rb', line 226

def simulator?
  !physical_device?
end

#simulator_allow_location(bundle_id) ⇒ Object

Authorize location permission.

Parameters:

  • bundle_id (String)

    bundle id to authorize

Raises:

  • (RuntimeError)

    if this is a physical device



474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
# File 'lib/run_loop/device.rb', line 474

def simulator_allow_location(bundle_id)
  if physical_device?
    raise RuntimeError, "This method is for Simulators only"
  end

  location_plist = simulator_locationd_plist_path
  unless File.exist? location_plist
    FileUtils.mkdir_p File.dirname location_plist
    pbuddy.create_plist(location_plist)
  end
  pbuddy.plist_set("#{bundle_id}", 'dict', nil, location_plist)
  pbuddy.plist_set("#{bundle_id}:BundleId", 'string', bundle_id, location_plist)
  pbuddy.plist_set("#{bundle_id}:Authorization", 'integer', 4, location_plist)
  pbuddy.plist_set("#{bundle_id}:SupportedAuthorizationMask", 'integer', 7, location_plist)

  # Authorization 2 mask 3 = Location Services granted
  # Authorization 4 mask 7 = Location Services && Background Location Services granted
  #
  # BundleId - String - bundle_id
  # Authorization - Integer - 2
  # SupportedAuthorizationMask - Integer - 3
end