run_loop_tcc 2.1.3
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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/bin/run-loop +19 -0
- data/lib/run_loop/abstract.rb +18 -0
- data/lib/run_loop/app.rb +372 -0
- data/lib/run_loop/cache/cache.rb +68 -0
- data/lib/run_loop/cli/cli.rb +48 -0
- data/lib/run_loop/cli/codesign.rb +24 -0
- data/lib/run_loop/cli/errors.rb +11 -0
- data/lib/run_loop/cli/instruments.rb +160 -0
- data/lib/run_loop/cli/locale.rb +31 -0
- data/lib/run_loop/cli/simctl.rb +257 -0
- data/lib/run_loop/cli/tcc.rb +139 -0
- data/lib/run_loop/codesign.rb +76 -0
- data/lib/run_loop/core.rb +902 -0
- data/lib/run_loop/core_simulator.rb +960 -0
- data/lib/run_loop/detect_aut/detect.rb +185 -0
- data/lib/run_loop/detect_aut/errors.rb +126 -0
- data/lib/run_loop/detect_aut/xamarin_studio.rb +46 -0
- data/lib/run_loop/detect_aut/xcode.rb +157 -0
- data/lib/run_loop/device.rb +722 -0
- data/lib/run_loop/device_agent/app/CBX-Runner.app.zip +0 -0
- data/lib/run_loop/device_agent/bin/xctestctl +0 -0
- data/lib/run_loop/device_agent/cbxrunner.rb +156 -0
- data/lib/run_loop/device_agent/frameworks/Frameworks.zip +0 -0
- data/lib/run_loop/device_agent/frameworks.rb +65 -0
- data/lib/run_loop/device_agent/ipa/CBX-Runner.app.zip +0 -0
- data/lib/run_loop/device_agent/launcher.rb +51 -0
- data/lib/run_loop/device_agent/xcodebuild.rb +91 -0
- data/lib/run_loop/device_agent/xctestctl.rb +109 -0
- data/lib/run_loop/directory.rb +179 -0
- data/lib/run_loop/dnssd.rb +148 -0
- data/lib/run_loop/dot_dir.rb +87 -0
- data/lib/run_loop/dylib_injector.rb +145 -0
- data/lib/run_loop/encoding.rb +56 -0
- data/lib/run_loop/environment.rb +361 -0
- data/lib/run_loop/fifo.rb +40 -0
- data/lib/run_loop/host_cache.rb +128 -0
- data/lib/run_loop/http/error.rb +15 -0
- data/lib/run_loop/http/request.rb +44 -0
- data/lib/run_loop/http/retriable_client.rb +166 -0
- data/lib/run_loop/http/server.rb +17 -0
- data/lib/run_loop/instruments.rb +436 -0
- data/lib/run_loop/ipa.rb +142 -0
- data/lib/run_loop/l10n.rb +93 -0
- data/lib/run_loop/language.rb +63 -0
- data/lib/run_loop/lipo.rb +132 -0
- data/lib/run_loop/lldb.rb +52 -0
- data/lib/run_loop/locale.rb +101 -0
- data/lib/run_loop/logging.rb +111 -0
- data/lib/run_loop/otool.rb +76 -0
- data/lib/run_loop/patches/awesome_print.rb +17 -0
- data/lib/run_loop/physical_device/life_cycle.rb +268 -0
- data/lib/run_loop/plist_buddy.rb +189 -0
- data/lib/run_loop/process_terminator.rb +128 -0
- data/lib/run_loop/process_waiter.rb +117 -0
- data/lib/run_loop/regex.rb +19 -0
- data/lib/run_loop/shell.rb +103 -0
- data/lib/run_loop/sim_control.rb +1264 -0
- data/lib/run_loop/simctl.rb +275 -0
- data/lib/run_loop/sqlite.rb +61 -0
- data/lib/run_loop/strings.rb +88 -0
- data/lib/run_loop/tcc/TCC.db +0 -0
- data/lib/run_loop/tcc/tcc.rb +240 -0
- data/lib/run_loop/template.rb +61 -0
- data/lib/run_loop/version.rb +182 -0
- data/lib/run_loop/xcode.rb +318 -0
- data/lib/run_loop/xcrun.rb +107 -0
- data/lib/run_loop/xcuitest.rb +550 -0
- data/lib/run_loop.rb +230 -0
- data/plists/simctl/com.apple.UIAutomation.plist +0 -0
- data/plists/simctl/com.apple.UIAutomationPlugIn.plist +0 -0
- data/scripts/calabash_script_uia.js +28184 -0
- data/scripts/lib/json2.min.js +26 -0
- data/scripts/lib/log.js +26 -0
- data/scripts/lib/on_alert.js +224 -0
- data/scripts/read-cmd.sh +2 -0
- data/scripts/run_dismiss_location.js +89 -0
- data/scripts/run_loop_basic.js +34 -0
- data/scripts/run_loop_fast_uia.js +188 -0
- data/scripts/run_loop_host.js +117 -0
- data/scripts/run_loop_shared_element.js +125 -0
- data/scripts/timeout3 +23 -0
- data/scripts/udidetect +0 -0
- data/vendor-licenses/FBSimulatorControl.LICENSE +30 -0
- data/vendor-licenses/xctestctl.LICENSE +32 -0
- metadata +443 -0
@@ -0,0 +1,550 @@
|
|
1
|
+
module RunLoop
|
2
|
+
|
3
|
+
# @!visibility private
|
4
|
+
class XCUITest
|
5
|
+
|
6
|
+
require "run_loop/shell"
|
7
|
+
include RunLoop::Shell
|
8
|
+
|
9
|
+
require "run_loop/encoding"
|
10
|
+
include RunLoop::Encoding
|
11
|
+
|
12
|
+
class HTTPError < RuntimeError; end
|
13
|
+
|
14
|
+
# @!visibility private
|
15
|
+
DEFAULTS = {
|
16
|
+
:port => 27753,
|
17
|
+
:simulator_ip => "127.0.0.1",
|
18
|
+
:http_timeout => RunLoop::Environment.ci? ? 120 : 60,
|
19
|
+
:version => "1.0"
|
20
|
+
}
|
21
|
+
|
22
|
+
# @!visibility private
|
23
|
+
def self.run(options={})
|
24
|
+
# logger = options[:logger]
|
25
|
+
simctl = options[:sim_control] || options[:simctl] || RunLoop::Simctl.new
|
26
|
+
xcode = options[:xcode] || RunLoop::Xcode.new
|
27
|
+
instruments = options[:instruments] || RunLoop::Instruments.new
|
28
|
+
|
29
|
+
# Find the Device under test, the App under test, UIA strategy, and reset options
|
30
|
+
device = RunLoop::Device.detect_device(options, xcode, simctl, instruments)
|
31
|
+
app_details = RunLoop::DetectAUT.detect_app_under_test(options)
|
32
|
+
reset_options = RunLoop::Core.send(:detect_reset_options, options)
|
33
|
+
|
34
|
+
app = app_details[:app]
|
35
|
+
bundle_id = app_details[:bundle_id]
|
36
|
+
|
37
|
+
if device.simulator? && app
|
38
|
+
core_sim = RunLoop::CoreSimulator.new(device, app, :xcode => xcode)
|
39
|
+
if reset_options
|
40
|
+
core_sim.reset_app_sandbox
|
41
|
+
end
|
42
|
+
|
43
|
+
simctl.ensure_software_keyboard(device)
|
44
|
+
core_sim.install
|
45
|
+
end
|
46
|
+
|
47
|
+
cbx_launcher = XCUITest.detect_cbx_launcher(options, device)
|
48
|
+
|
49
|
+
xcuitest = RunLoop::XCUITest.new(bundle_id, device, cbx_launcher)
|
50
|
+
xcuitest.launch
|
51
|
+
xcuitest
|
52
|
+
end
|
53
|
+
|
54
|
+
# @!visibility private
|
55
|
+
#
|
56
|
+
# @param [RunLoop::Device] device the device under test
|
57
|
+
def self.default_cbx_launcher(device)
|
58
|
+
RunLoop::DeviceAgent::XCTestctl.new(device)
|
59
|
+
end
|
60
|
+
|
61
|
+
# @!visibility private
|
62
|
+
# @param [Hash] options the options passed by the user
|
63
|
+
# @param [RunLoop::Device] device the device under test
|
64
|
+
def self.detect_cbx_launcher(options, device)
|
65
|
+
value = options[:cbx_launcher]
|
66
|
+
if value
|
67
|
+
if value == :xcodebuild
|
68
|
+
RunLoop::DeviceAgent::Xcodebuild.new(device)
|
69
|
+
elsif value == :xctestctl
|
70
|
+
RunLoop::DeviceAgent::XCTestctl.new(device)
|
71
|
+
else
|
72
|
+
raise(ArgumentError,
|
73
|
+
"Expected :cbx_launcher => #{value} to be :xcodebuild or :xctestctl")
|
74
|
+
end
|
75
|
+
else
|
76
|
+
XCUITest.default_cbx_launcher(device)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
attr_reader :bundle_id, :device, :cbx_launcher
|
81
|
+
|
82
|
+
# @!visibility private
|
83
|
+
#
|
84
|
+
# The app with `bundle_id` needs to be installed.
|
85
|
+
#
|
86
|
+
# @param [String] bundle_id The identifier of the app under test.
|
87
|
+
# @param [RunLoop::Device] device The device device.
|
88
|
+
def initialize(bundle_id, device, cbx_launcher)
|
89
|
+
@bundle_id = bundle_id
|
90
|
+
@device = device
|
91
|
+
@cbx_launcher = cbx_launcher
|
92
|
+
end
|
93
|
+
|
94
|
+
# @!visibility private
|
95
|
+
def to_s
|
96
|
+
"#<XCUITest #{url} : #{bundle_id} : #{device} : #{cbx_launcher}>"
|
97
|
+
end
|
98
|
+
|
99
|
+
# @!visibility private
|
100
|
+
def inspect
|
101
|
+
to_s
|
102
|
+
end
|
103
|
+
|
104
|
+
# @!visibility private
|
105
|
+
def launch
|
106
|
+
start = Time.now
|
107
|
+
launch_cbx_runner
|
108
|
+
launch_aut
|
109
|
+
elapsed = Time.now - start
|
110
|
+
RunLoop.log_debug("Took #{elapsed} seconds to launch #{bundle_id} on #{device}")
|
111
|
+
true
|
112
|
+
end
|
113
|
+
|
114
|
+
# @!visibility private
|
115
|
+
def running?
|
116
|
+
begin
|
117
|
+
health(ping_options)
|
118
|
+
rescue => _
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# @!visibility private
|
124
|
+
def stop
|
125
|
+
begin
|
126
|
+
shutdown
|
127
|
+
rescue => _
|
128
|
+
nil
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# @!visibility private
|
133
|
+
def launch_other_app(bundle_id)
|
134
|
+
launch_aut(bundle_id)
|
135
|
+
end
|
136
|
+
|
137
|
+
# @!visibility private
|
138
|
+
def runtime
|
139
|
+
options = http_options
|
140
|
+
request = request("device")
|
141
|
+
client = client(options)
|
142
|
+
response = client.get(request)
|
143
|
+
expect_200_response(response)
|
144
|
+
end
|
145
|
+
|
146
|
+
# @!visibility private
|
147
|
+
def tree
|
148
|
+
options = http_options
|
149
|
+
request = request("tree")
|
150
|
+
client = client(options)
|
151
|
+
response = client.get(request)
|
152
|
+
expect_200_response(response)
|
153
|
+
end
|
154
|
+
|
155
|
+
# @!visibility private
|
156
|
+
def keyboard_visible?
|
157
|
+
options = http_options
|
158
|
+
parameters = { :type => "Keyboard" }
|
159
|
+
request = request("query", parameters)
|
160
|
+
client = client(options)
|
161
|
+
response = client.post(request)
|
162
|
+
hash = expect_200_response(response)
|
163
|
+
result = hash["result"]
|
164
|
+
result.count != 0
|
165
|
+
end
|
166
|
+
|
167
|
+
# @!visibility private
|
168
|
+
def enter_text(string)
|
169
|
+
if !keyboard_visible?
|
170
|
+
raise RuntimeError, "Keyboard must be visible"
|
171
|
+
end
|
172
|
+
options = http_options
|
173
|
+
parameters = {
|
174
|
+
:gesture => "enter_text",
|
175
|
+
:options => {
|
176
|
+
:string => string
|
177
|
+
}
|
178
|
+
}
|
179
|
+
request = request("gesture", parameters)
|
180
|
+
client = client(options)
|
181
|
+
response = client.post(request)
|
182
|
+
expect_200_response(response)
|
183
|
+
end
|
184
|
+
|
185
|
+
# @!visibility private
|
186
|
+
def query(mark)
|
187
|
+
options = http_options
|
188
|
+
parameters = { :id => mark }
|
189
|
+
request = request("query", parameters)
|
190
|
+
client = client(options)
|
191
|
+
response = client.post(request)
|
192
|
+
expect_200_response(response)
|
193
|
+
end
|
194
|
+
|
195
|
+
# @!visibility private
|
196
|
+
def query_for_coordinate(mark)
|
197
|
+
body = query(mark)
|
198
|
+
coordinate_from_query_result(body)
|
199
|
+
end
|
200
|
+
|
201
|
+
# @!visibility private
|
202
|
+
def touch(mark, options={})
|
203
|
+
coordinate = query_for_coordinate(mark)
|
204
|
+
perform_coordinate_gesture("touch",
|
205
|
+
coordinate[:x], coordinate[:y],
|
206
|
+
options)
|
207
|
+
end
|
208
|
+
|
209
|
+
alias_method :tap, :touch
|
210
|
+
|
211
|
+
# @!visibility private
|
212
|
+
def double_tap(mark, options={})
|
213
|
+
coordinate = query_for_coordinate(mark)
|
214
|
+
perform_coordinate_gesture("double_tap",
|
215
|
+
coordinate[:x], coordinate[:y],
|
216
|
+
options)
|
217
|
+
end
|
218
|
+
|
219
|
+
# @!visibility private
|
220
|
+
def two_finger_tap(mark, options={})
|
221
|
+
coordinate = query_for_coordinate(mark)
|
222
|
+
perform_coordinate_gesture("two_finger_tap",
|
223
|
+
coordinate[:x], coordinate[:y],
|
224
|
+
options)
|
225
|
+
end
|
226
|
+
|
227
|
+
# @!visibility private
|
228
|
+
def rotate_home_button_to(position, sleep_for=1.0)
|
229
|
+
orientation = orientation_for_position(position)
|
230
|
+
parameters = {
|
231
|
+
:orientation => orientation
|
232
|
+
}
|
233
|
+
request = request("rotate_home_button_to", parameters)
|
234
|
+
client = client(http_options)
|
235
|
+
response = client.post(request)
|
236
|
+
json = expect_200_response(response)
|
237
|
+
sleep(sleep_for)
|
238
|
+
json
|
239
|
+
end
|
240
|
+
|
241
|
+
# @!visibility private
|
242
|
+
def perform_coordinate_gesture(gesture, x, y, options={})
|
243
|
+
parameters = {
|
244
|
+
:gesture => gesture,
|
245
|
+
:specifiers => {
|
246
|
+
:coordinate => {x: x, y: y}
|
247
|
+
},
|
248
|
+
:options => options
|
249
|
+
}
|
250
|
+
|
251
|
+
RunLoop.log_debug(%Q[
|
252
|
+
Sending request to perform '#{gesture}' with:
|
253
|
+
|
254
|
+
#{JSON.pretty_generate(parameters)}
|
255
|
+
|
256
|
+
])
|
257
|
+
request = request("gesture", parameters)
|
258
|
+
client = client(http_options)
|
259
|
+
response = client.post(request)
|
260
|
+
expect_200_response(response)
|
261
|
+
end
|
262
|
+
|
263
|
+
# @!visibility private
|
264
|
+
def coordinate_from_query_result(hash)
|
265
|
+
matches = hash["result"]
|
266
|
+
|
267
|
+
if matches.nil? || matches.empty?
|
268
|
+
raise "Expected #{hash} to contain some results"
|
269
|
+
end
|
270
|
+
|
271
|
+
rect = matches.first["rect"]
|
272
|
+
h = rect["height"]
|
273
|
+
w = rect["width"]
|
274
|
+
x = rect["x"]
|
275
|
+
y = rect["y"]
|
276
|
+
|
277
|
+
touchx = x + (w/2.0)
|
278
|
+
touchy = y + (h/2.0)
|
279
|
+
|
280
|
+
new_rect = rect.dup
|
281
|
+
new_rect[:center_x] = touchx
|
282
|
+
new_rect[:center_y] = touchy
|
283
|
+
|
284
|
+
RunLoop.log_debug(%Q[Rect from query:
|
285
|
+
|
286
|
+
#{JSON.pretty_generate(new_rect)}
|
287
|
+
|
288
|
+
])
|
289
|
+
{:x => touchx,
|
290
|
+
:y => touchy}
|
291
|
+
end
|
292
|
+
|
293
|
+
private
|
294
|
+
|
295
|
+
# @!visibility private
|
296
|
+
def xcrun
|
297
|
+
RunLoop::Xcrun.new
|
298
|
+
end
|
299
|
+
|
300
|
+
# @!visibility private
|
301
|
+
def url
|
302
|
+
@url ||= detect_device_agent_url
|
303
|
+
end
|
304
|
+
|
305
|
+
# @!visibility private
|
306
|
+
def detect_device_agent_url
|
307
|
+
url_from_environment ||
|
308
|
+
url_for_simulator ||
|
309
|
+
url_from_device_endpoint ||
|
310
|
+
url_from_device_name
|
311
|
+
end
|
312
|
+
|
313
|
+
# @!visibility private
|
314
|
+
def url_from_environment
|
315
|
+
url = RunLoop::Environment.device_agent_url
|
316
|
+
return if url.nil?
|
317
|
+
|
318
|
+
if url.end_with?("/")
|
319
|
+
url
|
320
|
+
else
|
321
|
+
"#{url}/"
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# @!visibility private
|
326
|
+
def url_for_simulator
|
327
|
+
if device.simulator?
|
328
|
+
"http://#{DEFAULTS[:simulator_ip]}:#{DEFAULTS[:port]}/"
|
329
|
+
else
|
330
|
+
nil
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
# @!visibility private
|
335
|
+
def url_from_device_endpoint
|
336
|
+
calabash_endpoint = RunLoop::Environment.device_endpoint
|
337
|
+
if calabash_endpoint
|
338
|
+
base = calabash_endpoint.split(":")[0..1].join(":")
|
339
|
+
"#{base}:#{DEFAULTS[:port]}/"
|
340
|
+
else
|
341
|
+
nil
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
# @!visibility private
|
346
|
+
# TODO This block is not well tested
|
347
|
+
# TODO extract to a module; Calabash can use to detect device endpoint
|
348
|
+
def url_from_device_name
|
349
|
+
# Transforms the default "Joshua's iPhone" to a DNS name.
|
350
|
+
device_name = device.name.gsub(/[']/, "").gsub(/[\s]/, "-")
|
351
|
+
|
352
|
+
# Replace diacritic markers and unknown characters.
|
353
|
+
transliterated = transliterate(device_name).tr("?", "")
|
354
|
+
|
355
|
+
# Anything that cannot be transliterated is a ?
|
356
|
+
replaced = transliterated.tr("?", "")
|
357
|
+
|
358
|
+
"http://#{replaced}.local:#{DEFAULTS[:port]}/"
|
359
|
+
end
|
360
|
+
|
361
|
+
# @!visibility private
|
362
|
+
def server
|
363
|
+
@server ||= RunLoop::HTTP::Server.new(url)
|
364
|
+
end
|
365
|
+
|
366
|
+
# @!visibility private
|
367
|
+
def client(options={})
|
368
|
+
RunLoop::HTTP::RetriableClient.new(server, options)
|
369
|
+
end
|
370
|
+
|
371
|
+
# @!visibility private
|
372
|
+
def versioned_route(route)
|
373
|
+
if ["health", "ping", "sessionIdentifier"].include?(route)
|
374
|
+
route
|
375
|
+
else
|
376
|
+
"#{DEFAULTS[:version]}/#{route}"
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
# @!visibility private
|
381
|
+
def request(route, parameters={})
|
382
|
+
versioned = versioned_route(route)
|
383
|
+
RunLoop::HTTP::Request.request(versioned, parameters)
|
384
|
+
end
|
385
|
+
|
386
|
+
# @!visibility private
|
387
|
+
def ping_options
|
388
|
+
@ping_options ||= { :timeout => 0.5, :retries => 1 }
|
389
|
+
end
|
390
|
+
|
391
|
+
# @!visibility private
|
392
|
+
def http_options
|
393
|
+
{
|
394
|
+
:timeout => DEFAULTS[:http_timeout],
|
395
|
+
:interval => 0.1,
|
396
|
+
:retries => (DEFAULTS[:http_timeout]/0.1).to_i
|
397
|
+
}
|
398
|
+
end
|
399
|
+
|
400
|
+
# @!visibility private
|
401
|
+
def session_delete
|
402
|
+
options = ping_options
|
403
|
+
request = request("delete")
|
404
|
+
client = client(options)
|
405
|
+
begin
|
406
|
+
response = client.delete(request)
|
407
|
+
body = expect_200_response(response)
|
408
|
+
RunLoop.log_debug("CBX-Runner says, #{body}")
|
409
|
+
body
|
410
|
+
rescue => e
|
411
|
+
RunLoop.log_debug("CBX-Runner session delete error: #{e}")
|
412
|
+
nil
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
# @!visibility private
|
417
|
+
# TODO expect 200 response and parse body (atm the body in not valid JSON)
|
418
|
+
def shutdown
|
419
|
+
session_delete
|
420
|
+
options = ping_options
|
421
|
+
request = request("shutdown")
|
422
|
+
client = client(options)
|
423
|
+
begin
|
424
|
+
response = client.post(request)
|
425
|
+
body = response.body
|
426
|
+
RunLoop.log_debug("CBX-Runner says, \"#{body}\"")
|
427
|
+
body
|
428
|
+
rescue => e
|
429
|
+
RunLoop.log_debug("CBX-Runner shutdown error: #{e}")
|
430
|
+
nil
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
# @!visibility private
|
435
|
+
# TODO expect 200 response and parse body (atm the body is not valid JSON)
|
436
|
+
def health(options={})
|
437
|
+
merged_options = http_options.merge(options)
|
438
|
+
request = request("health")
|
439
|
+
client = client(merged_options)
|
440
|
+
response = client.get(request)
|
441
|
+
body = response.body
|
442
|
+
RunLoop.log_debug("CBX-Runner driver says, \"#{body}\"")
|
443
|
+
body
|
444
|
+
end
|
445
|
+
|
446
|
+
# @!visibility private
|
447
|
+
def launch_cbx_runner
|
448
|
+
shutdown
|
449
|
+
|
450
|
+
options = {:log_cmd => true}
|
451
|
+
run_shell_command(["pkill", "xctestctl"], options)
|
452
|
+
run_shell_command(["pkill", "testmanagerd"], options)
|
453
|
+
run_shell_command(["pkill", "xcodebuild"], options)
|
454
|
+
|
455
|
+
start = Time.now
|
456
|
+
RunLoop.log_debug("Waiting for CBX-Runner to launch...")
|
457
|
+
pid = cbx_launcher.launch
|
458
|
+
health
|
459
|
+
RunLoop.log_debug("Took #{Time.now - start} launch and respond to /health")
|
460
|
+
|
461
|
+
pid
|
462
|
+
end
|
463
|
+
|
464
|
+
# @!visibility private
|
465
|
+
def launch_aut(bundle_id = @bundle_id)
|
466
|
+
client = client(http_options)
|
467
|
+
request = request("session", {:bundleID => bundle_id})
|
468
|
+
|
469
|
+
begin
|
470
|
+
response = client.post(request)
|
471
|
+
RunLoop.log_debug("Launched #{bundle_id} on #{device}")
|
472
|
+
RunLoop.log_debug("#{response.body}")
|
473
|
+
if device.simulator?
|
474
|
+
# It is not clear yet whether we should do this. There is a problem
|
475
|
+
# in the simulator_wait_for_stable_state; it waits too long.
|
476
|
+
# device.simulator_wait_for_stable_state
|
477
|
+
end
|
478
|
+
expect_200_response(response)
|
479
|
+
rescue => e
|
480
|
+
raise e.class, %Q[Could not launch #{bundle_id} on #{device}:
|
481
|
+
|
482
|
+
#{e.message}
|
483
|
+
|
484
|
+
Something went wrong.
|
485
|
+
]
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
# @!visibility private
|
490
|
+
def response_body_to_hash(response)
|
491
|
+
body = response.body
|
492
|
+
begin
|
493
|
+
JSON.parse(body)
|
494
|
+
rescue TypeError, JSON::ParserError => _
|
495
|
+
raise RunLoop::XCUITest::HTTPError,
|
496
|
+
"Could not parse response '#{body}'; the app has probably crashed"
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
# @!visibility private
|
501
|
+
def expect_200_response(response)
|
502
|
+
body = response_body_to_hash(response)
|
503
|
+
if response.status_code < 300 && !body["error"]
|
504
|
+
return body
|
505
|
+
end
|
506
|
+
|
507
|
+
if response.status_code > 300
|
508
|
+
raise RunLoop::XCUITest::HTTPError,
|
509
|
+
%Q[Expected status code < 300, found #{response.status_code}.
|
510
|
+
|
511
|
+
Server replied with:
|
512
|
+
|
513
|
+
#{body}
|
514
|
+
|
515
|
+
]
|
516
|
+
else
|
517
|
+
raise RunLoop::XCUITest::HTTPError,
|
518
|
+
%Q[Expected JSON response with no error, but found
|
519
|
+
|
520
|
+
#{body["error"]}
|
521
|
+
|
522
|
+
]
|
523
|
+
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
# @!visibility private
|
528
|
+
def orientation_for_position(position)
|
529
|
+
symbol = position.to_sym
|
530
|
+
|
531
|
+
case symbol
|
532
|
+
when :down, :bottom
|
533
|
+
return 1
|
534
|
+
when :up, :top
|
535
|
+
return 2
|
536
|
+
when :right
|
537
|
+
return 3
|
538
|
+
when :left
|
539
|
+
return 4
|
540
|
+
else
|
541
|
+
raise ArgumentError, %Q[
|
542
|
+
Could not coerce '#{position}' into a valid orientation.
|
543
|
+
|
544
|
+
Valid values are: :down, :up, :right, :left, :bottom, :top
|
545
|
+
]
|
546
|
+
end
|
547
|
+
end
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|