run_loop 2.1.7 → 2.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,15 @@ module RunLoop
5
5
  # @!visibility private
6
6
  #
7
7
  # A wrapper around the test-control binary.
8
- class IOSDeviceManager < RunLoop::DeviceAgent::Launcher
8
+ class IOSDeviceManager < RunLoop::DeviceAgent::LauncherStrategy
9
+
10
+ EXIT_CODES = {
11
+ "0" => :success,
12
+ "2" => :false
13
+ }.freeze
14
+
15
+ require "run_loop/shell"
16
+ include RunLoop::Shell
9
17
 
10
18
  # @!visibility private
11
19
  @@ios_device_manager = nil
@@ -40,7 +48,7 @@ but binary does not exist at that path.
40
48
 
41
49
  # @!visibility private
42
50
  def name
43
- :xctestctl
51
+ :ios_device_manager
44
52
  end
45
53
 
46
54
  # @!visibility private
@@ -55,56 +63,73 @@ but binary does not exist at that path.
55
63
 
56
64
  # @!visibility private
57
65
  def runner
58
- @runner ||= RunLoop::DeviceAgent::CBXRunner.new(device)
66
+ @runner ||= RunLoop::DeviceAgent::Runner.new(device)
59
67
  end
60
68
 
61
69
  # @!visibility private
62
70
  def self.log_file
63
- path = File.join(Launcher.dot_dir, "ios-device-manager.log")
71
+ path = File.join(LauncherStrategy.dot_dir, "ios-device-manager.log")
64
72
  FileUtils.touch(path) if !File.exist?(path)
65
73
  path
66
74
  end
67
75
 
68
76
  # @!visibility private
69
- def launch
77
+ def launch(options)
78
+ code_sign_identity = options[:code_sign_identity]
79
+
70
80
  RunLoop::DeviceAgent::Frameworks.instance.install
81
+ cmd = RunLoop::DeviceAgent::IOSDeviceManager.ios_device_manager
71
82
 
72
- # WIP: it is unclear what the behavior should be.
83
+ start = Time.now
73
84
  if device.simulator?
74
- # Simulator cannot be running for this version.
75
- CoreSimulator.quit_simulator
85
+ cbxapp = RunLoop::App.new(runner.runner)
76
86
 
77
- CoreSimulator.wait_for_simulator_state(device, "Shutdown")
87
+ # quits the simulator
88
+ sim = CoreSimulator.new(device, cbxapp)
89
+ sim.install
90
+ sim.launch_simulator
91
+ else
78
92
 
79
- # TODO: run-loop is responsible for detecting an outdated CBX-Runner
80
- # application and installing a new one. However, iOSDeviceManager
81
- # fails if simulator is already running.
93
+ if !code_sign_identity
94
+ raise ArgumentError, %Q[
95
+ Targeting a physical devices requires a code signing identity.
82
96
 
83
- # cbxapp = RunLoop::App.new(runner.runner)
84
- #
85
- # # quits the simulator
86
- # sim = CoreSimulator.new(device, cbxapp)
87
- # sim.install
88
- # sim.launch_simulator
89
- end
97
+ Rerun your test with:
90
98
 
91
- cmd = RunLoop::DeviceAgent::IOSDeviceManager.ios_device_manager
99
+ $ CODE_SIGN_IDENTITY="iPhone Developer: Your Name (ABCDEF1234)" cucumber
92
100
 
93
- args = ["start_test",
94
- "-r", runner.runner,
95
- "-t", runner.tester,
96
- "-d", device.udid]
101
+ ]
102
+ end
97
103
 
98
- code_sign_identity = RunLoop::Environment.code_sign_identity
99
- if !code_sign_identity
100
- code_sign_identity = "iPhone Developer"
101
- end
104
+ options = {:log_cmd => true}
105
+ args = [
106
+ cmd, "install",
107
+ "--device-id", device.udid,
108
+ "--app-bundle", runner.runner,
109
+ "--codesign-identity", code_sign_identity
110
+ ]
102
111
 
103
- if device.physical_device?
104
- args << "-c"
105
- args << code_sign_identity
112
+ start = Time.now
113
+ hash = run_shell_command(args, options)
114
+
115
+
116
+ if hash[:exit_status] != 0
117
+ raise RuntimeError, %Q[
118
+
119
+ Could not install #{runner.runner}. iOSDeviceManager says:
120
+
121
+ #{hash[:out]}
122
+
123
+ ]
124
+ end
106
125
  end
