Class: RunLoop::Device

Inherits:
Object
  • Object
show all
Includes:
Shell
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? ? 240 : 120
}

Constants included from Shell

Shell::DEFAULT_OPTIONS

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Shell

run_shell_command, #run_shell_command

Methods included from Encoding

#transliterate

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.



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

def name
  @name
end

#stateObject (readonly)

Returns the value of attribute state.



40
41
42
# File 'lib/run_loop/device.rb', line 40

def state
  @state
end

#udidObject (readonly)

Returns the value of attribute udid.



39
40
41
# File 'lib/run_loop/device.rb', line 39

def udid
  @udid
end

#versionObject (readonly)

Returns the value of attribute version.



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

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

def self.device_with_identifier(udid_or_name, options={})
  if options[:xcode]
    RunLoop.log_warn("device_with_identifier no longer uses :xcode option")
  end

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

  merged_options = default_options.merge(options)

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

  simulator = simctl.simulators.detect do |sim|
    sim.udid == udid_or_name ||
      sim.simulator_instruments_identifier_same_as?(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

#compatible_with_xcode_version?(xcode_version) ⇒ Boolean

Is the iOS version installed on this device compatible with an Xcode version?

Returns:

  • (Boolean)


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/run_loop/device.rb', line 244

def compatible_with_xcode_version?(xcode_version)
  ios_version = version

  if ios_version.major < (xcode_version.major + 2)
    if physical_device?
      return true
    else
      # iOS 8 simulators are available in Xcode 9
      # iOS 7 simulators are not available in Xcode 9
      if ios_version.major <= (xcode_version.major - 2)
        return false
      else
        return true
      end
    end
  end

  if ios_version.major == (xcode_version.major + 2)
    if xcode_version.major == 10 && xcode_version.minor == 3
      return ios_version.minor <= 4
    else
      return ios_version.minor <= xcode_version.minor
    end
  end
  false
end

#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.



283
284
285
286
287
288
289
290
291
292
293
# File 'lib/run_loop/device.rb', line 283

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 = nil) ⇒ String

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

Parameters:

  • xcode (RunLoop::Xcode) (defaults to: nil)

    The version of the active Xcode.

Returns:

  • (String)

    An instruments-ready device identifier.

Raises:

  • (RuntimeError)

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



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/run_loop/device.rb', line 201

def instruments_identifier(xcode=nil)
  if xcode
    RunLoop.deprecated("3.0.0",
                       "instruments_identifier no longer takes an argument")
  end
  if physical_device?
    udid
  else
    if version.patch
      version_part = "#{version.major}.#{version.minor}.#{version.patch}"
    else
      version_part = "#{version.major}.#{version.minor}"
    end

    "#{name} (#{version_part})"
  end
end

#physical_device?Boolean

Is this a physical device?

Returns:

  • (Boolean)

    Returns true if this is a device.



221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/run_loop/device.rb', line 221

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.



238
239
240
# File 'lib/run_loop/device.rb', line 238

def simulator?
  !physical_device?
end

#simulator_instruments_identifier_same_as?(identifier) ⇒ Boolean

iPhone 8 10.3.1 is the same as iPhone 10.3 when comparing identifiers

Returns:

  • (Boolean)


128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/run_loop/device.rb', line 128

def simulator_instruments_identifier_same_as?(identifier)
  instruments_id = instruments_identifier
  return true if instruments_id == identifier

  model_part = identifier.split(" (").first
  return false if model_part != name

  version_part = RunLoop::Version.new(identifier[RunLoop::Regex::VERSION_REGEX])

  return false if version.major != version_part.major
  return false if version.minor != version_part.minor

  true
end