run_loop_tcc 2.1.3

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.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/bin/run-loop +19 -0
  4. data/lib/run_loop/abstract.rb +18 -0
  5. data/lib/run_loop/app.rb +372 -0
  6. data/lib/run_loop/cache/cache.rb +68 -0
  7. data/lib/run_loop/cli/cli.rb +48 -0
  8. data/lib/run_loop/cli/codesign.rb +24 -0
  9. data/lib/run_loop/cli/errors.rb +11 -0
  10. data/lib/run_loop/cli/instruments.rb +160 -0
  11. data/lib/run_loop/cli/locale.rb +31 -0
  12. data/lib/run_loop/cli/simctl.rb +257 -0
  13. data/lib/run_loop/cli/tcc.rb +139 -0
  14. data/lib/run_loop/codesign.rb +76 -0
  15. data/lib/run_loop/core.rb +902 -0
  16. data/lib/run_loop/core_simulator.rb +960 -0
  17. data/lib/run_loop/detect_aut/detect.rb +185 -0
  18. data/lib/run_loop/detect_aut/errors.rb +126 -0
  19. data/lib/run_loop/detect_aut/xamarin_studio.rb +46 -0
  20. data/lib/run_loop/detect_aut/xcode.rb +157 -0
  21. data/lib/run_loop/device.rb +722 -0
  22. data/lib/run_loop/device_agent/app/CBX-Runner.app.zip +0 -0
  23. data/lib/run_loop/device_agent/bin/xctestctl +0 -0
  24. data/lib/run_loop/device_agent/cbxrunner.rb +156 -0
  25. data/lib/run_loop/device_agent/frameworks/Frameworks.zip +0 -0
  26. data/lib/run_loop/device_agent/frameworks.rb +65 -0
  27. data/lib/run_loop/device_agent/ipa/CBX-Runner.app.zip +0 -0
  28. data/lib/run_loop/device_agent/launcher.rb +51 -0
  29. data/lib/run_loop/device_agent/xcodebuild.rb +91 -0
  30. data/lib/run_loop/device_agent/xctestctl.rb +109 -0
  31. data/lib/run_loop/directory.rb +179 -0
  32. data/lib/run_loop/dnssd.rb +148 -0
  33. data/lib/run_loop/dot_dir.rb +87 -0
  34. data/lib/run_loop/dylib_injector.rb +145 -0
  35. data/lib/run_loop/encoding.rb +56 -0
  36. data/lib/run_loop/environment.rb +361 -0
  37. data/lib/run_loop/fifo.rb +40 -0
  38. data/lib/run_loop/host_cache.rb +128 -0
  39. data/lib/run_loop/http/error.rb +15 -0
  40. data/lib/run_loop/http/request.rb +44 -0
  41. data/lib/run_loop/http/retriable_client.rb +166 -0
  42. data/lib/run_loop/http/server.rb +17 -0
  43. data/lib/run_loop/instruments.rb +436 -0
  44. data/lib/run_loop/ipa.rb +142 -0
  45. data/lib/run_loop/l10n.rb +93 -0
  46. data/lib/run_loop/language.rb +63 -0
  47. data/lib/run_loop/lipo.rb +132 -0
  48. data/lib/run_loop/lldb.rb +52 -0
  49. data/lib/run_loop/locale.rb +101 -0
  50. data/lib/run_loop/logging.rb +111 -0
  51. data/lib/run_loop/otool.rb +76 -0
  52. data/lib/run_loop/patches/awesome_print.rb +17 -0
  53. data/lib/run_loop/physical_device/life_cycle.rb +268 -0
  54. data/lib/run_loop/plist_buddy.rb +189 -0
  55. data/lib/run_loop/process_terminator.rb +128 -0
  56. data/lib/run_loop/process_waiter.rb +117 -0
  57. data/lib/run_loop/regex.rb +19 -0
  58. data/lib/run_loop/shell.rb +103 -0
  59. data/lib/run_loop/sim_control.rb +1264 -0
  60. data/lib/run_loop/simctl.rb +275 -0
  61. data/lib/run_loop/sqlite.rb +61 -0
  62. data/lib/run_loop/strings.rb +88 -0
  63. data/lib/run_loop/tcc/TCC.db +0 -0
  64. data/lib/run_loop/tcc/tcc.rb +240 -0
  65. data/lib/run_loop/template.rb +61 -0
  66. data/lib/run_loop/version.rb +182 -0
  67. data/lib/run_loop/xcode.rb +318 -0
  68. data/lib/run_loop/xcrun.rb +107 -0
  69. data/lib/run_loop/xcuitest.rb +550 -0
  70. data/lib/run_loop.rb +230 -0
  71. data/plists/simctl/com.apple.UIAutomation.plist +0 -0
  72. data/plists/simctl/com.apple.UIAutomationPlugIn.plist +0 -0
  73. data/scripts/calabash_script_uia.js +28184 -0
  74. data/scripts/lib/json2.min.js +26 -0
  75. data/scripts/lib/log.js +26 -0
  76. data/scripts/lib/on_alert.js +224 -0
  77. data/scripts/read-cmd.sh +2 -0
  78. data/scripts/run_dismiss_location.js +89 -0
  79. data/scripts/run_loop_basic.js +34 -0
  80. data/scripts/run_loop_fast_uia.js +188 -0
  81. data/scripts/run_loop_host.js +117 -0
  82. data/scripts/run_loop_shared_element.js +125 -0
  83. data/scripts/timeout3 +23 -0
  84. data/scripts/udidetect +0 -0
  85. data/vendor-licenses/FBSimulatorControl.LICENSE +30 -0
  86. data/vendor-licenses/xctestctl.LICENSE +32 -0
  87. metadata +443 -0