107
126
 
127
+ RunLoop::log_debug("Took #{Time.now - start} seconds to install DeviceAgent");
128
+
129
+ cmd = RunLoop::DeviceAgent::IOSDeviceManager.ios_device_manager
130
+
131
+ args = ["start_test", "--device-id", device.udid]
132
+
108
133
  log_file = IOSDeviceManager.log_file
109
134
  FileUtils.rm_rf(log_file)
110
135
  FileUtils.touch(log_file)
@@ -122,11 +147,51 @@ but binary does not exist at that path.
122
147
  Process.detach(pid)
123
148
 
124
149
  if device.simulator?
125
- device.simulator_wait_for_stable_state
150
+ # Give it a whirl.
151
+ # device.simulator_wait_for_stable_state
126
152
  end
127
153
 
128
154
  pid.to_i
129
155
  end
156
+
157
+ def app_installed?(bundle_identifier)
158
+ options = {:log_cmd => true}
159
+
160
+ cmd = RunLoop::DeviceAgent::IOSDeviceManager.ios_device_manager
161
+
162
+ args = [
163
+ cmd, "is_installed",
164
+ "--device-id", device.udid,
165
+ "--bundle-identifier", bundle_identifier
166
+ ]
167
+
168
+ start = Time.now
169
+ hash = run_shell_command(args, options)
170
+
171
+ exit_status = EXIT_CODES[hash[:exit_status].to_s]
172
+ if exit_status == :success
173
+ true
174
+ elsif exit_status == :false
175
+ false
176
+ else
177
+ raise RuntimeError, %Q[
178
+
179
+ Could not check if app is installed:
180
+
181
+ bundle identifier: #{bundle_identifier}
182
+ device: #{device}
183
+
184
+ iOSDeviceManager says:
185
+
186
+ #{hash[:out]}
187
+
188
+ ]
189
+ end
190
+
191
+ RunLoop::log_debug("Took #{Time.now - start} seconds to check if app was installed");
192
+
193
+ hash[:exit_status] == 0
194
+ end
130
195
  end
131
196
  end
132
197
  end
@@ -4,9 +4,9 @@ module RunLoop
4
4
  module DeviceAgent
5
5
  # @!visibility private
6
6
  #
7
- # An abstract base class for something that can launch the CBXRunner on a
8
- # device. The CBXRunner is AKA the DeviceAgent.
9
- class Launcher
7
+ # A base class for something that can launch the DeviceAgent-Runner on a
8
+ # device.
9
+ class LauncherStrategy
10
10
  require "run_loop/abstract"
11
11
  include RunLoop::Abstract
12
12
 
@@ -14,7 +14,7 @@ module RunLoop
14
14
  attr_reader :device
15
15
 
16
16
  # @!visibility private
17
- # @param [RunLoop::Device] device where to launch the CBX-Runner
17
+ # @param [RunLoop::Device] device where to launch the DeviceAgent-Runner
18
18
  def initialize(device)
19
19
  @device = device
20
20
 
@@ -24,7 +24,7 @@ Invalid device:
24
24
 
25
25
  #{device}
26
26
 
27
- XCUITest is only available for iOS >= 9.0
27
+ DeviceAgent is only available for iOS >= 9.0
28
28
  ]
29
29
  end
30
30
  end
@@ -32,15 +32,15 @@ XCUITest is only available for iOS >= 9.0
32
32
  # @!visibility private
33
33
  # The name of this launcher. Must be a symbol (keyword). This value will
34
34
  # be used for the key :cbx_launcher in the RunLoop::Cache so Calabash
35
- # iOS can attach and reattach to an XCUITest instance.
35
+ # iOS can attach and reattach to a DeviceAgent instance.
36
36
  def name
37
37
  abstract_method!
38
38
  end
39
39
 
40
40
  # @!visibility private
41
41
  #
42
- # Does whatever it takes to launch the CBX-Runner on the device.
43
- def launch
42
+ # Does whatever it takes to launch the DeviceAgent-Runner on the device.
43
+ def launch(options)
44
44
  abstract_method!
45
45
  end
46
46
 
@@ -3,8 +3,7 @@ module RunLoop
3
3
  # @!visibility private
