run_loop 2.1.0.pre1 → 2.1.0
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 +4 -4
- data/lib/run_loop.rb +48 -44
- data/lib/run_loop/app.rb +83 -1
- data/lib/run_loop/cli/simctl.rb +8 -3
- data/lib/run_loop/core.rb +2 -0
- data/lib/run_loop/core_simulator.rb +54 -15
- data/lib/run_loop/environment.rb +22 -1
- data/lib/run_loop/http/retriable_client.rb +5 -1
- data/lib/run_loop/instruments.rb +3 -30
- data/lib/run_loop/ipa.rb +30 -1
- data/lib/run_loop/simctl.rb +89 -0
- data/lib/run_loop/version.rb +1 -1
- data/lib/run_loop/xcuitest.rb +313 -107
- metadata +5 -5
- data/lib/run_loop/simctl/plists.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee9ecaf4f1c6ec33be1a4e7640991c46e14f1938
|
4
|
+
data.tar.gz: 7348a649fd00ef921ccf16d049ca1f7c9c9b5421
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2de3db8241ef24056e0d140d569c30082d0436acd251134b0b2908ef063a9dc97e9be2be8431701340c21e80bc3cd2978a41893ffb3b787231aaff191b8637f
|
7
|
+
data.tar.gz: 46c3ca51600d39434fee5a25d47b41c1122846f5e7eb3852e079174c1393acfa18df5e0cb9605899d6341b7cad1e3846212ad8348de1be1aad13af2622555010
|
data/lib/run_loop.rb
CHANGED
@@ -31,7 +31,7 @@ require 'run_loop/cache/cache'
|
|
31
31
|
require 'run_loop/host_cache'
|
32
32
|
require 'run_loop/patches/awesome_print'
|
33
33
|
require 'run_loop/core_simulator'
|
34
|
-
require
|
34
|
+
require "run_loop/simctl"
|
35
35
|
require 'run_loop/template'
|
36
36
|
require "run_loop/locale"
|
37
37
|
require "run_loop/language"
|
@@ -66,64 +66,69 @@ module RunLoop
|
|
66
66
|
|
67
67
|
def self.run(options={})
|
68
68
|
|
69
|
-
if
|
70
|
-
|
69
|
+
if options[:xcuitest]
|
70
|
+
RunLoop::XCUITest.run(options)
|
71
|
+
else
|
72
|
+
|
73
|
+
if RunLoop::Instruments.new.instruments_app_running?
|
74
|
+
raise %q(The Instruments.app is open.
|
71
75
|
|
72
76
|
If the Instruments.app is open, the instruments command line tool cannot take
|
73
77
|
control of your application.
|
74
78
|
|
75
79
|
Please quit the Instruments.app and try again.)
|
76
80
|
|
77
|
-
|
81
|
+
end
|
78
82
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
else
|
83
|
-
if uia_strategy
|
84
|
-
script = default_script_for_uia_strategy(uia_strategy)
|
83
|
+
uia_strategy = options[:uia_strategy]
|
84
|
+
if options[:script]
|
85
|
+
script = validate_script(options[:script])
|
85
86
|
else
|
86
|
-
if
|
87
|
-
uia_strategy = :host
|
88
|
-
script = Core.script_for_key(:run_loop_host)
|
89
|
-
else
|
90
|
-
uia_strategy = :preferences
|
87
|
+
if uia_strategy
|
91
88
|
script = default_script_for_uia_strategy(uia_strategy)
|
89
|
+
else
|
90
|
+
if options[:calabash_lite]
|
91
|
+
uia_strategy = :host
|
92
|
+
script = Core.script_for_key(:run_loop_host)
|
93
|
+
else
|
94
|
+
uia_strategy = :preferences
|
95
|
+
script = default_script_for_uia_strategy(uia_strategy)
|
96
|
+
end
|
92
97
|
end
|
93
98
|
end
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
99
|
+
# At this point, 'script' has been chosen, but uia_strategy might not
|
100
|
+
unless uia_strategy
|
101
|
+
desired_script = options[:script]
|
102
|
+
if desired_script.is_a?(String) #custom path to script
|
103
|
+
uia_strategy = :host
|
104
|
+
elsif desired_script == :run_loop_host
|
105
|
+
uia_strategy = :host
|
106
|
+
elsif desired_script == :run_loop_fast_uia
|
107
|
+
uia_strategy = :preferences
|
108
|
+
elsif desired_script == :run_loop_shared_element
|
109
|
+
uia_strategy = :shared_element
|
110
|
+
else
|
111
|
+
raise "Inconsistent state: desired script #{desired_script} has not uia_strategy"
|
112
|
+
end
|
108
113
|
end
|
109
|
-
end
|
110
114
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
+
# At this point script and uia_strategy selected
|
116
|
+
cloned_options = options.clone
|
117
|
+
cloned_options[:script] = script
|
118
|
+
cloned_options[:uia_strategy] = uia_strategy
|
115
119
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
120
|
+
# Xcode and SimControl will not be properly cloned and we don't want
|
121
|
+
# them to be; we want to use the exact objects that were passed.
|
122
|
+
if options[:xcode]
|
123
|
+
cloned_options[:xcode] = options[:xcode]
|
124
|
+
end
|
121
125
|
|
122
|
-
|
123
|
-
|
124
|
-
|
126
|
+
if options[:sim_control]
|
127
|
+
cloned_options[:sim_control] = options[:sim_control]
|
128
|
+
end
|
125
129
|
|
126
|
-
|
130
|
+
Core.run_with_options(cloned_options)
|
131
|
+
end
|
127
132
|
end
|
128
133
|
|
129
134
|
def self.send_command(run_loop, cmd, options={timeout: 60}, num_retries=0, last_error=nil)
|
@@ -235,5 +240,4 @@ Please quit the Instruments.app and try again.)
|
|
235
240
|
def self.log_info(*args)
|
236
241
|
RunLoop::Logging.log_info(*args)
|
237
242
|
end
|
238
|
-
|
239
243
|
end
|
data/lib/run_loop/app.rb
CHANGED
@@ -33,7 +33,20 @@ Bundle must:
|
|
33
33
|
|
34
34
|
# @!visibility private
|
35
35
|
def to_s
|
36
|
-
|
36
|
+
cf_bundle_version = bundle_version
|
37
|
+
cf_bundle_short_version = short_bundle_version
|
38
|
+
|
39
|
+
if cf_bundle_version && cf_bundle_short_version
|
40
|
+
version = "#{cf_bundle_version.to_s} / #{cf_bundle_short_version}"
|
41
|
+
elsif cf_bundle_version
|
42
|
+
version = cf_bundle_version.to_s
|
43
|
+
elsif cf_bundle_short_version
|
44
|
+
version = cf_bundle_short_version
|
45
|
+
else
|
46
|
+
version = ""
|
47
|
+
end
|
48
|
+
|
49
|
+
"#<APP #{bundle_identifier} #{version} #{path}>"
|
37
50
|
end
|
38
51
|
|
39
52
|
# @!visibility private
|
@@ -130,6 +143,75 @@ Bundle must:
|
|
130
143
|
RunLoop::Codesign.distribution?(path)
|
131
144
|
end
|
132
145
|
|
146
|
+
# Returns the CFBundleShortVersionString of the app as Version instance.
|
147
|
+
#
|
148
|
+
# Apple docs:
|
149
|
+
#
|
150
|
+
# CFBundleShortVersionString specifies the release version number of the
|
151
|
+
# bundle, which identifies a released iteration of the app. The release
|
152
|
+
# version number is a string comprised of three period-separated integers.
|
153
|
+
#
|
154
|
+
# The first integer represents major revisions to the app, such as revisions
|
155
|
+
# that implement new features or major changes. The second integer denotes
|
156
|
+
# revisions that implement less prominent features. The third integer
|
157
|
+
# represents maintenance releases.
|
158
|
+
#
|
159
|
+
# The value for this key differs from the value for CFBundleVersion, which
|
160
|
+
# identifies an iteration (released or unreleased) of the app. This key can
|
161
|
+
# be localized by including it in your InfoPlist.strings files.
|
162
|
+
#
|
163
|
+
# @return [RunLoop::Version, nil] Returns a Version instance if the
|
164
|
+
# CFBundleShortVersion string is well formed and nil if not.
|
165
|
+
def marketing_version
|
166
|
+
string = plist_buddy.plist_read("CFBundleShortVersionString", info_plist_path)
|
167
|
+
begin
|
168
|
+
version = RunLoop::Version.new(string)
|
169
|
+
rescue
|
170
|
+
if string && string != ""
|
171
|
+
RunLoop.log_debug("CFBundleShortVersionString: '#{string}' is not a well formed version string")
|
172
|
+
else
|
173
|
+
RunLoop.log_debug("CFBundleShortVersionString is not defined in Info.plist")
|
174
|
+
end
|
175
|
+
version = nil
|
176
|
+
end
|
177
|
+
version
|
178
|
+
end
|
179
|
+
|
180
|
+
# See #marketing_version
|
181
|
+
alias_method :short_bundle_version, :marketing_version
|
182
|
+
|
183
|
+
# Returns the CFBundleVersionString of the app as Version instance.
|
184
|
+
#
|
185
|
+
# Apple docs:
|
186
|
+
#
|
187
|
+
# CFBundleVersion specifies the build version number of the bundle, which
|
188
|
+
# identifies an iteration (released or unreleased) of the bundle. The build
|
189
|
+
# version number should be a string comprised of three non-negative,
|
190
|
+
# period-separated integers with the first integer being greater than zero.
|
191
|
+
# The string should only contain numeric (0-9) and period (.) characters.
|
192
|
+
# Leading zeros are truncated from each integer and will be ignored (that
|
193
|
+
# is, 1.02.3 is equivalent to 1.2.3).
|
194
|
+
#
|
195
|
+
# @return [RunLoop::Version, nil] Returns a Version instance if the
|
196
|
+
# CFBundleVersion string is well formed and nil if not.
|
197
|
+
def build_version
|
198
|
+
string = plist_buddy.plist_read("CFBundleVersionString", info_plist_path)
|
199
|
+
begin
|
200
|
+
version = RunLoop::Version.new(string)
|
201
|
+
rescue
|
202
|
+
if string && string != ""
|
203
|
+
RunLoop.log_debug("CFBundleVersionString: '#{string}' is not a well formed version string")
|
204
|
+
else
|
205
|
+
RunLoop.log_debug("CFBundleVersionString is not defined in Info.plist")
|
206
|
+
end
|
207
|
+
version = nil
|
208
|
+
end
|
209
|
+
version
|
210
|
+
end
|
211
|
+
|
212
|
+
# See #build_version
|
213
|
+
alias_method :bundle_version, :build_version
|
214
|
+
|
133
215
|
# @!visibility private
|
134
216
|
# Collects the paths to executables in the bundle.
|
135
217
|
def executables
|
data/lib/run_loop/cli/simctl.rb
CHANGED
@@ -56,20 +56,25 @@ module RunLoop
|
|
56
56
|
debug = options[:debug]
|
57
57
|
device = options[:device]
|
58
58
|
|
59
|
+
manage_processes
|
60
|
+
|
59
61
|
if device
|
60
62
|
RunLoop::Environment.with_debugging(debug) do
|
63
|
+
RunLoop::CoreSimulator.erase(device)
|
61
64
|
launch_simulator(device, xcode)
|
62
65
|
end
|
63
66
|
else
|
64
|
-
|
67
|
+
RunLoop::Environment.with_debugging(debug) do
|
68
|
+
erase_and_launch_each_simulator
|
69
|
+
end
|
65
70
|
end
|
66
71
|
|
67
|
-
manage_processes
|
68
72
|
end
|
69
73
|
|
70
74
|
no_commands do
|
71
|
-
def
|
75
|
+
def erase_and_launch_each_simulator
|
72
76
|
sim_control.simulators.each do |simulator|
|
77
|
+
RunLoop::CoreSimulator.erase(simulator)
|
73
78
|
launch_simulator(simulator, xcode)
|
74
79
|
end
|
75
80
|
end
|
data/lib/run_loop/core.rb
CHANGED
@@ -676,6 +676,8 @@ Logfile: #{log_file}
|
|
676
676
|
#
|
677
677
|
# 1. enabling accessibility and software keyboard
|
678
678
|
# 2. installing / uninstalling apps
|
679
|
+
#
|
680
|
+
# TODO: move to CoreSimulator?
|
679
681
|
def self.prepare_simulator(app, device, xcode, simctl, reset_options)
|
680
682
|
|
681
683
|
# Validate the architecture.
|
@@ -57,7 +57,7 @@ class RunLoop::CoreSimulator
|
|
57
57
|
# This process is a daemon, and requires 'KILL' to terminate.
|
58
58
|
# Killing the process is fast, but it takes a long time to
|
59
59
|
# restart.
|
60
|
-
|
60
|
+
['com.apple.CoreSimulator.CoreSimulatorService', false],
|
61
61
|
|
62
62
|
# Probably do not need to quit this, but it is tempting to do so.
|
63
63
|
#['com.apple.CoreSimulator.SimVerificationService', false],
|
@@ -94,6 +94,12 @@ class RunLoop::CoreSimulator
|
|
94
94
|
# launchd_sim process.
|
95
95
|
['launchd_sim', false],
|
96
96
|
|
97
|
+
# Required for XCUITest termination; the simulator hangs otherwise.
|
98
|
+
["xpcproxy", false],
|
99
|
+
|
100
|
+
# Causes crash reports on Xcode < 7.0
|
101
|
+
["apsd", true],
|
102
|
+
|
97
103
|
# assetsd instances clobber each other and are not properly
|
98
104
|
# killed when quiting the simulator.
|
99
105
|
['assetsd', false],
|
@@ -167,7 +173,7 @@ class RunLoop::CoreSimulator
|
|
167
173
|
#
|
168
174
|
# @param [RunLoop::Device] simulator The simulator to erase
|
169
175
|
# @param [Hash] options Control the behavior of the method.
|
170
|
-
# @option options [Numeric] :
|
176
|
+
# @option options [Numeric] :timeout (180) How long tow wait for simctl to
|
171
177
|
# shutdown and erase the simulator. The timeout is apply separately to
|
172
178
|
# each command.
|
173
179
|
#
|
@@ -393,25 +399,46 @@ $ bundle exec run-loop simctl manage-processes
|
|
393
399
|
# relaunch it.
|
394
400
|
launch_simulator
|
395
401
|
|
396
|
-
|
397
|
-
|
398
|
-
hash = xcrun.exec(args, log_cmd: true, timeout: timeout)
|
402
|
+
tries = RunLoop::Environment.ci? ? 5 : 3
|
403
|
+
last_error = nil
|
399
404
|
|
400
|
-
|
405
|
+
RunLoop.log_debug("Trying #{tries} times to launch #{app.bundle_identifier} on #{device}")
|
401
406
|
|
402
|
-
|
403
|
-
|
404
|
-
|
407
|
+
tries.times do
|
408
|
+
hash = launch_app_with_simctl
|
409
|
+
exit_status = hash[:exit_status]
|
410
|
+
if exit_status != 0
|
411
|
+
RunLoop.log_debug("Failed to launch app.")
|
412
|
+
out.split($-0).each do |line|
|
413
|
+
RunLoop.log_debug(" #{line}")
|
414
|
+
end
|
415
|
+
# Simulator is probably in a bad state, but this will be super disruptive.
|
416
|
+
# Let's try a softer approach first - sleep.
|
417
|
+
# self.terminate_core_simulator_processes
|
418
|
+
sleep(0.5)
|
419
|
+
last_error = out
|
420
|
+
else
|
421
|
+
last_error = nil
|
422
|
+
break
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
if last_error
|
427
|
+
raise RuntimeError, %Q[Could not launch #{app.bundle_identifier} on #{device}
|
428
|
+
|
429
|
+
#{last_error}
|
430
|
+
|
431
|
+
]
|
405
432
|
end
|
406
433
|
|
407
434
|
options = {
|
408
|
-
|
409
|
-
|
435
|
+
:timeout => 10,
|
436
|
+
:raise_on_timeout => true
|
410
437
|
}
|
411
438
|
|
412
439
|
RunLoop::ProcessWaiter.new(app.executable_name, options).wait_for_any
|
413
|
-
|
414
440
|
device.simulator_wait_for_stable_state
|
441
|
+
|
415
442
|
true
|
416
443
|
end
|
417
444
|
|
@@ -588,6 +615,13 @@ Command had no output
|
|
588
615
|
installed_app_bundle_dir
|
589
616
|
end
|
590
617
|
|
618
|
+
# @!visibility private
|
619
|
+
def launch_app_with_simctl
|
620
|
+
args = ['simctl', 'launch', device.udid, app.bundle_identifier]
|
621
|
+
timeout = DEFAULT_OPTIONS[:launch_app_timeout]
|
622
|
+
xcrun.exec(args, log_cmd: true, timeout: timeout)
|
623
|
+
end
|
624
|
+
|
591
625
|
# Required for support of iOS 7 CoreSimulators. Can be removed when
|
592
626
|
# Xcode support is dropped.
|
593
627
|
def sdk_gte_8?
|
@@ -716,15 +750,20 @@ Command had no output
|
|
716
750
|
def installed_app_bundle_dir
|
717
751
|
sim_app_dir = device_applications_dir
|
718
752
|
return nil if !File.exist?(sim_app_dir)
|
719
|
-
|
720
|
-
|
753
|
+
|
754
|
+
if xcode.version_gte_7?
|
755
|
+
simctl = RunLoop::Simctl.new(device)
|
756
|
+
simctl.app_container(app.bundle_identifier)
|
757
|
+
else
|
758
|
+
Dir.glob("#{sim_app_dir}/**/*.app").find do |path|
|
759
|
+
RunLoop::App.new(path).bundle_identifier == app.bundle_identifier
|
760
|
+
end
|
721
761
|
end
|
722
762
|
end
|
723
763
|
|
724
764
|
# 1. Does nothing if the app is not installed.
|
725
765
|
# 2. Does nothing if the app the same as the app that is installed
|
726
766
|
# 3. Installs app if it is different from the installed app
|
727
|
-
#
|
728
767
|
def ensure_app_same
|
729
768
|
installed_app_bundle = installed_app_bundle_dir
|
730
769
|
|
data/lib/run_loop/environment.rb
CHANGED
@@ -229,6 +229,27 @@ module RunLoop
|
|
229
229
|
value = ENV["CI"]
|
230
230
|
!!value && value != ''
|
231
231
|
end
|
232
|
+
|
233
|
+
# !@visibility private
|
234
|
+
# Returns the value of CBXWS. This can be used to override the default
|
235
|
+
# CBXDriver.xcworkspace. You should only set this if you are actively
|
236
|
+
# developing the CBXDriver.
|
237
|
+
def self.cbxws
|
238
|
+
value = ENV["CBXWS"]
|
239
|
+
if value.nil? || value == ""
|
240
|
+
nil
|
241
|
+
else
|
242
|
+
path = File.expand_path(value)
|
243
|
+
if !File.directory?(path)
|
244
|
+
raise RuntimeError, %Q[CBXWS is set, but there is no workspace at
|
245
|
+
#{path}
|
246
|
+
|
247
|
+
Only set CBXWS if you are developing new features in the CBXRunner.
|
248
|
+
|
249
|
+
Check your environment.]
|
250
|
+
end
|
251
|
+
path
|
252
|
+
end
|
253
|
+
end
|
232
254
|
end
|
233
255
|
end
|
234
|
-
|
@@ -101,6 +101,10 @@ module RunLoop
|
|
101
101
|
request(request, :post, options)
|
102
102
|
end
|
103
103
|
|
104
|
+
def delete(request, options={})
|
105
|
+
request(request, :delete, options)
|
106
|
+
end
|
107
|
+
|
104
108
|
private
|
105
109
|
|
106
110
|
def request(request, request_method, options={})
|
@@ -134,7 +138,7 @@ module RunLoop
|
|
134
138
|
return client.send(request_method, @server.endpoint + request.route,
|
135
139
|
request.params, header)
|
136
140
|
rescue *RETRY_ON => e
|
137
|
-
RunLoop.log_debug("Rescued http error: #{e}")
|
141
|
+
#RunLoop.log_debug("Rescued http error: #{e}")
|
138
142
|
|
139
143
|
if first_try
|
140
144
|
if @on_error[e.class]
|
data/lib/run_loop/instruments.rb
CHANGED
@@ -124,15 +124,11 @@ module RunLoop
|
|
124
124
|
# Send a kill signal to any running `instruments` processes.
|
125
125
|
#
|
126
126
|
# Only one instruments process can be running at any one time.
|
127
|
-
|
128
|
-
# @param [RunLoop::Xcode] xcode Used to make check the
|
129
|
-
# active Xcode version.
|
130
|
-
def kill_instruments(xcode = RunLoop::Xcode.new)
|
131
|
-
kill_signal = kill_signal(xcode)
|
127
|
+
def kill_instruments(_=nil)
|
132
128
|
instruments_pids.each do |pid|
|
133
|
-
terminator = RunLoop::ProcessTerminator.new(pid,
|
129
|
+
terminator = RunLoop::ProcessTerminator.new(pid, "QUIT", "instruments")
|
134
130
|
unless terminator.kill_process
|
135
|
-
terminator = RunLoop::ProcessTerminator.new(pid,
|
131
|
+
terminator = RunLoop::ProcessTerminator.new(pid, "KILL", "instruments")
|
136
132
|
terminator.kill_process
|
137
133
|
end
|
138
134
|
end
|
@@ -369,29 +365,6 @@ module RunLoop
|
|
369
365
|
end.compact.sort
|
370
366
|
end
|
371
367
|
|
372
|
-
# @!visibility private
|
373
|
-
# The kill signal should be sent to instruments.
|
374
|
-
#
|
375
|
-
# When testing against iOS 8, sending -9 or 'TERM' causes the ScriptAgent
|
376
|
-
# process on the device to emit the following error until the device is
|
377
|
-
# rebooted.
|
378
|
-
#
|
379
|
-
# ```
|
380
|
-
# MobileGestaltHelper[909] <Error>: libMobileGestalt MobileGestalt.c:273: server_access_check denied access to question UniqueDeviceID for pid 796
|
381
|
-
# ScriptAgent[796] <Error>: libMobileGestalt MobileGestaltSupport.m:170: pid 796 (ScriptAgent) does not have sandbox access for re6Zb+zwFKJNlkQTUeT+/w and IS NOT appropriately entitled
|
382
|
-
# ScriptAgent[703] <Error>: libMobileGestalt MobileGestalt.c:534: no access to UniqueDeviceID (see <rdar://problem/11744455>)
|
383
|
-
# ```
|
384
|
-
#
|
385
|
-
# @see https://github.com/calabash/run_loop/issues/34
|
386
|
-
#
|
387
|
-
# @param [RunLoop::Xcode] xcode The Xcode tools to use to determine
|
388
|
-
# what version of Xcode is active.
|
389
|
-
# @return [String] Either 'QUIT' or 'TERM', depending on the Xcode
|
390
|
-
# version.
|
391
|
-
def kill_signal(xcode = RunLoop::Xcode.new)
|
392
|
-
xcode.version_gte_6? ? 'QUIT' : 'TERM'
|
393
|
-
end
|
394
|
-
|
395
368
|
# @!visibility private
|
396
369
|
#
|
397
370
|
# Execute an instruments command.
|
data/lib/run_loop/ipa.rb
CHANGED
@@ -25,7 +25,20 @@ module RunLoop
|
|
25
25
|
|
26
26
|
# @!visibility private
|
27
27
|
def to_s
|
28
|
-
|
28
|
+
cf_bundle_version = bundle_version
|
29
|
+
cf_bundle_short_version = short_bundle_version
|
30
|
+
|
31
|
+
if cf_bundle_version && cf_bundle_short_version
|
32
|
+
version = "#{cf_bundle_version.to_s}/#{cf_bundle_short_version}"
|
33
|
+
elsif cf_bundle_version
|
34
|
+
version = cf_bundle_version.to_s
|
35
|
+
elsif cf_bundle_short_version
|
36
|
+
version = cf_bundle_short_version
|
37
|
+
else
|
38
|
+
version = ""
|
39
|
+
end
|
40
|
+
|
41
|
+
"#<IPA #{bundle_identifier} #{version} #{path}>"
|
29
42
|
end
|
30
43
|
|
31
44
|
# @!visibility private
|
@@ -71,6 +84,22 @@ module RunLoop
|
|
71
84
|
app.distribution_signed?
|
72
85
|
end
|
73
86
|
|
87
|
+
# @!visibility private
|
88
|
+
def marketing_version
|
89
|
+
app.marketing_version
|
90
|
+
end
|
91
|
+
|
92
|
+
# See #marketing_version
|
93
|
+
alias_method :short_bundle_version, :marketing_version
|
94
|
+
|
95
|
+
# @!visibility private
|
96
|
+
def build_version
|
97
|
+
app.build_version
|
98
|
+
end
|
99
|
+
|
100
|
+
# See #build_version
|
101
|
+
alias_method :bundle_version, :build_version
|
102
|
+
|
74
103
|
private
|
75
104
|
|
76
105
|
# @!visibility private
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module RunLoop
|
2
|
+
|
3
|
+
# @!visibility private
|
4
|
+
# An interface to the `simctl` command line tool for CoreSimulator.
|
5
|
+
#
|
6
|
+
# Replacement for SimControl.
|
7
|
+
class Simctl
|
8
|
+
|
9
|
+
# @!visibility private
|
10
|
+
DEFAULTS = {
|
11
|
+
:timeout => RunLoop::Environment.ci? ? 90 : 30,
|
12
|
+
:log_cmd => true
|
13
|
+
}
|
14
|
+
|
15
|
+
# @!visibility private
|
16
|
+
SIMCTL_PLIST_DIR = lambda {
|
17
|
+
dirname = File.dirname(__FILE__)
|
18
|
+
joined = File.join(dirname, '..', '..', 'plists', 'simctl')
|
19
|
+
File.expand_path(joined)
|
20
|
+
}.call
|
21
|
+
|
22
|
+
# @!visibility private
|
23
|
+
def self.uia_automation_plist
|
24
|
+
File.join(SIMCTL_PLIST_DIR, 'com.apple.UIAutomation.plist')
|
25
|
+
end
|
26
|
+
|
27
|
+
# @!visibility private
|
28
|
+
def self.uia_automation_plugin_plist
|
29
|
+
File.join(SIMCTL_PLIST_DIR, 'com.apple.UIAutomationPlugIn.plist')
|
30
|
+
end
|
31
|
+
|
32
|
+
# @!visibility private
|
33
|
+
attr_accessor :device
|
34
|
+
|
35
|
+
# @!visibility private
|
36
|
+
#
|
37
|
+
# @param [RunLoop::Device] device Cannot be nil.
|
38
|
+
def initialize(device)
|
39
|
+
@device = device
|
40
|
+
end
|
41
|
+
|
42
|
+
# @!visibility private
|
43
|
+
def to_s
|
44
|
+
"#<Simctl: #{device.name} #{device.udid}>"
|
45
|
+
end
|
46
|
+
|
47
|
+
# @!visibility private
|
48
|
+
def inspect
|
49
|
+
to_s
|
50
|
+
end
|
51
|
+
|
52
|
+
# @!visibility private
|
53
|
+
#
|
54
|
+
# This method is not supported on Xcode < 7 - returns nil.
|
55
|
+
#
|
56
|
+
# @param [String] bundle_id The CFBundleIdentifier of the app.
|
57
|
+
# @return [String] The path to the .app bundle if it exists; nil otherwise.
|
58
|
+
def app_container(bundle_id)
|
59
|
+
return nil if !xcode.version_gte_7?
|
60
|
+
cmd = ["simctl", "get_app_container", device.udid, bundle_id]
|
61
|
+
hash = execute(cmd, DEFAULTS)
|
62
|
+
|
63
|
+
exit_status = hash[:exit_status]
|
64
|
+
if exit_status != 0
|
65
|
+
nil
|
66
|
+
else
|
67
|
+
hash[:out].strip
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
# @!visibility private
|
74
|
+
def execute(array, options)
|
75
|
+
merged = DEFAULTS.merge(options)
|
76
|
+
xcrun.exec(array, merged)
|
77
|
+
end
|
78
|
+
|
79
|
+
# @!visibility private
|
80
|
+
def xcrun
|
81
|
+
@xcrun ||= RunLoop::Xcrun.new
|
82
|
+
end
|
83
|
+
|
84
|
+
# @!visibility private
|
85
|
+
def xcode
|
86
|
+
@xcode ||= RunLoop::Xcode.new
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/run_loop/version.rb
CHANGED
data/lib/run_loop/xcuitest.rb
CHANGED
@@ -3,20 +3,44 @@ module RunLoop
|
|
3
3
|
# @!visibility private
|
4
4
|
class XCUITest
|
5
5
|
|
6
|
+
class HTTPError < RuntimeError; end
|
7
|
+
|
6
8
|
# @!visibility private
|
7
9
|
DEFAULTS = {
|
8
10
|
:port => 27753,
|
9
|
-
:simulator_ip => "127.0.0.1"
|
11
|
+
:simulator_ip => "127.0.0.1",
|
12
|
+
:http_timeout => RunLoop::Environment.ci? ? 120 : 60,
|
13
|
+
:version => "1.0"
|
10
14
|
}
|
11
15
|
|
12
16
|
# @!visibility private
|
13
|
-
def self.
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
def self.run(options={})
|
18
|
+
# logger = options[:logger]
|
19
|
+
simctl = options[:sim_control] || options[:simctl] || RunLoop::SimControl.new
|
20
|
+
xcode = options[:xcode] || RunLoop::Xcode.new
|
21
|
+
instruments = options[:instruments] || RunLoop::Instruments.new
|
22
|
+
|
23
|
+
# Find the Device under test, the App under test, UIA strategy, and reset options
|
24
|
+
device = RunLoop::Device.detect_device(options, xcode, simctl, instruments)
|
25
|
+
app_details = RunLoop::DetectAUT.detect_app_under_test(options)
|
26
|
+
reset_options = RunLoop::Core.send(:detect_reset_options, options)
|
27
|
+
|
28
|
+
app = app_details[:app]
|
29
|
+
bundle_id = app_details[:bundle_id]
|
30
|
+
|
31
|
+
if device.simulator? && app
|
32
|
+
core_sim = RunLoop::CoreSimulator.new(device, app, :xcode => xcode)
|
33
|
+
if reset_options
|
34
|
+
core_sim.reset_app_sandbox
|
35
|
+
end
|
36
|
+
|
37
|
+
simctl.ensure_software_keyboard(device)
|
38
|
+
core_sim.install
|
19
39
|
end
|
40
|
+
|
41
|
+
xcuitest = RunLoop::XCUITest.new(bundle_id, device)
|
42
|
+
xcuitest.launch
|
43
|
+
xcuitest
|
20
44
|
end
|
21
45
|
|
22
46
|
# @!visibility private
|
@@ -30,78 +54,262 @@ module RunLoop
|
|
30
54
|
end
|
31
55
|
|
32
56
|
# @!visibility private
|
33
|
-
|
57
|
+
#
|
58
|
+
# The app with `bundle_id` needs to be installed.
|
59
|
+
#
|
60
|
+
# @param [String] bundle_id The identifier of the app under test.
|
61
|
+
# @param [RunLoop::Device] device The device device.
|
62
|
+
def initialize(bundle_id, device)
|
34
63
|
@bundle_id = bundle_id
|
64
|
+
@device = device
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_s
|
68
|
+
"#<XCUITest #{url} : #{bundle_id} : #{device}>"
|
69
|
+
end
|
70
|
+
|
71
|
+
def inspect
|
72
|
+
to_s
|
35
73
|
end
|
36
74
|
|
37
75
|
# @!visibility private
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
76
|
+
def bundle_id
|
77
|
+
@bundle_id
|
78
|
+
end
|
79
|
+
|
80
|
+
# @!visibility private
|
81
|
+
def device
|
82
|
+
@device
|
83
|
+
end
|
84
|
+
|
85
|
+
# @!visibility private
|
86
|
+
def workspace
|
87
|
+
@workspace ||= lambda do
|
88
|
+
path = RunLoop::Environment.send(:cbxws)
|
89
|
+
if path
|
90
|
+
path
|
48
91
|
else
|
49
|
-
|
50
|
-
encoding_options = {
|
51
|
-
:invalid => :replace, # Replace invalid byte sequences
|
52
|
-
:undef => :replace, # Replace anything not defined in ASCII
|
53
|
-
:replace => '' # Use a blank for those replacements
|
54
|
-
}
|
55
|
-
encoded = device_name.encode(Encoding.find("ASCII"), encoding_options)
|
56
|
-
"http://#{encoded}.local:27753"
|
92
|
+
raise "TODO: figure out how to distribute the CBX-Runner"
|
57
93
|
end
|
94
|
+
end.call
|
95
|
+
end
|
96
|
+
|
97
|
+
def launch
|
98
|
+
start = Time.now
|
99
|
+
launch_cbx_runner
|
100
|
+
launch_aut
|
101
|
+
elapsed = Time.now - start
|
102
|
+
RunLoop.log_debug("Took #{elapsed} seconds to launch #{bundle_id} on #{device}")
|
103
|
+
true
|
104
|
+
end
|
105
|
+
|
106
|
+
# @!visibility private
|
107
|
+
def running?
|
108
|
+
begin
|
109
|
+
health(ping_options)
|
110
|
+
rescue => _
|
111
|
+
nil
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# @!visibility private
|
116
|
+
def stop
|
117
|
+
begin
|
118
|
+
shutdown
|
119
|
+
rescue => _
|
120
|
+
nil
|
58
121
|
end
|
59
122
|
end
|
60
123
|
|
61
124
|
# @!visibility private
|
62
|
-
def
|
125
|
+
def launch_other_app(bundle_id)
|
126
|
+
launch_aut(bundle_id)
|
127
|
+
end
|
63
128
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
129
|
+
# @!visibility private
|
130
|
+
def query(mark)
|
131
|
+
options = http_options
|
132
|
+
parameters = { :text => mark }
|
133
|
+
request = request("query", parameters)
|
134
|
+
client = client(options)
|
135
|
+
response = client.post(request)
|
136
|
+
expect_200_response(response)
|
137
|
+
end
|
138
|
+
|
139
|
+
# @!visibility private
|
140
|
+
def tap_mark(mark)
|
141
|
+
options = http_options
|
142
|
+
parameters = {
|
143
|
+
:gesture => "tap",
|
144
|
+
:text => mark
|
70
145
|
}
|
71
|
-
|
146
|
+
request = request("gesture", parameters)
|
147
|
+
client(options)
|
148
|
+
response = client.post(request)
|
149
|
+
expect_200_response(response)
|
150
|
+
end
|
72
151
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
152
|
+
# @!visibility private
|
153
|
+
def tap_coordinate(x, y)
|
154
|
+
options = http_options
|
155
|
+
parameters = {
|
156
|
+
:gesture => "tap_coordinate",
|
157
|
+
:coordinate => {x: x, y: y}
|
158
|
+
}
|
159
|
+
request = request("gesture", parameters)
|
160
|
+
client(options)
|
161
|
+
response = client.post(request)
|
162
|
+
expect_200_response(response)
|
163
|
+
end
|
80
164
|
|
81
|
-
|
165
|
+
# @!visibility private
|
166
|
+
def tap_query_result(hash)
|
167
|
+
rect = hash["rect"]
|
168
|
+
h = rect["height"]
|
169
|
+
w = rect["width"]
|
170
|
+
x = rect["x"]
|
171
|
+
y = rect["y"]
|
172
|
+
|
173
|
+
touchx = x + (h/2)
|
174
|
+
touchy = y + (w/2)
|
175
|
+
tap_coordinate(touchx, touchy)
|
176
|
+
end
|
82
177
|
|
83
|
-
|
84
|
-
raise RuntimeError, "No workspace found"
|
85
|
-
end
|
178
|
+
private
|
86
179
|
|
87
|
-
|
180
|
+
# @!visibility private
|
181
|
+
def xcrun
|
182
|
+
RunLoop::Xcrun.new
|
183
|
+
end
|
88
184
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
185
|
+
# @!visibility private
|
186
|
+
def url
|
187
|
+
@url ||= lambda do
|
188
|
+
if device.simulator?
|
189
|
+
"http://#{DEFAULTS[:simulator_ip]}:#{DEFAULTS[:port]}/"
|
190
|
+
else
|
191
|
+
# This block is untested.
|
192
|
+
calabash_endpoint = RunLoop::Environment.device_endpoint
|
193
|
+
if calabash_endpoint
|
194
|
+
base = calabash_endpoint.split(":")[0..1].join(":")
|
195
|
+
"http://#{base}:#{DEFAULTS[:port]}/"
|
196
|
+
else
|
197
|
+
device_name = device.name.gsub(/['\s]/, "")
|
198
|
+
encoding_options = {
|
199
|
+
:invalid => :replace, # Replace invalid byte sequences
|
200
|
+
:undef => :replace, # Replace anything not defined in ASCII
|
201
|
+
:replace => "" # Use a blank for those replacements
|
202
|
+
}
|
203
|
+
encoded = device_name.encode(Encoding.find("ASCII"), encoding_options)
|
204
|
+
"http://#{encoded}.local:27753/"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end.call
|
208
|
+
end
|
209
|
+
|
210
|
+
# @!visibility private
|
211
|
+
def server
|
212
|
+
@server ||= RunLoop::HTTP::Server.new(url)
|
213
|
+
end
|
214
|
+
|
215
|
+
# @!visibility private
|
216
|
+
def client(options={})
|
217
|
+
RunLoop::HTTP::RetriableClient.new(server, options)
|
218
|
+
end
|
219
|
+
|
220
|
+
# @!visibility private
|
221
|
+
def versioned_route(route)
|
222
|
+
if ["health", "ping", "sessionIdentifier"].include?(route)
|
223
|
+
route
|
94
224
|
else
|
225
|
+
"#{DEFAULTS[:version]}/#{route}"
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# @!visibility private
|
230
|
+
def request(route, parameters={})
|
231
|
+
versioned = versioned_route(route)
|
232
|
+
RunLoop::HTTP::Request.request(versioned, parameters)
|
233
|
+
end
|
234
|
+
|
235
|
+
# @!visibility private
|
236
|
+
def ping_options
|
237
|
+
@ping_options ||= { :timeout => 0.5, :retries => 1 }
|
238
|
+
end
|
95
239
|
|
240
|
+
# @!visibility private
|
241
|
+
def http_options
|
242
|
+
{
|
243
|
+
:timeout => DEFAULTS[:http_timeout],
|
244
|
+
:interval => 0.1,
|
245
|
+
:retries => (DEFAULTS[:http_timeout]/0.1).to_i
|
246
|
+
}
|
247
|
+
end
|
248
|
+
|
249
|
+
# @!visibility private
|
250
|
+
def session_delete
|
251
|
+
options = ping_options
|
252
|
+
request = request("delete")
|
253
|
+
client = client(options)
|
254
|
+
begin
|
255
|
+
response = client.delete(request)
|
256
|
+
body = expect_200_response(response)
|
257
|
+
RunLoop.log_debug("CBX-Runner says, #{body}")
|
258
|
+
body
|
259
|
+
rescue => e
|
260
|
+
RunLoop.log_debug("CBX-Runner session delete error: #{e}")
|
261
|
+
nil
|
96
262
|
end
|
263
|
+
end
|
97
264
|
|
265
|
+
# @!visibility private
|
266
|
+
# TODO expect 200 response and parse body (atm the body in not valid JSON)
|
267
|
+
def shutdown
|
268
|
+
session_delete
|
269
|
+
options = ping_options
|
270
|
+
request = request("shutdown")
|
271
|
+
client = client(options)
|
272
|
+
begin
|
273
|
+
response = client.post(request)
|
274
|
+
body = response.body
|
275
|
+
RunLoop.log_debug("CBX-Runner says, \"#{body}\"")
|
276
|
+
5.times do
|
277
|
+
begin
|
278
|
+
health
|
279
|
+
sleep(0.2)
|
280
|
+
rescue => _
|
281
|
+
break
|
282
|
+
end
|
283
|
+
end
|
284
|
+
body
|
285
|
+
rescue => e
|
286
|
+
RunLoop.log_debug("CBX-Runner shutdown error: #{e}")
|
287
|
+
nil
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
# @!visibility private
|
292
|
+
# TODO expect 200 response and parse body (atm the body is not valid JSON)
|
293
|
+
def health(options={})
|
294
|
+
merged_options = http_options.merge(options)
|
295
|
+
request = request("health")
|
296
|
+
client = client(merged_options)
|
297
|
+
response = client.get(request)
|
298
|
+
body = response.body
|
299
|
+
RunLoop.log_debug("CBX-Runner driver says, \"#{body}\"")
|
300
|
+
body
|
301
|
+
end
|
302
|
+
|
303
|
+
# @!visibility private
|
304
|
+
def xcodebuild
|
98
305
|
args = [
|
99
306
|
"xcrun",
|
100
307
|
"xcodebuild",
|
101
308
|
"-scheme", "CBXAppStub",
|
102
309
|
"-workspace", workspace,
|
103
310
|
"-config", "Debug",
|
104
|
-
"-destination",
|
311
|
+
"-destination",
|
312
|
+
"id=#{device.udid}",
|
105
313
|
"clean",
|
106
314
|
"test"
|
107
315
|
]
|
@@ -118,82 +326,80 @@ module RunLoop
|
|
118
326
|
|
119
327
|
pid = Process.spawn(*args, options)
|
120
328
|
Process.detach(pid)
|
329
|
+
pid
|
330
|
+
end
|
121
331
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
332
|
+
# @!visibility private
|
333
|
+
def launch_cbx_runner
|
334
|
+
# Fail fast if CBXWS is not defined.
|
335
|
+
# WIP - we will distribute the workspace somehow.
|
336
|
+
workspace
|
127
337
|
|
128
|
-
|
129
|
-
request = RunLoop::HTTP::Request.new("/health", {})
|
338
|
+
shutdown
|
130
339
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
340
|
+
if device.simulator?
|
341
|
+
# quits the simulator
|
342
|
+
sim = CoreSimulator.new(device, "")
|
343
|
+
sim.launch_simulator
|
344
|
+
else
|
345
|
+
# anything special about physical devices?
|
346
|
+
end
|
136
347
|
|
137
|
-
|
138
|
-
|
348
|
+
start = Time.now
|
349
|
+
pid = xcodebuild
|
350
|
+
RunLoop.log_debug("Waiting for CBX-Runner to build...")
|
351
|
+
health
|
139
352
|
|
140
|
-
RunLoop.log_debug("
|
353
|
+
RunLoop.log_debug("Took #{Time.now - start} seconds to build and launch")
|
141
354
|
pid.to_i
|
142
355
|
end
|
143
356
|
|
144
|
-
def launch_app
|
145
|
-
server = RunLoop::HTTP::Server.new(url)
|
146
|
-
request = RunLoop::HTTP::Request.request("/session", {:bundleID => bundle_id})
|
147
|
-
client = RunLoop::HTTP::RetriableClient.new(server)
|
148
|
-
response = client.post(request)
|
149
|
-
|
150
|
-
RunLoop.log_debug("Calabus driver says, \"#{response.body}\"")
|
151
|
-
end
|
152
|
-
|
153
357
|
# @!visibility private
|
154
|
-
def
|
155
|
-
|
156
|
-
|
358
|
+
def launch_aut(bundle_id = @bundle_id)
|
359
|
+
client = client(http_options)
|
360
|
+
request = request("session", {:bundleID => bundle_id})
|
157
361
|
|
158
|
-
|
159
|
-
|
362
|
+
begin
|
363
|
+
response = client.post(request)
|
364
|
+
RunLoop.log_debug("Launched #{bundle_id} on #{device}")
|
365
|
+
RunLoop.log_debug("#{response.body}")
|
366
|
+
if device.simulator?
|
367
|
+
device.simulator_wait_for_stable_state
|
160
368
|
end
|
369
|
+
expect_200_response(response)
|
370
|
+
rescue => e
|
371
|
+
raise e.class, %Q[Could not launch #{bundle_id} on #{device}:
|
161
372
|
|
162
|
-
|
163
|
-
:sim_control => simctl,
|
164
|
-
:instruments => instruments
|
165
|
-
}
|
166
|
-
|
167
|
-
device = RunLoop::Device.device_with_identifier(target, options)
|
168
|
-
|
169
|
-
if !device
|
170
|
-
raise RuntimeError, "Could not find a device"
|
171
|
-
end
|
373
|
+
#{e.message}
|
172
374
|
|
173
|
-
|
174
|
-
|
375
|
+
Something went wrong.
|
376
|
+
]
|
377
|
+
end
|
175
378
|
end
|
176
379
|
|
177
380
|
# @!visibility private
|
178
|
-
def
|
179
|
-
|
381
|
+
def response_body_to_hash(response)
|
382
|
+
body = response.body
|
383
|
+
begin
|
384
|
+
JSON.parse(body)
|
385
|
+
rescue TypeError, JSON::ParserError => _
|
386
|
+
raise RunLoop::XCUITest::HTTPError,
|
387
|
+
"Could not parse response '#{body}'; the app has probably crashed"
|
388
|
+
end
|
180
389
|
end
|
181
390
|
|
182
|
-
private
|
183
|
-
|
184
391
|
# @!visibility private
|
185
|
-
def
|
186
|
-
|
187
|
-
|
392
|
+
def expect_200_response(response)
|
393
|
+
body = response_body_to_hash(response)
|
394
|
+
return body if response.status_code < 300
|
188
395
|
|
189
|
-
|
190
|
-
|
191
|
-
@instruments ||= RunLoop::Instruments.new
|
192
|
-
end
|
396
|
+
raise RunLoop::XCUITest::HTTPError,
|
397
|
+
%Q[Expected status code < 200, found #{response.status_code}.
|
193
398
|
|
194
|
-
|
195
|
-
|
196
|
-
|
399
|
+
Server replied with:
|
400
|
+
|
401
|
+
#{body}
|
402
|
+
]
|
197
403
|
end
|
198
404
|
|
199
405
|
# @!visibility private
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: run_loop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Karl Krukow
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-03-
|
11
|
+
date: 2016-03-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -326,7 +326,7 @@ files:
|
|
326
326
|
- lib/run_loop/process_waiter.rb
|
327
327
|
- lib/run_loop/regex.rb
|
328
328
|
- lib/run_loop/sim_control.rb
|
329
|
-
- lib/run_loop/simctl
|
329
|
+
- lib/run_loop/simctl.rb
|
330
330
|
- lib/run_loop/strings.rb
|
331
331
|
- lib/run_loop/template.rb
|
332
332
|
- lib/run_loop/version.rb
|
@@ -362,9 +362,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
362
362
|
version: '2.0'
|
363
363
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
364
364
|
requirements:
|
365
|
-
- - "
|
365
|
+
- - ">="
|
366
366
|
- !ruby/object:Gem::Version
|
367
|
-
version:
|
367
|
+
version: '0'
|
368
368
|
requirements: []
|
369
369
|
rubyforge_project:
|
370
370
|
rubygems_version: 2.5.2
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module RunLoop
|
2
|
-
module Simctl
|
3
|
-
class Plists
|
4
|
-
|
5
|
-
SIMCTL_PLIST_DIR = lambda {
|
6
|
-
dirname = File.dirname(__FILE__)
|
7
|
-
joined = File.join(dirname, '..', '..', '..', 'plists', 'simctl')
|
8
|
-
File.expand_path(joined)
|
9
|
-
}.call
|
10
|
-
|
11
|
-
def self.uia_automation_plist
|
12
|
-
File.join(SIMCTL_PLIST_DIR, 'com.apple.UIAutomation.plist')
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.uia_automation_plugin_plist
|
16
|
-
File.join(SIMCTL_PLIST_DIR, 'com.apple.UIAutomationPlugIn.plist')
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|