macos.rb 0.50.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.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +572 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE +21 -0
  5. data/README.md +129 -0
  6. data/Rakefile +9 -0
  7. data/lib/Array/all_but_last.rb +7 -0
  8. data/lib/MacOS/Apple80211.rb +68 -0
  9. data/lib/MacOS/Applications.rb +73 -0
  10. data/lib/MacOS/Defaults/ComAppleDock.rb +153 -0
  11. data/lib/MacOS/Defaults/NSGlobalDomain/NSUserKeyEquivalents.rb +109 -0
  12. data/lib/MacOS/Defaults.rb +126 -0
  13. data/lib/MacOS/DiskUtil.rb +63 -0
  14. data/lib/MacOS/Dmg.rb +99 -0
  15. data/lib/MacOS/HardwarePort.rb +79 -0
  16. data/lib/MacOS/IfConfig.rb +183 -0
  17. data/lib/MacOS/Keychain.rb +26 -0
  18. data/lib/MacOS/LaunchControl.rb +36 -0
  19. data/lib/MacOS/SystemProfiler.rb +119 -0
  20. data/lib/MacOS/USBFlashDrive.rb +26 -0
  21. data/lib/MacOS/Users.rb +16 -0
  22. data/lib/MacOS/VERSION.rb +6 -0
  23. data/lib/MacOS/VPN.rb +13 -0
  24. data/lib/MacOS/Voice.rb +40 -0
  25. data/lib/MacOS.rb +27 -0
  26. data/lib/Module/alias_methods.rb +7 -0
  27. data/lib/Ordinal/Array.rb +8 -0
  28. data/lib/Ordinal.rb +157 -0
  29. data/lib/Thoran/Array/AllButLast/all_but_last.rb +28 -0
  30. data/lib/Thoran/Array/LastX/lastX.rb +33 -0
  31. data/lib/Thoran/Module/AliasMethods/alias_methods.rb +23 -0
  32. data/lib/Thoran/String/Capture/capture.rb +28 -0
  33. data/lib/Version.rb +381 -0
  34. data/macos.rb.gemspec +59 -0
  35. data/test/MacOS/Apple80211_test.rb +80 -0
  36. data/test/MacOS/Applications_test.rb +82 -0
  37. data/test/MacOS/Defaults/ComAppleDock_test.rb +66 -0
  38. data/test/MacOS/Defaults/NSGlobalDomain/NSUserKeyEquivalents_test.rb +67 -0
  39. data/test/MacOS/Defaults_test.rb +144 -0
  40. data/test/MacOS/DiskUtil_test.rb +48 -0
  41. data/test/MacOS/Dmg_test.rb +69 -0
  42. data/test/MacOS/HardwarePort_test.rb +46 -0
  43. data/test/MacOS/IfConfig_test.rb +144 -0
  44. data/test/MacOS/Keychain_test.rb +22 -0
  45. data/test/MacOS/LaunchControl_test.rb +40 -0
  46. data/test/MacOS/SystemProfiler_test.rb +69 -0
  47. data/test/MacOS/USBFlashDrive_test.rb +20 -0
  48. data/test/MacOS/Users_test.rb +10 -0
  49. data/test/MacOS/VPN_test.rb +18 -0
  50. data/test/MacOS/Voice_test.rb +46 -0
  51. data/test/MacOS_test.rb +17 -0
  52. data/test/VERSION_test.rb +19 -0
  53. data/test/commands_test.rb +50 -0
  54. data/test/fixtures/airport-I-deprecated.txt +2 -0
  55. data/test/fixtures/airport-I.txt +17 -0
  56. data/test/fixtures/defaults-domains.txt +1 -0
  57. data/test/fixtures/defaults-export-com.apple.dock.xml +47 -0
  58. data/test/fixtures/defaults-read-NSUserKeyEquivalents.txt +4 -0
  59. data/test/fixtures/df-k.txt +6 -0
  60. data/test/fixtures/diskutil-list-plist.xml +104 -0
  61. data/test/fixtures/dscl-list-users.txt +8 -0
  62. data/test/fixtures/ifconfig-a.txt +53 -0
  63. data/test/fixtures/networksetup-listallhardwareports.txt +39 -0
  64. data/test/fixtures/say-v.txt +8 -0
  65. data/test/fixtures/system_profiler-SPAirPortDataType.txt +44 -0
  66. data/test/fixtures/system_profiler-SPSoftwareDataType.txt +14 -0
  67. data/test/fixtures/system_profiler-SPUSBDataType.txt +58 -0
  68. data/test/gemspec_test.rb +50 -0
  69. data/test/helper.rb +18 -0
  70. metadata +183 -0