4
4
  module DeviceAgent
5
5
  # @!visibility private
6
- class CBXRunner
7
-
6
+ class Runner
8
7
 
9
8
  # @!visibility private
10
9
  @@cbxdevice = nil
@@ -19,7 +18,7 @@ module RunLoop
19
18
 
20
19
  # @!visibility private
21
20
  def self.detect_cbxsim
22
- @@cbxsim ||= lambda do
21
+ @@cbxsim ||= begin
23
22
  from_env = RunLoop::Environment.cbxsim
24
23
 
25
24
  if from_env
@@ -37,12 +36,12 @@ but runner does not exist at that path.
37
36
  else
38
37
  self.default_cbxsim
39
38
  end
40
- end.call
39
+ end
41
40
  end
42
41
 
43
42
  # @!visibility private
44
43
  def self.detect_cbxdevice
45
- @@cbxdevice ||= lambda do
44
+ @@cbxdevice ||= begin
46
45
  from_env = RunLoop::Environment.cbxdevice
47
46
 
48
47
  if from_env
@@ -60,12 +59,12 @@ but runner does not exist at that path.
60
59
  else
61
60
  self.default_cbxdevice
62
61
  end
63
- end.call
62
+ end
64
63
  end
65
64
 
66
65
  # @!visibility private
67
66
  def self.default_cbxdevice
68
- cbx = File.join(self.device_agent_dir, "ipa", "CBX-Runner.app")
67
+ cbx = File.join(self.device_agent_dir, "ipa", "DeviceAgent-Runner.app")
69
68
 
70
69
  if !File.exist?(cbx)
71
70
  self.expand_runner_archive("#{cbx}.zip")
@@ -76,7 +75,7 @@ but runner does not exist at that path.
76
75
 
77
76
  # @!visibility private
78
77
  def self.default_cbxsim
79
- cbx = File.join(self.device_agent_dir, "app", "CBX-Runner.app")
78
+ cbx = File.join(self.device_agent_dir, "app", "DeviceAgent-Runner.app")
80
79
 
81
80
  if !File.exist?(cbx)
82
81
  self.expand_runner_archive("#{cbx}.zip")
@@ -86,22 +85,13 @@ but runner does not exist at that path.
86
85
  end
87
86
 
88
87
  # @!visibility private
89
- # TODO move this behavior to shell.rb - should be able to call Shell.run_unix_command
90
88
  def self.expand_runner_archive(archive)
91
- shell = Class.new do
92
- require "run_loop/shell"
93
- include RunLoop::Shell
94
- def to_s; "#<CBXRunner Shell>"; end
95
- def inspect; to_s; end
96
- end.new
97
-
98
89
  dir = File.dirname(archive)
99
90
  options = { :log_cmd => true }
100
91
  Dir.chdir(dir) do
101
- RunLoop.log_unix_cmd("cd #{dir}")
102
- shell.run_shell_command(["ditto", "-xk", File.basename(archive), "."], options)
92
+ Shell.run_shell_command(["ditto", "-xk", File.basename(archive), "."], options)
103
93
  end
104
- File.join(dir, "CBX-Runner.app")
94
+ File.join(dir, "DeviceAgent-Runner.app")
105
95
  end
106
96
 
107
97
  # @!visibility private
@@ -115,18 +105,18 @@ but runner does not exist at that path.
115
105
 
116
106
  # @!visibility private
117
107
  def runner
118
- @runner ||= lambda do
108
+ @runner ||= begin
119
109
  if device.physical_device?
120
- RunLoop::DeviceAgent::CBXRunner.detect_cbxdevice
110
+ RunLoop::DeviceAgent::Runner.detect_cbxdevice
121
111
  else
122
- RunLoop::DeviceAgent::CBXRunner.detect_cbxsim
112
+ RunLoop::DeviceAgent::Runner.detect_cbxsim
123
113
  end
124
- end.call
114
+ end
125
115
  end
126
116
 
127
117
  # @!visibility private
128
118
  def tester
129
- @tester ||= File.join(runner, "PlugIns", "CBX.xctest")
119
+ @tester ||= File.join(runner, "PlugIns", "DeviceAgent.xctest")
130
120
  end
131
121
 
132
122
  # @!visibility private
@@ -143,7 +133,7 @@ but runner does not exist at that path.
143
133
 
