xcsim 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +37 -0
- data/bin/xcsim +252 -0
- data/lib/xcsim/rbAppBundles.rb +124 -0
- data/lib/xcsim/rbBundleInfo.rb +142 -0
- data/lib/xcsim/rbConstants.rb +28 -0
- data/lib/xcsim/rbDeviceID.rb +78 -0
- data/lib/xcsim/rbDeviceSet.rb +67 -0
- data/lib/xcsim/rbList.rb +148 -0
- data/lib/xcsim/rbOSDevices.rb +47 -0
- data/lib/xcsim/rbOSID.rb +89 -0
- data/lib/xcsim/rbReports.rb +42 -0
- data/lib/xcsim/rbXCSim.rb +121 -0
- data/lib/xcsim.rb +13 -0
- metadata +73 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7001c7d48bafa2526204efa6fe004587f6881890
|
4
|
+
data.tar.gz: 364f09686a392b1d893f3c3dcde0a3ea12be1974
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1409c1748ae77efb92f9d8ce41fdd7b12b9a6569eb3880428e406ab9b46354e0d41ca34ffaefc76192e18ea69cda43e26aea67ec092cedad688a8df1a96e8b41
|
7
|
+
data.tar.gz: 188424f9644888129ecaea27b6c6c497885bf458af282558f9f98f3422c9649fd880f42b3f76869d6afe69f962e19c9b1c3dcfd70baae06b83273b2c162a59f3
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Egor Chiglintsev.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# xcsim
|
2
|
+
|
3
|
+
xcsim is a command-line utility to simplify opening iOS Simulator application directories in Finder.
|
4
|
+
iOS Simulator uses UUIDs heavily for naming its directories, which makes them not really readable
|
5
|
+
by human eye. Consider for example:
|
6
|
+
|
7
|
+
/Users/User/Library/Developer/CoreSimulator/Devices/FC138577-990F-4069-9AB1-6D847B8529BD/data/
|
8
|
+
Containers/Data/Application/E34BF950-B343-4C73-B50F-EEEA0E34BFAC
|
9
|
+
|
10
|
+
Suppose you have an iOS application with bundle ID `com.yourcompany.appname` and want to peek
|
11
|
+
into its Documents folder, which is stored in iPhone 5s (iOS 9.2) simulator. Finding the right
|
12
|
+
directory could be a tedious task. With xcsim it is as easy as
|
13
|
+
|
14
|
+
xcsim --os "iOS 9.2" --device "iPhone 5s" com.yourcompany.appname
|
15
|
+
|
16
|
+
or even as just
|
17
|
+
|
18
|
+
xcsim com.yourcompany.appname
|
19
|
+
|
20
|
+
if it happens that default OS and device values are enough for your needs.
|
21
|
+
|
22
|
+
In case you're not sure which exact simulator your app is installed on, you can use xcsim to list
|
23
|
+
all present simulators by OS type, device name and view bundle IDs of the applications installed
|
24
|
+
on them.
|
25
|
+
|
26
|
+
By default xcsim invokes the `open` command to open the application data directory in Finder. By
|
27
|
+
providing the `--app` option you can open application bundle directory instead. Using `--echo`
|
28
|
+
option will print the full path to the selected directory instead of opening Finder.
|
29
|
+
|
30
|
+
Usage: `xcsim [options] [bundleID]`
|
31
|
+
|
32
|
+
-l, --list [OS], [DEVICE] List simulator OS, devices, app bundle IDs
|
33
|
+
-o, --os 'TYPE VERSION' Select simulator OS (default: 'iOS 9.2')
|
34
|
+
-d, --device 'TYPE' Select simulator device (default: 'iPhone 5s')
|
35
|
+
-a, --app Select application container directory instead of data directory
|
36
|
+
-e, --echo Echo the selected directory instead of opening it
|
37
|
+
-h, --help [OPTION] Print this message / help for individual options
|
data/bin/xcsim
ADDED
@@ -0,0 +1,252 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'cfpropertylist'
|
6
|
+
rescue LoadError
|
7
|
+
puts "require 'cfpropertylist' failed! Consider running 'gem install CFPropertyList'"
|
8
|
+
exit
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'xcsim'
|
12
|
+
|
13
|
+
def banner
|
14
|
+
<<-END_BANNER
|
15
|
+
xcsim is a command-line utility to simplify opening iOS Simulator application directories in Finder.
|
16
|
+
iOS Simulator uses UUIDs heavily for naming its directories, which makes them not really readable
|
17
|
+
by human eye. Consider for example:
|
18
|
+
|
19
|
+
/Users/User/Library/Developer/CoreSimulator/Devices/FC138577-990F-4069-9AB1-6D847B8529BD/data/
|
20
|
+
Containers/Data/Application/E34BF950-B343-4C73-B50F-EEEA0E34BFAC
|
21
|
+
|
22
|
+
Suppose you have an iOS application with bundle ID 'com.yourcompany.appname' and want to peek
|
23
|
+
into its Documents folder, which is stored in iPhone 5s (iOS 9.2) simulator. Finding the right
|
24
|
+
directory could be a tedious task. With xcsim it is as easy as
|
25
|
+
|
26
|
+
xcsim --os "iOS 9.2" --device "iPhone 5s" com.yourcompany.appname
|
27
|
+
|
28
|
+
or even as just
|
29
|
+
|
30
|
+
xcsim com.yourcompany.appname
|
31
|
+
|
32
|
+
if it happens that default OS and device values are enough for your needs.
|
33
|
+
|
34
|
+
In case you're not sure which exact simulator your app is installed on, you can use xcsim to list
|
35
|
+
all present simulators by OS type, device name and view bundle IDs of the applications installed
|
36
|
+
on them.
|
37
|
+
|
38
|
+
By default xcsim invokes the `open` command to open the application data directory in Finder. By
|
39
|
+
providing the `--app` option you can open application bundle directory instead. Using `--echo`
|
40
|
+
option will print the full path to the selected directory instead of opening Finder.
|
41
|
+
|
42
|
+
Usage: xcsim [options] [bundleID]
|
43
|
+
|
44
|
+
END_BANNER
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
def help_list
|
50
|
+
<<-END_LIST_HELP
|
51
|
+
Usage: xcsim --list [OS], [DEVICE]
|
52
|
+
|
53
|
+
List option allows listing available simulators/OS versions or bundle IDs of installed apps.
|
54
|
+
Actual output of the list option depends on the parameters provided. Both parameters are optional,
|
55
|
+
and if both present, should be separated by a comma.
|
56
|
+
|
57
|
+
Without parameters `xcsim --list` prints all iOS simulator OS versions available with respective
|
58
|
+
count of installed devices for each of the OS:
|
59
|
+
|
60
|
+
xcsim --list
|
61
|
+
|
62
|
+
iOS 8.0 (6 devices)
|
63
|
+
iOS 8.3 (5 devices)
|
64
|
+
iOS 9.0 (6 devices)
|
65
|
+
iOS 9.1 (4 devices)
|
66
|
+
iOS 9.2 (10 devices)
|
67
|
+
|
68
|
+
Both OS and DEVICE parameters can be used to partially match existing simulators and print the
|
69
|
+
matching results. Considering the example above, we could receive the following outputs:
|
70
|
+
|
71
|
+
xcsim --list 9 # note partial match on OS version
|
72
|
+
|
73
|
+
iOS 9.0 (6 devices)
|
74
|
+
iOS 9.1 (4 devices)
|
75
|
+
iOS 9.2 (10 devices)
|
76
|
+
|
77
|
+
xcsim --list 9.1 # exact match yields list of devices for the given OS version
|
78
|
+
|
79
|
+
iPhone 4s
|
80
|
+
iPhone 5s
|
81
|
+
iPhone 6 Plus
|
82
|
+
iPhone 6s Plus
|
83
|
+
|
84
|
+
xcsim --list iPad # partial match on device type, lists devices with OS versions
|
85
|
+
|
86
|
+
iPad 2 (iOS 8.0)
|
87
|
+
iPad Air (iOS 8.0)
|
88
|
+
iPad Air (iOS 8.3)
|
89
|
+
iPad Air (iOS 9.0)
|
90
|
+
iPad 2 (iOS 9.2)
|
91
|
+
iPad Air (iOS 9.2)
|
92
|
+
iPad Air 2 (iOS 9.2)
|
93
|
+
|
94
|
+
xcsim --list iOS 9.2, iPad Air 2 # exact device match lists bundle IDs of installed apps
|
95
|
+
|
96
|
+
com.yourcompany.helloapp
|
97
|
+
com.yourcompany.otherapp
|
98
|
+
END_LIST_HELP
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
def help_os
|
103
|
+
<<-END_OS_HELP
|
104
|
+
Usage: xcsim --os "iOS 9.2" com.yourcompany.appname
|
105
|
+
|
106
|
+
Sets the OS version to work with when searching for bundle ID (needs to be an exact match, i.e.
|
107
|
+
partial matches such as "iOS" or "iOS 9" are not allowed)
|
108
|
+
|
109
|
+
If omitted, a default OS will be used instead (#{XCSim::defaultOSName})
|
110
|
+
END_OS_HELP
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
def help_device
|
115
|
+
<<-END_DEVICE_HELP
|
116
|
+
Usage: xcsim --device "iPhone 5s" com.yourcompany.appname
|
117
|
+
|
118
|
+
Sets the device type to work with when searching for bundle ID (needs to be an exact match, i.e.
|
119
|
+
partial matches such as "iPhone" are not allowed)
|
120
|
+
|
121
|
+
If omitted, a default device will be used instead (#{XCSim::defaultDeviceName})
|
122
|
+
END_DEVICE_HELP
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def help_app
|
127
|
+
<<-END_APP_HELP
|
128
|
+
Usage: xcsim --app com.yourcompany.appname
|
129
|
+
|
130
|
+
Selects application bundle directory instead of application data directory (which is default).
|
131
|
+
END_APP_HELP
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
def help_echo
|
136
|
+
<<-END_ECHO_HELP
|
137
|
+
Usage: xcsim --echo com.yourcompany.appname
|
138
|
+
|
139
|
+
Prints the selected directory path instead of opening it.
|
140
|
+
END_ECHO_HELP
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
parser = OptionParser.new do |opts|
|
145
|
+
opts.banner = banner
|
146
|
+
|
147
|
+
opts.on_tail("-h", "--help [OPTION]", "Print this message / help for individual options") do |v|
|
148
|
+
if v == "l" || v == "list"
|
149
|
+
puts help_list
|
150
|
+
elsif v == "o" || v == "os"
|
151
|
+
puts help_os
|
152
|
+
elsif v == "d" || v == "device"
|
153
|
+
puts help_device
|
154
|
+
elsif v == "a" || v == "app"
|
155
|
+
puts help_app
|
156
|
+
elsif v == "e" || v == "echo"
|
157
|
+
puts help_echo
|
158
|
+
else
|
159
|
+
puts opts
|
160
|
+
end
|
161
|
+
exit
|
162
|
+
end
|
163
|
+
|
164
|
+
opts.on("-l", "--list [OS], [DEVICE]",
|
165
|
+
"List simulator OS, devices, app bundle IDs") do |v|
|
166
|
+
@command = :list
|
167
|
+
@argument = ([v] + ARGV).join(" ")
|
168
|
+
end
|
169
|
+
|
170
|
+
opts.on("-o", "--os 'TYPE VERSION'",
|
171
|
+
"Select simulator OS (default: '#{XCSim::defaultOSName}')") do |v|
|
172
|
+
@argument ||= {}
|
173
|
+
@argument[:os] = v
|
174
|
+
end
|
175
|
+
|
176
|
+
opts.on("-d", "--device 'TYPE'",
|
177
|
+
"Select simulator device (default: '#{XCSim::defaultDeviceName}')") do |v|
|
178
|
+
@argument ||= {}
|
179
|
+
@argument[:device] = v
|
180
|
+
end
|
181
|
+
|
182
|
+
opts.on("-a", "--app", "Select application container directory instead of data directory") do |v|
|
183
|
+
@argument ||= {}
|
184
|
+
@argument[:dir] = :app
|
185
|
+
end
|
186
|
+
|
187
|
+
opts.on("-e", "--echo", "Echo the selected directory instead of opening it") do |v|
|
188
|
+
@argument ||= {}
|
189
|
+
@argument[:output] = :echo
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
parser.parse!
|
194
|
+
|
195
|
+
begin
|
196
|
+
if @command == nil
|
197
|
+
if ARGV.count > 1
|
198
|
+
raise ArgumentError
|
199
|
+
end
|
200
|
+
|
201
|
+
@command ||= :bundle
|
202
|
+
@argument ||= {}
|
203
|
+
|
204
|
+
@argument[:bundleID] ||= ARGV.first
|
205
|
+
end
|
206
|
+
|
207
|
+
result = xcsim(@command, @argument)
|
208
|
+
|
209
|
+
case @command
|
210
|
+
when :list
|
211
|
+
unless result.empty?
|
212
|
+
puts XCSim::reportFromDeviceList(result)
|
213
|
+
else
|
214
|
+
puts "ERROR: Could not find simulators matching '#{@argument}'!\n"
|
215
|
+
puts parser
|
216
|
+
exit 1
|
217
|
+
end
|
218
|
+
|
219
|
+
when :bundle
|
220
|
+
path = (@argument[:dir] == :app) ? result.bundlePath : result.dataPath
|
221
|
+
|
222
|
+
if @argument[:output] == :echo
|
223
|
+
puts path
|
224
|
+
else
|
225
|
+
`open #{path}`
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
exit
|
230
|
+
|
231
|
+
rescue XCSim::NonUniqueBundleIDError => error
|
232
|
+
puts "ERROR: Multiple data directories matching bundle ID '#{error.bundleID}' on #{error.deviceID} " +
|
233
|
+
"found:"
|
234
|
+
puts error.directories
|
235
|
+
exit 1
|
236
|
+
|
237
|
+
rescue XCSim::OSNotFoundError => error
|
238
|
+
puts "ERROR: Unknown OS '#{error.name}'"
|
239
|
+
exit 1
|
240
|
+
|
241
|
+
rescue XCSim::DeviceNotFoundError => error
|
242
|
+
puts "ERROR: Unknown device '#{error.name}' for OS '#{error.os.id}'"
|
243
|
+
exit 1
|
244
|
+
|
245
|
+
rescue XCSim::BundleNotFoundError => error
|
246
|
+
puts "Unknown bundle ID '#{error.bundleID}' on #{error.device.name} (#{error.os.id})"
|
247
|
+
exit 1
|
248
|
+
|
249
|
+
rescue ArgumentError
|
250
|
+
puts parser
|
251
|
+
exit
|
252
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'cfpropertylist'
|
4
|
+
require_relative 'rbBundleInfo'
|
5
|
+
|
6
|
+
module XCSim
|
7
|
+
|
8
|
+
# An error raised by XCSim::findBundleDataPath function when multiple directories matching
|
9
|
+
# the given bundle ID are found on the same simulator device.
|
10
|
+
class NonUniqueBundleIDError < RuntimeError
|
11
|
+
|
12
|
+
# A DeviceID object representing the iOS Simulator on which the error has occurred
|
13
|
+
attr_reader :deviceID
|
14
|
+
|
15
|
+
# A bundle ID string, which caused the error
|
16
|
+
attr_reader :bundleID
|
17
|
+
|
18
|
+
# An array of strings containing absolute paths for directories matching the given bundle ID
|
19
|
+
attr_reader :directories
|
20
|
+
|
21
|
+
# Initializes a NonUniqueBundleIDError object with the given parameters
|
22
|
+
def initialize(deviceID, bundleID, directories)
|
23
|
+
@bundleID = bundleID
|
24
|
+
@deviceID = deviceID
|
25
|
+
@directories = directories
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
# call-seq:
|
32
|
+
# findBundleDataPath(deviceID, bundleID) => String
|
33
|
+
#
|
34
|
+
# +deviceID+:: A DeviceID object representing the device simulator on which the application
|
35
|
+
# is installed
|
36
|
+
# +bundleID+:: Bundle ID of the application to search for
|
37
|
+
#
|
38
|
+
# Finds an absolute path for application data directory of an application with a given bundle ID
|
39
|
+
# installed on a given device.
|
40
|
+
#
|
41
|
+
# Searches for directories matching +bundleID+ inside the application data directory of the
|
42
|
+
# device. Raises a NonUniqueBundleIDError if multiple matching directories
|
43
|
+
# are found (bundle IDs are expected to be unique). Returns the found directory or +nil+
|
44
|
+
# otherwise.
|
45
|
+
def self.findBundleDataPath(deviceID, bundleID)
|
46
|
+
path = deviceID.appDataPath
|
47
|
+
subdirs = Dir.entries(path).select do |entry|
|
48
|
+
File.directory? File.join(path, entry) and !(entry =='.' || entry == '..')
|
49
|
+
end
|
50
|
+
|
51
|
+
metadataPairs = subdirs.map do |dir|
|
52
|
+
metadataPath = "#{path}/#{dir}/#{BUNDLE_METADATA_PLIST}"
|
53
|
+
|
54
|
+
if File.exists? metadataPath
|
55
|
+
plist = CFPropertyList::List.new(:file => metadataPath)
|
56
|
+
plist = CFPropertyList.native_types(plist.value)
|
57
|
+
|
58
|
+
{ :plist => plist, :dir => "#{path}/#{dir}" }
|
59
|
+
else
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
.select{ |pair| pair != nil }
|
64
|
+
|
65
|
+
result = metadataPairs.select{ |pair| pair[:plist][METADATA_ID] == bundleID }
|
66
|
+
|
67
|
+
if result.count > 1
|
68
|
+
raise NonUniqueBundleIDError.new(deviceID, bundleID, result.map{|pair| pair[:dir]})
|
69
|
+
elsif result.empty?
|
70
|
+
return nil
|
71
|
+
else
|
72
|
+
result.first[:dir]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
# call-seq:
|
79
|
+
# parseInstalledBundles(deviceID) => Hash
|
80
|
+
#
|
81
|
+
# +deviceID+:: A DeviceID object to search installed app bundles for
|
82
|
+
#
|
83
|
+
# Searches the given iOS Simulator device for installed application bundles (excluding the
|
84
|
+
# system applications such as Safari) and returns a Hash of <tt>bundle ID string =>
|
85
|
+
# BundleInfo</tt> containing BundleInfo instances corresponding the found bundles.
|
86
|
+
#
|
87
|
+
# May raise a NonUniqueBundleIDError if multiple data directories are found for one
|
88
|
+
# of the bundles.
|
89
|
+
def self.parseInstalledBundles(deviceID)
|
90
|
+
path = deviceID.appBundlesPath
|
91
|
+
subdirs = Dir.entries(path).select do |entry|
|
92
|
+
File.directory? File.join(path, entry) and !(entry =='.' || entry == '..')
|
93
|
+
end
|
94
|
+
|
95
|
+
bundlePlists = subdirs.map do |dir|
|
96
|
+
plistPath = "#{path}/#{dir}/#{BUNDLE_METADATA_PLIST}"
|
97
|
+
|
98
|
+
if File.exists? plistPath
|
99
|
+
plist = CFPropertyList::List.new(:file => plistPath)
|
100
|
+
plist = CFPropertyList.native_types(plist.value)
|
101
|
+
|
102
|
+
{ :plist => plist, :dir => "#{path}/#{dir}" }
|
103
|
+
else
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
end
|
107
|
+
.select { |plist| plist != nil }
|
108
|
+
|
109
|
+
bundleInfos = bundlePlists.map do |pair|
|
110
|
+
bundleID = pair[:plist][METADATA_ID]
|
111
|
+
bundlePath = pair[:dir]
|
112
|
+
dataPath = findBundleDataPath(deviceID, bundleID)
|
113
|
+
BundleInfo.new(bundleID, bundlePath, dataPath)
|
114
|
+
end
|
115
|
+
|
116
|
+
bundleInfosHash = {}
|
117
|
+
bundleInfos.each{ |info| bundleInfosHash[info.bundleID] = info }
|
118
|
+
|
119
|
+
bundleInfosHash
|
120
|
+
end
|
121
|
+
|
122
|
+
end # module XCSim
|
123
|
+
|
124
|
+
# eof
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module XCSim
|
4
|
+
# An error, which is raised by GetBundle class when iOS Simulator OS version specified by
|
5
|
+
# the +:os+ option could not be found.
|
6
|
+
class OSNotFoundError < RuntimeError
|
7
|
+
|
8
|
+
# Name of the OS provided in +:os+ option
|
9
|
+
attr_reader :name
|
10
|
+
|
11
|
+
# Initializes an OSNotFoundError instance with the given OS name
|
12
|
+
def initialize(name)
|
13
|
+
@name = name
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
# An error, which is raised by GetBundle class when iOS Simulator device model specified by
|
20
|
+
# the +:device+ option could not be found.
|
21
|
+
class DeviceNotFoundError < RuntimeError
|
22
|
+
|
23
|
+
# An OSDevices object corresponding to the OS version, which was used when searching
|
24
|
+
# for the device
|
25
|
+
attr_reader :os
|
26
|
+
|
27
|
+
# Device name specified in the +:device+ option
|
28
|
+
attr_reader :name
|
29
|
+
|
30
|
+
# Initializes a DeviceNotFoundError instance with a given os and name
|
31
|
+
def initialize(os, name)
|
32
|
+
@os = os
|
33
|
+
@name = name
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
# An error, which is raised by GetBundle class when bundle ID specified by +:bundleID+ option
|
40
|
+
# has not been found (even with partial match) on the device
|
41
|
+
class BundleNotFoundError < RuntimeError
|
42
|
+
|
43
|
+
# An OSDevices object corresponding to the OS version, which was used when searching
|
44
|
+
# for the application bundle
|
45
|
+
attr_reader :os
|
46
|
+
|
47
|
+
# A DeviceID object corresponding to the device, which was used when searching for the
|
48
|
+
# application bundle
|
49
|
+
attr_reader :device
|
50
|
+
|
51
|
+
# Bundle ID provided in +:bundleID+ option
|
52
|
+
attr_reader :bundleID
|
53
|
+
|
54
|
+
# Initializes a BundleNotFoundError instance with a given os, device and bundleID
|
55
|
+
def initialize(os, device, bundleID)
|
56
|
+
@os = os
|
57
|
+
@device = device
|
58
|
+
@bundleID = bundleID
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
# Contains information about a certain application bundle
|
65
|
+
class BundleInfo
|
66
|
+
|
67
|
+
# Bundle ID of the application
|
68
|
+
attr_reader :bundleID
|
69
|
+
|
70
|
+
# Absolute path for the application bundle directory
|
71
|
+
attr_reader :bundlePath
|
72
|
+
|
73
|
+
# Absolute path for the application data directory
|
74
|
+
attr_reader :dataPath
|
75
|
+
|
76
|
+
# Initializes a BundleInfo instance with a given +bundleID+, +bundlePath+, +dataPath+
|
77
|
+
def initialize(bundleID, bundlePath, dataPath)
|
78
|
+
@bundleID = bundleID
|
79
|
+
@bundlePath = bundlePath
|
80
|
+
@dataPath = dataPath
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns +bundleID+
|
84
|
+
def inspect
|
85
|
+
@bundleID
|
86
|
+
end
|
87
|
+
|
88
|
+
# Same as #inspect
|
89
|
+
def to_s
|
90
|
+
inspect
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
# A class encapsulating logic of searching for an applciation bundle in #xcsim function
|
97
|
+
# (i.e. implementation of #xcsim bundle mode)
|
98
|
+
class GetBundle
|
99
|
+
# Initializes a GetBundle instance with a given device set of OSDevices type
|
100
|
+
def initialize(deviceSet)
|
101
|
+
@deviceSet = deviceSet
|
102
|
+
end
|
103
|
+
|
104
|
+
# Performs search for the application bundle matching the provided options.
|
105
|
+
# See #xcsim description (Bundle Mode section) for more info.
|
106
|
+
#
|
107
|
+
# Returns a BundleInfo object
|
108
|
+
def withOptions(options)
|
109
|
+
bundleID = options[:bundleID]
|
110
|
+
|
111
|
+
if bundleID == nil
|
112
|
+
raise ArgumentError
|
113
|
+
end
|
114
|
+
|
115
|
+
os = osNamed(options[:os] || XCSim::defaultOSName)
|
116
|
+
device = deviceNamed(os, options[:device] || XCSim::defaultDeviceName)
|
117
|
+
bundles = XCSim::parseInstalledBundles(device).values
|
118
|
+
|
119
|
+
matchingBundles = bundles.select { |bundle| bundle.bundleID.end_with? bundleID }
|
120
|
+
|
121
|
+
if matchingBundles.count > 1
|
122
|
+
raise NonUniqueBundleIDError.new(device, bundleID, matchingBundles.map { |b| b.bundleID })
|
123
|
+
elsif matchingBundles.empty?
|
124
|
+
raise BundleNotFoundError.new(os, device, bundleID)
|
125
|
+
else
|
126
|
+
return matchingBundles.first
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
def osNamed(name)
|
132
|
+
@deviceSet[OSID.fromString(name)] || (raise OSNotFoundError.new(name))
|
133
|
+
end
|
134
|
+
|
135
|
+
def deviceNamed(os, name)
|
136
|
+
os.devices[name] || (raise DeviceNotFoundError.new(os, name))
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
end # module XCSim
|
141
|
+
|
142
|
+
# eof
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# XCSim module contains utility for parsing iOS Simulator metadata plist files
|
4
|
+
# and providing access to the bundle and data directories of the applications
|
5
|
+
# installed on iOS Simulator.
|
6
|
+
module XCSim
|
7
|
+
|
8
|
+
# Absolute path of the directory, which stores all of thr iOS Simulator data
|
9
|
+
SIMULATORS_ROOT = File.expand_path("~/Library/Developer/CoreSimulator/Devices")
|
10
|
+
|
11
|
+
# Path for the application bundles directory relative to a concrete iOS Simulator device dir
|
12
|
+
DEVICE_APP_BUNDLES_RELATIVE_PATH = "data/Containers/Bundle/Application"
|
13
|
+
|
14
|
+
# Path for the application data directory relative to a concrete iOS Simulator device dir
|
15
|
+
DEVICE_APP_DATA_RELATIVE_PATH = "data/Containers/Data/Application"
|
16
|
+
|
17
|
+
# Name of the +device_set.plist+ file
|
18
|
+
DEVICE_SET_PLIST = "device_set.plist"
|
19
|
+
|
20
|
+
# Name of the plist file containing a certain application's metadata
|
21
|
+
BUNDLE_METADATA_PLIST = ".com.apple.mobile_container_manager.metadata.plist"
|
22
|
+
|
23
|
+
# A key in application metadata plist file, which corresponds to the bundle ID of the application
|
24
|
+
METADATA_ID = "MCMMetadataIdentifier"
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
# eof
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require_relative 'rbConstants'
|
4
|
+
|
5
|
+
module XCSim
|
6
|
+
|
7
|
+
# DeviceID is a pair of device name string and a +GUID+, which identifies
|
8
|
+
# the concrete iOS Simulator in the file system. DeviceID provides access
|
9
|
+
# to the application bundle and application documents paths.
|
10
|
+
class DeviceID
|
11
|
+
|
12
|
+
# Name of the device (e.g. <tt>'iPhone 5s'</tt>)
|
13
|
+
attr_reader :name
|
14
|
+
|
15
|
+
# Creates a DeviceID instance from a string prefixed by a standard
|
16
|
+
# prefix encountered in +device_set.plist+ (+com.apple.CoreSimulator.SimDeviceType.+)
|
17
|
+
#
|
18
|
+
# Does not perform any additional validation other than checking the prefix.
|
19
|
+
def self.fromPrefixedString(string, guid)
|
20
|
+
unless string.start_with? @@PREFIX
|
21
|
+
return nil
|
22
|
+
end
|
23
|
+
|
24
|
+
name = string.sub(@@PREFIX, "").gsub("-", " ")
|
25
|
+
return DeviceID.new(name, guid)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Initializes a DeviceID instance with a given name and +GUID+.
|
29
|
+
def initialize(name, guid)
|
30
|
+
@name = name
|
31
|
+
@guid = guid
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns an absolute path for application bundles directory
|
35
|
+
# corresponding to the given device.
|
36
|
+
def appBundlesPath
|
37
|
+
"#{SIMULATORS_ROOT}/#{@guid}/#{DEVICE_APP_BUNDLES_RELATIVE_PATH}"
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns an absolute path for application data directory
|
41
|
+
# corresponding to the given device
|
42
|
+
def appDataPath
|
43
|
+
"#{SIMULATORS_ROOT}/#{@guid}/#{DEVICE_APP_DATA_RELATIVE_PATH}"
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns device name
|
47
|
+
def inspect
|
48
|
+
@name
|
49
|
+
end
|
50
|
+
|
51
|
+
# Same as #inspect
|
52
|
+
def to_s
|
53
|
+
inspect
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns a string used for indexing device definitions in +device_set.plist+
|
57
|
+
# in +com.apple.CoreSimulator.SimDeviceType.iPad-2+ format.
|
58
|
+
def key
|
59
|
+
"#{@@PREFIX}#{@name.gsub(" ","-")}"
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns +inspect.hash+
|
63
|
+
def hash
|
64
|
+
inspect.hash
|
65
|
+
end
|
66
|
+
|
67
|
+
# Checks equality by <tt>@name</tt>
|
68
|
+
def eql?(other)
|
69
|
+
@name.eql?(other.name)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
@@PREFIX = "com.apple.CoreSimulator.SimDeviceType."
|
74
|
+
end # class DeviceID
|
75
|
+
|
76
|
+
end # module XCSim
|
77
|
+
|
78
|
+
# eof
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'cfpropertylist'
|
4
|
+
|
5
|
+
require_relative 'rbOSID'
|
6
|
+
require_relative 'rbDeviceID'
|
7
|
+
require_relative 'rbOSDevices'
|
8
|
+
|
9
|
+
module XCSim
|
10
|
+
|
11
|
+
# call-seq:
|
12
|
+
# parseDeviceSet(absolutePath) => Hash
|
13
|
+
#
|
14
|
+
# Parses a +device_set.plist+ file located at the given +path+
|
15
|
+
#
|
16
|
+
# Returns a Hash of <tt>OSID => OSDevices</tt> for iOS Simulator OSes,
|
17
|
+
# which have at least one device simulator installed.
|
18
|
+
def self.parseDeviceSet(path)
|
19
|
+
plist = CFPropertyList::List.new(:file => path)
|
20
|
+
plist = CFPropertyList.native_types(plist.value)
|
21
|
+
defaultDevices = plist["DefaultDevices"]
|
22
|
+
|
23
|
+
osIDs = defaultDevices
|
24
|
+
.keys
|
25
|
+
.map{|s| OSID.fromPrefixedString(s) }
|
26
|
+
.select{|id| id != nil}
|
27
|
+
|
28
|
+
oses = osIDs.map do |id|
|
29
|
+
osDevices = defaultDevices[id.key]
|
30
|
+
devices = osDevices
|
31
|
+
.keys
|
32
|
+
.map{ |s| DeviceID.fromPrefixedString(s, osDevices[s])}
|
33
|
+
.select{ |device| device != nil }
|
34
|
+
.select{ |device| File.directory? device.appBundlesPath }
|
35
|
+
|
36
|
+
(devices.count > 0) ? OSDevices.new(id, devices) : nil
|
37
|
+
end
|
38
|
+
.select{ |os| os != nil }
|
39
|
+
|
40
|
+
osHash = {}
|
41
|
+
oses.each{ |os| osHash[os.id] = os }
|
42
|
+
|
43
|
+
osHash
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns default device set of OSDevices class (as parsed by #parseDeviceSet with default
|
47
|
+
# iOS Simulators path)
|
48
|
+
def self.deviceSet
|
49
|
+
@@deviceSet
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns default OS name for use in #xcsim bundle mode when no +:os+ option is provided.
|
53
|
+
# Selects the OS with the highest version number as the default.
|
54
|
+
def self.defaultOSName
|
55
|
+
@@deviceSet.keys.max.to_s
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the default device name for use in #xcsim bundle mode when no +:device+
|
59
|
+
# option is provided.
|
60
|
+
def self.defaultDeviceName
|
61
|
+
"iPhone 5s"
|
62
|
+
end
|
63
|
+
|
64
|
+
@@deviceSet = parseDeviceSet("#{XCSim::SIMULATORS_ROOT}/#{XCSim::DEVICE_SET_PLIST}")
|
65
|
+
end # module XCSim
|
66
|
+
|
67
|
+
# eof
|
data/lib/xcsim/rbList.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require_relative 'rbAppBundles'
|
4
|
+
|
5
|
+
module XCSim
|
6
|
+
# An aggregate object containing information on a single iOS Simulator device
|
7
|
+
# returned by GetDeviceList#withPattern or GetDeviceList#allDevices
|
8
|
+
class DeviceListItem
|
9
|
+
|
10
|
+
# An OSDevices object corresponding to the device OS version
|
11
|
+
attr_reader :os
|
12
|
+
|
13
|
+
# A DeviceID object corresponding to the device
|
14
|
+
attr_reader :device
|
15
|
+
|
16
|
+
# An array of BundleInfo objects corresponding to the applications installed on the
|
17
|
+
# simulator in question
|
18
|
+
attr_reader :bundles
|
19
|
+
|
20
|
+
# Initializes a DeviceListItem instance with a given os, device and bundles array
|
21
|
+
def initialize(os, device, bundles)
|
22
|
+
@os = os
|
23
|
+
@device = device
|
24
|
+
@bundles = bundles
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns a string in <tt>"iPhone 5s (iOS 9.2)"</tt> format
|
28
|
+
def fullName
|
29
|
+
"#{device.name} (#{os.id})"
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns device name
|
33
|
+
def shortName
|
34
|
+
device.name
|
35
|
+
end
|
36
|
+
|
37
|
+
# Same as #fullName
|
38
|
+
def inspect
|
39
|
+
fullName
|
40
|
+
end
|
41
|
+
|
42
|
+
# Same as #inspect
|
43
|
+
def to_s
|
44
|
+
inspect
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
# A class encapsulating logic of searching available OS verions / device models / installed apps
|
50
|
+
# in #xcsim function (i.e. implementation of #xcsim list mode)
|
51
|
+
class GetDeviceList
|
52
|
+
|
53
|
+
# Initializes a GetDeviceList instance with a given device set of OSDevices class
|
54
|
+
def initialize(deviceSet)
|
55
|
+
@deviceSet = deviceSet
|
56
|
+
end
|
57
|
+
|
58
|
+
# Performs search for the iOS simulators matching the provided pattern.
|
59
|
+
# See #xcsim description (List Mode section) for more info.
|
60
|
+
#
|
61
|
+
# Returns an array of DeviceListItem
|
62
|
+
def withPattern(pattern)
|
63
|
+
if pattern.length == 0
|
64
|
+
return allDevices
|
65
|
+
else
|
66
|
+
pattern = parsePattern(pattern)
|
67
|
+
matchingOSes = getMatchingOSList(pattern[:os])
|
68
|
+
|
69
|
+
devicePairs = matchingOSes
|
70
|
+
.map{ |os| os.devices.values.map{ |d| {:os => os, :device => d} } }
|
71
|
+
.flatten
|
72
|
+
|
73
|
+
# try finding a strict match first (disambiguate "iPad Air" from "iPad Air 2")
|
74
|
+
# find partial matches otherwise
|
75
|
+
matchingDevices =
|
76
|
+
strictDeviceMatches(devicePairs, pattern[:device]) ||
|
77
|
+
partialDeviceMatches(devicePairs, pattern[:device]) ||
|
78
|
+
[]
|
79
|
+
|
80
|
+
return matchingDevices.map do |pair|
|
81
|
+
DeviceListItem.new(pair[:os], pair[:device],
|
82
|
+
XCSim::parseInstalledBundles(pair[:device]).values)
|
83
|
+
end
|
84
|
+
.flatten
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns an array of DeviceListItem corresponding to all installed iOS Simulators
|
89
|
+
def allDevices
|
90
|
+
@deviceSet.values.map do |os|
|
91
|
+
os.devices.values.map do |device|
|
92
|
+
DeviceListItem.new(os, device, XCSim::parseInstalledBundles(device).values)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
.flatten
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
def parsePattern(pattern)
|
100
|
+
components = pattern.split(",").map{ |s| s.strip }
|
101
|
+
|
102
|
+
case components.count
|
103
|
+
when 2
|
104
|
+
# assume first pattern is always for OS,
|
105
|
+
# second - for device
|
106
|
+
osPattern = components.first
|
107
|
+
devicePattern = components.last
|
108
|
+
|
109
|
+
when 1
|
110
|
+
# we don't know whether the pattern is for OS or device,
|
111
|
+
# try OS first
|
112
|
+
osPattern = components.first
|
113
|
+
devicePattern = nil
|
114
|
+
matchingOSes = getMatchingOSList(osPattern)
|
115
|
+
|
116
|
+
# if no OSes match, assume the pattern is for device
|
117
|
+
if matchingOSes.empty?
|
118
|
+
osPattern = nil
|
119
|
+
matchingOSes = nil
|
120
|
+
devicePattern = components.first
|
121
|
+
end
|
122
|
+
|
123
|
+
else
|
124
|
+
raise ArgumentError
|
125
|
+
end
|
126
|
+
|
127
|
+
{ :os => osPattern, :device => devicePattern }
|
128
|
+
end
|
129
|
+
|
130
|
+
def strictDeviceMatches(osDevicePairs, pattern)
|
131
|
+
matches = osDevicePairs.select{ |p| p[:device].name == pattern }
|
132
|
+
matches.empty? ? nil : matches
|
133
|
+
end
|
134
|
+
|
135
|
+
def partialDeviceMatches(osDevicePairs, pattern)
|
136
|
+
matches = osDevicePairs.select{ |p| p[:device].name.include? (pattern || "") }
|
137
|
+
matches.empty? ? nil : matches
|
138
|
+
end
|
139
|
+
|
140
|
+
def getMatchingOSList(pattern)
|
141
|
+
return @deviceSet.values.select{ |os| os.id.to_s.include? (pattern || "") }
|
142
|
+
end
|
143
|
+
|
144
|
+
end # class GetDeviceList
|
145
|
+
|
146
|
+
end # module XCSim
|
147
|
+
|
148
|
+
# eof
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module XCSim
|
4
|
+
|
5
|
+
# A collection of DeviceID objects related to a given iOS Simulator OS version
|
6
|
+
class OSDevices
|
7
|
+
|
8
|
+
# An OSID of the iOS Simulator OS version related to the OSDevices collection
|
9
|
+
attr_reader :id
|
10
|
+
|
11
|
+
# A hash of <tt>deviceName => DeviceID</tt>
|
12
|
+
attr_reader :devices
|
13
|
+
|
14
|
+
# Initializes an OSDevices instance with a given OSID and a collection of devices
|
15
|
+
# +id+:: OSID to associate with the OSDevices object
|
16
|
+
# +devices+:: A collection of DeviceID objects. Should support #each method for enumeration.
|
17
|
+
def initialize(id, devices)
|
18
|
+
@id = id
|
19
|
+
|
20
|
+
devicesHash = {}
|
21
|
+
devices.each{ |d| devicesHash[d.name] = d }
|
22
|
+
|
23
|
+
@devices = devicesHash
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns a string in <tt>'iOS 9.0 (5 devices)'</tt> format
|
27
|
+
def inspect
|
28
|
+
"#{@id.inspect} (#{@devices.count} devices)"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Same as #inspect
|
32
|
+
def to_s
|
33
|
+
inspect
|
34
|
+
end
|
35
|
+
|
36
|
+
include Comparable
|
37
|
+
|
38
|
+
# Compares by +id+
|
39
|
+
def <=>(other)
|
40
|
+
@id <=> other.id
|
41
|
+
end
|
42
|
+
|
43
|
+
end # class OSDevicess
|
44
|
+
|
45
|
+
end # module XCSim
|
46
|
+
|
47
|
+
# eof
|
data/lib/xcsim/rbOSID.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module XCSim
|
4
|
+
|
5
|
+
# OSID is used to uniquely identify an iOS Simulator operating system
|
6
|
+
# (including watchOS and tvOS operating systems) and is basically
|
7
|
+
# a pair of OS type string ('iOS', 'watchOS', 'tvOS') and version string
|
8
|
+
# ('9.0', '9.1' etc.)
|
9
|
+
class OSID
|
10
|
+
# Simulated OS type string ('iOS', 'watchOS', 'tvOS')
|
11
|
+
attr_reader :type
|
12
|
+
|
13
|
+
# Simulated OS version string ('7.0', '8.0', '9.0' etc.)
|
14
|
+
attr_reader :version
|
15
|
+
|
16
|
+
# Creates an OSID instance from a string prefixed by a standard
|
17
|
+
# prefix encountered in +device_set.plist+ (+com.apple.CoreSimulator.SimRuntime.+)
|
18
|
+
#
|
19
|
+
# Does not perform any additional validation other than checking the prefix.
|
20
|
+
def self.fromPrefixedString(string)
|
21
|
+
unless string.start_with? @@PREFIX
|
22
|
+
return nil
|
23
|
+
end
|
24
|
+
|
25
|
+
components = string.sub(@@PREFIX, "").split("-")
|
26
|
+
type = components.first
|
27
|
+
version = components[1..-1].join(".")
|
28
|
+
|
29
|
+
return OSID.new(type, version)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Creates an OSID instance from a non-prefixed string. Assumes 'iOS 9.2' format
|
33
|
+
# where components are separated by whitespace and the first component represents
|
34
|
+
# OS type while all others are concatenated and assumed to represent OS version.
|
35
|
+
def self.fromString(string)
|
36
|
+
components = string.split(" ")
|
37
|
+
type = components.first
|
38
|
+
version = components[1..-1].join(".")
|
39
|
+
|
40
|
+
return OSID.new(type, version)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Creates an OSID instance with a given type and version.
|
44
|
+
def initialize(type, version)
|
45
|
+
@type = type
|
46
|
+
@version = version
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns a string in <tt>'iOS 9.2'</tt> format
|
50
|
+
def inspect
|
51
|
+
"#{@type} #{@version}"
|
52
|
+
end
|
53
|
+
|
54
|
+
# Same as #inspect
|
55
|
+
def to_s
|
56
|
+
inspect
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns a string used for indexing OS definitions in +device_set.plist+
|
60
|
+
# in +com.apple.CoreSimulator.SimRuntime.iOS-7-1+ format.
|
61
|
+
def key
|
62
|
+
"#{@@PREFIX}#{@type}-#{@version.gsub(".","-")}"
|
63
|
+
end
|
64
|
+
|
65
|
+
include Comparable
|
66
|
+
|
67
|
+
# Uses @version for comparison (allows using <tt>Array#max</tt> on arrays of OSID
|
68
|
+
# for selecting max version)
|
69
|
+
def <=>(other)
|
70
|
+
@version <=> other.version
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns +inspect.hash+
|
74
|
+
def hash
|
75
|
+
inspect.hash
|
76
|
+
end
|
77
|
+
|
78
|
+
# Checks attribute-wise equality (compares @type and @version)
|
79
|
+
def eql?(other)
|
80
|
+
@type.eql?(other.type) && @version.eql?(other.version)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
@@PREFIX = "com.apple.CoreSimulator.SimRuntime."
|
85
|
+
end # class OSID
|
86
|
+
|
87
|
+
end # module XCSim
|
88
|
+
|
89
|
+
# eof
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module XCSim
|
4
|
+
|
5
|
+
# Prepares a more human-readable report from an array of DeviceListItem objects.
|
6
|
+
#
|
7
|
+
# Depending on the unique OS versions and device models referenced by the +list+ items,
|
8
|
+
# may return one of the following results:
|
9
|
+
#
|
10
|
+
# List of application bundles:: When +list+ contains a single item
|
11
|
+
# List of device names:: When +list+ contains multiple devices from a single OS version
|
12
|
+
# List of OS version strings:: When +list+ contains a multiple devices from multiple OS version
|
13
|
+
def self.reportFromDeviceList(list)
|
14
|
+
uniqueOSes = list.map{ |item| item.os.id }.uniq
|
15
|
+
uniqueDevices = list.map { |item| item.device.name }.uniq
|
16
|
+
|
17
|
+
countByOS = {}
|
18
|
+
list.each do |item|
|
19
|
+
count = countByOS[item.os.id] || 0
|
20
|
+
countByOS[item.os.id] = count+1
|
21
|
+
end
|
22
|
+
|
23
|
+
if uniqueOSes.empty? || uniqueDevices.empty?
|
24
|
+
raise ArgumentError
|
25
|
+
|
26
|
+
elsif uniqueOSes.count == 1 && uniqueDevices.count == 1
|
27
|
+
list.first.bundles
|
28
|
+
|
29
|
+
elsif uniqueOSes.count == 1
|
30
|
+
list.map{ |item| item.device.name }
|
31
|
+
|
32
|
+
elsif false == (countByOS.values.include? 1)
|
33
|
+
countByOS.map { |id, count| "#{id} (#{count} devices)"}
|
34
|
+
|
35
|
+
else
|
36
|
+
list
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end # module XCSim
|
41
|
+
|
42
|
+
#eof
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require_relative 'rbConstants'
|
4
|
+
require_relative 'rbDeviceSet'
|
5
|
+
require_relative 'rbList'
|
6
|
+
|
7
|
+
# call-seq:
|
8
|
+
# xcsim(:list, pattern) => Array of DeviceListItem
|
9
|
+
# xcsim(:bundle, options) => BundleInfo
|
10
|
+
#
|
11
|
+
# Main function to call when using xcsim gem. #xcsim works in two modes: list mode and bundle
|
12
|
+
# mode. List mode is for getting information about installed iOS Simulators, available OS versions
|
13
|
+
# and bundle IDs of the applications installed on these smiulators. Bundle mode focuses on
|
14
|
+
# individual applications and provides means of getting absolute paths for the application's data
|
15
|
+
# or bundle directories.
|
16
|
+
#
|
17
|
+
# == List mode
|
18
|
+
#
|
19
|
+
# List mode is invoked by calling
|
20
|
+
#
|
21
|
+
# xcsim(:list, pattern)
|
22
|
+
#
|
23
|
+
# Pattern is expected to be a String containing partial match for iOS version / device model pair
|
24
|
+
# in <tt>'iOS 9.2, iPhone 5s'</tt> format. Both of the patterns are optional and coult be omitted,
|
25
|
+
# i.e. the following calls are all valid:
|
26
|
+
#
|
27
|
+
# xcsim(:list, "iOS 9.2, iPhone 5s") # exact match for both iOS version and device
|
28
|
+
# xcsim(:list, "iOS 9.2") # match only iOS version
|
29
|
+
# xcsim(:list, "iPhone 5s") # match only device
|
30
|
+
# xcsim(:list, "iOS") # partial match for iOS version (all iOS simulators)
|
31
|
+
# xcsim(:list, "9.2") # partial match for iOS version (including watchOS, tvOS etc.)
|
32
|
+
# xcsim(:list, "iPad") # partial match for device model (all iPads)
|
33
|
+
# xcsim(:list, "9, iPhone") # partial match for both iOS version and device model
|
34
|
+
#
|
35
|
+
# In list mode #xcsim function returns an array of DeviceListItem object representing
|
36
|
+
# iOS Simulators found, which match the pattern provided. Partial matches are processed
|
37
|
+
# in a case-sensitive manner (basically, it finds all devices, whose OS version has the provided
|
38
|
+
# OS version pattern as a substring and whose device model contains the provided device model
|
39
|
+
# pattern as a substring).
|
40
|
+
#
|
41
|
+
# Each DeviceListItem will contain info about applications installed on the corresponding
|
42
|
+
# Simulator, excluding system apps (Safari et al.)
|
43
|
+
#
|
44
|
+
# Note that since #xcsim signature is
|
45
|
+
#
|
46
|
+
# def xcsim(*args)
|
47
|
+
#
|
48
|
+
# multiple arguments may be provided to the function. In list mode all arguments are concatenated
|
49
|
+
# into a single +pattern+ string by using single whitespace as a separator.
|
50
|
+
#
|
51
|
+
# == Bundle mode
|
52
|
+
#
|
53
|
+
# Bundle mode is invoked by calling
|
54
|
+
#
|
55
|
+
# xcsim(:bundle, options)
|
56
|
+
#
|
57
|
+
# Options is expected to be a Hash with the following keys:
|
58
|
+
#
|
59
|
+
# +:bundleID+:: Specifies the bundle ID of the application in question. #xcsim will then try
|
60
|
+
# to locate the bundle with the ID provided and return the corresponding BundleInfo.
|
61
|
+
# This is the only required option.
|
62
|
+
#
|
63
|
+
# Bundle ID can be partially matched. Actual bundle IDs of the applications found
|
64
|
+
# in the iOS Simulator are checked for having the provided bundle ID suffix, so
|
65
|
+
# passing <tt>"application"</tt> as the +:bundleID+ option will match bundle ID
|
66
|
+
# <tt>"com.yourcompany.application"</tt> for example. If multiple matches are found,
|
67
|
+
# the result is ambiguous and #xcsim will raise an error. See Errors section for more
|
68
|
+
# info.
|
69
|
+
#
|
70
|
+
# +:os+:: Specifies, which iOS version to use when searching in <tt>"iOS 9.2"</tt> format.
|
71
|
+
# If omitted, #xcsim will default the OS version to the latest one found in the
|
72
|
+
# parsed +device_set.plist+
|
73
|
+
#
|
74
|
+
# +:device+:: Specifies, which device model to use when searching in <tt>"iPad Air 2"</tt>
|
75
|
+
# format. If omitted, #xcsim will default the device model to <tt>"iPhone 5s"</tt>
|
76
|
+
#
|
77
|
+
# Note that the +:os+ and +:device+ options do not perform partial matching to the contrary of
|
78
|
+
# what the list mode does.
|
79
|
+
#
|
80
|
+
# #xcsim will return a BundleInfo instance for bundle matching the options or raise an error.
|
81
|
+
#
|
82
|
+
# == Errors
|
83
|
+
#
|
84
|
+
# #xcsim may raise errors of several types when matching its results:
|
85
|
+
#
|
86
|
+
# ArgumentError:: An ArgumentError is raised if the parameters provided do not make sense
|
87
|
+
# as described above. That is, if in list mode multiple comma-separated
|
88
|
+
# components are found in the pattern string, or in bundle mode +:bundleID+
|
89
|
+
# parameter is not found, an ArgumentError will be raised.
|
90
|
+
#
|
91
|
+
# NonUniqueBundleIDError:: XCSim::NonUniqueBundleIDError is raised when multiple data or bundle
|
92
|
+
# directories are found matching the same bundle ID (should not generally
|
93
|
+
# happen since bundle IDs are unique in iOS Simulator) or when partial
|
94
|
+
# match of bundle ID in bundle mode finds multiple results.
|
95
|
+
#
|
96
|
+
# OSNotFoundError:: XCSim::OSNotFoundError is raised in bundle mode when OS version specified
|
97
|
+
# by the +:os+ option cannot be found in installed iOS simulators list.
|
98
|
+
#
|
99
|
+
# DeviceNotFoundError:: XCSim::DeviceNotFoundError is raised in bundle mode when device model
|
100
|
+
# specified by the +:device+ option cannot be found in installed iOS
|
101
|
+
# simulators list for the selected OS version.
|
102
|
+
#
|
103
|
+
# BundleNotFoundError:: XCSim::BundleNotFoundError is raised in bundle mode when the bundle ID
|
104
|
+
# specified by the +:bundleID+ option does not match any application
|
105
|
+
# installed on the selected iOS Simulator.
|
106
|
+
#
|
107
|
+
def xcsim(*args)
|
108
|
+
case args.first
|
109
|
+
when :list
|
110
|
+
return XCSim::GetDeviceList.new(XCSim.deviceSet).withPattern(args[1..-1].join(" "))
|
111
|
+
|
112
|
+
when :bundle
|
113
|
+
raise ArgumentError if args.count != 2
|
114
|
+
return XCSim::GetBundle.new(XCSim.deviceSet).withOptions(args.last)
|
115
|
+
|
116
|
+
else
|
117
|
+
raise ArgumentError
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# eof
|
data/lib/xcsim.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'cfpropertylist'
|
5
|
+
rescue LoadError
|
6
|
+
puts "require 'cfpropertylist' failed! Consider running 'gem install CFPropertyList'"
|
7
|
+
exit
|
8
|
+
end
|
9
|
+
|
10
|
+
require_relative 'xcsim/rbXCSim'
|
11
|
+
require_relative 'xcsim/rbReports'
|
12
|
+
|
13
|
+
# eof
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xcsim
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Egor Chiglintsev
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: CFPropertyList
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.2'
|
27
|
+
description: |
|
28
|
+
xcsim is a command-line utility to simplify opening iOS Simulator application/data directories. It's as simple as running `xcsim com.yourcompany.application`.
|
29
|
+
email: egor.chiglintsev@gmail.com
|
30
|
+
executables:
|
31
|
+
- xcsim
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- LICENSE
|
36
|
+
- README.md
|
37
|
+
- bin/xcsim
|
38
|
+
- lib/xcsim.rb
|
39
|
+
- lib/xcsim/rbAppBundles.rb
|
40
|
+
- lib/xcsim/rbBundleInfo.rb
|
41
|
+
- lib/xcsim/rbConstants.rb
|
42
|
+
- lib/xcsim/rbDeviceID.rb
|
43
|
+
- lib/xcsim/rbDeviceSet.rb
|
44
|
+
- lib/xcsim/rbList.rb
|
45
|
+
- lib/xcsim/rbOSDevices.rb
|
46
|
+
- lib/xcsim/rbOSID.rb
|
47
|
+
- lib/xcsim/rbReports.rb
|
48
|
+
- lib/xcsim/rbXCSim.rb
|
49
|
+
homepage: https://github.com/wanderwaltz/xcsim
|
50
|
+
licenses:
|
51
|
+
- MIT
|
52
|
+
metadata: {}
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 2.2.2
|
70
|
+
signing_key:
|
71
|
+
specification_version: 4
|
72
|
+
summary: Open iOS Simulator application/data directories by bundle ID
|
73
|
+
test_files: []
|