run_loop 2.1.7 → 2.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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