@@ -0,0 +1,275 @@
1
+ module RunLoop
2
+
3
+ # @!visibility private
4
+ # An interface to the `simctl` command line tool for CoreSimulator.
5
+ #
6
+ # Replacement for SimControl.
7
+ class Simctl
8
+
9
+ # @!visibility private
10
+ DEFAULTS = {
11
+ :timeout => RunLoop::Environment.ci? ? 90 : 30,
12
+ :log_cmd => true
13
+ }
14
+
15
+ # @!visibility private
16
+ SIMCTL_PLIST_DIR = lambda {
17
+ dirname = File.dirname(__FILE__)
18
+ joined = File.join(dirname, '..', '..', 'plists', 'simctl')
19
+ File.expand_path(joined)
20
+ }.call
21
+
22
+ # @!visibility private
23
+ def self.uia_automation_plist
24
+ File.join(SIMCTL_PLIST_DIR, 'com.apple.UIAutomation.plist')
25
+ end
26
+
27
+ # @!visibility private
28
+ def self.uia_automation_plugin_plist
29
+ File.join(SIMCTL_PLIST_DIR, 'com.apple.UIAutomationPlugIn.plist')
30
+ end
31
+
32
+ # @!visibility private
33
+ attr_reader :device
34
+
35
+ # @!visibility private
36
+ def initialize
37
+ @ios_devices = []
38
+ @tvos_devices = []
39
+ @watchos_devices = []
40
+ end
41
+
42
+ # @!visibility private
43
+ def to_s
44
+ "#<Simctl: #{xcode.version}>"
45
+ end
46
+
47
+ # @!visibility private
48
+ def inspect
49
+ to_s
50
+ end
51
+
52
+ # @!visibility private
53
+ def simulators
54
+ simulators = ios_devices
55
+ if simulators.empty?
56
+ simulators = fetch_devices![:ios]
57
+ end
58
+ simulators
59
+ end
60
+
61
+ # @!visibility private
62
+ #
63
+ # This method is not supported on Xcode < 7 - returns nil.
64
+ #
65
+ # Simulator must be booted in El Cap, which makes this method useless for us
66
+ # because we have to do a bunch of pre-launch checks for sandbox resetting.
67
+ #
68
+ # Testing has shown that moving the device in and out of the booted state
69
+ # takes a long time (seconds) and is unpredictable.
70
+ #
71
+ # TODO ensure a booted state.
72
+ #
73
+ # @param [String] bundle_id The CFBundleIdentifier of the app.
74
+ # @param [RunLoop::Device] device The device under test.
75
+ # @return [String] The path to the .app bundle if it exists; nil otherwise.
76
+ def app_container(device, bundle_id)
77
+ return nil if !xcode.version_gte_7?
78
+ cmd = ["simctl", "get_app_container", device.udid, bundle_id]
79
+ hash = execute(cmd, DEFAULTS)
80
+
81
+ exit_status = hash[:exit_status]
82
+ if exit_status != 0
83
+ nil
84
+ else
85
+ hash[:out].strip
86
+ end
87
+ end
88
+
89
+ # @!visibility private
90
+ #
91
+ # SimControl compatibility
92
+ def ensure_accessibility(device)
93
+ sim_control.ensure_accessibility(device)
94
+ end
95
+
96
+ # @!visibility private
97
+ #
98
+ # SimControl compatibility
99
+ def ensure_software_keyboard(device)
100
+ sim_control.ensure_software_keyboard(device)
101
+ end
102
+
103
+ # @!visibility private
104
+ #
105
+ # TODO Make this private again; exposed for SimControl compatibility.
106
+ def xcode
107
+ @xcode ||= RunLoop::Xcode.new
108
+ end
109
+
110
+ private
111
+
112
+ # @!visibility private
113
+ attr_reader :ios_devices, :tvos_devices, :watchos_devices
114
+
115
+ # @!visibility private
116
+ def execute(array, options)
117
+ merged = DEFAULTS.merge(options)
118
+ xcrun.run_command_in_context(array, merged)
119
+ end
120
+
121
+ # @!visibility private
122
+ #
123
+ # Starting in Xcode 7, simctl allows a --json option for listing devices.
124
+ #
125
+ # On Xcode 6, we will fall back to SimControl which does a line-by-line
126
+ # processing of `simctl list devices`. tvOS and watchOS devices are not
127
+ # available on Xcode < 7.
128
+ #
129
+ # This is a destructive operation on `@ios_devices`, `@tvos_devices`, and
130
+ # `@watchos_devices`. Callers should check for existing devices to avoid
131
+ # the overhead of calling `simctl list devices --json`.
132
+ def fetch_devices!
133
+ if !xcode.version_gte_7?
134
+ return {
135
+ :ios => sim_control.simulators,
136
+ :tvos => [],
137
+ :watchos => []
138
+ }
139
+ end
140
+
141
+ @ios_devices = []
142
+ @tvos_devices = []
143
+ @watchos_devices = []
144
+
145
+ cmd = ["simctl", "list", "devices", "--json"]
146
+ hash = execute(cmd, DEFAULTS)
147
+
148
+ out = hash[:out]
149
+ exit_status = hash[:exit_status]
150
+ if exit_status != 0
151
+ raise RuntimeError, %Q[simctl exited #{exit_status}:
152
+
153
+ #{out}
154
+
155
+ while trying to list devices.
156
+ ]
157
+ end
158
+
159
+ devices = json_to_hash(out)["devices"]
160
+
161
+ devices.each do |key, device_list|
162
+ version = device_key_to_version(key)
163
+ bucket = bucket_for_key(key)
164
+
165
+ device_list.each do |record|
166
+ if device_available?(record)
167
+ bucket << device_from_record(record, version)
168
+ end
169
+ end
170
+ end
171
+ {
172
+ :ios => ios_devices,
173
+ :tvos => tvos_devices,
174
+ :watchos => watchos_devices
175
+ }
176
+ end
177
+
178
+ # @!visibility private
179
+ #
180
+ # command_runner_ng combines stderr and stdout and starting in Xcode 7.3,
181
+ # simctl has started generating stderr output. This must be filtered out
182
+ # so that we can parse the JSON response.
183
+ def filter_stderr(out)
184
+ out.split($-0).map do |line|
185
+ if stderr_line?(line)
186
+ nil
187
+ else
188
+ line
189
+ end
190
+ end.compact.join($-0)
191
+ end
192
+
193
+ # @!visibility private
194
+ def stderr_line?(line)
195
+ line[/CoreSimulatorService/, 0] || line[/simctl\[.+\]/, 0]
196
+ end
197
+
198
+ # @!visibility private
199
+ def json_to_hash(json)
200
+ filtered = filter_stderr(json)
201
+ begin
202
+ JSON.parse(filtered)
203
+ rescue TypeError, JSON::ParserError => e
204
+ raise RuntimeError, %Q[Could not parse simctl JSON response:
205
+
206
+ #{e}
207
+ ]
208
+ end
209
+ end
210
+
211
+ # @!visibility private
212
+ def device_key_is_ios?(key)
213
+ key[/iOS/, 0]
214
+ end
215
+
216
+ # @!visibility private
217
+ def device_key_is_tvos?(key)
218
+ key[/tvOS/, 0]
219
+ end
220
+
221
+ # @!visibility private
222
+ def device_key_is_watchos?(key)
223
+ key[/watchOS/, 0]
224
+ end
225
+
226
+ # @!visibility private
227
+ def device_key_to_version(key)
228
+ str = key.split(" ").last
229
+ RunLoop::Version.new(str)
230
+ end
231
+
232
+ # @!visibility private
233
+ def device_available?(record)
234
+ record["availability"] == "(available)"
235
+ end
236
+
237
+ # @!visibility private
238
+ def device_from_record(record, version)
239
+ RunLoop::Device.new(record["name"],
240
+ version,
241
+ record["udid"],
242
+ record["state"])
243
+ end
244
+
245
+ # @!visibility private
246
+ def bucket_for_key(key)
247
+ if device_key_is_ios?(key)
248
+ bin = @ios_devices
249
+ elsif device_key_is_tvos?(key)
250
+ bin = @tvos_devices
251
+ elsif device_key_is_watchos?(key)
252
+ bin = @watchos_devices
253
+ else
254
+ raise RuntimeError, "Unexpected key while processing simctl output:
255
+
256
+ key = #{key}
257
+
258
+ is not an iOS, tvOS, or watchOS device"
259
+ end
260
+ bin
261
+ end
262
+
263
+ # @!visibility private
264
+ def xcrun
265
+ @xcrun ||= RunLoop::Xcrun.new
266
+ end
267
+
268
+ # @!visibility private
269
+ # Support for Xcode < 7 when trying to collect simulators. Xcode 7 allows
270
+ # a --json option which is much easier to parse.
271
+ def sim_control
272
+ @sim_control ||= RunLoop::SimControl.new
273
+ end
274
+ end
275
+ end
@@ -0,0 +1,61 @@
1
+ module RunLoop
2
+ # @!visibility private
3
+ class Sqlite
4
+
5
+ # @!visibility private
6
+ # MacOS ships with sqlite3
7
+ SQLITE3 = "/usr/bin/sqlite3"
8
+
9
+ # @!visibility private
10
+ def self.exec(file, sql)
11
+ if !File.exist?(file)
12
+ raise ArgumentError,
13
+ %Q{sqlite database must exist at path:
14
+
15
+ #{file}
16
+ }
17
+ end
18
+
19
+ if sql.nil? || sql == ""
20
+ raise ArgumentError, "Sql argument must not be nil or the empty string"
21
+ end
22
+
23
+ args = [SQLITE3, file, sql]
24
+ hash = self.xcrun.exec(args, {:log_cmd => true})
25
+
26
+ out = hash[:out]
27
+ exit_status = hash[:exit_status]
28
+
29
+ if exit_status.nil? || exit_status != 0
30
+ raise RuntimeError,
31
+ %Q{
32
+ Could not complete sqlite operation:
33
+
34
+ file: #{file}
35
+ sql: #{sql}
36
+ out: #{out}
37
+
38
+ Exited with status: '#{exit_status}'
39
+ }
40
+ end
41
+
42
+ out
43
+ end
44
+
45
+ # @!visibilty private
46
+ def self.parse(string, delimiter="|")
47
+ if string == nil
48
+ []
49
+ else
50
+ string.split(delimiter)
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def self.xcrun
57
+ RunLoop::Xcrun.new
58
+ end
59
+ end
60
+ end
61
+
@@ -0,0 +1,88 @@
1
+ module RunLoop
2
+ # @!visibility private
3
+ #
4
+ # A class for interacting with the strings tool
5
+ class Strings
6
+
7
+ # @!visibility private
8
+ attr_reader :path
9
+
10
+ # @!visibility private
11
+ def initialize(path)
12
+ @path = path
13
+
14
+ if !Strings.valid_path?(path)
15
+ raise ArgumentError,
16
+ %Q{File:
17
+
18
+ #{path}
19
+
20
+ must exist and not be a directory.
21
+ }
22
+ end
23
+ end
24
+
25
+ # @!visibility private
26
+ def to_s
27
+ "#<STRINGS: #{path}>"
28
+ end
29
+
30
+ # @!visibility private
31
+ def inspect
32
+ to_s
33
+ end
34
+
35
+ # @!visibility private
36
+ #
37
+ # @return [RunLoop::Version] A version instance or nil if the file
38
+ # at path does not contain server version information.
39
+ def server_version
40
+ regex = /CALABASH VERSION: (\d+\.\d+\.\d+(\.pre\d+)?)/
41
+ match = dump[regex, 0]
42
+
43
+ if match
44
+ str = match.split(":")[1]
45
+ RunLoop::Version.new(str)
46
+ else
47
+ nil
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ # @!visibility private
54
+ def dump
55
+ args = ["strings", path]
56
+ opts = { :log_cmd => true }
57
+
58
+ hash = xcrun.run_command_in_context(args, opts)
59
+
60
+ if hash[:exit_status] != 0
61
+ raise RuntimeError,
62
+ %Q{Could not get strings info from file:
63
+
64
+ #{path}
65
+
66
+ #{args.join(" ")}
67
+
68
+ exited #{hash[:exit_status]} with the following output:
69
+
70
+ #{hash[:out]}
71
+ }
72
+ end
73
+
74
+ @dump = hash[:out]
75
+ end
76
+
77
+ # @!visibility private
78
+ def self.valid_path?(path)
79
+ File.exist?(path) && !File.directory?(path)
80
+ end
81
+
82
+ # @!visibility private
83
+ def xcrun
84
+ RunLoop::Xcrun.new
85
+ end
86
+ end
87
+ end
88
+
Binary file
@@ -0,0 +1,240 @@
1
+ module RunLoop
2
+ # @!visibility private
3
+ class TCC
4
+
5
+ # @!visibility private
6
+ PRIVACY_SERVICES = {
7
+ :calendar => "kTCCServiceCalendar",
8
+ :camera => "kTCCServiceCamera",
9
+ :contacts => "kTCCServiceAddressBook",
10
+ :microphone => "kTCCServiceMicrophone",
11
+ :motion => "kTCCServiceMotion",
12
+ :photos => "kTCCServicePhotos",
13
+ :reminders => "kTCCServiceReminders",
14
+ :twitter => "kTCCServiceTwitter"
15
+ }
16
+
17
+ # Returns a list of known services as keys that can be passed
18
+ # to RunLoop::TCC.allow or RunLoop::TCC.deny.
19
+ def self.services
20
+ PRIVACY_SERVICES.map do |key, _|
21
+ key
22
+ end
23
+ end
24
+
25
+ # Prohibits the `device` from popping a Privacy Alert for `service`.
26
+ #
27
+ # Only works on iOS Simulator.
28
+ #
29
+ # There is a set of known services like :camera, :microphone, and :twitter,
30
+ # but you can pass arbritary services - this may or may not have an effect
31
+ # on your application.
32
+ #
33
+ # @param [String, RunLoop::Device] device
34
+ # @param [String] bundle_id
35
+ # @param [Array] services An array of services. The default is to allow
36
+ # all services.
37
+ #
38
+ # @raise [ArgumentError] If device is a physical device.
39
+ # @raise [ArgumentError] If not device with identifier can be found.
40
+ def self.allow(device, bundle_id, services = [])
41
+ _device = self.ensure_device(device)
42
+
43
+ if services.empty?
44
+ _services = self.services
45
+ else
46
+ _services = services
47
+ end
48
+
49
+ tcc = self.tcc(device, bundle_id)
50
+
51
+ _services.each do |service|
52
+ tcc.allow_service(service)
53
+ end
54
+ _services
55
+ end
56
+
57
+ # Force the `device` to pop a Privacy Alert for `service`.
58
+ #
59
+ # Only works on iOS Simulator.
60
+ #
61
+ # @param [String, RunLoop::Device] device
62
+ # @param [String] bundle_id
63
+ # @param [Array] services An array of services. The default is to deny
64
+ # all services.
65
+ #
66
+ # @raise [ArgumentError] If device is a physical device.
67
+ # @raise [ArgumentError] If not device with identifier can be found.
68
+ def self.deny(device, bundle_id, services = [])
69
+ _device = self.ensure_device(device)
70
+
71
+ if services.empty?
72
+ _services = self.services
73
+ else
74
+ _services = services
75
+ end
76
+
77
+ tcc = self.tcc(device, bundle_id)
78
+
79
+ _services.each do |service|
80
+ tcc.deny_service(service)
81
+ end
82
+ _services
83
+ end
84
+
85
+ # @!visibility private
86
+ def initialize(device, bundle_id)
87
+ @device = device
88
+ @bundle_id = bundle_id
89
+
90
+ if device.physical_device?
91
+ raise(ArgumentError, "Managing the TCC.db only works on simulators")
92
+ end
93
+
94
+ if !bundle_id.is_a?(String)
95
+ raise(ArgumentError, "Managing the TCC.db requires a valid bundle_id")
96
+ end
97
+ end
98
+
99
+ # @!visibility private
100
+ def allow_service(service)
101
+ service_name = service_name(service)
102
+ state = service_is_allowed(service_name)
103
+
104
+ return true if state == true
105
+
106
+ if state == nil
107
+ insert_allowed(service_name, 1)
108
+ else
109
+ update_allowed(service_name, 1)
110
+ end
111
+ true
112
+ end
113
+
114
+ # @!visibility private
115
+ def deny_service(service)
116
+ service_name = service_name(service)
117
+ state = service_is_allowed(service_name)
118
+
119
+ # state == false; need to update prompt_count
120
+ if state == nil
121
+ insert_allowed(service_name, 0)
122
+ else
123
+ update_allowed(service_name, 0)
124
+ end
125
+ true
126
+ end
127
+
128
+ # @!visibility private
129
+ def delete_service(service)
130
+ service_name = service_name(service)
131
+ state = service_is_allowed(service_name)
132
+
133
+ return true if state.nil?
134
+ delete_allowed(service_name)
135
+ true
136
+ end
137
+
138
+ private
139
+
140
+ # @!visibility private
141
+ attr_reader :device, :bundle_id
142
+
143
+ # @!visibility private
144
+ ACCESS_COLUMNS = [
145
+ "service",
146
+ "client",
147
+ "client_type",
148
+ "allowed",
149
+ "prompt_count"
150
+ ]
151
+
152
+ # @!visibility private
153
+ def where(service)
154
+ %Q{WHERE client="#{client}" AND service="#{service}"}
155
+ end
156
+
157
+ # @!visibility private
158
+ def service_is_allowed(service)
159
+ service_name = service_name(service)
160
+ sql = %Q{SELECT allowed FROM access #{where(service_name)}}
161
+ out = RunLoop::Sqlite.exec(db, sql)
162
+
163
+ case out
164
+ when ""
165
+ return nil
166
+ when "1"
167
+ return true
168
+ when "0"
169
+ return false
170
+ else
171
+ raise RuntimeError, %Q{Expected '', '1', or '0' found: '#{out}'"}
172
+ end
173
+ end
174
+
175
+ # @!visibility private
176
+ def access_columns
177
+ "service, client, client_type, allowed, prompt_count"
178
+ end
179
+
180
+ # @!visibility private
181
+ def access_values(service, state)
182
+ %Q{"#{service}", "#{client}", 0, #{state}, #{state}}
183
+ end
184
+
185
+ # @!visibility private
186
+ def insert_allowed(service, state)
187
+ sql = %Q{INSERT INTO access (#{access_columns}) VALUES (#{access_values(service, state)})}
188
+ RunLoop::Sqlite.exec(db, sql)
189
+ end
190
+
191
+ # @!visibility private
192
+ def update_allowed(service, state)
193
+ sql = %Q{UPDATE access SET allowed=#{state}, prompt_count=#{state} #{where(service)}}
194
+ RunLoop::Sqlite.exec(db, sql)
195
+ end
196
+
197
+ # @!visibility private
198
+ def delete_allowed(service)
199
+ sql = %Q{DELETE FROM access #{where(service)}}
200
+ RunLoop::Sqlite.exec(db, sql)
201
+ end
202
+
203
+ # @!visibility private
204
+ def service_name(key)
205
+ PRIVACY_SERVICES[key] || key
206
+ end
207
+
208
+ # @!visibility private
209
+ def client
210
+ bundle_id
211
+ end
212
+
213
+ # @!visibility private
214
+ def db
215
+ device.simulator_tcc_db
216
+ end
217
+
218
+ # @!visibility private
219
+ def self.tcc(device, bundle_id)
220
+ RunLoop::TCC.new(device, bundle_id)
221
+ end
222
+
223
+ # @!visibility private
224
+ def self.ensure_device(device)
225
+ if device.is_a?(RunLoop::Device)
226
+ simulator = device
227
+ else
228
+ simulator = RunLoop::Device.device_with_identifier(device)
229
+ end
230
+
231
+ if simulator.physical_device?
232
+ raise ArgumentError,
233
+ "Cannot manage Privacy Settings on physical devices"
234
+ end
235
+
236
+ simulator
237
+ end
238
+ end
239
+ end
240
+
@@ -0,0 +1,61 @@
1
+ require 'erb'
2
+
3
+ module RunLoop
4
+
5
+ # @!visibility private
6
+ # Class to break up javascript templates in to reusable chunks
7
+ class UIAScriptTemplate < ERB
8
+
9
+ # @!visibility private
10
+ def initialize(template_root, template_relative_path)
11
+ @template_root = template_root
12
+ template_path = File.join(@template_root, template_relative_path)
13
+ @template = File.read(template_path).force_encoding("utf-8")
14
+ super(@template)
15
+ end
16
+
17
+ # @!visibility private
18
+ def render_template(template_relative_path)
19
+ UIAScriptTemplate.new(@template_root, template_relative_path).result
20
+ end
21
+
22
+ # @!visibility private
23
+ def result
24
+ super(binding)
25
+ end
26
+
27
+ # @!visibility private
28
+ def self.sub_path_var!(javascript, results_dir)
29
+ self.substitute_variable!(javascript, "PATH", results_dir)
30
+ end
31
+
32
+ # @!visibility private
33
+ def self.sub_read_script_path_var!(javascript, read_cmd_sh)
34
+ self.substitute_variable!(javascript, "READ_SCRIPT_PATH", read_cmd_sh)
35
+ end
36
+
37
+ # @!visibility private
38
+ def self.sub_timeout_script_path_var!(javascript, timeout_sh)
39
+ self.substitute_variable!(javascript, "TIMEOUT_SCRIPT_PATH", timeout_sh)
40
+ end
41
+
42
+ # @!visibility private
43
+ def self.sub_flush_uia_logs_var!(javascript, value)
44
+ self.substitute_variable!(javascript, "FLUSH_LOGS", value)
45
+ end
46
+
47
+ # @!visibility private
48
+ #
49
+ # Legacy and XTC - related to :no_flush which is a deprecated option.
50
+ #
51
+ # Replaced with :flush_uia_logs
52
+ def self.sub_mode_var!(javascript, value)
53
+ self.substitute_variable!(javascript, "MODE", value)
54
+ end
55
+
56
+ # @!visibility private
57
+ def self.substitute_variable!(javascript, variable, value)
58
+ javascript.gsub!(/\$#{variable}/, value)
59
+ end
60
+ end
61
+ end