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.
@@ -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] xcode_tools The Xcode tools to use to determine
36
- # what version of Xcode is active.
37
- def kill_instruments(xcode_tools = RunLoop::XCTools.new)
38
- kill_signal = kill_signal xcode_tools
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] xcode_tools The Xcode tools to use to determine
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(xcode_tools = RunLoop::XCTools.new)
193
- xcode_tools.xcode_version_gte_6? ? 'QUIT' : 'TERM'
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
@@ -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
- # Returns an instance of XCTools.
18
- # @return [RunLoop::XCTools] The xcode tools instance that is used internally.
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
- # Are we running Xcode 7 or above?
25
- #
26
- # This is a convenience method.
27
- #
28
- # @return [Boolean] `true` if the current Xcode version is >= 7.0
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
- xctools.xcode_version_gte_7?
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
- xctools.xcode_version_gte_6?
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
- xctools.xcode_version_gte_51?
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[XCODE_6_SIM_UDID_REGEX,0] != nil
314
+ udid.length == 36 and udid[CORE_SIMULATOR_UDID_REGEX,0] != nil
313
315
  end
314
316
 
315
317
  def simulators
316
- unless xcode_version_gte_51?
318
+ unless xcode_version_gte_6?
317
319
  raise RuntimeError, 'simctl is only available on Xcode >= 6'
318
320
  end
319
321
 
320
- if xcode_version_gte_6?
321
- hash = simctl_list :devices
322
- sims = []
323
- hash.each_pair do |sdk, list|
324
- list.each do |details|
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
- XCODE_6_SIM_UDID_REGEX = /[A-F0-9]{8}-([A-F0-9]{4}-){3}[A-F0-9]{12}/.freeze
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 = xctools.xcode_developer_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 = XCODE_6_SIM_UDID_REGEX
705
+ regex = CORE_SIMULATOR_UDID_REGEX
708
706
  else
709
- regex = /(\d)\.(\d)\.?(\d)?(-64)?/
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[XCODE_6_SIM_UDID_REGEX, 0]
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[XCODE_6_SIM_UDID_REGEX, 0]
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
- xctools.instruments(:sims).each do |elm|
1018
- launch_name = elm[/\A.+\((\d\.\d(\.\d)? Simulator\))/, 0]
1019
- udid = elm[XCODE_6_SIM_UDID_REGEX,0]
1020
- sdk_version = elm[/(\d\.\d(\.\d)? Simulator)/, 0].split(' ').first
1021
- value =
1022
- {
1023
- :launch_name => launch_name,
1024
- :udid => udid,
1025
- :sdk_version => RunLoop::Version.new(sdk_version)
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[/(\d\.\d(\.\d)?)/,0]
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
- unavailable_skd = line[/Unavailable/, 0]
1164
- if unavailable_skd
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[XCODE_6_SIM_UDID_REGEX,0]
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]