run_loop 1.4.1 → 1.5.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/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]
|