data/lib/Version.rb ADDED
@@ -0,0 +1,381 @@
1
+ # Version.rb
2
+ # Version
3
+
4
+ # 20220213
5
+ # 0.14.0
6
+
7
+ # Description: This class is able to compare strings containing version numbers.
8
+
9
+ # Changes since 0.13:
10
+ # 1. Removed all reference to version_codes.
11
+
12
+ lib_dir = File.dirname(File.expand_path(__FILE__))
13
+ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
14
+
15
+ require 'Module/alias_methods'
16
+ require 'Ordinal/Array'
17
+ require 'Thoran/String/Capture/capture'
18
+
19
+ class Version
20
+
21
+ module VERSION; STRING = '0.13.0'; end
22
+
23
+ class << self
24
+
25
+ include Enumerable
26
+ include Comparable
27
+
28
+ attr_accessor :version_strings
29
+
30
+ def <<(version)
31
+ self.version_strings ||= []
32
+ self.version_strings << to_version_string(version)
33
+ end
34
+ alias_methods :append, :push, :<<
35
+
36
+ def sorted(*versions)
37
+ versions = versions.flatten
38
+ return_value = versions.collect{|version| Version.new(to_version_string(version))}.sort
39
+ return_value = return_value.collect{|e| e.to_s} if versions.all?{|e| e.is_a?(String)}
40
+ return_value
41
+ end
42
+
43
+ def latest(*versions)
44
+ sorted(*versions).last
45
+ end
46
+ alias_methods :last, :most_recent, :newest, :highest, :latest
47
+
48
+ def major(version)
49
+ major = to_version_string(version).split('.').first
50
+ major = major.split('-').first if major && hyphenated_part?(version)
51
+ major
52
+ end
53
+ alias_methods :major_version, :major_number, :major_version_number, :major
54
+
55
+ def minor(version)
56
+ minor = to_version_string(version).split('.').second
57
+ minor = minor.split('-').first if minor && hyphenated_part?(version)
58
+ minor
59
+ end
60
+ alias_methods :minor_version, :minor_number, :minor_version_number, :minor
61
+
62
+ def tiny(version)
63
+ tiny = to_version_string(version).split('.').third
64
+ tiny = tiny.split('-').first if tiny && hyphenated_part?(version)
65
+ tiny
66
+ end
67
+ alias_methods :tiny_version, :tiny_number, :tiny_version_number, :tiny
68
+
69
+ def build(version)
70
+ build = to_version_string(version).split('.').fourth
71
+ build = build.split('-').first if build && hyphenated_part?(version)
72
+ build
73
+ end
74
+ alias_methods :build_version, :build_number, :build_version_number, :build
75
+
76
+ def patch(version)
77
+ if hyphenated_part = self.hyphenated_part(version)
78
+ hyphenated_part(version).capture(/^(p\d+)/) # It's unlikely that there would be a p without a number.
79
+ end
80
+ end
81
+ alias_methods :patch_level, :patch_version, :patch_number, :patch_version_number, :patch
82
+
83
+ def dev(version)
84
+ if hyphenated_part = self.hyphenated_part(version)
85
+ hyphenated_part(version).capture(/^(dev\d*)/)
86
+ end
87
+ end
88
+ alias_methods :dev_version, :dev_number, :dev_version_number, :dev
89
+ alias_methods :development, :development_version, :development_number, :development_version_number, :dev
90
+
91
+ def preview(version)
92
+ if hyphenated_part = self.hyphenated_part(version)
93
+ hyphenated_part.capture(/^(preview\d*)/) # It's possible that there will be a preview without a number.
94
+ end
95
+ end
96
+ alias_methods :preview_version, :preview_number, :preview_version_number, :preview
97
+
98
+ def release_candidate(version)
99
+ if hyphenated_part = self.hyphenated_part(version)
100
+ hyphenated_part(version).capture(/^(rc\d*)/)
101
+ end
102
+ end
103
+ alias_methods :release_candidate_version, :release_candidate_number, :release_candidate_version_number, :release_candidate
104
+ alias_methods :rc, :rc_version, :rc_number, :rc_version_number, :release_candidate
105
+
106
+ def hyphenated_part(version)
107
+ if to_version_string(version).match(/-/)
108
+ to_version_string(version).split('-').last
109
+ end
110
+ end
111
+
112
+ def <=>(version_1, version_2)
113
+ version_1 = Version.new(Version.to_version_string(version_1))
114
+ version_2 = Version.new(Version.to_version_string(version_2))
115
+ if version_1.has_hyphenated_part? || version_2.has_hyphenated_part?
116
+ if (to_a(version_1, false) <=> to_a(version_2, false)) == 0
117
+ if version_1.hyphenated_part == version_2.hyphenated_part
118
+ 0
119
+ elsif version_1.hyphenated_part =~ /^dev/
120
+ case version_2.hyphenated_part
121
+ when /^dev/; version_1.hyphenated_part <=> version_2.hyphenated_part
122
+ when /^preview/; -1
123
+ when /^rc/; -1
124
+ when /^p\d+/; -1
125
+ else; -1
126
+ end
127
+ elsif version_1.hyphenated_part =~ /^preview/
128
+ case version_2.hyphenated_part
129
+ when /^preview/; version_1.hyphenated_part <=> version_2.hyphenated_part
130
+ when /^dev/; 1
131
+ when /^rc/; -1
132
+ when /^p\d+/; -1
133
+ else; -1
134
+ end
135
+ elsif version_1.hyphenated_part =~ /^rc/
136
+ case version_2.hyphenated_part
137
+ when /^rc/; version_1.hyphenated_part <=> version_2.hyphenated_part
138
+ when /^dev/; 1
139
+ when /^preview/; 1
140
+ when /^p\d+/; -1
141
+ else; -1
142
+ end
143
+ elsif version_1.hyphenated_part =~ /^p\d+/
144
+ case version_2.hyphenated_part
145
+ when /^p\d+/; version_1.hyphenated_part <=> version_2.hyphenated_part
146
+ when /^dev/; 1
147
+ when /^preview/; 1
148
+ when /^rc/; 1
149
+ else; -1 # This assumes that 2.1.0 != 2.1.0-p0 (ie. 2.1.0-p0 < 2.1.0) and that for all other patch levels that anything with no patch level is superior.
150
+ end
151
+ else
152
+ case version_2.hyphenated_part
153
+ when /^dev/; 1
154
+ when /^preview/; 1
155
+ when /^rc/; 1
156
+ when /^p\d+/; 1
157
+ else; 1
158
+ end
159
+ end
160
+ else
161
+ to_a(version_1, false) <=> to_a(version_2, false)
162
+ end
163
+ else
164
+ to_a(version_1) <=> to_a(version_2)
165
+ end
166
+ end
167
+
168
+ def ===(version_1, version_2)
169
+ version_1_size = to_a(version_1).size
170
+ version_2_size = to_a(version_2).size
171
+ maximum_shared_places = [version_1_size, version_2_size].min
172
+ if version_1_size < version_2_size
173
+ new_version_2 = to_a(version_2).take(maximum_shared_places).join('.')
174
+ self.<=>(version_1, new_version_2).zero? ? true : false
175
+ elsif version_1_size > version_2_size
176
+ new_version_1 = to_a(version_1).take(maximum_shared_places).join('.')
177
+ self.<=>(new_version_1, version_2).zero? ? true : false
178
+ else
179
+ self.<=>(version_1, version_2).zero? ? true : false
180
+ end
181
+ end
182
+
183
+ def each
184
+ version_strings.each{|version_string| yield Version.new(version_string)}
185
+ end
186
+
187
+ def to_a(version, include_hyphenated = true)
188
+ to_a = [major(version), minor(version), tiny(version), build(version)]
189
+ to_a = to_a + [hyphenated_part(version)] if include_hyphenated
190
+ to_a.compact
191
+ end
192
+
193
+ def to_version_string(version)
194
+ version.to_s
195
+ end
196
+
197
+ # boolean methods
198
+
199
+ def major?(version)
200
+ major(version) ? true : false
201
+ end
202
+ alias_methods :major_version?, :major_number?, :major_version_number?, :major?
203
+
204
+ def minor?(version)
205
+ minor(version) ? true : false
206
+ end
207
+ alias_methods :minor_version?, :minor_number?, :minor_version_number?, :minor?
208
+
209
+ def tiny?(version)
210
+ tiny(version) ? true : false
211
+ end
212
+ alias_methods :tiny_version?, :tiny_number?, :tiny_version_number?, :tiny?
213
+
214
+ def build?(version)
215
+ build(version) ? true : false
216
+ end
217
+ alias_methods :build_version?, :build_number?, :build_version_number?, :build?
218
+
219
+ def patch?(version)
220
+ patch(version) ? true : false
221
+ end
222
+ alias_methods :patch_level?, :patch_version?, :patch_number?, :patch_version_number?, :patch?
223
+
224
+ def dev?(version)
225
+ dev(version) ? true : false
226
+ end
227
+ alias_methods :dev_version?, :dev_number?, :dev_version_number?, :dev?
228
+ alias_methods :development?, :development_version?, :development_number?, :development_version_number?, :dev?
229
+
230
+ def preview?(version)
231
+ preview(version) ? true : false
232
+ end
233
+ alias_methods :preview_version?, :preview_number?, :preview_version_number?, :preview?
234
+
235
+ def release_candidate?(version)
236
+ release_candidate(version) ? true : false
237
+ end
238
+ alias_methods :release_candidate_version?, :release_candidate_number?, :release_candidate_version_number?, :release_candidate?
239
+ alias_methods :rc?, :rc_version?, :rc_number?, :rc_version_number?, :release_candidate?
240
+
241
+ def hyphenated_part?(version)
242
+ hyphenated_part(version) ? true : false
243
+ end
244
+ alias_methods :has_hyphenated_part?, :hyphenated_part?
245
+
246
+ end # class << self
247
+
248
+ include Comparable
249
+
250
+ attr_accessor :version_string
251
+
252
+ def initialize(version_string = nil)
253
+ @version_string = version_string.to_s
254
+ end
255
+
256
+ def major
257
+ self.class.major(version_string)
258
+ end
259
+ alias_methods :major_version, :major_number, :major_version_number, :major
260
+
261
+ def minor
262
+ self.class.minor(version_string)
263
+ end
264
+ alias_methods :minor_version, :minor_number, :minor_version_number, :minor
265
+
266
+ def tiny
267
+ self.class.tiny(version_string)
268
+ end
269
+ alias_methods :tiny_version, :tiny_number, :tiny_version_number, :tiny
270
+
271
+ def build
272
+ self.class.build(version_string)
273
+ end
274
+ alias_methods :build_version, :build_number, :build_version_number, :build
275
+
276
+ def patch
277
+ self.class.patch(version_string)
278
+ end
279
+ alias_methods :patch_level, :patch_version, :patch_number, :patch_version_number, :patch
280
+
281
+ def dev
282
+ self.class.dev(version_string)
283
+ end
284
+ alias_methods :dev_version, :dev_number, :dev_version_number, :dev
285
+ alias_methods :development, :development_version, :development_number, :development_version_number, :dev
286
+
287
+ def preview
288
+ self.class.preview(version_string)
289
+ end
290
+ alias_methods :preview_version, :preview_number, :preview_version_number, :patch
291
+
292
+ def release_candidate
293
+ self.class.release_candidate(version_string)
294
+ end
295
+ alias_methods :release_candidte_level, :release_candidate_version, :release_candidate_number, :release_candidate_version_number, :release_candidate
296
+ alias_methods :rc, :rc_version, :rc_number, :rc_version_number, :release_candidate
297
+
298
+ def hyphenated_part
299
+ patch || dev || preview || release_candidate
300
+ end
301
+
302
+ def <=>(other_version)
303
+ self.class.<=>(version_string, other_version)
304
+ end
305
+
306
+ def ===(other_version)
307
+ self.class.===(version_string, other_version)
308
+ end
309
+
310
+ def to_a
311
+ self.class.to_a(version_string)
312
+ end
313
+
314
+ def to_s
315
+ version_string
316
+ end
317
+
318
+ def least_significant_version_part_name
319
+ if patch?
320
+ :patch
321
+ elsif build?
322
+ :build
323
+ elsif tiny?
324
+ :tiny
325
+ elsif minor?
326
+ :minor
327
+ else
328
+ :major
329
+ end
330
+ end
331
+
332
+ # boolean methods
333
+
334
+ def major?
335
+ self.class.major?(version_string)
336
+ end
337
+ alias_methods :major_version?, :major_number?, :major_version_number?, :major?
338
+
339
+ def minor?
340
+ self.class.minor?(version_string)
341
+ end
342
+ alias_methods :minor_version?, :minor_number?, :minor_version_number?, :minor?
343
+
344
+ def tiny?
345
+ self.class.tiny?(version_string)
346
+ end
347
+ alias_methods :tiny_version?, :tiny_number?, :tiny_version_number?, :tiny?
348
+
349
+ def build?
350
+ self.class.build?(version_string)
351
+ end
352
+ alias_methods :build_version?, :build_number?, :build_version_number?, :build?
353
+
354
+ def patch?
355
+ self.class.patch?(version_string)
356
+ end
357
+ alias_methods :patch_level?, :patch_version?, :patch_number?, :patch_version_number?, :patch?
358
+
359
+ def dev?
360
+ self.class.dev?(version_string)
361
+ end
362
+ alias_methods :dev_version?, :dev_number?, :dev_version_number?, :dev?
363
+ alias_methods :development?, :development_version?, :development_number?, :development_version_number?, :dev?
364
+
365
+ def preview?
366
+ self.class.preview?(version_string) ? true : false
367
+ end
368
+ alias_methods :preview_version?, :preview_number?, :preview_version_number?, :preview?
369
+
370
+ def release_candidate?
371
+ self.class.release_candidate?(version_string) ? true : false
372
+ end
373
+ alias_methods :release_candidate_version?, :release_candidate_number?, :release_candidate_version_number?, :release_candidate?
374
+ alias_methods :rc?, :rc_version?, :rc_number?, :rc_version_number?, :release_candidate?
375
+
376
+ def hyphenated_part?
377
+ patch? || dev? || preview? || release_candidate?
378
+ end
379
+ alias_methods :has_hyphenated_part?, :hyphenated_part?
380
+
381
+ end
data/macos.rb.gemspec ADDED
@@ -0,0 +1,59 @@
1
+ # macos.rb.gemspec
2
+
3
+ require_relative './lib/MacOS/VERSION'
4
+
5
+ class Gem::Specification
6
+ def dependencies=(gems)
7
+ gems.each{|gem| add_dependency(*gem)}
8
+ end
9
+
10
+ def development_dependencies=(gems)
11
+ gems.each{|gem| add_development_dependency(*gem)}
12
+ end
13
+ end
14
+
15
+ Gem::Specification.new do |spec|
16
+ spec.name = 'macos.rb'
17
+ spec.version = MacOS::VERSION
18
+
19
+ spec.summary = "Work with macOS from Ruby."
20
+ spec.description = "Work with macOS from Ruby: network interfaces and hardware ports, the system's version, defaults, disks and disk images, USB flash drives, applications, users, voices, the keychain and launchd."
21
+
22
+ spec.author = 'thoran'
23
+ spec.email = 'code@thoran.com'
24
+ spec.homepage = 'https://github.com/thoran/macos.rb'
25
+ spec.license = 'MIT'
26
+
27
+ spec.metadata = {
28
+ 'bug_tracker_uri' => 'https://github.com/thoran/macos.rb/issues',
29
+ 'changelog_uri' => 'https://github.com/thoran/macos.rb/blob/master/CHANGELOG',
30
+ 'source_code_uri' => 'https://github.com/thoran/macos.rb',
31
+ 'documentation_uri' => 'https://github.com/thoran/macos.rb/blob/master/README.md',
32
+ }
33
+
34
+ spec.require_paths = ['lib']
35
+ spec.required_ruby_version = '>= 2.7'
36
+
37
+ spec.files = [
38
+ 'macos.rb.gemspec',
39
+ Dir['lib/**/*.rb'],
40
+ Dir['test/**/*.rb'],
41
+ Dir['test/fixtures/*'],
42
+ 'CHANGELOG',
43
+ 'Gemfile',
44
+ 'LICENSE',
45
+ 'Rakefile',
46
+ 'README.md',
47
+ ].flatten
48
+
49
+ spec.dependencies = %w{
50
+ CFPropertyList
51
+ plist
52
+ }
53
+
54
+ spec.development_dependencies = %w{
55
+ minitest
56
+ minitest-mock
57
+ rake
58
+ }
59
+ end
@@ -0,0 +1,80 @@
1
+ require_relative '../helper'
2
+ require 'MacOS/Apple80211'
3
+
4
+ describe MacOS::Apple80211 do
5
+ let(:wifi){fixture('system_profiler-SPAirPortDataType.txt')}
6
+
7
+ def with_sources(airport, system_profiler = fixture('system_profiler-SPAirPortDataType.txt'), &block)
8
+ MacOS::Apple80211.stub(:airport_i, airport) do
9
+ MacOS::Apple80211.stub(:system_profiler_airport, system_profiler, &block)
10
+ end
11
+ end
12
+
13
+ describe "where airport -I reports, as before macOS 14.4" do
14
+ it "reads its attributes" do
15
+ with_sources(fixture('airport-I.txt')) do
16
+ attributes = MacOS::Apple80211.attributes
17
+ _(attributes['BSSID']).must_equal('00:00:5e:00:53:20')
18
+ _(attributes['agrCtlRSSI']).must_equal('-52')
19
+ _(attributes['op mode']).must_equal('station')
20
+ end
21
+ end
22
+
23
+ it "gives the channel, SSID and link authentication" do
24
+ with_sources(fixture('airport-I.txt')) do
25
+ _(MacOS::Apple80211.channel).must_equal('36,80')
26
+ _(MacOS::Apple80211.ssid).must_equal('ExampleNet')
27
+ _(MacOS::Apple80211.link_auth).must_equal('wpa2-psk')
28
+ end
29
+ end
30
+ end
31
+
32
+ describe "where airport -I gives only its deprecation, as from macOS 14.4 on" do
33
+ it "reads the current network from system_profiler instead" do
34
+ with_sources(fixture('airport-I-deprecated.txt')) do
35
+ attributes = MacOS::Apple80211.attributes
36
+ _(attributes['PHY Mode']).must_equal('802.11ax')
37
+ _(attributes['Signal / Noise']).must_equal('-52 dBm / -94 dBm')
38
+ _(attributes.keys).wont_include('WARNING')
39
+ end
40
+ end
41
+
42
+ it "gives its channel and security as system_profiler gives them" do
43
+ with_sources(fixture('airport-I-deprecated.txt')) do
44
+ _(MacOS::Apple80211.channel).must_equal('36 (5GHz, 80MHz)')
45
+ _(MacOS::Apple80211.link_auth).must_equal('WPA2 Personal')
46
+ end
47
+ end
48
+
49
+ it "gives no SSID where macOS redacts the network's name" do
50
+ with_sources(fixture('airport-I-deprecated.txt')) do
51
+ _(MacOS::Apple80211.ssid).must_be_nil
52
+ end
53
+ end
54
+
55
+ it "gives the SSID where macOS doesn't redact it" do
56
+ with_sources(fixture('airport-I-deprecated.txt'), wifi.sub(' <redacted>:', ' ExampleNet:')) do
57
+ _(MacOS::Apple80211.ssid).must_equal('ExampleNet')
58
+ end
59
+ end
60
+
61
+ it "gives nothing where there's no current network" do
62
+ with_sources(fixture('airport-I-deprecated.txt'), "Wi-Fi:\n\n Interfaces:\n en0:\n Status: Off\n") do
63
+ _(MacOS::Apple80211.attributes).must_equal({})
64
+ _(MacOS::Apple80211.channel).must_be_nil
65
+ end
66
+ end
67
+ end
68
+
69
+ describe "where airport isn't there" do
70
+ it "reads system_profiler" do
71
+ missing = ->(command){raise Errno::ENOENT, command}
72
+ MacOS::Apple80211.stub(:`, missing) do
73
+ _(MacOS::Apple80211.airport_i).must_equal('')
74
+ end
75
+ with_sources('') do
76
+ _(MacOS::Apple80211.channel).must_equal('36 (5GHz, 80MHz)')
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,82 @@
1
+ require_relative '../helper'
2
+ require 'fileutils'
3
+ require 'tmpdir'
4
+ require 'MacOS/Applications'
5
+
6
+ describe MacOS::Applications do
7
+ let(:paths){['/System/Applications/Mail.app', '/Applications/Example App.app']}
8
+
9
+ def with_paths(&block)
10
+ MacOS::Applications.stub(:paths, paths, &block)
11
+ end
12
+
13
+ describe "finding an application" do
14
+ it "finds one by its name, with or without .app" do
15
+ with_paths do
16
+ _(MacOS::Applications.path_for('Mail')).must_equal('/System/Applications/Mail.app')
17
+ _(MacOS::Applications.path_for('Example App.app')).must_equal('/Applications/Example App.app')
18
+ end
19
+ end
20
+
21
+ it "knows whether one is installed" do
22
+ with_paths do
23
+ _(MacOS::Applications.installed?('Mail')).must_equal(true)
24
+ _(MacOS::Applications.installed?('Nowhere')).must_equal(false)
25
+ end
26
+ end
27
+ end
28
+
29
+ describe "where it looks" do
30
+ def by_directory(&block)
31
+ MacOS::Applications.stub(:in_directory, ->(directory){["#{directory}/In.app"]}) do
32
+ MacOS::Applications.stub(:under_directory, ->(directory){["#{directory}/Under.app"]}, &block)
33
+ end
34
+ end
35
+
36
+ it "looks in the system, local and user domains, the user's own searched all the way down" do
37
+ by_directory do
38
+ _(MacOS::Applications.paths('example')).must_equal(%w{
39
+ /System/Applications/In.app /System/Applications/Utilities/In.app
40
+ /Applications/In.app /Applications/Utilities/In.app
41
+ /Users/example/Applications/Under.app
42
+ })
43
+ end
44
+ end
45
+
46
+ it "looks in every real user's Applications" do
47
+ by_directory do
48
+ MacOS::Users.stub(:all, %w{another example}) do
49
+ _(MacOS::Applications.all_user_paths).must_equal(%w{/Users/another/Applications/Under.app /Users/example/Applications/Under.app})
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ describe "the applications in a directory" do
56
+ it "finds those directly in it, and those anywhere under it but not inside another application" do
57
+ Dir.mktmpdir do |directory|
58
+ %w{Top.app Sub/Nested.app Top.app/Contents/Helpers/Helper.app}.each{|path| FileUtils.mkdir_p(File.join(directory, path))}
59
+ _(MacOS::Applications.in_directory(directory).map{|path| File.basename(path)}).must_equal(%w{Top.app})
60
+ _(MacOS::Applications.under_directory(directory).map{|path| File.basename(path)}.sort).must_equal(%w{Nested.app Top.app})
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "starting an application" do
66
+ it "opens it, by each of its names" do
67
+ with_paths do
68
+ commands = commands_run_by(MacOS::Applications) do
69
+ %i{start launch open run}.each{|method| MacOS::Applications.send(method, 'Mail')}
70
+ end
71
+ _(commands).must_equal([['open', '/System/Applications/Mail.app']] * 4)
72
+ end
73
+ end
74
+
75
+ it "does nothing for an application which isn't installed" do
76
+ with_paths do
77
+ commands = commands_run_by(MacOS::Applications){_(MacOS::Applications.start('Nowhere')).must_be_nil}
78
+ _(commands).must_equal([])
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,66 @@
1
+ require_relative '../../helper'
2
+ require 'MacOS/Defaults/ComAppleDock'
3
+
4
+ describe MacOS::Defaults::ComAppleDock do
5
+ let(:dock){MacOS::Defaults::ComAppleDock}
6
+
7
+ def with_dock(&block)
8
+ MacOS::Defaults::ComAppleDock.stub(:`, fixture('defaults-export-com.apple.dock.xml'), &block)
9
+ end
10
+
11
+ describe "writing attributes" do
12
+ it "marks booleans as -bool and integers as -int" do
13
+ _(dock.attribute_write_command('autohide', true)).must_equal('defaults write com.apple.dock autohide -bool true')
14
+ _(dock.attribute_write_command('tilesize', 48)).must_equal('defaults write com.apple.dock tilesize -int 48')
15
+ _(dock.attribute_write_command('orientation', 'left')).must_equal('defaults write com.apple.dock orientation left')
16
+ end
17
+
18
+ it "loads a configuration, adding each persistent app and writing each other attribute" do
19
+ commands = commands_run_by(dock){dock.load('persistent-apps' => ['/Applications/Example.app'], 'autohide' => true)}
20
+ _(commands.size).must_equal(2)
21
+ _(commands.first.first).must_match(%r{\Adefaults write com\.apple\.dock persistent-apps -array-add '<dict>.*file:///Applications/Example\.app}m)
22
+ _(commands.last).must_equal(['defaults write com.apple.dock autohide -bool true'])
23
+ end
24
+
25
+ it "deletes the persistent apps and others, and the recent apps" do
26
+ commands = commands_run_by(dock){dock.delete_all_items}
27
+ _(commands.flatten).must_equal(%w{persistent-apps persistent-others recent-apps}.map{|item| "defaults delete com.apple.dock #{item}"})
28
+ end
29
+ end
30
+
31
+ describe "an app's tile" do
32
+ it "is the app's file URL in the Dock's tile data" do
33
+ _(dock.urlified_app_string('/Applications/Example.app')).must_equal('file:///Applications/Example.app')
34
+ _(dock.app_plist_data('file:///Applications/Example.app')).must_include('<string>file:///Applications/Example.app</string>')
35
+ end
36
+ end
37
+
38
+ describe "reading" do
39
+ it "reads the persistent apps back as paths, a user's own with ~ for their home" do
40
+ with_dock do
41
+ _(dock.read('persistent-apps')).must_equal('persistent-apps' => ['/System/Applications/Mail.app/', '~/Applications/Example App.app'])
42
+ end
43
+ end
44
+
45
+ it "reads booleans as yes and no, and other attributes as they are" do
46
+ with_dock do
47
+ _(dock.read('autohide', 'magnification', 'tilesize', 'orientation')).must_equal(
48
+ 'autohide' => 'yes', 'magnification' => 'no', 'tilesize' => 48, 'orientation' => 'bottom'
49
+ )
50
+ end
51
+ end
52
+
53
+ it "leaves out the persistent others and the recent apps" do
54
+ with_dock do
55
+ _(dock.read.keys).wont_include('recent-apps')
56
+ _(dock.read.keys).wont_include('persistent-others')
57
+ end
58
+ end
59
+
60
+ it "writes what it reads as YAML" do
61
+ with_dock do
62
+ _(YAML.load(dock.to_yaml)['tilesize']).must_equal(48)
63
+ end
64
+ end
65
+ end
66
+ end