run_loop 1.0.0.pre3 → 1.0.0.pre5
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|