maid 0.10.0.pre.alpha.3 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +14 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.github/workflows/coverage.yml +3 -3
- data/.github/workflows/lint.yml +9 -1
- data/.github/workflows/merge-gatekeeper.yml +20 -0
- data/.github/workflows/release.yml +13 -20
- data/.github/workflows/stale.yml +25 -0
- data/.github/workflows/test.yml +8 -7
- data/.gitignore +1 -1
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +3 -1
- data/.rubocop_todo.yml +105 -107
- data/.ruby-version +1 -1
- data/CHANGELOG.md +23 -0
- data/Dockerfile +13 -0
- data/Gemfile.lock +226 -0
- data/Guardfile +2 -0
- data/README.md +82 -49
- data/Rakefile +9 -0
- data/SECURITY.md +29 -0
- data/fixtures/files/test_rules.rb +3 -0
- data/fixtures/vcr_cassettes/Dependency_expectations/Geocoder/translates_latitude_and_longitude_into_street_addresses.yml +42 -0
- data/fixtures/vcr_cassettes/Maid_Tools/_location_city/given_a_JPEG_image/reports_the_known_location.yml +42 -0
- data/lib/maid/logger/logger.rb +63 -0
- data/lib/maid/maid.rb +6 -22
- data/lib/maid/repeat.rb +2 -2
- data/lib/maid/rule.rb +2 -2
- data/lib/maid/rule_container.rb +2 -2
- data/lib/maid/tools.rb +3 -3
- data/lib/maid/trash_migration.rb +2 -0
- data/lib/maid/version.rb +1 -1
- data/lib/maid/watch.rb +2 -2
- data/lib/maid.rb +3 -2
- data/maid.gemspec +14 -9
- data/release-please-config.json +18 -0
- data/script/docker-test +7 -0
- data/spec/dependency_spec.rb +1 -1
- data/spec/fakefs_helper.rb +13 -0
- data/spec/lib/maid/logger/logger_spec.rb +64 -0
- data/spec/lib/maid/maid_spec.rb +113 -103
- data/spec/lib/maid/rake/single_rule_spec.rb +1 -1
- data/spec/lib/maid/tools_spec.rb +384 -225
- data/spec/lib/maid/trash_migration_spec.rb +7 -5
- data/spec/spec_helper.rb +17 -1
- metadata +124 -44
- data/Vagrantfile +0 -14
- data/script/vagrant-provision +0 -43
- data/script/vagrant-test +0 -7
- data/script/vagrant-test-all +0 -34
data/spec/lib/maid/tools_spec.rb
CHANGED
@@ -1,19 +1,32 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
module Maid
|
4
|
-
# NOTE:
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# *
|
9
|
-
|
4
|
+
# NOTE: FakeFS is disabled intentionally, because it causes weird and subtle
|
5
|
+
# issues that are hard to debug. See https://github.com/maid/maid/issues/315
|
6
|
+
# FIXME: Split test suite into smaller files, likely one file per described
|
7
|
+
# method.
|
8
|
+
# FIXME: Replace *all* occurences of writing to `~/` in favour or `/tmp/`.
|
9
|
+
# FIXME: Fix all the RSpec/MultipleMemoizeHelpers issues in Rubocop.
|
10
|
+
describe Tools, fakefs: false do
|
11
|
+
let(:filefixtures_path) { File.expand_path(File.dirname(__FILE__) + '../../../fixtures/files/') }
|
12
|
+
let(:filefixtures_glob) { "#{filefixtures_path}/*" }
|
13
|
+
let(:image_path) { File.join(filefixtures_path, 'ruby.jpg') }
|
14
|
+
let(:unknown_path) { File.join(filefixtures_path, 'unknown.foo') }
|
15
|
+
let(:test_basedir) { '/tmp/maid-specs' }
|
16
|
+
let(:logfile) { "#{test_basedir}/test.log" }
|
17
|
+
let(:maid) { Maid.new({ log_device: logfile }) }
|
18
|
+
|
10
19
|
before do
|
20
|
+
FileUtils.mkdir_p(File.dirname(logfile))
|
21
|
+
|
11
22
|
@home = File.expand_path('~')
|
12
23
|
@now = Time.now
|
13
24
|
|
14
25
|
expect(Maid.ancestors).to include(Tools)
|
15
26
|
|
16
|
-
|
27
|
+
# FIXME: Keeping the double to avoid a large refactor for now, but this
|
28
|
+
# probably isn't necessary anymore.
|
29
|
+
@logger = double('::Logger').as_null_object
|
17
30
|
@maid = Maid.new(logger: @logger)
|
18
31
|
|
19
32
|
# Prevent warnings from showing when testing deprecated methods:
|
@@ -23,19 +36,24 @@ module Maid
|
|
23
36
|
allow(@maid).to receive(:cmd)
|
24
37
|
end
|
25
38
|
|
39
|
+
after do
|
40
|
+
# Ensure each test has a fresh logfile
|
41
|
+
FileUtils.rm_rf(File.dirname(logfile))
|
42
|
+
end
|
43
|
+
|
26
44
|
describe '#move' do
|
27
45
|
before do
|
28
|
-
@src_dir = File.join(
|
29
|
-
@
|
30
|
-
@src_file = File.join(@src_dir, @
|
31
|
-
@dst_dir = File.join(
|
46
|
+
@src_dir = File.join(test_basedir, 'Source')
|
47
|
+
@filename = 'foo.zip'
|
48
|
+
@src_file = File.join(@src_dir, @filename)
|
49
|
+
@dst_dir = File.join(test_basedir, 'Destination')
|
32
50
|
FileUtils.mkdir_p(File.expand_path(@src_dir))
|
33
51
|
FileUtils.touch(File.expand_path(@src_file))
|
34
52
|
FileUtils.mkdir_p(File.expand_path(@dst_dir))
|
35
53
|
end
|
36
54
|
|
37
55
|
it 'moves expanded paths, passing file_options' do
|
38
|
-
dest_file = File.expand_path(File.join(@dst_dir, @
|
56
|
+
dest_file = File.expand_path(File.join(@dst_dir, @filename))
|
39
57
|
|
40
58
|
@maid.move(@src_file, @dst_dir)
|
41
59
|
expect(File.exist?(dest_file)).to be(true)
|
@@ -48,12 +66,12 @@ module Maid
|
|
48
66
|
|
49
67
|
# FIXME: Example is too long, shouldn't need the rubocop::disable
|
50
68
|
it 'handles multiple from paths' do # rubocop:disable RSpec/ExampleLength
|
51
|
-
|
52
|
-
second_src_file = File.join(@src_dir,
|
69
|
+
second_filename = 'bar.zip'
|
70
|
+
second_src_file = File.join(@src_dir, second_filename)
|
53
71
|
FileUtils.touch(File.expand_path(second_src_file))
|
54
72
|
src_files = [@src_file, second_src_file]
|
55
|
-
dst_file = File.expand_path(File.join(@dst_dir, @
|
56
|
-
second_dst_file = File.expand_path(File.join(@dst_dir,
|
73
|
+
dst_file = File.expand_path(File.join(@dst_dir, @filename))
|
74
|
+
second_dst_file = File.expand_path(File.join(@dst_dir, second_filename))
|
57
75
|
|
58
76
|
@maid.move(src_files, @dst_dir)
|
59
77
|
expect(File.exist?(dst_file)).to be(true)
|
@@ -61,47 +79,64 @@ module Maid
|
|
61
79
|
end
|
62
80
|
|
63
81
|
context 'given the destination directory does not exist' do
|
82
|
+
let(:src_file) { File.join(test_basedir, 'test_file') }
|
83
|
+
let(:dst_dir) { File.join(test_basedir, 'dest') }
|
84
|
+
let(:dst_file) { File.join(dst_dir, File.basename(src_file)) }
|
85
|
+
|
64
86
|
before do
|
65
|
-
FileUtils.
|
87
|
+
FileUtils.mkdir_p(File.dirname(src_file))
|
88
|
+
FileUtils.touch(src_file)
|
89
|
+
FileUtils.mkdir_p(dst_dir)
|
90
|
+
FileUtils.rmdir(dst_dir)
|
91
|
+
FileUtils.rm_rf(logfile)
|
92
|
+
|
93
|
+
maid.move(src_file, dst_dir)
|
66
94
|
end
|
67
95
|
|
68
|
-
|
69
|
-
|
70
|
-
|
96
|
+
after do
|
97
|
+
FileUtils.rm_rf([File.dirname(src_file), File.dirname(dst_file)])
|
98
|
+
end
|
99
|
+
|
100
|
+
it "doesn't move the file" do
|
101
|
+
expect(File.exist?(dst_file)).to be false
|
102
|
+
end
|
71
103
|
|
72
|
-
|
73
|
-
|
104
|
+
it 'logs a warning about it' do
|
105
|
+
expect(File.read(logfile)).to match(/skipping move.*#{File.dirname(dst_file)}.*not.*directory/)
|
74
106
|
end
|
75
107
|
end
|
76
108
|
|
77
109
|
context 'when the destination file already exists' do
|
78
|
-
let(:src_file) { '
|
79
|
-
let(:dst_dir) { '
|
110
|
+
let(:src_file) { File.join(test_basedir, 'test_file') }
|
111
|
+
let(:dst_dir) { File.join(test_basedir, 'dest') }
|
80
112
|
let(:dst_file) { File.join(dst_dir, File.basename(src_file)) }
|
81
|
-
let(:maid) { Maid.new(logger: @logger) }
|
82
113
|
|
83
114
|
before do
|
84
115
|
FileUtils.mkdir_p(File.dirname(src_file))
|
85
116
|
FileUtils.mkdir_p(dst_dir)
|
86
117
|
FileUtils.touch(src_file)
|
118
|
+
# Necessary to have different mtimes, they're identical otherwise
|
119
|
+
sleep 0.05
|
87
120
|
FileUtils.touch(dst_file)
|
88
121
|
end
|
89
122
|
|
90
123
|
after do
|
91
|
-
FileUtils.rm_rf([src_file, dst_file])
|
124
|
+
FileUtils.rm_rf([File.dirname(src_file), File.dirname(dst_file)])
|
92
125
|
end
|
93
126
|
|
94
127
|
context 'by default' do
|
95
128
|
let!(:original_mtime) { File.stat(dst_file).mtime }
|
96
129
|
|
97
130
|
before do
|
131
|
+
FileUtils.rm_rf(logfile)
|
132
|
+
|
98
133
|
maid.move(src_file, dst_dir)
|
99
134
|
end
|
100
135
|
|
101
136
|
it 'logs an info message' do
|
102
|
-
expect(
|
103
|
-
expect(
|
104
|
-
expect(
|
137
|
+
expect(File.read(logfile)).to match(/INFO.*already/)
|
138
|
+
expect(File.read(logfile)).to match(/INFO.*anyway/)
|
139
|
+
expect(File.read(logfile)).not_to match(/INFO.*skipping/)
|
105
140
|
end
|
106
141
|
|
107
142
|
it 'overwrites destination' do
|
@@ -113,12 +148,14 @@ module Maid
|
|
113
148
|
let!(:original_mtime) { File.stat(dst_file).mtime }
|
114
149
|
|
115
150
|
before do
|
151
|
+
FileUtils.rm_rf(logfile)
|
152
|
+
|
116
153
|
maid.move(src_file, dst_dir, clobber: false)
|
117
154
|
end
|
118
155
|
|
119
156
|
it 'logs an info message' do
|
120
|
-
expect(
|
121
|
-
expect(
|
157
|
+
expect(File.read(logfile)).not_to match(/INFO.*anyway/)
|
158
|
+
expect(File.read(logfile)).to match(/INFO.*skipping/)
|
122
159
|
end
|
123
160
|
|
124
161
|
it "doesn't overwrite the destination file" do
|
@@ -129,138 +166,225 @@ module Maid
|
|
129
166
|
end
|
130
167
|
|
131
168
|
describe '#rename' do
|
169
|
+
let(:src_file) { File.join(test_basedir, 'test_file') }
|
170
|
+
let(:dst_dir) { File.join(test_basedir, 'dest') }
|
171
|
+
let(:dst_file) { File.join(dst_dir, 'dest_test_file') }
|
172
|
+
|
132
173
|
before do
|
133
|
-
|
134
|
-
FileUtils.
|
135
|
-
|
136
|
-
@expanded_src_name = "#{@home}/Source/foo.zip"
|
174
|
+
FileUtils.mkdir_p(File.dirname(src_file))
|
175
|
+
FileUtils.touch(src_file)
|
176
|
+
end
|
137
177
|
|
138
|
-
|
139
|
-
|
140
|
-
@expanded_dst_name = "#{@home}/Destination/bar.zip"
|
178
|
+
after do
|
179
|
+
FileUtils.rm_rf([File.dirname(src_file), File.dirname(dst_file)])
|
141
180
|
end
|
142
181
|
|
143
182
|
it 'creates needed directories' do
|
144
|
-
expect(File.directory?(
|
145
|
-
|
146
|
-
|
183
|
+
expect(File.directory?(dst_dir)).to be(false)
|
184
|
+
|
185
|
+
maid.rename(src_file, dst_file)
|
186
|
+
|
187
|
+
expect(File.directory?(dst_dir)).to be(true)
|
147
188
|
end
|
148
189
|
|
149
190
|
it 'moves the file from the source to the destination' do
|
150
|
-
expect(File.exist?(
|
151
|
-
expect(File.exist?(
|
152
|
-
|
153
|
-
|
154
|
-
|
191
|
+
expect(File.exist?(src_file)).to be(true)
|
192
|
+
expect(File.exist?(dst_file)).to be(false)
|
193
|
+
|
194
|
+
maid.rename(src_file, dst_file)
|
195
|
+
|
196
|
+
expect(File.exist?(src_file)).to be(false)
|
197
|
+
expect(File.exist?(dst_file)).to be(true)
|
155
198
|
end
|
156
199
|
|
157
200
|
context 'given the target already exists' do
|
201
|
+
let(:src_file) { File.join(test_basedir, 'test_file') }
|
202
|
+
let(:dst_dir) { File.join(test_basedir, 'dest') }
|
203
|
+
let(:dst_file) { File.join(dst_dir, 'dest_test_file') }
|
204
|
+
|
158
205
|
before do
|
159
|
-
FileUtils.mkdir_p(
|
160
|
-
FileUtils.touch(
|
206
|
+
FileUtils.mkdir_p(File.dirname(src_file))
|
207
|
+
FileUtils.touch(src_file)
|
208
|
+
# Necessary to have different mtimes, they're identical otherwise
|
209
|
+
sleep 0.05
|
210
|
+
FileUtils.mkdir_p(File.dirname(dst_file))
|
211
|
+
FileUtils.touch(dst_file)
|
212
|
+
|
213
|
+
maid.rename(src_file, dst_file)
|
214
|
+
end
|
215
|
+
|
216
|
+
after do
|
217
|
+
FileUtils.rm_rf([File.dirname(src_file), File.dirname(dst_file)])
|
161
218
|
end
|
162
219
|
|
163
220
|
it 'does not move' do
|
164
|
-
expect(
|
221
|
+
expect(File.stat(src_file).mtime).not_to eq(File.stat(dst_file).mtime)
|
222
|
+
end
|
165
223
|
|
166
|
-
|
224
|
+
it 'logs a message' do
|
225
|
+
expect(File.read(logfile)).to match(/WARN.*skipping rename.*overwrite/)
|
167
226
|
end
|
168
227
|
end
|
169
228
|
end
|
170
229
|
|
171
230
|
describe '#trash' do
|
231
|
+
let(:src_file) { File.join(test_basedir, 'test_file') }
|
232
|
+
let(:trash_file) { File.join(maid.trash_path, File.basename(src_file)) }
|
233
|
+
let(:dst_dir) { File.join(test_basedir, 'dest') }
|
234
|
+
let(:dst_file) { File.join(dst_dir, File.basename(src_file)) }
|
235
|
+
|
172
236
|
before do
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
@src_file = File.join(@src_dir, @file_name)
|
177
|
-
FileUtils.mkdir_p(File.expand_path(@src_dir))
|
178
|
-
FileUtils.touch(File.expand_path(@src_file))
|
237
|
+
FileUtils.mkdir_p(File.dirname(src_file))
|
238
|
+
FileUtils.touch(src_file)
|
239
|
+
end
|
179
240
|
|
180
|
-
|
241
|
+
after do
|
242
|
+
FileUtils.rm_rf([File.dirname(src_file), File.dirname(dst_file)])
|
181
243
|
end
|
182
244
|
|
183
245
|
it 'moves the path to the trash' do
|
184
|
-
|
185
|
-
expect(File.exist?(
|
246
|
+
maid.trash(src_file)
|
247
|
+
expect(File.exist?(File.join(maid.trash_path, File.basename(src_file)))).to be(true)
|
186
248
|
end
|
187
249
|
|
188
250
|
it 'uses a safe path if the target exists' do
|
189
251
|
# Without an offset, ISO8601 parses to local time, which is what we want here.
|
190
252
|
Timecop.freeze(Time.parse('2011-05-22T16:53:52')) do
|
191
|
-
FileUtils.touch(
|
192
|
-
|
193
|
-
new_trash_file = File.join(
|
253
|
+
FileUtils.touch(trash_file)
|
254
|
+
maid.trash(src_file)
|
255
|
+
new_trash_file = File.join(maid.trash_path, File.basename(src_file) + ' 2011-05-22-16-53-52')
|
256
|
+
|
194
257
|
expect(File.exist?(new_trash_file)).to be(true)
|
195
258
|
end
|
196
259
|
end
|
197
260
|
|
198
|
-
|
199
|
-
|
200
|
-
second_src_file = File.join(@src_dir, second_file_name)
|
201
|
-
FileUtils.touch(File.expand_path(second_src_file))
|
202
|
-
src_files = [@src_file, second_src_file]
|
203
|
-
second_trash_file = File.join(@trash_path, second_file_name)
|
261
|
+
context 'with multiple files' do
|
262
|
+
let(:src_file2) { "#{src_file}_2" }
|
204
263
|
|
205
|
-
|
264
|
+
before do
|
265
|
+
FileUtils.touch(File.expand_path(src_file2))
|
266
|
+
maid.trash([src_file, src_file2])
|
267
|
+
end
|
206
268
|
|
207
|
-
|
208
|
-
|
209
|
-
|
269
|
+
after do
|
270
|
+
FileUtils.rm_rf([src_file, src_file2])
|
271
|
+
trash_file = File.join(maid.trash_path, File.basename(src_file))
|
272
|
+
trash_file2 = File.join(maid.trash_path, File.basename(src_file2))
|
273
|
+
FileUtils.rm_rf([trash_file, trash_file2])
|
274
|
+
end
|
210
275
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
expect(File.exist?(@trash_file)).not_to be(true)
|
276
|
+
it 'deletes all files' do
|
277
|
+
expect(File.exist?(File.join(maid.trash_path, File.basename(src_file)))).to be(true)
|
278
|
+
expect(File.exist?(File.join(maid.trash_path, File.basename(src_file2)))).to be(true)
|
279
|
+
end
|
216
280
|
end
|
217
281
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
282
|
+
context 'when given the `:remove_over` option' do
|
283
|
+
context 'with files larger than :remove_over' do
|
284
|
+
let(:trash_file) { File.join(maid.trash_path, File.basename(src_file)) }
|
285
|
+
|
286
|
+
before do
|
287
|
+
allow(maid).to receive(:disk_usage).and_return(1025)
|
288
|
+
|
289
|
+
maid.trash(src_file, remove_over: 1.mb)
|
290
|
+
end
|
291
|
+
|
292
|
+
after do
|
293
|
+
FileUtils.rm_rf(trash_file)
|
294
|
+
end
|
295
|
+
|
296
|
+
it 'removes matching files' do
|
297
|
+
expect(File.exist?(src_file)).not_to be(true)
|
298
|
+
end
|
299
|
+
|
300
|
+
it "doesn't move file to trash" do
|
301
|
+
expect(File.exist?(File.join(maid.trash_path, File.basename(src_file)))).not_to be(true)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
context 'with files smaller than :remove_over' do
|
306
|
+
before do
|
307
|
+
allow(maid).to receive(:disk_usage).and_return(1023)
|
308
|
+
|
309
|
+
maid.trash(src_file, remove_over: 1.mb)
|
310
|
+
end
|
311
|
+
|
312
|
+
it "doesn't delete them" do
|
313
|
+
expect(File.exist?(File.join(maid.trash_path, File.basename(src_file)))).to be(true)
|
314
|
+
end
|
315
|
+
end
|
222
316
|
end
|
223
317
|
end
|
224
318
|
|
225
319
|
describe '#remove' do
|
320
|
+
let(:src_file) { File.join(test_basedir, 'test_file') }
|
321
|
+
|
226
322
|
before do
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
FileUtils.mkdir_p(File.expand_path(@src_dir))
|
231
|
-
FileUtils.touch(File.expand_path(@src_file))
|
323
|
+
FileUtils.mkdir_p(File.dirname(src_file))
|
324
|
+
FileUtils.touch(src_file)
|
325
|
+
FileUtils.rm_rf(logfile)
|
232
326
|
end
|
233
327
|
|
234
|
-
|
235
|
-
|
236
|
-
expect(File.exist?(@src_file)).to be(false)
|
328
|
+
after do
|
329
|
+
FileUtils.rm_rf(File.dirname(src_file))
|
237
330
|
end
|
238
331
|
|
239
|
-
|
240
|
-
|
241
|
-
|
332
|
+
context 'with the default options' do
|
333
|
+
before do
|
334
|
+
maid.remove(src_file)
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'removes expanded paths, passing options' do
|
338
|
+
expect(File.exist?(src_file)).to be(false)
|
339
|
+
end
|
340
|
+
|
341
|
+
it 'logs the remove' do
|
342
|
+
expect(File.read(logfile)).to match(/INFO.*Removing #{src_file}/)
|
343
|
+
end
|
242
344
|
end
|
243
345
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
346
|
+
context 'with the :secure option set' do
|
347
|
+
before do
|
348
|
+
allow(FileUtils).to receive(:rm_r).and_call_original
|
349
|
+
|
350
|
+
maid.remove(src_file, { secure: true })
|
351
|
+
end
|
352
|
+
|
353
|
+
it 'passes it to FileUtils.rm_r' do
|
354
|
+
expected_args = [File.expand_path(src_file), hash_including(secure: true)]
|
355
|
+
|
356
|
+
expect(FileUtils).to have_received(:rm_r).with(*expected_args)
|
357
|
+
end
|
248
358
|
end
|
249
359
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
360
|
+
context 'with for :force option set' do
|
361
|
+
before do
|
362
|
+
allow(FileUtils).to receive(:rm_r).and_call_original
|
363
|
+
|
364
|
+
maid.remove(src_file, { force: true })
|
365
|
+
end
|
366
|
+
|
367
|
+
it 'sets the force option' do
|
368
|
+
expected_args = [File.expand_path(src_file), hash_including(force: true)]
|
369
|
+
|
370
|
+
expect(FileUtils).to have_received(:rm_r).with(*expected_args)
|
371
|
+
end
|
254
372
|
end
|
255
373
|
|
256
|
-
|
257
|
-
|
258
|
-
FileUtils.touch(File.expand_path(second_src_file))
|
259
|
-
@src_files = [@src_file, second_src_file]
|
374
|
+
context 'with multiple paths' do
|
375
|
+
let(:src_files) { [src_file, "#{src_file}_2"] }
|
260
376
|
|
261
|
-
|
262
|
-
|
263
|
-
|
377
|
+
before do
|
378
|
+
FileUtils.touch(src_files)
|
379
|
+
|
380
|
+
maid.remove(src_files)
|
381
|
+
end
|
382
|
+
|
383
|
+
it 'deletes every path' do
|
384
|
+
src_files.each do |file|
|
385
|
+
expect(File.exist?(file)).to be(false)
|
386
|
+
end
|
387
|
+
end
|
264
388
|
end
|
265
389
|
end
|
266
390
|
|
@@ -299,69 +423,96 @@ module Maid
|
|
299
423
|
end
|
300
424
|
|
301
425
|
context 'with multiple directories' do
|
426
|
+
let(:src_file) { File.join(test_basedir, 'multi', 'test_file') }
|
427
|
+
let(:src_file2) { "#{src_file}_2" }
|
428
|
+
|
302
429
|
before do
|
303
|
-
|
304
|
-
FileUtils.touch(
|
305
|
-
FileUtils.
|
306
|
-
FileUtils.touch(@other_file)
|
430
|
+
FileUtils.mkdir_p(File.dirname(src_file))
|
431
|
+
FileUtils.touch(src_file)
|
432
|
+
FileUtils.touch(src_file2)
|
307
433
|
end
|
308
434
|
|
309
435
|
it 'lists files in directories when using regexp-like glob patterns' do
|
310
|
-
expect(
|
436
|
+
expect(maid.dir("#{File.dirname(src_file)}/*")).to eq([src_file, src_file2])
|
311
437
|
end
|
312
438
|
|
313
439
|
it 'lists files in directories when using recursive glob patterns' do
|
314
|
-
|
440
|
+
# TODO: Once we ditch Ruby 2.7, we can do this instead:
|
441
|
+
# expect(maid.dir("#{File.dirname(src_file, 2)}/**/test_*")).to eq([src_file, src_file2])
|
442
|
+
expect(maid.dir("#{File.dirname(src_file)}/../**/test_*")).to eq([src_file, src_file2])
|
315
443
|
end
|
316
444
|
end
|
317
445
|
end
|
318
446
|
|
319
447
|
describe '#files' do
|
320
|
-
|
321
|
-
@file = (@dir = "#{@home}/Downloads") + '/foo.zip'
|
322
|
-
FileUtils.mkdir_p(@dir)
|
323
|
-
FileUtils.mkdir(@dir + '/notfile')
|
324
|
-
end
|
448
|
+
let(:file) { File.join(test_basedir, 'test_file') }
|
325
449
|
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
450
|
+
context 'with a single file' do
|
451
|
+
before do
|
452
|
+
FileUtils.mkdir_p(File.dirname(file))
|
453
|
+
FileUtils.touch(file)
|
454
|
+
end
|
330
455
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
456
|
+
after do
|
457
|
+
FileUtils.rm_rf(File.dirname(file))
|
458
|
+
end
|
459
|
+
|
460
|
+
it 'lists only files in a directory' do
|
461
|
+
expect(maid.files("#{File.dirname(file)}/*_file")).to eq([file])
|
462
|
+
end
|
336
463
|
end
|
337
464
|
|
338
465
|
context 'with multiple files' do
|
466
|
+
let(:first_file) { "#{file}_1" }
|
467
|
+
let(:second_file) { "#{file}_2" }
|
468
|
+
|
339
469
|
before do
|
340
|
-
|
341
|
-
FileUtils.touch(
|
342
|
-
FileUtils.touch(
|
470
|
+
FileUtils.mkdir_p(File.dirname(first_file))
|
471
|
+
FileUtils.touch(first_file)
|
472
|
+
FileUtils.touch(second_file)
|
473
|
+
end
|
474
|
+
|
475
|
+
after do
|
476
|
+
FileUtils.rm_rf(File.dirname(first_file))
|
477
|
+
FileUtils.rm_rf(File.dirname(second_file))
|
343
478
|
end
|
344
479
|
|
345
480
|
it 'list files in all provided globs' do
|
346
|
-
expect(
|
481
|
+
expect(maid.dir(["#{File.dirname(first_file)}/*_1",
|
482
|
+
"#{File.dirname(second_file)}/*_2",])).to eq([first_file, second_file])
|
347
483
|
end
|
348
484
|
|
349
485
|
it 'lists files when using regexp-like glob patterns' do
|
350
|
-
expect(@maid.dir(
|
486
|
+
expect(@maid.dir("#{File.dirname(first_file)}/*{1,2}")).to eq([first_file, second_file])
|
351
487
|
end
|
352
488
|
end
|
353
489
|
|
354
490
|
context 'with multiple directories' do
|
491
|
+
let(:first_file) { "#{file}_1" }
|
492
|
+
let(:second_file) { "#{file}_2" }
|
493
|
+
let(:first_dir) { "#{File.dirname(file)}/test_dir_1" }
|
494
|
+
let(:second_dir) { "#{File.dirname(file)}/test_dir_2" }
|
495
|
+
let(:test_dir) { File.dirname(file) }
|
496
|
+
|
355
497
|
before do
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
498
|
+
# Building this:
|
499
|
+
# "#{test_dir}/"
|
500
|
+
# ├── test_dir_1
|
501
|
+
# ├── test_dir_2
|
502
|
+
# ├── test_first_file
|
503
|
+
# └── test_second_file
|
504
|
+
FileUtils.mkdir_p(first_dir)
|
505
|
+
FileUtils.mkdir_p(second_dir)
|
506
|
+
FileUtils.touch(first_file)
|
507
|
+
FileUtils.touch(second_file)
|
361
508
|
end
|
362
509
|
|
363
|
-
|
364
|
-
|
510
|
+
after do
|
511
|
+
FileUtils.rm_rf(test_dir)
|
512
|
+
end
|
513
|
+
|
514
|
+
it 'only lists the files' do
|
515
|
+
expect(maid.dir("#{test_dir}/**/*{file}*")).to eq([first_file, second_file])
|
365
516
|
end
|
366
517
|
end
|
367
518
|
end
|
@@ -397,23 +548,27 @@ module Maid
|
|
397
548
|
|
398
549
|
describe '#find' do
|
399
550
|
before do
|
400
|
-
@dir = File.join(
|
401
|
-
@
|
402
|
-
@file = File.join(@dir, @
|
551
|
+
@dir = File.join(test_basedir, 'Source')
|
552
|
+
@filename = 'foo.zip'
|
553
|
+
@file = File.join(@dir, @filename)
|
403
554
|
FileUtils.mkdir_p(File.expand_path(@dir))
|
404
555
|
FileUtils.touch(File.expand_path(@file))
|
405
556
|
@dir_expand_path = File.expand_path(@dir)
|
406
|
-
@
|
557
|
+
@fileexpand_path = File.expand_path(@file)
|
558
|
+
end
|
559
|
+
|
560
|
+
after do
|
561
|
+
FileUtils.rm_rf(@dir)
|
407
562
|
end
|
408
563
|
|
409
564
|
it 'delegates to Find.find with an expanded path' do
|
410
565
|
f = ->(arg) {}
|
411
|
-
expect(Find).to receive(:find).with(@
|
566
|
+
expect(Find).to receive(:find).with(@fileexpand_path, &f)
|
412
567
|
@maid.find(@file, &f)
|
413
568
|
end
|
414
569
|
|
415
570
|
it "returns an array of all the files' names when no block is given" do
|
416
|
-
expect(@maid.find(@dir)).to contain_exactly(@dir_expand_path, @
|
571
|
+
expect(@maid.find(@dir)).to contain_exactly(@dir_expand_path, @fileexpand_path)
|
417
572
|
end
|
418
573
|
end
|
419
574
|
|
@@ -477,22 +632,26 @@ module Maid
|
|
477
632
|
|
478
633
|
context 'when the file does not exist' do
|
479
634
|
it 'raises an error' do
|
480
|
-
expect(@maid).to receive(:cmd).and_return(
|
635
|
+
expect(@maid).to receive(:cmd).and_return("du: cannot access `foo.zip': No such file or directory")
|
481
636
|
expect { @maid.disk_usage('foo.zip') }.to raise_error(RuntimeError)
|
482
637
|
end
|
483
638
|
end
|
484
639
|
end
|
485
640
|
|
486
641
|
describe '#created_at' do
|
642
|
+
let(:file) { File.join(test_basedir, 'test_file') }
|
643
|
+
|
487
644
|
before do
|
488
|
-
|
645
|
+
FileUtils.mkdir_p(File.dirname(file))
|
646
|
+
FileUtils.touch(file)
|
647
|
+
end
|
648
|
+
|
649
|
+
after do
|
650
|
+
FileUtils.rm_rf(File.dirname(file))
|
489
651
|
end
|
490
652
|
|
491
653
|
it 'gives the created time of the file' do
|
492
|
-
|
493
|
-
FileUtils.touch(File.expand_path(@path))
|
494
|
-
expect(@maid.created_at(@path)).to eq(Time.now)
|
495
|
-
end
|
654
|
+
expect(maid.created_at(file)).to eq(File.stat(file).ctime)
|
496
655
|
end
|
497
656
|
end
|
498
657
|
|
@@ -613,15 +772,21 @@ module Maid
|
|
613
772
|
end
|
614
773
|
|
615
774
|
describe '#copy' do
|
616
|
-
let(:src_file) { File.join(
|
775
|
+
let(:src_file) { File.join(test_basedir, 'src', 'test_file') }
|
617
776
|
let(:src_dir) { File.dirname(src_file) }
|
618
|
-
let(:dst_file) { File.join(
|
777
|
+
let(:dst_file) { File.join(test_basedir, 'dest', 'test_file') }
|
619
778
|
let(:dst_dir) { File.dirname(dst_file) }
|
620
779
|
|
621
780
|
before do
|
622
781
|
FileUtils.mkdir_p(File.expand_path(src_dir))
|
623
782
|
FileUtils.touch(File.expand_path(src_file))
|
624
783
|
FileUtils.mkdir_p(File.expand_path(dst_dir))
|
784
|
+
FileUtils.rm_rf(logfile)
|
785
|
+
end
|
786
|
+
|
787
|
+
after do
|
788
|
+
FileUtils.rm_rf(src_dir)
|
789
|
+
FileUtils.rm_rf(dst_dir)
|
625
790
|
end
|
626
791
|
|
627
792
|
it 'copies expanded paths, passing file_options' do
|
@@ -629,16 +794,23 @@ module Maid
|
|
629
794
|
expect(File.exist?(File.expand_path(dst_file))).to be_truthy
|
630
795
|
end
|
631
796
|
|
632
|
-
|
633
|
-
|
634
|
-
|
797
|
+
context "when destination doesn't exist" do
|
798
|
+
before do
|
799
|
+
maid.copy(src_file, dst_dir)
|
800
|
+
end
|
801
|
+
|
802
|
+
it 'logs the copy' do
|
803
|
+
expect(File.read(logfile)).to match(/INFO.*cp.*#{dst_dir}/)
|
804
|
+
end
|
635
805
|
end
|
636
806
|
|
637
|
-
|
638
|
-
|
639
|
-
|
807
|
+
context 'when destination exists' do
|
808
|
+
it 'does not copy if the target already exists' do
|
809
|
+
FileUtils.touch(File.expand_path(dst_file))
|
810
|
+
expect(@logger).to receive(:warn)
|
640
811
|
|
641
|
-
|
812
|
+
@maid.copy(src_file, dst_dir)
|
813
|
+
end
|
642
814
|
end
|
643
815
|
|
644
816
|
context 'with multiple `from` paths' do
|
@@ -658,22 +830,10 @@ module Maid
|
|
658
830
|
end
|
659
831
|
end
|
660
832
|
end
|
661
|
-
end
|
662
|
-
|
663
|
-
describe Tools, fakefs: false do
|
664
|
-
let(:file_fixtures_path) { File.expand_path(File.dirname(__FILE__) + '../../../fixtures/files/') }
|
665
|
-
let(:file_fixtures_glob) { "#{file_fixtures_path}/*" }
|
666
|
-
let(:image_path) { File.join(file_fixtures_path, 'ruby.jpg') }
|
667
|
-
let(:unknown_path) { File.join(file_fixtures_path, 'unknown.foo') }
|
668
|
-
|
669
|
-
before do
|
670
|
-
@logger = double('Logger').as_null_object
|
671
|
-
@maid = Maid.new(logger: @logger)
|
672
|
-
end
|
673
833
|
|
674
834
|
describe '#dupes_in' do
|
675
835
|
it 'lists duplicate files in arrays' do
|
676
|
-
dupes =
|
836
|
+
dupes = maid.dupes_in(filefixtures_glob)
|
677
837
|
expect(dupes.first).to be_a(Array)
|
678
838
|
|
679
839
|
basenames = dupes.flatten.map { |p| File.basename(p) }
|
@@ -683,7 +843,7 @@ module Maid
|
|
683
843
|
|
684
844
|
describe '#verbose_dupes_in' do
|
685
845
|
it 'lists all but the shortest-named dupe' do
|
686
|
-
dupes =
|
846
|
+
dupes = maid.verbose_dupes_in(filefixtures_glob)
|
687
847
|
|
688
848
|
basenames = dupes.flatten.map { |p| File.basename(p) }
|
689
849
|
expect(basenames).to eq(%w[bar.zip foo.zip])
|
@@ -694,13 +854,13 @@ module Maid
|
|
694
854
|
it 'lists all but the oldest dupe' do
|
695
855
|
# FIXME: Broken on Ruby 2.1.0-preview2, maybe because of FakeFS
|
696
856
|
#
|
697
|
-
# oldest_path = "#{
|
857
|
+
# oldest_path = "#{filefixtures_path}/foo.zip"
|
698
858
|
# FileUtils.touch(oldest_path, :mtime => Time.new(1970, 1, 1))
|
699
859
|
|
700
|
-
FileUtils.touch("#{
|
701
|
-
FileUtils.touch("#{
|
860
|
+
FileUtils.touch("#{filefixtures_path}/bar.zip")
|
861
|
+
FileUtils.touch("#{filefixtures_path}/1.zip")
|
702
862
|
|
703
|
-
dupes =
|
863
|
+
dupes = maid.newest_dupes_in(filefixtures_glob)
|
704
864
|
|
705
865
|
basenames = dupes.flatten.map { |p| File.basename(p) }
|
706
866
|
expect(basenames).to match_array(%w[bar.zip 1.zip])
|
@@ -710,28 +870,28 @@ module Maid
|
|
710
870
|
describe '#dimensions_px' do
|
711
871
|
context 'given a JPEG image' do
|
712
872
|
it 'reports the known size' do
|
713
|
-
expect(
|
873
|
+
expect(maid.dimensions_px(image_path)).to eq([32, 32])
|
714
874
|
end
|
715
875
|
end
|
716
876
|
|
717
877
|
context 'given an unknown type' do
|
718
878
|
it 'returns nil' do
|
719
|
-
expect(
|
879
|
+
expect(maid.dimensions_px(unknown_path)).to be_nil
|
720
880
|
end
|
721
881
|
end
|
722
882
|
end
|
723
883
|
|
724
884
|
describe '#location_city' do
|
725
885
|
context 'given a JPEG image' do
|
726
|
-
it 'reports the known location' do
|
727
|
-
sydney_path = File.join(
|
728
|
-
expect(
|
886
|
+
it 'reports the known location', vcr: { record: :new_episodes } do
|
887
|
+
sydney_path = File.join(filefixtures_path, 'sydney.jpg')
|
888
|
+
expect(maid.location_city(sydney_path)).to eq('Sydney, New South Wales, AU')
|
729
889
|
end
|
730
890
|
end
|
731
891
|
|
732
892
|
context 'given an unknown type' do
|
733
893
|
it 'returns nil' do
|
734
|
-
expect(
|
894
|
+
expect(maid.location_city(unknown_path)).to be_nil
|
735
895
|
end
|
736
896
|
end
|
737
897
|
end
|
@@ -739,13 +899,13 @@ module Maid
|
|
739
899
|
describe '#mime_type' do
|
740
900
|
context 'given a JPEG image' do
|
741
901
|
it 'reports "image/jpeg"' do
|
742
|
-
expect(
|
902
|
+
expect(maid.mime_type(image_path)).to eq('image/jpeg')
|
743
903
|
end
|
744
904
|
end
|
745
905
|
|
746
906
|
context 'given an unknown type' do
|
747
907
|
it 'returns nil' do
|
748
|
-
expect(
|
908
|
+
expect(maid.mime_type(unknown_path)).to be_nil
|
749
909
|
end
|
750
910
|
end
|
751
911
|
end
|
@@ -753,13 +913,13 @@ module Maid
|
|
753
913
|
describe '#media_type' do
|
754
914
|
context 'given a JPEG image' do
|
755
915
|
it 'reports "image"' do
|
756
|
-
expect(
|
916
|
+
expect(maid.media_type(image_path)).to eq('image')
|
757
917
|
end
|
758
918
|
end
|
759
919
|
|
760
920
|
context 'given an unknown type' do
|
761
921
|
it 'returns nil' do
|
762
|
-
expect(
|
922
|
+
expect(maid.media_type(unknown_path)).to be_nil
|
763
923
|
end
|
764
924
|
end
|
765
925
|
end
|
@@ -767,7 +927,7 @@ module Maid
|
|
767
927
|
describe '#where_content_type' do
|
768
928
|
context 'given "image"' do
|
769
929
|
it 'only lists the fixture JPEGs' do
|
770
|
-
matches =
|
930
|
+
matches = maid.where_content_type(maid.dir(filefixtures_glob), 'image')
|
771
931
|
|
772
932
|
expect(matches.length).to eq(2)
|
773
933
|
expect(matches.first).to end_with('spec/fixtures/files/ruby.jpg')
|
@@ -787,19 +947,19 @@ module Maid
|
|
787
947
|
end
|
788
948
|
|
789
949
|
it 'returns false for non-empty directories' do
|
790
|
-
expect(
|
950
|
+
expect(maid.tree_empty?(@non_empty_dir)).to be(false)
|
791
951
|
end
|
792
952
|
|
793
953
|
it 'returns true for empty directories' do
|
794
|
-
expect(
|
954
|
+
expect(maid.tree_empty?(@empty_dir)).to be(true)
|
795
955
|
end
|
796
956
|
|
797
957
|
it 'returns true for directories with empty subdirectories' do
|
798
|
-
expect(
|
958
|
+
expect(maid.tree_empty?(@parent_of_empty_dir)).to be(true)
|
799
959
|
end
|
800
960
|
|
801
961
|
it 'returns false for directories with non-empty subdirectories' do
|
802
|
-
expect(
|
962
|
+
expect(maid.tree_empty?(@root)).to be(false)
|
803
963
|
end
|
804
964
|
end
|
805
965
|
|
@@ -837,45 +997,44 @@ module Maid
|
|
837
997
|
'g/y/a/c',
|
838
998
|
].sort
|
839
999
|
|
840
|
-
expect(
|
1000
|
+
expect(maid.ignore_child_dirs(src).sort).to eq(expected)
|
841
1001
|
end
|
842
1002
|
end
|
843
1003
|
end
|
844
1004
|
|
845
1005
|
describe 'OSX tag support', fakefs: false do
|
846
|
-
let(:
|
1006
|
+
let(:test_basedir) { '/tmp/maid-specs' }
|
1007
|
+
let(:test_file) { File.join(test_basedir, 'tag.zip') }
|
847
1008
|
let(:test_dir) { File.dirname(test_file) }
|
848
|
-
let(:
|
849
|
-
let(:original_file_options) {
|
1009
|
+
let(:filename) { File.basename(test_file) }
|
1010
|
+
let(:original_file_options) { maid.file_options.clone }
|
1011
|
+
let(:maid) { Maid.new(log_device: File::NULL) }
|
850
1012
|
|
851
1013
|
before do
|
852
|
-
@logger = double('Logger').as_null_object
|
853
|
-
@maid = Maid.new(logger: @logger)
|
854
|
-
|
855
1014
|
FileUtils.mkdir_p(test_dir)
|
856
1015
|
FileUtils.touch(test_file)
|
857
|
-
|
1016
|
+
maid.file_options[:noop] = false
|
858
1017
|
end
|
859
1018
|
|
860
1019
|
after do
|
861
1020
|
FileUtils.rm_r(test_dir)
|
862
|
-
|
1021
|
+
maid.file_options[:noop] = original_file_options[:noop]
|
863
1022
|
end
|
864
1023
|
|
865
1024
|
describe '#tags' do
|
866
1025
|
it 'returns tags from a file that has one' do
|
867
1026
|
if Platform.has_tag_available?
|
868
|
-
|
869
|
-
|
870
|
-
expect(
|
1027
|
+
maid.file_options[:noop] = false
|
1028
|
+
maid.add_tag(test_file, 'Test')
|
1029
|
+
expect(maid.tags(test_file)).to eq(['Test'])
|
871
1030
|
end
|
872
1031
|
end
|
873
1032
|
|
874
1033
|
it 'returns tags from a file that has serveral tags' do
|
875
1034
|
if Platform.has_tag_available?
|
876
|
-
|
877
|
-
|
878
|
-
expect(
|
1035
|
+
maid.file_options[:noop] = false
|
1036
|
+
maid.add_tag(test_file, %w[Test Twice])
|
1037
|
+
expect(maid.tags(test_file)).to eq(%w[Test Twice])
|
879
1038
|
end
|
880
1039
|
end
|
881
1040
|
end
|
@@ -883,22 +1042,22 @@ module Maid
|
|
883
1042
|
describe '#has_tags?' do
|
884
1043
|
it 'returns true for a file with tags' do
|
885
1044
|
if Platform.has_tag_available?
|
886
|
-
|
887
|
-
expect(
|
1045
|
+
maid.add_tag(test_file, 'Test')
|
1046
|
+
expect(maid.has_tags?(test_file)).to be(true)
|
888
1047
|
end
|
889
1048
|
end
|
890
1049
|
|
891
1050
|
it 'returns false for a file without tags' do
|
892
|
-
expect(
|
1051
|
+
expect(maid.has_tags?(test_file)).to be(false)
|
893
1052
|
end
|
894
1053
|
end
|
895
1054
|
|
896
1055
|
describe '#contains_tag?' do
|
897
1056
|
it 'returns true a file with the given tag' do
|
898
1057
|
if Platform.has_tag_available?
|
899
|
-
|
900
|
-
expect(
|
901
|
-
expect(
|
1058
|
+
maid.add_tag(test_file, 'Test')
|
1059
|
+
expect(maid.contains_tag?(test_file, 'Test')).to be(true)
|
1060
|
+
expect(maid.contains_tag?(test_file, 'Not there')).to be(false)
|
902
1061
|
end
|
903
1062
|
end
|
904
1063
|
end
|
@@ -906,8 +1065,8 @@ module Maid
|
|
906
1065
|
describe '#add_tag' do
|
907
1066
|
it 'adds the given tag to a file' do
|
908
1067
|
if Platform.has_tag_available?
|
909
|
-
|
910
|
-
expect(
|
1068
|
+
maid.add_tag(test_file, 'Test')
|
1069
|
+
expect(maid.contains_tag?(test_file, 'Test')).to be(true)
|
911
1070
|
end
|
912
1071
|
end
|
913
1072
|
end
|
@@ -915,10 +1074,10 @@ module Maid
|
|
915
1074
|
describe '#remove_tag' do
|
916
1075
|
it 'removes the given tag from a file' do
|
917
1076
|
if Platform.has_tag_available?
|
918
|
-
|
919
|
-
expect(
|
920
|
-
|
921
|
-
expect(
|
1077
|
+
maid.add_tag(test_file, 'Test')
|
1078
|
+
expect(maid.contains_tag?(test_file, 'Test')).to be(true)
|
1079
|
+
maid.remove_tag(test_file, 'Test')
|
1080
|
+
expect(maid.contains_tag?(test_file, 'Test')).to be(false)
|
922
1081
|
end
|
923
1082
|
end
|
924
1083
|
end
|
@@ -926,11 +1085,11 @@ module Maid
|
|
926
1085
|
describe '#set_tag' do
|
927
1086
|
it 'sets the given tags on a file' do
|
928
1087
|
if Platform.has_tag_available?
|
929
|
-
|
930
|
-
expect(
|
931
|
-
|
932
|
-
expect(
|
933
|
-
expect(
|
1088
|
+
maid.set_tag(test_file, 'Test')
|
1089
|
+
expect(maid.contains_tag?(test_file, 'Test')).to be(true)
|
1090
|
+
maid.set_tag(test_file, %w[Test Twice])
|
1091
|
+
expect(maid.contains_tag?(test_file, 'Test')).to be(true)
|
1092
|
+
expect(maid.contains_tag?(test_file, 'Twice')).to be(true)
|
934
1093
|
end
|
935
1094
|
end
|
936
1095
|
end
|