144
134
  # @!visibility private
145
135
  def info_plist
146
- @info_plist ||= File.join(runner, "PlugIns", "CBX.xctest", "Info.plist")
136
+ @info_plist ||= File.join(runner, "Info.plist")
147
137
  end
148
138
 
149
139
  # @!visibility private
@@ -5,7 +5,7 @@ module RunLoop
5
5
  module DeviceAgent
6
6
 
7
7
  # @!visibility private
8
- class Xcodebuild < RunLoop::DeviceAgent::Launcher
8
+ class Xcodebuild < RunLoop::DeviceAgent::LauncherStrategy
9
9
 
10
10
  # @!visibility private
11
11
  def self.log_file
@@ -30,7 +30,7 @@ module RunLoop
30
30
  end
31
31
 
32
32
  # @!visibility private
33
- def launch
33
+ def launch(_)
34
34
  workspace
35
35
 
36
36
  if device.simulator?
@@ -48,14 +48,25 @@ module RunLoop
48
48
 
49
49
  # @!visibility private
50
50
  def workspace
51
- @workspace ||= lambda do
52
- path = RunLoop::Environment.send(:cbxws)
53
- if path
51
+ @workspace ||= begin
52
+ path = RunLoop::Environment.send(:cbxws) || default_workspace
53
+
54
+ if File.exist?(path)
54
55
  path
55
56
  else
56
- raise "The CBXWS env var is undefined. Are you a maintainer?"
57
+ raise(RuntimeError, %Q[
58
+ Cannot find the DeviceAgent.xcworkspace.
59
+
60
+ Expected it here:
61
+
62
+ #{path}
63
+
64
+ Use the CBXWS environment variable to override the default.
65
+
66
+ ])
67
+
57
68
  end
58
- end.call
69
+ end
59
70
  end
60
71
 
61
72
  # @!visibility private
@@ -68,7 +79,7 @@ module RunLoop
68
79
  args = [
69
80
  "xcrun",
70
81
  "xcodebuild",
71
- "-scheme", "CBXAppStub",
82
+ "-scheme", "AppStub",
72
83
  "-workspace", workspace,
73
84
  "-config", "Debug",
74
85
  "-destination",
@@ -95,6 +106,12 @@ module RunLoop
95
106
  Process.detach(pid)
96
107
  pid.to_i
97
108
  end
109
+
110
+ def default_workspace
111
+ this_dir = File.expand_path(File.dirname(__FILE__))
112
+ relative = File.expand_path(File.join(this_dir, "..", "..", "..", ".."))
113
+ File.join(relative, "DeviceAgent.iOS/DeviceAgent.xcworkspace")
114
+ end
98
115
  end
99
116
  end
100
117
  end
@@ -316,9 +316,11 @@ module RunLoop
316
316
  end
317
317
 
318
318
  # !@visibility private
319
- # Returns the value of CBXWS. This can be used to override the default
320
- # CBXDriver.xcworkspace. You should only set this if you are actively
321
- # developing the CBXDriver.
319
+ # Returns the value of CBXWS. This can be used in conjunction with
320
+ # :cbx_launcher => :xcodebuild to launch the DeviceAgent rather than
321
+ # iOSDeviceManager.
322
+ #
323
+ # You should only set this if you are actively developing the DeviceAgent.
322
324
  def self.cbxws
323
325
  value = ENV["CBXWS"]
324
326
  if value.nil? || value == ""
@@ -326,12 +328,14 @@ module RunLoop
326
328
  else
327
329
  path = File.expand_path(value)
328
330
  if !File.directory?(path)
329
- raise RuntimeError, %Q[CBXWS is set, but there is no workspace at
331
+ raise RuntimeError, %Q[
332
+ CBXWS is set, but there is no workspace at
333
+
330
334
  #{path}
331
335
 
332
- Only set CBXWS if you are developing new features in the CBXRunner.
336
+ Only set CBXWS if you are developing new features in the DeviceAgent.
333
337
 
334
- Check your environment.]
338
+ ]
335
339
  end
336
340
  path
337
341
  end
@@ -113,7 +113,13 @@ module RunLoop
113
113
  interval = options.fetch(:interval, @interval)
114
114
  header = options.fetch(:header, HEADER)
115
115
 
116
- RunLoop.log_debug("HTTP: #{request_method} #{@server.endpoint + request.route} #{options}")
116
+ if RunLoop::Environment.debug?
117
+ http_options = {
118
+ :retries => retries,
119
+ :timeout => timeout
120
+ }
121
+ RunLoop.log_debug("HTTP: #{request_method} #{@server.endpoint + request.route} #{http_options}")
122
+ end
117
123
 
118
124
  start_time = Time.now
119
125
  last_error = nil
@@ -24,6 +24,7 @@ module RunLoop
24
24
  "Shutdown" => 1,
25
25
  "Shutting Down" => 2,
26
26
  "Booted" => 3,
27
+ "Plist Missing" => -1
27
28
  }.freeze
