maid 0.10.0 → 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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +14 -0
  3. data/.github/workflows/coverage.yml +3 -3
  4. data/.github/workflows/lint.yml +9 -1
  5. data/.github/workflows/merge-gatekeeper.yml +20 -0
  6. data/.github/workflows/release.yml +13 -20
  7. data/.github/workflows/stale.yml +25 -0
  8. data/.github/workflows/test.yml +8 -7
  9. data/.gitignore +1 -1
  10. data/.release-please-manifest.json +1 -1
  11. data/.rubocop.yml +3 -1
  12. data/.rubocop_todo.yml +105 -107
  13. data/.ruby-version +1 -1
  14. data/CHANGELOG.md +16 -0
  15. data/Dockerfile +13 -0
  16. data/Gemfile.lock +226 -0
  17. data/Guardfile +2 -0
  18. data/README.md +82 -49
  19. data/Rakefile +9 -0
  20. data/fixtures/files/test_rules.rb +3 -0
  21. data/lib/maid/logger/logger.rb +63 -0
  22. data/lib/maid/maid.rb +6 -22
  23. data/lib/maid/repeat.rb +2 -2
  24. data/lib/maid/rule.rb +2 -2
  25. data/lib/maid/rule_container.rb +2 -2
  26. data/lib/maid/tools.rb +3 -3
  27. data/lib/maid/trash_migration.rb +2 -0
  28. data/lib/maid/version.rb +1 -1
  29. data/lib/maid/watch.rb +2 -2
  30. data/lib/maid.rb +3 -2
  31. data/maid.gemspec +12 -9
  32. data/release-please-config.json +18 -0
  33. data/script/docker-test +7 -0
  34. data/spec/fakefs_helper.rb +13 -0
  35. data/spec/lib/maid/logger/logger_spec.rb +64 -0
  36. data/spec/lib/maid/maid_spec.rb +113 -103
  37. data/spec/lib/maid/rake/single_rule_spec.rb +1 -1
  38. data/spec/lib/maid/tools_spec.rb +383 -224
  39. data/spec/lib/maid/trash_migration_spec.rb +7 -5
  40. data/spec/spec_helper.rb +9 -1
  41. metadata +89 -42
  42. data/Vagrantfile +0 -14
  43. data/script/vagrant-provision +0 -43
  44. data/script/vagrant-test +0 -7
  45. data/script/vagrant-test-all +0 -34
@@ -1,19 +1,32 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  module Maid
4
- # NOTE: Please use FakeFS instead of mocking and stubbing specific calls which happen to modify the filesystem.
5
- #
6
- # More info:
7
- #
8
- # * [FakeFS](https://github.com/defunkt/fakefs)
9
- describe Tools, fakefs: true do
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
- @logger = double('Logger').as_null_object
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('~', 'Source')
29
- @file_name = 'foo.zip'
30
- @src_file = File.join(@src_dir, @file_name)
31
- @dst_dir = File.join('~', 'Destination')
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, @file_name))
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
- second_file_name = 'bar.zip'
52
- second_src_file = File.join(@src_dir, second_file_name)
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, @file_name))
56
- second_dst_file = File.expand_path(File.join(@dst_dir, second_file_name))
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.rmdir(File.expand_path(@dst_dir))
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
- it 'does not overwrite when moving' do
69
- expect(FileUtils).not_to receive(:mv)
70
- expect(@logger).to receive(:warn).once
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
- another_file = "#{@src_file}.1"
73
- @maid.move([@src_file, another_file], @dst_dir)
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) { '/tmp/src/test_file' }
79
- let(:dst_dir) { '/tmp/dest/' }
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(@logger).to have_received(:info).with(/already/)
103
- expect(@logger).to have_received(:info).with(/anyway/)
104
- expect(@logger).not_to have_received(:info).with(/skipping/)
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(@logger).not_to have_received(:info).with(/anyway/)
121
- expect(@logger).to have_received(:info).with(/skipping/)
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
- @src_file = (@src_dir = '~/Source/') + (@file_name = 'foo.zip')
134
- FileUtils.mkdir_p(File.expand_path(@src_dir))
135
- FileUtils.touch(File.expand_path(@src_file))
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
- @dst_name = '~/Destination/bar.zip'
139
- @expanded_dst_dir = "#{@home}/Destination/"
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?(@expanded_dst_dir)).to be(false)
145
- @maid.rename(@src_file, @dst_name)
146
- expect(File.directory?(@expanded_dst_dir)).to be(true)
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?(@expanded_src_name)).to be(true)
151
- expect(File.exist?(@expanded_dst_name)).to be(false)
152
- @maid.rename(@src_file, @dst_name)
153
- expect(File.exist?(@expanded_src_name)).to be(false)
154
- expect(File.exist?(@expanded_dst_name)).to be(true)
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(@expanded_dst_dir)
160
- FileUtils.touch(@expanded_dst_name)
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(@logger).to receive(:warn)
221
+ expect(File.stat(src_file).mtime).not_to eq(File.stat(dst_file).mtime)
222
+ end
165
223
 
