run_loop 1.0.0.pre3 → 1.0.0.pre5
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/core.rb +164 -41
- data/lib/run_loop/sim_control.rb +169 -2
- data/lib/run_loop/version.rb +1 -1
- data/scripts/calabash-uia-min.js +7 -0
- data/scripts/read-cmd.sh +2 -0
- data/scripts/run_loop_fast_uia.js +0 -2
- data/scripts/run_loop_host.js +16 -8
- metadata +10 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fc544f0548cb7f02b158070fa88a30215d494c4f
|
4
|
+
data.tar.gz: c6db656c4e9b52ed5225f0eedea4edc178cc604f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 127d85c861b0180ccd3d6f628047b30a8e8216802f76f8add3b68e299cf1fb75355dd3e98772ba8e425814e08b8e84462d1f76d5bd334ec40fd2dd5708b1c3b2
|
7
|
+
data.tar.gz: 3a3cab1c95a93fb7860ef0c636170f360d70f9b95bca6038b654250e97589f4585dba30b2731e5736d8eccf23ee6a56c12950e2a07dd40cea63d27ed892c885c
|
data/lib/run_loop/core.rb
CHANGED
@@ -3,6 +3,7 @@ require 'tmpdir'
|
|
3
3
|
require 'timeout'
|
4
4
|
require 'json'
|
5
5
|
require 'open3'
|
6
|
+
require 'erb'
|
6
7
|
|
7
8
|
module RunLoop
|
8
9
|
|
@@ -21,6 +22,8 @@ module RunLoop
|
|
21
22
|
:run_loop_host => 'run_loop_host.js'
|
22
23
|
}
|
23
24
|
|
25
|
+
READ_SCRIPT_PATH = File.join(SCRIPTS_PATH, 'read-cmd.sh')
|
26
|
+
|
24
27
|
def self.scripts_path
|
25
28
|
SCRIPTS_PATH
|
26
29
|
end
|
@@ -67,9 +70,10 @@ module RunLoop
|
|
67
70
|
sim_control ||= options[:sim_control] || RunLoop::SimControl.new
|
68
71
|
xctools ||= options[:xctools] || sim_control.xctools
|
69
72
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
+
if self.simulator_target?(options, sim_control)
|
74
|
+
# @todo only enable accessibility on the targeted simulator
|
75
|
+
sim_control.enable_accessibility_on_sims({:verbose => true})
|
76
|
+
end
|
73
77
|
|
74
78
|
device_target = options[:udid] || options[:device_target] || detect_connected_device || 'simulator'
|
75
79
|
if device_target && device_target.to_s.downcase == 'device'
|
@@ -94,31 +98,50 @@ module RunLoop
|
|
94
98
|
|
95
99
|
code = File.read(options[:script])
|
96
100
|
code = code.gsub(/\$PATH/, results_dir)
|
101
|
+
code = code.gsub(/\$READ_SCRIPT_PATH/, READ_SCRIPT_PATH)
|
97
102
|
code = code.gsub(/\$MODE/, 'FLUSH') unless options[:no_flush]
|
98
103
|
|
99
|
-
repl_path = File.join(results_dir, 'repl-cmd.
|
100
|
-
|
104
|
+
repl_path = File.join(results_dir, 'repl-cmd.pipe')
|
105
|
+
FileUtils.rm_f(repl_path)
|
101
106
|
|
102
|
-
|
107
|
+
uia_strategy = options[:uia_strategy]
|
108
|
+
if uia_strategy == :host
|
109
|
+
unless system(%Q[mkfifo "#{repl_path}"])
|
110
|
+
raise 'Unable to create pipe (mkfifo failed)'
|
111
|
+
end
|
112
|
+
end
|
103
113
|
|
114
|
+
cal_script = File.join(SCRIPTS_PATH, 'calabash_script_uia.js')
|
115
|
+
File.open(script, 'w') do |file|
|
116
|
+
file.puts IO.read(cal_script)
|
117
|
+
file.puts code
|
118
|
+
end
|
104
119
|
|
105
120
|
# Compute udid and bundle_dir / bundle_id from options and target depending on Xcode version
|
106
121
|
udid, bundle_dir_or_bundle_id = udid_and_bundle_for_launcher(device_target, options, xctools)
|
107
122
|
|
108
123
|
args = options.fetch(:args, [])
|
109
124
|
|
110
|
-
inject_dylib =
|
125
|
+
inject_dylib = self.dylib_path_from_options options
|
126
|
+
# WIP This is brute-force call against all lldb processes.
|
127
|
+
self.ensure_lldb_not_running if inject_dylib
|
111
128
|
|
112
129
|
log_file ||= File.join(results_dir, 'run_loop.out')
|
113
130
|
|
114
131
|
if ENV['DEBUG']=='1'
|
115
|
-
|
132
|
+
exclude = [:device_target, :udid, :sim_control, :args, :inject_dylib, :app]
|
133
|
+
options.each_pair { |key, value|
|
134
|
+
unless exclude.include? key
|
135
|
+
puts "#{key} => #{value}"
|
136
|
+
end
|
137
|
+
}
|
116
138
|
puts "device_target=#{device_target}"
|
117
139
|
puts "udid=#{udid}"
|
118
140
|
puts "bundle_dir_or_bundle_id=#{bundle_dir_or_bundle_id}"
|
119
141
|
puts "script=#{script}"
|
120
142
|
puts "log_file=#{log_file}"
|
121
143
|
puts "timeout=#{timeout}"
|
144
|
+
puts "uia_strategy=#{options[:uia_strategy]}"
|
122
145
|
puts "args=#{args}"
|
123
146
|
puts "inject_dylib=#{inject_dylib}"
|
124
147
|
end
|
@@ -158,25 +181,39 @@ module RunLoop
|
|
158
181
|
f.write pid
|
159
182
|
end
|
160
183
|
|
161
|
-
run_loop = {:pid => pid,
|
184
|
+
run_loop = {:pid => pid,
|
185
|
+
:index => 1,
|
186
|
+
:uia_strategy => uia_strategy,
|
187
|
+
:udid => udid,
|
188
|
+
:app => bundle_dir_or_bundle_id,
|
189
|
+
:repl_path => repl_path,
|
190
|
+
:log_file => log_file,
|
191
|
+
:results_dir => results_dir}
|
162
192
|
|
163
193
|
uia_timeout = options[:uia_timeout] || (ENV['UIA_TIMEOUT'] && ENV['UIA_TIMEOUT'].to_f) || 10
|
164
194
|
|
165
195
|
raw_lldb_output = nil
|
166
196
|
before = Time.now
|
167
197
|
begin
|
198
|
+
|
199
|
+
File.open(repl_path, 'w') { |file| file.puts "0:UIALogger.logMessage('Listening for run loop commands');" }
|
200
|
+
|
168
201
|
Timeout::timeout(timeout, TimeoutError) do
|
169
202
|
read_response(run_loop, 0, uia_timeout)
|
170
203
|
end
|
204
|
+
|
205
|
+
# inject_dylib will be nil or a path to a dylib
|
171
206
|
if inject_dylib
|
172
207
|
lldb_template_file = File.join(scripts_path,'calabash.lldb.erb')
|
173
|
-
require 'erb'
|
174
208
|
lldb_template = ::ERB.new(File.read(lldb_template_file))
|
175
209
|
lldb_template.filename = lldb_template_file
|
176
210
|
|
211
|
+
# Special!
|
212
|
+
# These are required by the ERB in calabash.lldb.erb
|
213
|
+
# noinspection RubyUnusedLocalVariable
|
177
214
|
cf_bundle_executable = find_cf_bundle_executable(bundle_dir_or_bundle_id)
|
178
|
-
|
179
|
-
dylib_path_for_target =
|
215
|
+
# noinspection RubyUnusedLocalVariable
|
216
|
+
dylib_path_for_target = inject_dylib
|
180
217
|
|
181
218
|
lldb_cmd = lldb_template.result(binding)
|
182
219
|
|
@@ -187,11 +224,23 @@ module RunLoop
|
|
187
224
|
|
188
225
|
if ENV['DEBUG'] == '1'
|
189
226
|
puts "lldb script #{lldb_script}"
|
227
|
+
puts "=== lldb script ==="
|
228
|
+
counter = 0
|
229
|
+
File.open(lldb_script, 'r').readlines.each { |line|
|
230
|
+
puts "#{counter} #{line}"
|
231
|
+
counter = counter + 1
|
232
|
+
}
|
233
|
+
puts "=== lldb script ==="
|
190
234
|
end
|
191
235
|
|
192
|
-
|
193
|
-
|
194
|
-
|
236
|
+
# Forcing a timeout. Do not retry here. If lldb is hanging,
|
237
|
+
# RunLoop::Core.run* needs to be called again. Put another way,
|
238
|
+
# instruments and lldb must be terminated.
|
239
|
+
Retriable.retriable({:tries => 1, :timeout => 12, :interval => 1}) do
|
240
|
+
raw_lldb_output = `xcrun lldb -s #{lldb_script}`
|
241
|
+
if ENV['DEBUG'] == '1'
|
242
|
+
puts raw_lldb_output
|
243
|
+
end
|
195
244
|
end
|
196
245
|
end
|
197
246
|
rescue TimeoutError => e
|
@@ -204,6 +253,7 @@ module RunLoop
|
|
204
253
|
puts "script=#{script}"
|
205
254
|
puts "log_file=#{log_file}"
|
206
255
|
puts "timeout=#{timeout}"
|
256
|
+
puts "uia_strategy=#{uia_strategy}"
|
207
257
|
puts "args=#{args}"
|
208
258
|
puts "lldb_output=#{raw_lldb_output}" if raw_lldb_output
|
209
259
|
end
|
@@ -219,6 +269,56 @@ module RunLoop
|
|
219
269
|
run_loop
|
220
270
|
end
|
221
271
|
|
272
|
+
# @!visibility private
|
273
|
+
# Are we targeting a simulator?
|
274
|
+
#
|
275
|
+
# @note The behavior of this method is different than the corresponding
|
276
|
+
# method in Calabash::Cucumber::Launcher method. If
|
277
|
+
# `:device_target => {nil | ''}`, then the calabash-ios method returns
|
278
|
+
# _false_. I am basing run-loop's behavior off the behavior in
|
279
|
+
# `self.udid_and_bundle_for_launcher`
|
280
|
+
#
|
281
|
+
# @see {Core::RunLoop.udid_and_bundle_for_launcher}
|
282
|
+
def self.simulator_target?(run_options, sim_control = RunLoop::SimControl.new)
|
283
|
+
value = run_options[:device_target]
|
284
|
+
|
285
|
+
# match the behavior of udid_and_bundle_for_launcher
|
286
|
+
return true if value.nil? or value == ''
|
287
|
+
|
288
|
+
# support for 'simulator' and Xcode >= 5.1 device targets
|
289
|
+
return true if value.downcase.include?('simulator')
|
290
|
+
|
291
|
+
# if Xcode < 6.0, we are done
|
292
|
+
return false if not sim_control.xcode_version_gte_6?
|
293
|
+
|
294
|
+
# support for Xcode >= 6 simulator udids
|
295
|
+
return true if sim_control.sim_udid? value
|
296
|
+
|
297
|
+
# support for Xcode >= 6 'named simulators'
|
298
|
+
sims = sim_control.simulators.each
|
299
|
+
sims.find_index { |device| device.name == value } != nil
|
300
|
+
end
|
301
|
+
|
302
|
+
# Extracts the value of :inject_dylib from options Hash.
|
303
|
+
# @param options [Hash] arguments passed to {RunLoop.run}
|
304
|
+
# @return [String, nil] If the options contains :inject_dylibs and it is a
|
305
|
+
# path to a dylib that exists, return the path. Otherwise return nil or
|
306
|
+
# raise an error.
|
307
|
+
# @raise [RuntimeError] If :inject_dylib points to a path that does not exist.
|
308
|
+
# @raise [ArgumentError] If :inject_dylib is not a String.
|
309
|
+
def self.dylib_path_from_options(options)
|
310
|
+
inject_dylib = options.fetch(:inject_dylib, nil)
|
311
|
+
return nil if inject_dylib.nil?
|
312
|
+
unless inject_dylib.is_a? String
|
313
|
+
raise ArgumentError, "Expected :inject_dylib to be a path to a dylib, but found '#{inject_dylib}'"
|
314
|
+
end
|
315
|
+
dylib_path = File.expand_path(inject_dylib)
|
316
|
+
unless File.exist?(dylib_path)
|
317
|
+
raise "Cannot load dylib. The file '#{dylib_path}' does not exist."
|
318
|
+
end
|
319
|
+
dylib_path
|
320
|
+
end
|
321
|
+
|
222
322
|
def self.find_cf_bundle_executable(bundle_dir_or_bundle_id)
|
223
323
|
unless File.directory?(bundle_dir_or_bundle_id)
|
224
324
|
raise "Injecting dylibs currently only works with simulator and app bundles"
|
@@ -291,26 +391,10 @@ module RunLoop
|
|
291
391
|
|
292
392
|
def self.write_request(run_loop, cmd)
|
293
393
|
repl_path = run_loop[:repl_path]
|
394
|
+
index = run_loop[:index]
|
395
|
+
File.open(repl_path, 'w') {|f| f.puts("#{index}:#{cmd}")}
|
396
|
+
run_loop[:index] = index + 1
|
294
397
|
|
295
|
-
cur = File.read(repl_path)
|
296
|
-
|
297
|
-
colon = cur.index(':')
|
298
|
-
|
299
|
-
if colon.nil?
|
300
|
-
raise "Illegal contents of #{repl_path}: #{cur}"
|
301
|
-
end
|
302
|
-
index = cur[0, colon].to_i + 1
|
303
|
-
|
304
|
-
|
305
|
-
tmp_cmd = File.join(File.dirname(repl_path), '__repl-cmd.txt')
|
306
|
-
File.open(tmp_cmd, 'w') do |f|
|
307
|
-
f.write("#{index}:#{cmd}")
|
308
|
-
if ENV['DEBUG']
|
309
|
-
puts "Wrote: #{index}:#{cmd}"
|
310
|
-
end
|
311
|
-
end
|
312
|
-
|
313
|
-
FileUtils.mv(tmp_cmd, repl_path)
|
314
398
|
index
|
315
399
|
end
|
316
400
|
|
@@ -490,12 +574,38 @@ module RunLoop
|
|
490
574
|
pids_str.split(',').map { |pid| pid.to_i }
|
491
575
|
end
|
492
576
|
|
577
|
+
# @todo This is a WIP
|
578
|
+
# @todo Needs rspec test
|
579
|
+
def self.ensure_lldb_not_running
|
580
|
+
descripts = `xcrun ps x -o pid,command | grep "lldb" | grep -v grep`.strip.split("\n")
|
581
|
+
descripts.each do |process_desc|
|
582
|
+
pid = process_desc.split(' ').first
|
583
|
+
Open3.popen3("xcrun kill -9 #{pid} && xcrun wait #{pid}") do |_, stdout, stderr, _|
|
584
|
+
out = stdout.read.strip
|
585
|
+
err = stderr.read.strip
|
586
|
+
next if out.to_s.empty? and err.to_s.empty?
|
587
|
+
# there lots of 'ownership' problems trying to kill the lldb process
|
588
|
+
#puts "kill process '#{pid}' => stdout: '#{out}' | stderr: '#{err}'"
|
589
|
+
end
|
590
|
+
end
|
591
|
+
end
|
493
592
|
end
|
494
593
|
|
495
594
|
|
496
595
|
def self.run(options={})
|
497
|
-
|
596
|
+
|
597
|
+
uia_strategy = options[:uia_strategy]
|
598
|
+
if uia_strategy
|
599
|
+
if uia_strategy == :host
|
600
|
+
script = Core.script_for_key(:run_loop_host)
|
601
|
+
else
|
602
|
+
script = Core.script_for_key(:run_loop_fast_uia)
|
603
|
+
end
|
604
|
+
else
|
605
|
+
uia_strategy, script = validate_script(options)
|
606
|
+
end
|
498
607
|
options[:script] = script
|
608
|
+
options[:uia_strategy] = uia_strategy
|
499
609
|
|
500
610
|
Core.run_with_options(options)
|
501
611
|
end
|
@@ -546,24 +656,37 @@ module RunLoop
|
|
546
656
|
|
547
657
|
|
548
658
|
def self.validate_script(options)
|
659
|
+
if options[:calabash_lite]
|
660
|
+
return :host, Core.script_for_key(:run_loop_host)
|
661
|
+
end
|
662
|
+
uia_strategy = options[:uia_strategy]
|
663
|
+
if uia_strategy
|
664
|
+
if uia_strategy == :host
|
665
|
+
script = :run_loop_host
|
666
|
+
elsif uia_strategy == :preferences
|
667
|
+
script = :run_loop_fast_uia
|
668
|
+
else
|
669
|
+
raise "Invalid :uia_strategy #{uia_strategy}"
|
670
|
+
end
|
671
|
+
return uia_strategy, Core.script_for_key(script)
|
672
|
+
end
|
673
|
+
|
549
674
|
script = options[:script]
|
550
675
|
if script
|
551
676
|
if script.is_a?(Symbol)
|
677
|
+
uia_strategy = (script == :run_loop_fast_uia ? :preferences : :host)
|
552
678
|
script = Core.script_for_key(script)
|
553
679
|
unless script
|
554
680
|
raise "Unknown script for symbol: #{options[:script]}. Options: #{Core::SCRIPTS.keys.join(', ')}"
|
555
681
|
end
|
556
|
-
elsif script.is_a?(String)
|
557
|
-
unless File.exist?(script)
|
558
|
-
raise "File does not exist: #{script}"
|
559
|
-
end
|
560
682
|
else
|
561
|
-
raise "
|
683
|
+
raise "Script must be a symbol: #{script}"
|
562
684
|
end
|
563
685
|
else
|
564
686
|
script = Core.script_for_key(:run_loop_fast_uia)
|
687
|
+
uia_strategy = :preferences
|
565
688
|
end
|
566
|
-
script
|
689
|
+
return uia_strategy, script
|
567
690
|
end
|
568
691
|
|
569
692
|
end
|
data/lib/run_loop/sim_control.rb
CHANGED
@@ -30,6 +30,16 @@ module RunLoop
|
|
30
30
|
xctools.xcode_version_gte_6?
|
31
31
|
end
|
32
32
|
|
33
|
+
# @!visibility private
|
34
|
+
# Are we running Xcode 5.1 or above?
|
35
|
+
#
|
36
|
+
# This is a convenience method.
|
37
|
+
#
|
38
|
+
# @return [Boolean] `true` if the current Xcode version is >= 5.1
|
39
|
+
def xcode_version_gte_51?
|
40
|
+
xctools.xcode_version_gte_51?
|
41
|
+
end
|
42
|
+
|
33
43
|
# Return an instance of PlistBuddy.
|
34
44
|
# @return [RunLoop::PlistBuddy] The plist buddy instance that is used internally.
|
35
45
|
def pbuddy
|
@@ -286,6 +296,32 @@ module RunLoop
|
|
286
296
|
results.all?
|
287
297
|
end
|
288
298
|
|
299
|
+
# Is the arg a valid Xcode >= 6.0 simulator udid?
|
300
|
+
# @param [String] udid the String to check
|
301
|
+
# @return [Boolean] Returns true iff the `udid` matches /[A-F0-9]{8}-([A-F0-9]{4}-){3}[A-F0-9]{12}/
|
302
|
+
def sim_udid?(udid)
|
303
|
+
udid.length == 36 and udid[XCODE_6_SIM_UDID_REGEX,0] != nil
|
304
|
+
end
|
305
|
+
|
306
|
+
def simulators
|
307
|
+
unless xcode_version_gte_51?
|
308
|
+
raise RuntimeError, 'simctl is only available on Xcode >= 6'
|
309
|
+
end
|
310
|
+
|
311
|
+
if xcode_version_gte_6?
|
312
|
+
hash = simctl_list :devices
|
313
|
+
sims = []
|
314
|
+
hash.each_pair do |sdk, list|
|
315
|
+
list.each do |details|
|
316
|
+
sims << RunLoop::Device.new(details[:name], sdk, details[:udid])
|
317
|
+
end
|
318
|
+
end
|
319
|
+
sims
|
320
|
+
else
|
321
|
+
raise NotImplementedError, 'the simulators method is not available yet for Xcode 5.1.1'
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
289
325
|
private
|
290
326
|
|
291
327
|
|
@@ -326,7 +362,7 @@ module RunLoop
|
|
326
362
|
#
|
327
363
|
# I don't know what the behavior is on Xcode 5.0*.
|
328
364
|
:inspector_showing => {:key => 'AXInspectorEnabled',
|
329
|
-
:value => '
|
365
|
+
:value => 'false',
|
330
366
|
:type => 'bool'},
|
331
367
|
|
332
368
|
# Controls if the Accessibility Inspector is expanded or not
|
@@ -783,5 +819,136 @@ module RunLoop
|
|
783
819
|
end
|
784
820
|
res.all?
|
785
821
|
end
|
822
|
+
|
823
|
+
# @!visibility private
|
824
|
+
#
|
825
|
+
# A ruby interface to the `simctl list` command.
|
826
|
+
#
|
827
|
+
# @note This is an Xcode >= 6.0 method.
|
828
|
+
# @raise [RuntimeError] if called on Xcode < 6.0
|
829
|
+
# @return [Hash] A hash whose primary key is a base SDK. For example,
|
830
|
+
# SDK 7.0.3 => "7.0". The value of the Hash will vary based on what is
|
831
|
+
# being listed.
|
832
|
+
def simctl_list(what)
|
833
|
+
unless xcode_version_gte_6?
|
834
|
+
raise RuntimeError, 'simctl is only available on Xcode >= 6'
|
835
|
+
end
|
836
|
+
|
837
|
+
case what
|
838
|
+
when :devices
|
839
|
+
simctl_list_devices
|
840
|
+
when :runtimes
|
841
|
+
# The 'com.apple.CoreSimulator.SimRuntime.iOS-7-0' is the runtime-id,
|
842
|
+
# which can be used to create devices.
|
843
|
+
simctl_list_runtimes
|
844
|
+
else
|
845
|
+
allowed = [:devices, :runtimes]
|
846
|
+
raise ArgumentError, "expected '#{what}' to be one of '#{allowed}'"
|
847
|
+
end
|
848
|
+
end
|
849
|
+
|
850
|
+
# @!visibility private
|
851
|
+
#
|
852
|
+
# Helper method for simctl_list.
|
853
|
+
#
|
854
|
+
# @example
|
855
|
+
# RunLoop::SimControl.new.simctl_list :devices
|
856
|
+
# {
|
857
|
+
# "7.1" =>
|
858
|
+
# [
|
859
|
+
# {
|
860
|
+
# :name => "iPhone 4s",
|
861
|
+
# :udid => "3BC5E3D7-9B81-4CE0-9C76-1888287F507B",
|
862
|
+
# :state => "Shutdown"
|
863
|
+
# }
|
864
|
+
# ],
|
865
|
+
# "8.0" => [
|
866
|
+
# {
|
867
|
+
# :name => "iPad 2",
|
868
|
+
# :udid => "D8F224D3-A59F-4F01-81AB-1959557A7E4E",
|
869
|
+
# :state => "Shutdown"
|
870
|
+
# }
|
871
|
+
# ]
|
872
|
+
# }
|
873
|
+
# @return [Hash<Array<Hash>>] Lists of available simulator details keyed by
|
874
|
+
# base sdk version.
|
875
|
+
# @see #simctl_list
|
876
|
+
def simctl_list_devices
|
877
|
+
cmd = 'xcrun simctl list devices'
|
878
|
+
Open3.popen3(cmd) do |_, stdout, stderr, _|
|
879
|
+
out = stdout.read.strip
|
880
|
+
err = stderr.read.strip
|
881
|
+
if ENV['DEBUG_UNIX_CALLS'] == '1'
|
882
|
+
puts "#{cmd} => stdout: '#{out}' | stderr: '#{err}'"
|
883
|
+
end
|
884
|
+
|
885
|
+
current_sdk = nil
|
886
|
+
res = {}
|
887
|
+
out.split("\n").each do |line|
|
888
|
+
possible_sdk = line[/(\d\.\d(\.\d)?)/,0]
|
889
|
+
if possible_sdk
|
890
|
+
current_sdk = possible_sdk
|
891
|
+
res[current_sdk] = []
|
892
|
+
next
|
893
|
+
end
|
894
|
+
|
895
|
+
if current_sdk
|
896
|
+
name = line.split('(').first.strip
|
897
|
+
udid = line[XCODE_6_SIM_UDID_REGEX,0]
|
898
|
+
state = line[/(Booted|Shutdown)/,0]
|
899
|
+
res[current_sdk] << {:name => name, :udid => udid, :state => state}
|
900
|
+
end
|
901
|
+
end
|
902
|
+
res
|
903
|
+
end
|
904
|
+
end
|
905
|
+
|
906
|
+
# @!visibility private
|
907
|
+
# Helper method for simctl_list
|
908
|
+
#
|
909
|
+
# @example
|
910
|
+
# RunLoop::SimControl.new.simctl_list :runtimes
|
911
|
+
# {
|
912
|
+
# "7.0" => {
|
913
|
+
# :sdk => "7.0.3",
|
914
|
+
# :runtime => "com.apple.CoreSimulator.SimRuntime.iOS-7-0"
|
915
|
+
# },
|
916
|
+
# "7.1" => {
|
917
|
+
# :sdk => "7.1",
|
918
|
+
# :runtime => "com.apple.CoreSimulator.SimRuntime.iOS-7-1"
|
919
|
+
# },
|
920
|
+
# "8.0" => {
|
921
|
+
# :sdk => "8.0",
|
922
|
+
# :runtime => "com.apple.CoreSimulator.SimRuntime.iOS-8-0"
|
923
|
+
# }
|
924
|
+
# }
|
925
|
+
# @see #simctl_list
|
926
|
+
def simctl_list_runtimes
|
927
|
+
# The 'com.apple.CoreSimulator.SimRuntime.iOS-7-0' is the runtime-id,
|
928
|
+
# which can be used to create devices.
|
929
|
+
cmd = 'xcrun simctl list runtimes'
|
930
|
+
Open3.popen3(cmd) do |_, stdout, stderr, _|
|
931
|
+
out = stdout.read.strip
|
932
|
+
err = stderr.read.strip
|
933
|
+
if ENV['DEBUG_UNIX_CALLS'] == '1'
|
934
|
+
puts "#{cmd} => stdout: '#{out}' | stderr: '#{err}'"
|
935
|
+
end
|
936
|
+
# Ex.
|
937
|
+
# == Runtimes ==
|
938
|
+
# iOS 7.0 (7.0.3 - 11B507) (com.apple.CoreSimulator.SimRuntime.iOS-7-0)
|
939
|
+
# iOS 7.1 (7.1 - 11D167) (com.apple.CoreSimulator.SimRuntime.iOS-7-1)
|
940
|
+
# iOS 8.0 (8.0 - 12A4331d) (com.apple.CoreSimulator.SimRuntime.iOS-8-0)
|
941
|
+
lines = out.split("\n").delete_if { |line| not line =~ /com.apple.CoreSimulator.SimRuntime/ }
|
942
|
+
res = {}
|
943
|
+
lines.each do |line|
|
944
|
+
key = line[/iOS (\d.\d)/,1]
|
945
|
+
sdk_version = line[/(\d.\d)(.\d)?\s-/, 0].tr(' -','')
|
946
|
+
runtime = line[/com.apple.CoreSimulator.SimRuntime.iOS-\d-\d/,0]
|
947
|
+
value = {:sdk => sdk_version, :runtime => runtime}
|
948
|
+
res[key] = value
|
949
|
+
end
|
950
|
+
res
|
951
|
+
end
|
952
|
+
end
|
786
953
|
end
|
787
|
-
end
|
954
|
+
end
|
data/lib/run_loop/version.rb
CHANGED
@@ -0,0 +1,7 @@
|
|
1
|
+
(function(){function m(){return h.frontMostApp()}function r(){return m().mainWindow()}function s(){return m().windows().toArray()}function n(){return m().keyboard()}function l(a,c){this.reason=a;this.a=c||"";this.message=this.toString()}function k(a){return!a||a instanceof UIAElementNil}function t(a,c){var b=c||[],e,d;if(k(a))return b;e=a.elements();for(var f=0,g=e.length;f<g;f+=1)d=e[f],b.push(d),t(d,b);return b}function q(a,c){for(var b=0,e=c.length;b<e;b+=1)a.push(c[b])}function u(a,c){var b=[];
|
2
|
+
if(k(c))return b;c instanceof this[a]&&b.push(c);for(var e=c.elements(),d=0,f=e.length;d<f;d+=1)q(b,u(a,e[d]));return b}function x(a,c){var b=null;if(a instanceof Array){if(3===a.length){var e=a[0],b=a[1],d=a[2];if("string"==typeof d)if(-1==d.indexOf("'"))d="'"+d+"'";else if(-1==d.indexOf('"'))d='"'+d+'"';else throw new l("Escaping for filters not supported yet.");b=c.withPredicate(e+" "+b+" "+d);return!k(b)}return!1}for(e in a)if(a.hasOwnProperty(e))if(b=a[e],"marked"==e){if(c.name()!=b&&c.label()!=
|
3
|
+
b&&(!c.value||c.value!=b))return!1}else if(b=c.withValueForKey(b,e),k(b))return!1;return!0}function v(a,c){if(c(a))return a;var b,e;if(k(a))return null;b=a.elements();for(var d=0,f=b.length;d<f;d+=1)if(e=b[d],v(e,c))return e;return null}function w(a){h.delay(a);return h}var g={},h=UIATarget.localTarget();h.setTimeout(0);l.prototype=error();l.prototype.toString=function(){var a="UIAutomationError[reason="+this.reason;0<this.a.length&&(a+=", details="+this.a);return a+"]"};g.sleep=w;g.query=function(a,
|
4
|
+
c){if(!c)return g.query(a,s());c instanceof UIAElement&&(c=[c]);var b=c,e=null,d=null,f=[],p,h,k,l;p=0;for(k=a.length;p<k;p+=1){e=a[p];h=0;for(l=b.length;h<l;h+=1)d=b[h],"string"===typeof e?"*"===e||"view"==e||"UIAElement"===e?q(f,t(d,[d])):q(f,u(e,d)):x(e,d)&&f.push(d);b=f;f=[]}return b};g.keyboard_visible=function(){return!k(n())};g.keyboard_enter_text=function(a,c){if(!g.keyboard_visible())throw new l("Keyboard not visible");c=c||{};if(c.unsafe)return n().typeString(a),!0;var b=v(r(),function(a){return 1==
|
5
|
+
a.hasKeyboardFocus()});if(k(b))return n().typeString(a),!0;var e=c.initial_text||"",d=new Date,f=c.timeout||60,h=n();do try{return h.typeString(a),!0}catch(m){UIALogger.logMessage("keyboard_enter_text failed: "+m),UIALogger.logMessage("keyboard_enter_text retrying with restore to: "+e),b.setValue(e)}while(!(new Date-d>=1E3*f));throw new l("Unable to enter text","text: "+a+" failed after retrying for "+f);};g.deactivate=function(a){h.deactivateAppForDuration(a)};g.tap_offset=function(a,c,b){b=b||{};
|
6
|
+
return b.unsafe?function(){return c.apply(this,arguments)}:function(){var e=new Date,d=b.timeout||60,f=b.frequency||.1;do try{return c.apply(this,arguments)}catch(g){UIALogger.logMessage(a+"Error: "+g+". Arguments: "+arguments[0]+", "+arguments[1]),w(f)}while(!(new Date-e>=1E3*d));throw new l(a,"Arguments: "+arguments[0]+", "+arguments[1]);}}("tap_offset failed",function(a,c){h.tapWithOptions(a,c||{})},{timeout:60,frequency:.5});this.target=h;this.uia=g;g.app=m;g.window=r;g.windows=s;g.keyboard=n;
|
7
|
+
g.alert=function(){return m().alert()}})();
|
data/scripts/read-cmd.sh
ADDED
data/scripts/run_loop_host.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
//#import "calabash_script_uia.js"
|
2
2
|
|
3
3
|
if (typeof JSON !== 'object') {
|
4
4
|
JSON = {};
|
@@ -151,7 +151,11 @@ var commandPath = "$PATH";
|
|
151
151
|
if (!/\/$/.test(commandPath)) {
|
152
152
|
commandPath += "/";
|
153
153
|
}
|
154
|
-
commandPath += "repl-cmd.
|
154
|
+
commandPath += "repl-cmd.pipe";
|
155
|
+
|
156
|
+
var blockingReadScriptPath = "$READ_SCRIPT_PATH";
|
157
|
+
|
158
|
+
|
155
159
|
|
156
160
|
|
157
161
|
var _expectedIndex = 0,//expected index of next command
|
@@ -269,19 +273,20 @@ var target = null,
|
|
269
273
|
|
270
274
|
while (true) {
|
271
275
|
target = UIATarget.localTarget();
|
276
|
+
|
272
277
|
host = target.host();
|
273
|
-
target.delay(0.2);
|
274
278
|
try {
|
275
|
-
_process = host.performTaskWithPathArgumentsTimeout("/bin/
|
276
|
-
[commandPath],
|
277
|
-
|
279
|
+
_process = host.performTaskWithPathArgumentsTimeout("/bin/bash",
|
280
|
+
[blockingReadScriptPath, commandPath],
|
281
|
+
//[commandPath],
|
282
|
+
1);
|
278
283
|
|
279
284
|
} catch (e) {
|
280
|
-
Log.output("Timeout on
|
285
|
+
Log.output("Timeout on read command...");
|
281
286
|
continue;
|
282
287
|
}
|
283
288
|
if (_process.exitCode != 0) {
|
284
|
-
Log.output("unable to execute
|
289
|
+
Log.output("unable to execute: " + blockingReadScriptPath + " " + commandPath + " exitCode " + _process.exitCode + ". Error: " + _process.stderr + _process.stdout);
|
285
290
|
}
|
286
291
|
else {
|
287
292
|
_input = _process.stdout;
|
@@ -291,7 +296,9 @@ while (true) {
|
|
291
296
|
_actualIndex = parseInt(_input.substring(0, _index), 10);
|
292
297
|
if (!isNaN(_actualIndex) && _actualIndex >= _expectedIndex) {
|
293
298
|
_exp = _input.substring(_index + 1, _input.length);
|
299
|
+
Log.output("Execute: "+_exp);
|
294
300
|
_result = eval(_exp);
|
301
|
+
Log.output("res: "+_result);
|
295
302
|
}
|
296
303
|
else {//likely old command is lingering...
|
297
304
|
continue;
|
@@ -309,6 +316,7 @@ while (true) {
|
|
309
316
|
}
|
310
317
|
|
311
318
|
_expectedIndex++;
|
319
|
+
Log.output("log result: "+_result);
|
312
320
|
Log.result("success", _result);
|
313
321
|
|
314
322
|
}
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: run_loop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.pre5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Karl Krukow
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.18'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0.
|
26
|
+
version: '0.18'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: json
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - ~>
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '1.
|
47
|
+
version: '1.3'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '1.
|
54
|
+
version: '1.3'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: CFPropertyList
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -210,6 +210,7 @@ files:
|
|
210
210
|
- lib/run_loop/version.rb
|
211
211
|
- lib/run_loop/xctools.rb
|
212
212
|
- lib/run_loop.rb
|
213
|
+
- scripts/calabash-uia-min.js
|
213
214
|
- scripts/calabash_script_uia.js
|
214
215
|
- scripts/json2-min.js
|
215
216
|
- scripts/json2.js
|
@@ -218,6 +219,7 @@ files:
|
|
218
219
|
- scripts/run_loop_host.js
|
219
220
|
- scripts/udidetect
|
220
221
|
- scripts/calabash.lldb.erb
|
222
|
+
- scripts/read-cmd.sh
|
221
223
|
- LICENSE
|
222
224
|
homepage: http://calaba.sh
|
223
225
|
licenses:
|