28
29
 
29
30
  # @!visibility private
@@ -38,24 +39,28 @@ module RunLoop
38
39
 
39
40
  # @!visibility private
40
41
  def self.ensure_valid_core_simulator_service
42
+ max_tries = 3
43
+ valid = false
44
+ 3.times do |try|
45
+ valid = self.valid_core_simulator_service?
46
+ break if valid
47
+ RunLoop.log_debug("Invalid CoreSimulator service for active Xcode: try #{try + 1} of #{max_tries}")
48
+ end
49
+ valid
50
+ end
51
+
52
+ # @!visibility private
53
+ def self.valid_core_simulator_service?
41
54
  require "run_loop/shell"
42
55
  args = ["xcrun", "simctl", "help"]
43
56
 
44
- max_tries = 3
45
- 3.times do |try|
46
- hash = {}
47
- begin
48
- hash = Shell.run_shell_command(args)
49
- if hash[:exit_status] != 0
50
- RunLoop.log_debug("Invalid CoreSimulator service for active Xcode: try #{try + 1} of #{max_tries}")
51
- else
52
- return true
53
- end
54
- rescue RunLoop::Shell::Error => _
55
- RunLoop.log_debug("Invalid CoreSimulator service for active Xcode, retrying #{try + 1} of #{max_tries}")
56
- end
57
+ begin
58
+ hash = Shell.run_shell_command(args)
59
+ hash[:exit_status] == 0 &&
60
+ !hash[:out][/Failed to locate a valid instance of CoreSimulatorService/]
61
+ rescue RunLoop::Shell::Error => _
62
+ false
57
63
  end
58
- false
59
64
  end
60
65
 
61
66
  # @!visibility private
@@ -119,7 +124,11 @@ module RunLoop
119
124
  # @!visibility private
120
125
  def simulator_state_as_int(device)
121
126
  plist = device.simulator_device_plist
122
- pbuddy.plist_read("state", plist).to_i
127
+ if File.exist?(plist)
128
+ pbuddy.plist_read("state", plist).to_i
129
+ else
130
+ SIM_STATES["Plist Missing"]
131
+ end
123
132
  end
124
133
 
125
134
  # @!visibility private
@@ -1,5 +1,5 @@
1
1
  module RunLoop
2
- VERSION = "2.1.7"
2
+ VERSION = "2.1.8"
3
3
 
4
4
  # A model of a software release version that can be used to compare two versions.
5
5
  #
@@ -7,12 +7,17 @@ module RunLoop
7
7
  # However, the semantic versioning spec is incompatible with RubyGem's patterns
8
8
  # for pre-release gems.
9
9
  #
10
- # > "But returning to the practical: No release version of SemVer is compatible with Rubygems." - _David Kellum_
10
+ # > "But returning to the practical: No release version of SemVer is compatible
11
+ # > with Rubygems." - _David Kellum_
11
12
  #
12
13
  # Calabash and RunLoop version numbers will be in the form `<major>.<minor>.<patch>[.pre<N>]`.
13
14
  #
14
15
  # @see http://semver.org/
15
16
  # @see http://gravitext.com/2012/07/22/versioning.html
17
+ #
18
+ # TODO Expand to handle versions with more than 1 "." and no "."
19
+ # ^ Needs to handle arbitrary versions from Info.plists. In particular it
20
+ # needs to handle a unix timestamp - found the DeviceAgent-Runner.app.
16
21
  class Version
17
22
 
18
23
  # @!attribute [rw] major
@@ -237,7 +237,7 @@ module RunLoop
237
237
  # @raise [RuntimeError] If path to Xcode.app/Contents/Developer
238
238
  # cannot be determined.
