run_loop 1.4.1 → 1.5.0
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/app.rb +10 -0
- data/lib/run_loop/cli/simctl.rb +9 -3
- data/lib/run_loop/core.rb +65 -37
- data/lib/run_loop/device.rb +71 -19
- data/lib/run_loop/host_cache.rb +10 -0
- data/lib/run_loop/instruments.rb +223 -23
- data/lib/run_loop/ipa.rb +5 -0
- data/lib/run_loop/l10n.rb +117 -0
- data/lib/run_loop/lipo.rb +10 -0
- data/lib/run_loop/regex.rb +19 -0
- data/lib/run_loop/sim_control.rb +63 -54
- data/lib/run_loop/simctl/bridge.rb +6 -1
- data/lib/run_loop/version.rb +5 -1
- data/lib/run_loop/xcode.rb +228 -0
- data/lib/run_loop/xctools.rb +109 -94
- data/lib/run_loop.rb +22 -0
- data/scripts/run_loop_fast_uia.js +7 -1
- data/scripts/run_loop_host.js +8 -2
- data/scripts/run_loop_shared_element.js +7 -1
- metadata +6 -4
data/lib/run_loop/instruments.rb
CHANGED
@@ -5,6 +5,24 @@ module RunLoop
|
|
5
5
|
# @note All instruments commands are run in the context of `xcrun`.
|
6
6
|
class Instruments
|
7
7
|
|
8
|
+
include RunLoop::Regex
|
9
|
+
|
10
|
+
attr_reader :xcode
|
11
|
+
|
12
|
+
def xcode
|
13
|
+
@xcode ||= RunLoop::Xcode.new
|
14
|
+
end
|
15
|
+
|
16
|
+
# @!visibility private
|
17
|
+
def to_s
|
18
|
+
"#<Instruments #{version.to_s}>"
|
19
|
+
end
|
20
|
+
|
21
|
+
# @!visibility private
|
22
|
+
def inspect
|
23
|
+
to_s
|
24
|
+
end
|
25
|
+
|
8
26
|
# Returns an Array of instruments process ids.
|
9
27
|
#
|
10
28
|
# @note The `block` parameter is included for legacy API and will be
|
@@ -32,10 +50,17 @@ module RunLoop
|
|
32
50
|
#
|
33
51
|
# Only one instruments process can be running at any one time.
|
34
52
|
#
|
35
|
-
# @param [RunLoop::XCTools]
|
36
|
-
#
|
37
|
-
def kill_instruments(
|
38
|
-
|
53
|
+
# @param [RunLoop::Xcode, RunLoop::XCTools] xcode Used to make check the
|
54
|
+
# active Xcode version.
|
55
|
+
def kill_instruments(xcode = RunLoop::Xcode.new)
|
56
|
+
if xcode.is_a?(RunLoop::XCTools)
|
57
|
+
RunLoop.deprecated('1.5.0',
|
58
|
+
%q(
|
59
|
+
RunLoop::XCTools has been replaced with RunLoop::Xcode.
|
60
|
+
Please update your sources to pass an instance of RunLoop::Xcode))
|
61
|
+
end
|
62
|
+
|
63
|
+
kill_signal = kill_signal(xcode)
|
39
64
|
instruments_pids.each do |pid|
|
40
65
|
terminator = RunLoop::ProcessTerminator.new(pid, kill_signal, 'instruments')
|
41
66
|
unless terminator.kill_process
|
@@ -77,8 +102,144 @@ module RunLoop
|
|
77
102
|
pid.to_i
|
78
103
|
end
|
79
104
|
|
105
|
+
# Returns the instruments version.
|
106
|
+
# @return [RunLoop::Version] A version object.
|
107
|
+
def version
|
108
|
+
@instruments_version ||= lambda do
|
109
|
+
execute_command([]) do |_, stderr, _|
|
110
|
+
version_str = stderr.read[VERSION_REGEX, 0]
|
111
|
+
RunLoop::Version.new(version_str)
|
112
|
+
end
|
113
|
+
end.call
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns an array of Instruments.app templates.
|
117
|
+
#
|
118
|
+
# Depending on the Xcode version Instruments.app templates will either be:
|
119
|
+
#
|
120
|
+
# * A full path to the template. # Xcode 5 and Xcode > 5 betas
|
121
|
+
# * The name of a template. # Xcode >= 6 (non beta)
|
122
|
+
#
|
123
|
+
# **Maintainers!** The rules above are important and explain why we can't
|
124
|
+
# simply filter by `~= /tracetemplate/`.
|
125
|
+
#
|
126
|
+
# Templates that users have saved will always be full paths - regardless
|
127
|
+
# of the Xcode version.
|
128
|
+
#
|
129
|
+
# @return [Array<String>] Instruments.app templates.
|
130
|
+
def templates
|
131
|
+
@instruments_templates ||= lambda do
|
132
|
+
if xcode.version_gte_6?
|
133
|
+
execute_command(['-s', 'templates']) do |stdout, stderr, _|
|
134
|
+
filter_stderr_spam(stderr)
|
135
|
+
stdout.read.chomp.split("\n").map do |elm|
|
136
|
+
stripped = elm.strip.tr('"', '')
|
137
|
+
if stripped == '' || stripped == 'Known Templates:'
|
138
|
+
nil
|
139
|
+
else
|
140
|
+
stripped
|
141
|
+
end
|
142
|
+
end.compact
|
143
|
+
end
|
144
|
+
elsif xcode.version_gte_51?
|
145
|
+
execute_command(['-s', 'templates']) do |stdout, stderr, _|
|
146
|
+
err = stderr.read
|
147
|
+
if !err.nil? || err != ''
|
148
|
+
$stderr.puts stderr.read
|
149
|
+
end
|
150
|
+
|
151
|
+
stdout.read.strip.split("\n").delete_if do |path|
|
152
|
+
not path =~ /tracetemplate/
|
153
|
+
end.map { |elm| elm.strip }
|
154
|
+
end
|
155
|
+
else
|
156
|
+
raise "Xcode version '#{xcode.version}' is not supported."
|
157
|
+
end
|
158
|
+
end.call
|
159
|
+
end
|
160
|
+
|
161
|
+
# Returns an array the available physical devices.
|
162
|
+
#
|
163
|
+
# @return [Array<RunLoop::Device>] All the devices will be physical
|
164
|
+
# devices.
|
165
|
+
def physical_devices
|
166
|
+
@instruments_physical_devices ||= lambda do
|
167
|
+
execute_command(['-s', 'devices']) do |stdout, stderr, _|
|
168
|
+
filter_stderr_spam(stderr)
|
169
|
+
all = stdout.read.chomp.split("\n")
|
170
|
+
valid = all.select do |device|
|
171
|
+
device =~ DEVICE_UDID_REGEX
|
172
|
+
end
|
173
|
+
valid.map do |device|
|
174
|
+
udid = device[DEVICE_UDID_REGEX, 0]
|
175
|
+
version = device[VERSION_REGEX, 0]
|
176
|
+
name = device.split('(').first.strip
|
177
|
+
RunLoop::Device.new(name, version, udid)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end.call
|
181
|
+
end
|
182
|
+
|
183
|
+
# Returns an array of the available simulators.
|
184
|
+
#
|
185
|
+
# **Xcode 5.1**
|
186
|
+
# * iPad Retina - Simulator - iOS 7.1
|
187
|
+
#
|
188
|
+
# **Xcode 6**
|
189
|
+
# * iPad Retina (8.3 Simulator) [EA79555F-ADB4-4D75-930C-A745EAC8FA8B]
|
190
|
+
#
|
191
|
+
# **Xcode 7**
|
192
|
+
# * iPhone 6 (9.0) [3EDC9C6E-3096-48BF-BCEC-7A5CAF8AA706]
|
193
|
+
# * iPhone 6 (9.0) + Apple Watch - 38mm (2.0) [EE3C200C-69BA-4816-A087-0457C5FCEDA0]
|
194
|
+
#
|
195
|
+
# @return [Array<RunLoop::Device>] All the devices will be simulators.
|
196
|
+
def simulators
|
197
|
+
@instruments_simulators ||= lambda do
|
198
|
+
execute_command(['-s', 'devices']) do |stdout, stderr, _|
|
199
|
+
filter_stderr_spam(stderr)
|
200
|
+
lines = stdout.read.chomp.split("\n")
|
201
|
+
lines.map do |line|
|
202
|
+
stripped = line.strip
|
203
|
+
if line_is_simulator?(stripped) &&
|
204
|
+
!line_is_simulator_paired_with_watch?(stripped)
|
205
|
+
|
206
|
+
version = stripped[VERSION_REGEX, 0]
|
207
|
+
|
208
|
+
if line_is_xcode5_simulator?(stripped)
|
209
|
+
name = line
|
210
|
+
udid = line
|
211
|
+
else
|
212
|
+
name = stripped.split('(').first.strip
|
213
|
+
udid = line[CORE_SIMULATOR_UDID_REGEX, 0]
|
214
|
+
end
|
215
|
+
|
216
|
+
RunLoop::Device.new(name, version, udid)
|
217
|
+
else
|
218
|
+
nil
|
219
|
+
end
|
220
|
+
end.compact
|
221
|
+
end
|
222
|
+
end.call
|
223
|
+
end
|
224
|
+
|
80
225
|
private
|
81
226
|
|
227
|
+
# @!visibility private
|
228
|
+
#
|
229
|
+
# ```
|
230
|
+
# $ ps x -o pid,command | grep -v grep | grep instruments
|
231
|
+
# 98081 sh -c xcrun instruments -w "43be3f89d9587e9468c24672777ff6241bd91124" < args >
|
232
|
+
# 98082 /Xcode/6.0.1/Xcode.app/Contents/Developer/usr/bin/instruments -w < args >
|
233
|
+
# ```
|
234
|
+
#
|
235
|
+
# When run from run-loop (via rspec), expect this:
|
236
|
+
#
|
237
|
+
# ```
|
238
|
+
# $ ps x -o pid,command | grep -v grep | grep instruments
|
239
|
+
# 98082 /Xcode/6.0.1/Xcode.app/Contents/Developer/usr/bin/instruments -w < args >
|
240
|
+
# ```
|
241
|
+
INSTRUMENTS_FIND_PIDS_CMD = 'ps x -o pid,command | grep -v grep | grep instruments'
|
242
|
+
|
82
243
|
# @!visibility private
|
83
244
|
# Parses the run-loop options hash into an array of arguments that can be
|
84
245
|
# passed to `Process.spawn` to launch instruments.
|
@@ -109,22 +270,6 @@ module RunLoop
|
|
109
270
|
array + options.fetch(:args, [])
|
110
271
|
end
|
111
272
|
|
112
|
-
# @!visibility private
|
113
|
-
#
|
114
|
-
# ```
|
115
|
-
# $ ps x -o pid,command | grep -v grep | grep instruments
|
116
|
-
# 98081 sh -c xcrun instruments -w "43be3f89d9587e9468c24672777ff6241bd91124" < args >
|
117
|
-
# 98082 /Xcode/6.0.1/Xcode.app/Contents/Developer/usr/bin/instruments -w < args >
|
118
|
-
# ```
|
119
|
-
#
|
120
|
-
# When run from run-loop (via rspec), expect this:
|
121
|
-
#
|
122
|
-
# ```
|
123
|
-
# $ ps x -o pid,command | grep -v grep | grep instruments
|
124
|
-
# 98082 /Xcode/6.0.1/Xcode.app/Contents/Developer/usr/bin/instruments -w < args >
|
125
|
-
# ```
|
126
|
-
INSTRUMENTS_FIND_PIDS_CMD = 'ps x -o pid,command | grep -v grep | grep instruments'
|
127
|
-
|
128
273
|
# @!visibility private
|
129
274
|
#
|
130
275
|
# Executes `ps_cmd` to find instruments processes and returns the result.
|
@@ -185,12 +330,67 @@ module RunLoop
|
|
185
330
|
#
|
186
331
|
# @see https://github.com/calabash/run_loop/issues/34
|
187
332
|
#
|
188
|
-
# @param [RunLoop::XCTools]
|
333
|
+
# @param [RunLoop::Xcode, RunLoop::XCTools] xcode The Xcode tools to use to determine
|
189
334
|
# what version of Xcode is active.
|
190
335
|
# @return [String] Either 'QUIT' or 'TERM', depending on the Xcode
|
191
336
|
# version.
|
192
|
-
def kill_signal(
|
193
|
-
|
337
|
+
def kill_signal(xcode = RunLoop::Xcode.new)
|
338
|
+
if xcode.is_a?(RunLoop::XCTools)
|
339
|
+
RunLoop.deprecated('1.5.0',
|
340
|
+
%q(
|
341
|
+
RunLoop::XCTools has been replaced with RunLoop::Xcode.
|
342
|
+
Please update your sources to pass an instance of RunLoop::Xcode))
|
343
|
+
end
|
344
|
+
xcode.version_gte_6? ? 'QUIT' : 'TERM'
|
345
|
+
end
|
346
|
+
|
347
|
+
# @!visibility private
|
348
|
+
#
|
349
|
+
# Execute an instruments command.
|
350
|
+
# @param [Array] args An array of arguments
|
351
|
+
def execute_command(args)
|
352
|
+
Open3.popen3('xcrun', 'instruments', *args) do |_, stdout, stderr, process_status|
|
353
|
+
yield stdout, stderr, process_status
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
# @!visibility private
|
358
|
+
#
|
359
|
+
# Filters `instruments` spam.
|
360
|
+
def filter_stderr_spam(stderr)
|
361
|
+
# Xcode 6 GM is spamming "WebKit Threading Violations"
|
362
|
+
stderr.read.strip.split("\n").each do |line|
|
363
|
+
unless line[/WebKit Threading Violation/, 0]
|
364
|
+
$stderr.puts line
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
# @!visibility private
|
370
|
+
def line_is_simulator?(line)
|
371
|
+
line_is_core_simulator?(line) || line_is_xcode5_simulator?(line)
|
372
|
+
end
|
373
|
+
|
374
|
+
# @!visibility private
|
375
|
+
def line_is_xcode5_simulator?(line)
|
376
|
+
!line[CORE_SIMULATOR_UDID_REGEX, 0] && line[/Simulator/, 0]
|
377
|
+
end
|
378
|
+
|
379
|
+
# @!visibility private
|
380
|
+
def line_is_core_simulator?(line)
|
381
|
+
return nil if !line_has_a_version?(line)
|
382
|
+
|
383
|
+
line[CORE_SIMULATOR_UDID_REGEX, 0]
|
384
|
+
end
|
385
|
+
|
386
|
+
# @!visibility private
|
387
|
+
def line_has_a_version?(line)
|
388
|
+
line[VERSION_REGEX, 0]
|
389
|
+
end
|
390
|
+
|
391
|
+
# @!visibility private
|
392
|
+
def line_is_simulator_paired_with_watch?(line)
|
393
|
+
line[CORE_SIMULATOR_UDID_REGEX, 0] && line[/Apple Watch/, 0]
|
194
394
|
end
|
195
395
|
end
|
196
396
|
end
|
data/lib/run_loop/ipa.rb
CHANGED
@@ -35,6 +35,11 @@ module RunLoop
|
|
35
35
|
"#<IPA: #{bundle_identifier}: '#{path}'>"
|
36
36
|
end
|
37
37
|
|
38
|
+
# @!visibility private
|
39
|
+
def inspect
|
40
|
+
to_s
|
41
|
+
end
|
42
|
+
|
38
43
|
# The bundle identifier of this ipa.
|
39
44
|
# @return [String] A string representation of this ipa's CFBundleIdentifier
|
40
45
|
# @raise [RuntimeError] If ipa does not expand into a Payload/<app name>.app
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module RunLoop
|
2
|
+
class L10N
|
3
|
+
|
4
|
+
# Find the localized name for a given key_code
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# lookup_localization_name('delete.key', 'da') => 'Slet'
|
8
|
+
#
|
9
|
+
# @param [String] key_code the localization signifier, e.g. 'delete.key'
|
10
|
+
# @param [String] localized_lang an iso language code returned by calabash ios server
|
11
|
+
#
|
12
|
+
# @return [String] the localized name
|
13
|
+
def lookup_localization_name(key_code, localized_lang)
|
14
|
+
lookup_table_dir = lang_dir(localized_lang)
|
15
|
+
return nil unless lookup_table_dir
|
16
|
+
|
17
|
+
key_name_lookup_table(lookup_table_dir)[key_code]
|
18
|
+
end
|
19
|
+
|
20
|
+
UIKIT_AXBUNDLE_PATH_CORE_SIM = 'Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/AccessibilityBundles/UIKit.axbundle/'
|
21
|
+
|
22
|
+
LANG_CODE_TO_LANG_NAME_MAP = {
|
23
|
+
'en' => 'English',
|
24
|
+
'nl' => 'Dutch',
|
25
|
+
'fr' => 'French',
|
26
|
+
'de' => 'German',
|
27
|
+
'es' => 'Spanish',
|
28
|
+
'it' => 'Italian',
|
29
|
+
'jp' => 'Japanese'
|
30
|
+
}
|
31
|
+
|
32
|
+
# @!visibility private
|
33
|
+
def to_s
|
34
|
+
"#<L10N #{uikit_bundle_l10n_path}>"
|
35
|
+
end
|
36
|
+
|
37
|
+
# @!visibility private
|
38
|
+
def inspect
|
39
|
+
to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
# maps the ios keyboard localization to a language directory where we can
|
43
|
+
# find a key-code -> localized-label mapping
|
44
|
+
def lang_dir(localized_lang)
|
45
|
+
l10n_path = uikit_bundle_l10n_path
|
46
|
+
|
47
|
+
## 2 char + _ + sub localization
|
48
|
+
# en_GB.lproj
|
49
|
+
lang_dir_name = "#{localized_lang}.lproj".sub('-','_')
|
50
|
+
if File.exists?(File.join(l10n_path, lang_dir_name))
|
51
|
+
return lang_dir_name
|
52
|
+
end
|
53
|
+
|
54
|
+
# 2 char iso language code
|
55
|
+
# vi.lproj
|
56
|
+
two_char_country_code = localized_lang.split('-')[0]
|
57
|
+
lang_dir_name = "#{two_char_country_code}.lproj"
|
58
|
+
if File.exists?(File.join(l10n_path, lang_dir_name))
|
59
|
+
return lang_dir_name
|
60
|
+
end
|
61
|
+
|
62
|
+
# Full name
|
63
|
+
# e.g. Dutch.lproj
|
64
|
+
lang_dir_name = "#{LANG_CODE_TO_LANG_NAME_MAP[two_char_country_code]}.lproj"
|
65
|
+
if is_full_name?(two_char_country_code) &&
|
66
|
+
File.exists?(File.join(l10n_path, lang_dir_name))
|
67
|
+
return lang_dir_name
|
68
|
+
end
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
|
72
|
+
def uikit_bundle_l10n_path
|
73
|
+
developer_dir = xcode.developer_dir
|
74
|
+
if !developer_dir
|
75
|
+
nil
|
76
|
+
else
|
77
|
+
if xcode.version_gte_6?
|
78
|
+
File.join(developer_dir, UIKIT_AXBUNDLE_PATH_CORE_SIM)
|
79
|
+
else
|
80
|
+
['7.1', '7.0', '6.1'].map do |sdk|
|
81
|
+
path = axbundle_path_for_sdk(developer_dir, sdk)
|
82
|
+
if File.exist?(path)
|
83
|
+
path
|
84
|
+
else
|
85
|
+
nil
|
86
|
+
end
|
87
|
+
end.compact.first
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Xcode 5.1.1 path.
|
93
|
+
def axbundle_path_for_sdk(developer_dir, sdk)
|
94
|
+
File.join(developer_dir,
|
95
|
+
'Platforms/iPhoneSimulator.platform/Developer/SDKs',
|
96
|
+
"iPhoneSimulator#{sdk}.sdk",
|
97
|
+
'System/Library/AccessibilityBundles/UIKit.axbundle')
|
98
|
+
end
|
99
|
+
|
100
|
+
def is_full_name?(two_letter_country_code)
|
101
|
+
LANG_CODE_TO_LANG_NAME_MAP.has_key?(two_letter_country_code)
|
102
|
+
end
|
103
|
+
|
104
|
+
def key_name_lookup_table(lang_dir_name)
|
105
|
+
path = File.join(uikit_bundle_l10n_path, lang_dir_name, 'Accessibility.strings')
|
106
|
+
JSON.parse(`plutil -convert json #{path} -o -`)
|
107
|
+
end
|
108
|
+
|
109
|
+
# @!visibility private
|
110
|
+
attr_reader :xcode
|
111
|
+
|
112
|
+
# @!visibility private
|
113
|
+
def xcode
|
114
|
+
@xcode ||= RunLoop::Xcode.new
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
data/lib/run_loop/lipo.rb
CHANGED
@@ -22,6 +22,16 @@ module RunLoop
|
|
22
22
|
@plist_buddy = RunLoop::PlistBuddy.new
|
23
23
|
end
|
24
24
|
|
25
|
+
# @!visibility private
|
26
|
+
def to_s
|
27
|
+
"#<Lipo #{bundle_path}>"
|
28
|
+
end
|
29
|
+
|
30
|
+
# @!visibility private
|
31
|
+
def inspect
|
32
|
+
to_s
|
33
|
+
end
|
34
|
+
|
25
35
|
# Inspect the `CFBundleExecutable` in the app bundle path with `lipo` and
|
26
36
|
# compare the result with the target device's instruction set.
|
27
37
|
#
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module RunLoop
|
2
|
+
|
3
|
+
# @!visibility private
|
4
|
+
module Regex
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
CORE_SIMULATOR_UDID_REGEX = /[A-F0-9]{8}-([A-F0-9]{4}-){3}[A-F0-9]{12}/.freeze
|
8
|
+
|
9
|
+
# @!visibility private
|
10
|
+
XCODE_511_SIMULATOR_REGEX = /(\d)\.(\d)\.?(\d)?(-64)?/.freeze
|
11
|
+
|
12
|
+
# @!visibility private
|
13
|
+
DEVICE_UDID_REGEX = /[a-f0-9]{40}/.freeze
|
14
|
+
|
15
|
+
# @!visibility private
|
16
|
+
VERSION_REGEX = /(\d\.\d(\.\d)?)/.freeze
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
data/lib/run_loop/sim_control.rb
CHANGED
@@ -14,40 +14,42 @@ module RunLoop
|
|
14
14
|
# @todo `puts` calls need to be replaced with proper logging
|
15
15
|
class SimControl
|
16
16
|
|
17
|
-
|
18
|
-
|
17
|
+
include RunLoop::Regex
|
18
|
+
|
19
|
+
# @deprecated 1.5.0 - replaced by #xcode
|
19
20
|
def xctools
|
21
|
+
RunLoop.deprecated('1.5.0', 'Replaced by RunLoop::Xcode')
|
20
22
|
@xctools ||= RunLoop::XCTools.new
|
21
23
|
end
|
22
24
|
|
23
25
|
# @!visibility private
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
#
|
26
|
+
def xcode
|
27
|
+
@xcode ||= RunLoop::Xcode.new
|
28
|
+
end
|
29
|
+
|
30
|
+
# @!visibility private
|
31
|
+
def xcode_version
|
32
|
+
xcode.version
|
33
|
+
end
|
34
|
+
|
35
|
+
# @!visibility private
|
29
36
|
def xcode_version_gte_7?
|
30
|
-
|
37
|
+
xcode.version_gte_7?
|
31
38
|
end
|
32
39
|
|
33
40
|
# @!visibility private
|
34
|
-
# Are we running Xcode 6 or above?
|
35
|
-
#
|
36
|
-
# This is a convenience method.
|
37
|
-
#
|
38
|
-
# @return [Boolean] `true` if the current Xcode version is >= 6.0
|
39
41
|
def xcode_version_gte_6?
|
40
|
-
|
42
|
+
xcode.version_gte_6?
|
41
43
|
end
|
42
44
|
|
43
45
|
# @!visibility private
|
44
|
-
# Are we running Xcode 5.1 or above?
|
45
|
-
#
|
46
|
-
# This is a convenience method.
|
47
|
-
#
|
48
|
-
# @return [Boolean] `true` if the current Xcode version is >= 5.1
|
49
46
|
def xcode_version_gte_51?
|
50
|
-
|
47
|
+
xcode.version_gte_51?
|
48
|
+
end
|
49
|
+
|
50
|
+
# @!visibility private
|
51
|
+
def xcode_developer_dir
|
52
|
+
xcode.developer_dir
|
51
53
|
end
|
52
54
|
|
53
55
|
# Return an instance of PlistBuddy.
|
@@ -309,26 +311,22 @@ module RunLoop
|
|
309
311
|
# @param [String] udid the String to check
|
310
312
|
# @return [Boolean] Returns true iff the `udid` matches /[A-F0-9]{8}-([A-F0-9]{4}-){3}[A-F0-9]{12}/
|
311
313
|
def sim_udid?(udid)
|
312
|
-
udid.length == 36 and udid[
|
314
|
+
udid.length == 36 and udid[CORE_SIMULATOR_UDID_REGEX,0] != nil
|
313
315
|
end
|
314
316
|
|
315
317
|
def simulators
|
316
|
-
unless
|
318
|
+
unless xcode_version_gte_6?
|
317
319
|
raise RuntimeError, 'simctl is only available on Xcode >= 6'
|
318
320
|
end
|
319
321
|
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
sims << RunLoop::Device.new(details[:name], sdk, details[:udid], details[:state])
|
326
|
-
end
|
322
|
+
hash = simctl_list :devices
|
323
|
+
sims = []
|
324
|
+
hash.each_pair do |sdk, list|
|
325
|
+
list.each do |details|
|
326
|
+
sims << RunLoop::Device.new(details[:name], sdk, details[:udid], details[:state])
|
327
327
|
end
|
328
|
-
sims
|
329
|
-
else
|
330
|
-
raise NotImplementedError, 'the simulators method is not available yet for Xcode 5.1.1'
|
331
328
|
end
|
329
|
+
sims
|
332
330
|
end
|
333
331
|
|
334
332
|
def accessibility_enabled?(device)
|
@@ -586,7 +584,7 @@ module RunLoop
|
|
586
584
|
# @!visibility private
|
587
585
|
# A regex for finding directories under ~/Library/Developer/CoreSimulator/Devices
|
588
586
|
# and parsing the output of `simctl list sessions`.
|
589
|
-
|
587
|
+
CORE_SIMULATOR_UDID_REGEX = /[A-F0-9]{8}-([A-F0-9]{4}-){3}[A-F0-9]{12}/.freeze
|
590
588
|
|
591
589
|
CORE_SIMULATOR_KEYBOARD_PROPERTIES_HASH =
|
592
590
|
{
|
@@ -639,7 +637,7 @@ module RunLoop
|
|
639
637
|
# Xcode.
|
640
638
|
def sim_app_path
|
641
639
|
@sim_app_path ||= lambda {
|
642
|
-
dev_dir =
|
640
|
+
dev_dir = xcode_developer_dir
|
643
641
|
if xcode_version_gte_7?
|
644
642
|
"#{dev_dir}/Applications/Simulator.app"
|
645
643
|
elsif xcode_version_gte_6?
|
@@ -704,9 +702,9 @@ module RunLoop
|
|
704
702
|
def existing_sim_sdk_or_device_data_dirs
|
705
703
|
base_dir = sim_app_support_dir
|
706
704
|
if xcode_version_gte_6?
|
707
|
-
regex =
|
705
|
+
regex = CORE_SIMULATOR_UDID_REGEX
|
708
706
|
else
|
709
|
-
regex =
|
707
|
+
regex = XCODE_511_SIMULATOR_REGEX
|
710
708
|
end
|
711
709
|
dirs = Dir.glob("#{base_dir}/*").select { |path|
|
712
710
|
path =~ regex
|
@@ -840,13 +838,12 @@ module RunLoop
|
|
840
838
|
quit_sim
|
841
839
|
|
842
840
|
verbose = merged_opts[:verbose]
|
843
|
-
target_udid = sim_data_dir[
|
841
|
+
target_udid = sim_data_dir[CORE_SIMULATOR_UDID_REGEX, 0]
|
844
842
|
|
845
843
|
# Directory contains simulators not reported by instruments -s devices
|
846
844
|
simulator_details = sim_details_keyed_with_udid[target_udid]
|
847
845
|
if simulator_details.nil?
|
848
846
|
if verbose
|
849
|
-
xcode_version = xctools.xcode_version
|
850
847
|
puts ["INFO: Skipping '#{target_udid}' directory because",
|
851
848
|
"there is no corresponding simulator for active Xcode (version '#{xcode_version}')"].join("\n")
|
852
849
|
end
|
@@ -930,13 +927,12 @@ module RunLoop
|
|
930
927
|
quit_sim
|
931
928
|
|
932
929
|
verbose = merged_opts[:verbose]
|
933
|
-
target_udid = sim_data_dir[
|
930
|
+
target_udid = sim_data_dir[CORE_SIMULATOR_UDID_REGEX, 0]
|
934
931
|
|
935
932
|
# Directory contains simulators not reported by instruments -s devices
|
936
933
|
simulator_details = sim_details_keyed_with_udid[target_udid]
|
937
934
|
if simulator_details.nil?
|
938
935
|
if verbose
|
939
|
-
xcode_version = xctools.xcode_version
|
940
936
|
puts ["INFO: Skipping '#{target_udid}' directory because",
|
941
937
|
"there is no corresponding simulator for active Xcode (version '#{xcode_version}')"].join("\n")
|
942
938
|
end
|
@@ -1014,16 +1010,17 @@ module RunLoop
|
|
1014
1010
|
end
|
1015
1011
|
|
1016
1012
|
hash = {}
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
value =
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1013
|
+
|
1014
|
+
simulators.each do |device|
|
1015
|
+
launch_name = device.instruments_identifier(xcode)
|
1016
|
+
udid = device.udid
|
1017
|
+
value = {
|
1018
|
+
:launch_name => device.instruments_identifier(xcode),
|
1019
|
+
:udid => device.udid,
|
1020
|
+
:sdk_version => device.version
|
1021
|
+
|
1022
|
+
}
|
1023
|
+
|
1027
1024
|
if primary_key == :udid
|
1028
1025
|
key = udid
|
1029
1026
|
else
|
@@ -1153,15 +1150,27 @@ module RunLoop
|
|
1153
1150
|
res = {}
|
1154
1151
|
out.split("\n").each do |line|
|
1155
1152
|
|
1156
|
-
possible_sdk = line[
|
1153
|
+
possible_sdk = line[VERSION_REGEX,0]
|
1157
1154
|
if possible_sdk
|
1158
1155
|
current_sdk = possible_sdk
|
1159
1156
|
res[current_sdk] = []
|
1160
1157
|
next
|
1161
1158
|
end
|
1162
1159
|
|
1163
|
-
|
1164
|
-
if
|
1160
|
+
unavailable_sdk = line[/Unavailable/, 0]
|
1161
|
+
if unavailable_sdk
|
1162
|
+
current_sdk = nil
|
1163
|
+
next
|
1164
|
+
end
|
1165
|
+
|
1166
|
+
watch_os = line[/watchOS/, 0]
|
1167
|
+
if watch_os
|
1168
|
+
current_sdk = nil
|
1169
|
+
next
|
1170
|
+
end
|
1171
|
+
|
1172
|
+
watch = line[/Apple Watch/, 0]
|
1173
|
+
if watch
|
1165
1174
|
current_sdk = nil
|
1166
1175
|
next
|
1167
1176
|
end
|
@@ -1169,7 +1178,7 @@ module RunLoop
|
|
1169
1178
|
if current_sdk
|
1170
1179
|
unless line[/unavailable/,0]
|
1171
1180
|
name = line.split('(').first.strip
|
1172
|
-
udid = line[
|
1181
|
+
udid = line[CORE_SIMULATOR_UDID_REGEX,0]
|
1173
1182
|
state = line[/(Booted|Shutdown)/,0]
|
1174
1183
|
res[current_sdk] << {:name => name, :udid => udid, :state => state}
|
1175
1184
|
end
|
@@ -194,7 +194,12 @@ module RunLoop::Simctl
|
|
194
194
|
['launchd_sim', true],
|
195
195
|
|
196
196
|
# Yes, but does not always appear.
|
197
|
-
['CoreSimulatorBridge', true]
|
197
|
+
['CoreSimulatorBridge', true],
|
198
|
+
|
199
|
+
# Xcode 7
|
200
|
+
['ids_simd', true],
|
201
|
+
['com.apple.CoreSimulator.CoreSimulatorService', true],
|
202
|
+
['com.apple.CoreSimulator.SimVerificationService', true]
|
198
203
|
].each do |pair|
|
199
204
|
name = pair[0]
|
200
205
|
send_term = pair[1]
|