166
- @maid.rename(@src_file, @dst_name)
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
- @trash_path = @maid.trash_path
174
- @src_dir = File.join('~', 'Source/')
175
- @file_name = 'foo.zip'
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
- @trash_file = File.join(@trash_path, @file_name)
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
- @maid.trash(@src_file)
185
- expect(File.exist?(@trash_file)).to be(true)
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(@trash_file)
192
- @maid.trash(@src_file)
193
- new_trash_file = File.join(@trash_path, @file_name + ' 2011-05-22-16-53-52')
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
- it 'handles multiple paths' do
199
- second_file_name = 'bar.zip'
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
- @maid.trash(src_files)
264
+ before do
265
+ FileUtils.touch(File.expand_path(src_file2))
266
+ maid.trash([src_file, src_file2])
267
+ end
206
268
 
207
- expect(File.exist?(@trash_file)).to be(true)
208
- expect(File.exist?(second_trash_file)).to be(true)
209
- end
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
- it 'removes files greater then the remove option size' do
212
- allow(@maid).to receive(:disk_usage).and_return(1025)
213
- @maid.trash(@src_file, remove_over: 1.mb)
214
- expect(File.exist?(@src_file)).not_to be(true)
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
- it 'trashes files less then the remove option size' do
219
- allow(@maid).to receive(:disk_usage).and_return(1023)
220
- @maid.trash(@src_file, remove_over: 1.mb)
221
- expect(File.exist?(@trash_file)).to be(true)
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
- @src_dir = File.join('~', 'Source')
228
- @file_name = 'foo.zip'
229
- @src_file = File.join(@src_dir, @file_name)
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
- it 'removes expanded paths, passing options' do
235
- @maid.remove(@src_file)
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
- it 'logs the remove' do
240
- expect(@logger).to receive(:info)
241
- @maid.remove(@src_file)
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
- it 'sets the secure option' do
245
- @options = @maid.file_options.merge(secure: true)
246
- expect(FileUtils).to receive(:rm_r).with(File.expand_path(@src_file), @options)
247
- @maid.remove(@src_file, secure: true)
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
- it 'sets the force option' do
251
- @options = @maid.file_options.merge(force: true)
252
- expect(FileUtils).to receive(:rm_r).with(File.expand_path(@src_file), @options)
253
- @maid.remove(@src_file, force: true)
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
- it 'handles multiple paths' do
257
- second_src_file = File.join(@src_dir, 'bar.zip')
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
- @maid.remove(@src_files)
262
- expect(File.exist?(File.expand_path(@src_file))).to be(false)
263
- expect(File.exist?(File.expand_path(second_src_file))).to be(false)
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
- @other_file = "#{@home}/Desktop/bar.zip"
304
- FileUtils.touch(@file)
305
- FileUtils.mkdir_p(File.dirname(@other_file))
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(@maid.dir('~/{Desktop,Downloads}/*.zip')).to eq([@other_file, @file])
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
- expect(@maid.dir('~/**/*.zip')).to eq([@other_file, @file])
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
- before do
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
- it 'lists only files in a directory' do
327
- FileUtils.touch(@file)
328
- expect(@maid.files('~/Downloads/*.zip')).to eq([@file])
329
- end
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
- it 'lists multiple files in alphabetical order' do
332
- # It doesn't occur with `FakeFS` as far as I can tell, but Ubuntu (and possibly OS X) can give the results out
333
- # of lexical order. That makes using the `dry-run` output difficult to use.
334
- allow(Dir).to receive(:glob).and_return(%w[/home/foo/b.zip /home/foo/a.zip /home/foo/c.zip])
335
- expect(@maid.dir('~/Downloads/*.zip')).to eq(%w[/home/foo/a.zip /home/foo/b.zip /home/foo/c.zip])
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
- @other_file = "#{@dir}/qux.tgz"
341
- FileUtils.touch(@file)
342
- FileUtils.touch(@other_file)
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(@maid.dir(%w[~/Downloads/*.tgz ~/Downloads/*.zip])).to eq([@file, @other_file])
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('~/Downloads/*.{tgz,zip}')).to eq([@file, @other_file])
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
- @other_file = "#{@home}/Desktop/bar.zip"
357
- FileUtils.touch(@file)
358
- FileUtils.mkdir_p(File.dirname(@other_file))
359
- FileUtils.mkdir(@home + '/Desktop/notfile')
360
- FileUtils.touch(@other_file)
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
- it 'lists files in directories when using regexp-like glob patterns' do
364
- expect(@maid.dir('~/{Desktop,Downloads}/*.zip')).to eq([@other_file, @file])
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('~', 'Source')
401
- @file_name = 'foo.zip'
402
- @file = File.join(@dir, @file_name)
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
- @file_expand_path = File.expand_path(@file)
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(@file_expand_path, &f)
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, @file_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('du: cannot access `foo.zip\': No such file or directory')
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
- @path = '~/test.txt'
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
- Timecop.freeze(@now) do
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('~', 'Source', 'foo.zip') }
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('~', 'Destination', 'foo.zip') }
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
- it 'logs the copy' do
633
- expect(@logger).to receive(:info)
634
- @maid.copy(src_file, dst_dir)
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
- it 'does not copy if the target already exists' do
638
- FileUtils.touch(File.expand_path(dst_file))
639
- expect(@logger).to receive(:warn)
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
- @maid.copy(src_file, dst_dir)
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 = @maid.dupes_in(file_fixtures_glob)
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 = @maid.verbose_dupes_in(file_fixtures_glob)
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 = "#{file_fixtures_path}/foo.zip"
857
+ # oldest_path = "#{filefixtures_path}/foo.zip"
698
858
  # FileUtils.touch(oldest_path, :mtime => Time.new(1970, 1, 1))
699
859
 
700
- FileUtils.touch("#{file_fixtures_path}/bar.zip")
701
- FileUtils.touch("#{file_fixtures_path}/1.zip")
860
+ FileUtils.touch("#{filefixtures_path}/bar.zip")
861
+ FileUtils.touch("#{filefixtures_path}/1.zip")
702
862
 
703
- dupes = @maid.newest_dupes_in(file_fixtures_glob)
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,13 +870,13 @@ 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(@maid.dimensions_px(image_path)).to eq([32, 32])
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(@maid.dimensions_px(unknown_path)).to be_nil
879
+ expect(maid.dimensions_px(unknown_path)).to be_nil
720
880
  end
721
881
  end
722
882
  end
@@ -724,14 +884,14 @@ module Maid
724
884
  describe '#location_city' do
725
885
  context 'given a JPEG image' do
726
886
  it 'reports the known location', vcr: { record: :new_episodes } do
727
- sydney_path = File.join(file_fixtures_path, 'sydney.jpg')
728
- expect(@maid.location_city(sydney_path)).to eq('Sydney, New South Wales, AU')
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(@maid.location_city(unknown_path)).to be_nil
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(@maid.mime_type(image_path)).to eq('image/jpeg')
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(@maid.mime_type(unknown_path)).to be_nil
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(@maid.media_type(image_path)).to eq('image')
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(@maid.media_type(unknown_path)).to be_nil
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 = @maid.where_content_type(@maid.dir(file_fixtures_glob), 'image')
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(@maid.tree_empty?(@non_empty_dir)).to be(false)
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(@maid.tree_empty?(@empty_dir)).to be(true)
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(@maid.tree_empty?(@parent_of_empty_dir)).to be(true)
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(@maid.tree_empty?(@root)).to be(false)
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(@maid.ignore_child_dirs(src).sort).to eq(expected)
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(:test_file) { '~/.maid/test/tag.zip' }
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(:file_name) { File.basename(test_file) }
849
- let(:original_file_options) { @maid.file_options.clone }
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
- @maid.file_options[:noop] = false
1016
+ maid.file_options[:noop] = false
858
1017
  end
859
1018
 
860
1019
  after do
861
1020
  FileUtils.rm_r(test_dir)
862
- @maid.file_options[:noop] = original_file_options[:noop]
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
- @maid.file_options[:noop] = false
869
- @maid.add_tag(test_file, 'Test')
870
- expect(@maid.tags(test_file)).to eq(['Test'])
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
- @maid.file_options[:noop] = false
877
- @maid.add_tag(test_file, %w[Test Twice])
878
- expect(@maid.tags(test_file)).to eq(%w[Test Twice])
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
- @maid.add_tag(test_file, 'Test')
887
- expect(@maid.has_tags?(test_file)).to be(true)
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(@maid.has_tags?(test_file)).to be(false)
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
- @maid.add_tag(test_file, 'Test')
900
- expect(@maid.contains_tag?(test_file, 'Test')).to be(true)
901
- expect(@maid.contains_tag?(test_file, 'Not there')).to be(false)
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
- @maid.add_tag(test_file, 'Test')
910
- expect(@maid.contains_tag?(test_file, 'Test')).to be(true)
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
- @maid.add_tag(test_file, 'Test')
919
- expect(@maid.contains_tag?(test_file, 'Test')).to be(true)
920
- @maid.remove_tag(test_file, 'Test')
921
- expect(@maid.contains_tag?(test_file, 'Test')).to be(false)
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
- @maid.set_tag(test_file, 'Test')
930
- expect(@maid.contains_tag?(test_file, 'Test')).to be(true)
931
- @maid.set_tag(test_file, %w[Test Twice])
932
- expect(@maid.contains_tag?(test_file, 'Test')).to be(true)
933
- expect(@maid.contains_tag?(test_file, 'Twice')).to be(true)
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