239
239
  def developer_dir
240
- @xcode_developer_dir ||= lambda do
240
+ @xcode_developer_dir ||= begin
241
241
  if RunLoop::Environment.developer_dir
242
242
  path = RunLoop::Environment.developer_dir
243
243
  else
@@ -267,7 +267,7 @@ $ man xcode-select
267
267
  }
268
268
  end
269
269
  path
270
- end.call
270
+ end
271
271
  end
272
272
 
273
273
  private
data/lib/run_loop.rb CHANGED
@@ -20,9 +20,14 @@ require 'run_loop/plist_buddy'
20
20
  require "run_loop/codesign"
21
21
  require 'run_loop/app'
22
22
  require 'run_loop/ipa'
23
- require "run_loop/device_agent/cbxrunner"
23
+ require "run_loop/http/error"
24
+ require "run_loop/http/server"
25
+ require "run_loop/http/request"
26
+ require "run_loop/http/retriable_client"
27
+ require "run_loop/device_agent/client"
28
+ require "run_loop/device_agent/runner"
24
29
  require "run_loop/device_agent/frameworks"
25
- require "run_loop/device_agent/launcher"
30
+ require "run_loop/device_agent/launcher_strategy"
26
31
  require "run_loop/device_agent/ios_device_manager"
27
32
  require "run_loop/device_agent/xcodebuild"
28
33
  require "run_loop/detect_aut/errors"
@@ -42,11 +47,6 @@ require "run_loop/simctl"
42
47
  require 'run_loop/template'
43
48
  require "run_loop/locale"
44
49
  require "run_loop/language"
45
- require "run_loop/xcuitest"
46
- require "run_loop/http/error"
47
- require "run_loop/http/server"
48
- require "run_loop/http/request"
49
- require "run_loop/http/retriable_client"
50
50
  require "run_loop/physical_device/life_cycle"
51
51
  require "run_loop/physical_device/ios_device_manager"
52
52
 
@@ -110,7 +110,7 @@ module RunLoop
110
110
 
111
111
  gesture_performer = RunLoop.detect_gesture_performer(cloned_options, xcode, device)
112
112
  if gesture_performer == :device_agent
113
- RunLoop::XCUITest.run(cloned_options)
113
+ RunLoop::DeviceAgent::Client.run(cloned_options)
114
114
  else
115
115
  if RunLoop::Instruments.new.instruments_app_running?
116
116
  raise %q(The Instruments.app is open.
@@ -261,8 +261,8 @@ Invalid Xcode and iOS combination:
261
261
  Xcode version: #{xcode.version.to_s}
262
262
  iOS version: #{device.version.to_s}
263
263
 
264
- Calabash cannot test iOS < 9.0 using Xcode 8 because XCUITest is not compatible
265
- with iOS < 9.0 and UIAutomation is not available in Xcode 8.
264
+ Calabash cannot test iOS < 9.0 using Xcode 8 because DeviceAgent is not
265
+ compatible with iOS < 9.0 and UIAutomation is not available in Xcode 8.
266
266
 
267
267
  You can rerun your test if you have Xcode 7 installed:
268
268
 
@@ -280,7 +280,7 @@ $ DEVELOPER_DIR=/path/to/Xcode/7.3.1/Xcode.app/Contents/Developer cucumber
280
280
  #
281
281
  # First pass at choosing the correct code path.
282
282
  #
283
- # We don't know if we can test on iOS 8 with UIAutomation or XCUITest on
283
+ # We don't know if we can test on iOS 8 with UIAutomation or DeviceAgent on
284
284
  # Xcode 8.
285
285
  #
286
286
  # @param [Hash] options The options passed by the user
@@ -311,8 +311,8 @@ Invalid Xcode and iOS combination:
311
311
  Xcode version: #{xcode.version.to_s}
312
312
  iOS version: #{device.version.to_s}
313
313
 
314
- Calabash cannot test iOS < 9.0 using Xcode 8 because XCUITest is not compatible
315
- with iOS < 9.0 and UIAutomation is not available in Xcode 8.
314
+ Calabash cannot test iOS < 9.0 using Xcode 8 because DeviceAgent is not
315
+ compatible with iOS < 9.0 and UIAutomation is not available in Xcode 8.
316
316
 
317
317
  You can rerun your test if you have Xcode 7 installed:
318
318