listen 2.7.6 → 2.7.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -0
  3. data/.rspec +0 -0
  4. data/.rubocop.yml +0 -0
  5. data/.travis.yml +0 -0
  6. data/.yardopts +0 -0
  7. data/CHANGELOG.md +0 -0
  8. data/CONTRIBUTING.md +0 -0
  9. data/Gemfile +2 -0
  10. data/Guardfile +2 -0
  11. data/LICENSE.txt +0 -0
  12. data/README.md +0 -0
  13. data/Rakefile +0 -0
  14. data/lib/listen.rb +0 -0
  15. data/lib/listen/adapter.rb +0 -0
  16. data/lib/listen/adapter/base.rb +47 -21
  17. data/lib/listen/adapter/bsd.rb +31 -25
  18. data/lib/listen/adapter/darwin.rb +13 -12
  19. data/lib/listen/adapter/linux.rb +45 -36
  20. data/lib/listen/adapter/polling.rb +12 -7
  21. data/lib/listen/adapter/tcp.rb +9 -4
  22. data/lib/listen/adapter/windows.rb +46 -58
  23. data/lib/listen/change.rb +12 -8
  24. data/lib/listen/cli.rb +0 -0
  25. data/lib/listen/directory.rb +30 -22
  26. data/lib/listen/file.rb +9 -8
  27. data/lib/listen/listener.rb +35 -12
  28. data/lib/listen/options.rb +23 -0
  29. data/lib/listen/queue_optimizer.rb +23 -13
  30. data/lib/listen/record.rb +98 -21
  31. data/lib/listen/silencer.rb +21 -40
  32. data/lib/listen/tcp.rb +0 -0
  33. data/lib/listen/tcp/broadcaster.rb +0 -0
  34. data/lib/listen/tcp/message.rb +0 -0
  35. data/lib/listen/version.rb +1 -1
  36. data/listen.gemspec +0 -0
  37. data/spec/acceptance/listen_spec.rb +0 -0
  38. data/spec/acceptance/tcp_spec.rb +0 -0
  39. data/spec/lib/listen/adapter/base_spec.rb +17 -16
  40. data/spec/lib/listen/adapter/bsd_spec.rb +0 -0
  41. data/spec/lib/listen/adapter/darwin_spec.rb +11 -4
  42. data/spec/lib/listen/adapter/linux_spec.rb +20 -29
  43. data/spec/lib/listen/adapter/polling_spec.rb +15 -13
  44. data/spec/lib/listen/adapter/tcp_spec.rb +6 -3
  45. data/spec/lib/listen/adapter/windows_spec.rb +0 -0
  46. data/spec/lib/listen/adapter_spec.rb +0 -0
  47. data/spec/lib/listen/change_spec.rb +21 -27
  48. data/spec/lib/listen/directory_spec.rb +60 -42
  49. data/spec/lib/listen/file_spec.rb +16 -20
  50. data/spec/lib/listen/listener_spec.rb +136 -99
  51. data/spec/lib/listen/record_spec.rb +205 -62
  52. data/spec/lib/listen/silencer_spec.rb +44 -114
  53. data/spec/lib/listen/tcp/broadcaster_spec.rb +0 -0
  54. data/spec/lib/listen/tcp/listener_spec.rb +8 -5
  55. data/spec/lib/listen/tcp/message_spec.rb +0 -0
  56. data/spec/lib/listen_spec.rb +0 -0
  57. data/spec/spec_helper.rb +0 -0
  58. data/spec/support/acceptance_helper.rb +15 -4
  59. data/spec/support/fixtures_helper.rb +0 -0
  60. data/spec/support/platform_helper.rb +0 -0
  61. metadata +3 -2
@@ -1,21 +1,25 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Listen::Listener do
3
+ include Listen
4
+
5
+ describe Listener do
4
6
  subject { described_class.new(options) }
5
7
  let(:options) { {} }
6
- let(:registry) { instance_double(Celluloid::Registry, :[]= => true) }
8
+ let(:registry) { instance_double(Celluloid::Registry) }
7
9
 
8
10
  let(:supervisor) do
9
11
  instance_double(Celluloid::SupervisionGroup, add: true, pool: true)
10
12
  end
11
13
 
12
- let(:record) { instance_double(Listen::Record, terminate: true, build: true) }
13
- let(:silencer) { instance_double(Listen::Silencer, terminate: true) }
14
- let(:adapter) { instance_double(Listen::Adapter::Base, start: nil) }
14
+ let(:record) { instance_double(Record, terminate: true, build: true) }
15
+ let(:silencer) { instance_double(Silencer, configure: nil) }
16
+ let(:adapter) { instance_double(Adapter::Base, start: nil) }
17
+
15
18
  before do
19
+ allow(Listen::Silencer).to receive(:new) { silencer }
20
+
16
21
  allow(Celluloid::Registry).to receive(:new) { registry }
17
22
  allow(Celluloid::SupervisionGroup).to receive(:run!) { supervisor }
18
- allow(registry).to receive(:[]).with(:silencer) { silencer }
19
23
  allow(registry).to receive(:[]).with(:adapter) { adapter }
20
24
  allow(registry).to receive(:[]).with(:record) { record }
21
25
  end
@@ -75,31 +79,25 @@ describe Listen::Listener do
75
79
  allow(silencer).to receive(:silenced?) { false }
76
80
  end
77
81
 
78
- it 'registers silencer' do
79
- expect(supervisor).to receive(:add).
80
- with(Listen::Silencer, as: :silencer, args: subject)
81
-
82
- subject.start
83
- end
84
-
85
82
  it 'supervises change_pool' do
86
83
  expect(supervisor).to receive(:pool).
87
- with(Listen::Change, as: :change_pool, args: subject)
84
+ with(Change, as: :change_pool, args: subject)
88
85
 
89
86
  subject.start
90
87
  end
91
88
 
92
89
  it 'supervises adaper' do
93
- allow(Listen::Adapter).to receive(:select) { Listen::Adapter::Polling }
90
+ allow(Adapter).to receive(:select) { Adapter::Polling }
91
+ options = [mq: subject, directories: []]
94
92
  expect(supervisor).to receive(:add).
95
- with(Listen::Adapter::Polling, as: :adapter, args: subject)
93
+ with(Adapter::Polling, as: :adapter, args: options)
96
94
 
97
95
  subject.start
98
96
  end
99
97
 
100
98
  it 'supervises record' do
101
99
  expect(supervisor).to receive(:add).
102
- with(Listen::Record, as: :record, args: subject)
100
+ with(Record, as: :record, args: subject)
103
101
 
104
102
  subject.start
105
103
  end
@@ -122,11 +120,14 @@ describe Listen::Listener do
122
120
  it 'calls block on changes' do
123
121
  foo = instance_double(Pathname, to_s: 'foo', exist?: true)
124
122
 
123
+ dir = instance_double(Pathname)
124
+ allow(dir).to receive(:+).with('foo') { foo }
125
+
125
126
  block_stub = instance_double(Proc)
126
127
  subject.block = block_stub
127
128
  expect(block_stub).to receive(:call).with(['foo'], [], [])
128
129
  subject.start
129
- subject.queue(:file, :modified, foo)
130
+ subject.queue(:file, :modified, dir, 'foo')
130
131
  sleep 0.25
131
132
  end
132
133
  end
@@ -199,20 +200,11 @@ describe Listen::Listener do
199
200
  end
200
201
 
201
202
  describe '#ignore' do
202
- let(:new_silencer) { instance_double(Listen::Silencer) }
203
- before { allow(Celluloid::Actor).to receive(:[]=) }
204
-
205
- it 'resets silencer actor' do
206
- expect(Listen::Silencer).to receive(:new).with(subject) { new_silencer }
207
- expect(registry).to receive(:[]=).with(:silencer, new_silencer)
208
- subject.ignore(/foo/)
209
- end
210
-
211
203
  context 'with existing ignore options' do
212
204
  let(:options) { { ignore: /bar/ } }
213
205
 
214
206
  it 'adds up to existing ignore options' do
215
- expect(Listen::Silencer).to receive(:new).with(subject)
207
+ expect(silencer).to receive(:configure).with(options)
216
208
  subject.ignore(/foo/)
217
209
  expect(subject.options).to include(ignore: [/bar/, /foo/])
218
210
  end
@@ -222,7 +214,7 @@ describe Listen::Listener do
222
214
  let(:options) { { ignore: [/bar/] } }
223
215
 
224
216
  it 'adds up to existing ignore options' do
225
- expect(Listen::Silencer).to receive(:new).with(subject)
217
+ expect(silencer).to receive(:configure).with(options)
226
218
  subject.ignore(/foo/)
227
219
  expect(subject.options).to include(ignore: [[/bar/], /foo/])
228
220
  end
@@ -230,21 +222,20 @@ describe Listen::Listener do
230
222
  end
231
223
 
232
224
  describe '#ignore!' do
233
- let(:new_silencer) { instance_double(Listen::Silencer) }
234
- before { allow(Celluloid::Actor).to receive(:[]=) }
225
+ context 'with no existing options' do
226
+ let(:options) { {} }
235
227
 
236
- it 'resets silencer actor' do
237
- expect(Listen::Silencer).to receive(:new).with(subject) { new_silencer }
238
- expect(registry).to receive(:[]=).with(:silencer, new_silencer)
239
- subject.ignore!(/foo/)
240
- expect(subject.options).to include(ignore!: /foo/)
228
+ it 'sets options' do
229
+ expect(silencer).to receive(:configure).with(options)
230
+ subject
231
+ end
241
232
  end
242
233
 
243
234
  context 'with existing ignore! options' do
244
235
  let(:options) { { ignore!: /bar/ } }
245
236
 
246
237
  it 'overwrites existing ignore options' do
247
- expect(Listen::Silencer).to receive(:new).with(subject)
238
+ expect(silencer).to receive(:configure).with(options)
248
239
  subject.ignore!([/foo/])
249
240
  expect(subject.options).to include(ignore!: [/foo/])
250
241
  end
@@ -254,7 +245,7 @@ describe Listen::Listener do
254
245
  let(:options) { { ignore: /bar/ } }
255
246
 
256
247
  it 'deletes ignore options' do
257
- expect(Listen::Silencer).to receive(:new).with(subject)
248
+ expect(silencer).to receive(:configure).with(options)
258
249
  subject.ignore!([/foo/])
259
250
  expect(subject.options).to_not include(ignore: /bar/)
260
251
  end
@@ -262,20 +253,11 @@ describe Listen::Listener do
262
253
  end
263
254
 
264
255
  describe '#only' do
265
- let(:new_silencer) { instance_double(Listen::Silencer) }
266
- before { allow(Celluloid::Actor).to receive(:[]=) }
267
-
268
- it 'resets silencer actor' do
269
- expect(Listen::Silencer).to receive(:new).with(subject) { new_silencer }
270
- expect(registry).to receive(:[]=).with(:silencer, new_silencer)
271
- subject.only(/foo/)
272
- end
273
-
274
256
  context 'with existing only options' do
275
257
  let(:options) { { only: /bar/ } }
276
258
 
277
259
  it 'overwrites existing ignore options' do
278
- expect(Listen::Silencer).to receive(:new).with(subject)
260
+ expect(silencer).to receive(:configure).with(options)
279
261
  subject.only([/foo/])
280
262
  expect(subject.options).to include(only: [/foo/])
281
263
  end
@@ -287,60 +269,69 @@ describe Listen::Listener do
287
269
  allow(silencer).to receive(:silenced?) { false }
288
270
 
289
271
  subject.block = proc do |modified, added, _|
290
- expect(modified).to eql(['foo.txt'])
291
- expect(added).to eql(['bar.txt'])
272
+ expect(modified).to eql(['foo/bar.txt'])
273
+ expect(added).to eql(['foo/baz.txt'])
292
274
  end
293
275
 
294
- foo = instance_double(
276
+ bar = instance_double(
295
277
  Pathname,
296
- to_s: 'foo.txt',
278
+ to_s: 'foo/bar.txt',
297
279
  exist?: true,
298
280
  directory?: false)
299
281
 
300
- bar = instance_double(
282
+ baz = instance_double(
301
283
  Pathname,
302
- to_s: 'bar.txt',
284
+ to_s: 'foo/baz.txt',
303
285
  exist?: true,
304
286
  directory?: false)
305
287
 
288
+ dir = instance_double(Pathname)
289
+ expect(dir).to receive(:+).with('bar.txt') { bar }
290
+ expect(dir).to receive(:+).with('baz.txt') { baz }
291
+
306
292
  subject.start
307
- subject.queue(:file, :modified, foo, {})
308
- subject.queue(:file, :added, bar, {})
293
+ subject.queue(:file, :modified, dir, 'bar.txt', {})
294
+ subject.queue(:file, :added, dir, 'baz.txt', {})
309
295
  sleep 0.25
310
296
  end
311
297
  end
312
298
 
313
299
  describe '_smoosh_changes' do
314
300
  it 'recognizes rename from temp file' do
315
- path = instance_double(
301
+ bar = instance_double(
316
302
  Pathname,
317
- to_s: 'foo',
303
+ to_s: 'bar',
318
304
  exist?: true,
319
305
  directory?: false)
320
306
 
307
+ foo = instance_double(Pathname, to_s: 'foo')
308
+ allow(foo).to receive(:+).with('bar') { bar }
309
+
321
310
  changes = [
322
- [:file, :modified, path],
323
- [:file, :removed, path],
324
- [:file, :added, path],
325
- [:file, :modified, path]
311
+ [:file, :modified, foo, 'bar'],
312
+ [:file, :removed, foo, 'bar'],
313
+ [:file, :added, foo, 'bar'],
314
+ [:file, :modified, foo, 'bar']
326
315
  ]
327
316
  allow(silencer).to receive(:silenced?) { false }
328
317
  smooshed = subject.send :_smoosh_changes, changes
329
- expect(smooshed).to eq(modified: ['foo'], added: [], removed: [])
318
+ expect(smooshed).to eq(modified: ['bar'], added: [], removed: [])
330
319
  end
331
320
 
332
- it 'recognizes deleted temp file' do
333
- path = instance_double(
321
+ it 'ignores deleted temp file' do
322
+ bar = instance_double(
334
323
  Pathname,
335
- to_s: 'foo',
336
- exist?: false,
337
- directory?: false)
324
+ to_s: 'bar',
325
+ exist?: false)
326
+
327
+ foo = instance_double(Pathname, to_s: 'foo')
328
+ allow(foo).to receive(:+).with('bar') { bar }
338
329
 
339
330
  changes = [
340
- [:file, :added, path],
341
- [:file, :modified, path],
342
- [:file, :removed, path],
343
- [:file, :modified, path]
331
+ [:file, :added, foo, 'bar'],
332
+ [:file, :modified, foo, 'bar'],
333
+ [:file, :removed, foo, 'bar'],
334
+ [:file, :modified, foo, 'bar']
344
335
  ]
345
336
  allow(silencer).to receive(:silenced?) { false }
346
337
  smooshed = subject.send :_smoosh_changes, changes
@@ -349,19 +340,21 @@ describe Listen::Listener do
349
340
 
350
341
  it 'recognizes double move as modification' do
351
342
  # e.g. "mv foo x && mv x foo" is like "touch foo"
352
- path = instance_double(
343
+ bar = instance_double(
353
344
  Pathname,
354
- to_s: 'foo',
355
- exist?: true,
356
- directory?: false)
345
+ to_s: 'bar',
346
+ exist?: true)
347
+
348
+ dir = instance_double(Pathname, to_s: 'foo')
349
+ allow(dir).to receive(:+).with('bar') { bar }
357
350
 
358
351
  changes = [
359
- [:file, :removed, path],
360
- [:file, :added, path]
352
+ [:file, :removed, dir, 'bar'],
353
+ [:file, :added, dir, 'bar']
361
354
  ]
362
355
  allow(silencer).to receive(:silenced?) { false }
363
356
  smooshed = subject.send :_smoosh_changes, changes
364
- expect(smooshed).to eq(modified: ['foo'], added: [], removed: [])
357
+ expect(smooshed).to eq(modified: ['bar'], added: [], removed: [])
365
358
  end
366
359
 
367
360
  context 'with cookie' do
@@ -370,11 +363,15 @@ describe Listen::Listener do
370
363
  foo = instance_double(
371
364
  Pathname,
372
365
  to_s: 'foo',
373
- exist?: true,
374
- directory?: false)
366
+ exist?: true)
367
+
368
+ dir = instance_double(Pathname, to_s: 'foo')
369
+ allow(dir).to receive(:+).with('foo') { foo }
370
+
371
+ changes = [[:file, :moved_to, dir, 'foo', cookie: 4321]]
372
+ expect(silencer).to receive(:silenced?).
373
+ with(Pathname('foo'), :file) { false }
375
374
 
376
- changes = [[:file, :moved_to, foo, cookie: 4321]]
377
- expect(silencer).to receive(:silenced?).with(foo, :file) { false }
378
375
  smooshed = subject.send :_smoosh_changes, changes
379
376
  expect(smooshed).to eq(modified: [], added: ['foo'], removed: [])
380
377
  end
@@ -392,15 +389,21 @@ describe Listen::Listener do
392
389
  exist?: true,
393
390
  directory?: false)
394
391
 
392
+ dir = instance_double(Pathname)
393
+ allow(dir).to receive(:+).with('foo') { foo }
394
+ allow(dir).to receive(:+).with('bar') { bar }
395
+
395
396
  changes = [
396
- [:file, :moved_from, foo , cookie: 4321],
397
- [:file, :moved_to, bar, cookie: 4321]
397
+ [:file, :moved_from, dir, 'foo', cookie: 4321],
398
+ [:file, :moved_to, dir, 'bar', cookie: 4321]
398
399
  ]
399
400
 
400
401
  expect(silencer).to receive(:silenced?).
401
- twice.with(foo, :file) { false }
402
+ twice.with(Pathname('foo'), :file) { false }
403
+
404
+ expect(silencer).to receive(:silenced?).
405
+ with(Pathname('bar'), :file) { false }
402
406
 
403
- expect(silencer).to receive(:silenced?).with(bar, :file) { false }
404
407
  smooshed = subject.send :_smoosh_changes, changes
405
408
  expect(smooshed).to eq(modified: [], added: ['bar'], removed: [])
406
409
  end
@@ -410,7 +413,7 @@ describe Listen::Listener do
410
413
 
411
414
  ignored = instance_double(
412
415
  Pathname,
413
- to_s: 'foo',
416
+ to_s: 'ignored',
414
417
  exist?: true,
415
418
  directory?: false)
416
419
 
@@ -420,26 +423,60 @@ describe Listen::Listener do
420
423
  exist?: true,
421
424
  directory?: false)
422
425
 
426
+ dir = instance_double(Pathname)
427
+ allow(dir).to receive(:+).with('foo') { foo }
428
+ allow(dir).to receive(:+).with('ignored') { ignored }
429
+
423
430
  changes = [
424
- [:file, :moved_from, ignored, cookie: 4321],
425
- [:file, :moved_to, foo , cookie: 4321]
431
+ [:file, :moved_from, dir, 'ignored', cookie: 4321],
432
+ [:file, :moved_to, dir, 'foo' , cookie: 4321]
426
433
  ]
427
- expect(silencer).to receive(:silenced?).with(ignored, :file) { true }
428
- expect(silencer).to receive(:silenced?).with(foo, :file) { false }
434
+
435
+ expect(silencer).to receive(:silenced?).
436
+ with(Pathname('ignored'), :file) { true }
437
+
438
+ expect(silencer).to receive(:silenced?).
439
+ with(Pathname('foo'), :file) { false }
440
+
429
441
  smooshed = subject.send :_smoosh_changes, changes
430
442
  expect(smooshed).to eq(modified: ['foo'], added: [], removed: [])
431
443
  end
432
444
  end
433
445
 
434
446
  context 'with no cookie' do
435
- it 'recognizes properly ignores files' do
436
- ignored = instance_double(Pathname, to_s: 'foo', exist?: true)
437
-
438
- changes = [[:file, :modified, ignored]]
439
- expect(silencer).to receive(:silenced?).with(ignored, :file) { true }
440
- smooshed = subject.send :_smoosh_changes, changes
441
- expect(smooshed).to eq(modified: [], added: [], removed: [])
447
+ context 'with ignored file' do
448
+ let(:dir) { instance_double(Pathname) }
449
+ let(:ignored) { instance_double(Pathname, to_s: 'foo', exist?: true) }
450
+
451
+ before do
452
+ expect(silencer).to receive(:silenced?).
453
+ with(Pathname('ignored'), :file) { true }
454
+
455
+ allow(dir).to receive(:+).with('ignored') { ignored }
456
+ end
457
+
458
+ it 'recognizes properly ignores files' do
459
+ changes = [[:file, :modified, dir, 'ignored']]
460
+ smooshed = subject.send :_smoosh_changes, changes
461
+ expect(smooshed).to eq(modified: [], added: [], removed: [])
462
+ end
442
463
  end
443
464
  end
444
465
  end
466
+
467
+ context 'when listener is stopped' do
468
+
469
+ before do
470
+ allow(registry).to receive(:[]).with(:change_pool) { nil }
471
+ subject.stop
472
+ end
473
+
474
+ let(:dir) { instance_double(Pathname) }
475
+
476
+ it 'queuing does not crash when no worker is available' do
477
+ expect do
478
+ subject.send(:_queue_raw_change, :dir, dir, 'path', recursive: true)
479
+ end.to_not raise_error
480
+ end
481
+ end
445
482
  end
@@ -8,127 +8,270 @@ describe Listen::Record do
8
8
  end
9
9
 
10
10
  let(:record) { Listen::Record.new(listener) }
11
- let(:path) { '/dir/path/file.rb' }
11
+ let(:dir) { instance_double(Pathname, to_s: '/dir') }
12
12
 
13
- describe '#set_path' do
14
- it 'sets path by spliting dirname and basename' do
15
- record.set_path(:file, path)
16
- expect(record.paths['/dir/path']).to eq('file.rb' => { type: :file })
13
+ describe '#update_file' do
14
+ context 'with path in watched dir' do
15
+ it 'sets path by spliting dirname and basename' do
16
+ record.update_file(dir, 'file.rb', mtime: 1.1)
17
+ expect(record.paths['/dir']).to eq('file.rb' => { mtime: 1.1 })
18
+ end
19
+
20
+ it 'sets path and keeps old data not overwritten' do
21
+ record.update_file(dir, 'file.rb', foo: 1, bar: 2)
22
+ record.update_file(dir, 'file.rb', foo: 3)
23
+ watched_dir = record.paths['/dir']
24
+ expect(watched_dir).to eq('file.rb' => { foo: 3, bar: 2 })
25
+ end
26
+ end
27
+
28
+ context 'with subdir path' do
29
+ it 'sets path by spliting dirname and basename' do
30
+ record.update_file(dir, 'path/file.rb', mtime: 1.1)
31
+ expect(record.paths['/dir']['path']).to eq('file.rb' => { mtime: 1.1 })
32
+ end
33
+
34
+ it 'sets path and keeps old data not overwritten' do
35
+ record.update_file(dir, 'path/file.rb', foo: 1, bar: 2)
36
+ record.update_file(dir, 'path/file.rb', foo: 3)
37
+ file_data = record.paths['/dir']['path']['file.rb']
38
+ expect(file_data).to eq(foo: 3, bar: 2)
39
+ end
40
+ end
41
+ end
42
+
43
+ describe '#add_dir' do
44
+ it 'sets itself when .' do
45
+ record.add_dir(dir, '.')
46
+ expect(record.paths['/dir']).to eq({})
47
+ end
48
+
49
+ it 'sets itself when nil' do
50
+ record.add_dir(dir, nil)
51
+ expect(record.paths['/dir']).to eq({})
52
+ end
53
+
54
+ it 'sets itself when empty' do
55
+ record.add_dir(dir, '')
56
+ expect(record.paths['/dir']).to eq({})
57
+ end
58
+
59
+ it 'correctly sets new directory data' do
60
+ record.add_dir(dir, 'path/subdir')
61
+ expect(record.paths['/dir']).to eq('path/subdir' => {})
17
62
  end
18
63
 
19
64
  it 'sets path and keeps old data not overwritten' do
20
- record.set_path(:file, path, foo: 1, bar: 2)
21
- record.set_path(:file, path, foo: 3)
22
- file_data = record.paths['/dir/path']['file.rb']
23
- expect(file_data).to eq(foo: 3, bar: 2, type: :file)
65
+ record.add_dir(dir, 'path/subdir')
66
+ record.update_file(dir, 'path/subdir/file.rb', mtime: 1.1)
67
+ record.add_dir(dir, 'path/subdir')
68
+ record.update_file(dir, 'path/subdir/file2.rb', mtime: 1.2)
69
+ record.add_dir(dir, 'path/subdir')
70
+
71
+ watched = record.paths['/dir']
72
+ expect(watched.keys).to eq ['path/subdir']
73
+ expect(watched['path/subdir'].keys).to eq %w(file.rb file2.rb)
74
+
75
+ subdir = watched['path/subdir']
76
+ expect(subdir['file.rb']).to eq(mtime: 1.1)
77
+ expect(subdir['file2.rb']).to eq(mtime: 1.2)
24
78
  end
25
79
  end
26
80
 
27
81
  describe '#unset_path' do
28
82
  context 'path is present' do
29
- before { record.set_path(:file, path) }
83
+ before { record.update_file(dir, 'path/file.rb', mtime: 1.1) }
30
84
 
31
85
  it 'unsets path' do
32
- record.unset_path(path)
33
- expect(record.paths).to eq('/dir/path' => {})
86
+ record.unset_path(dir, 'path/file.rb')
87
+ expect(record.paths).to eq('/dir' => { 'path' => {} })
34
88
  end
35
89
  end
36
90
 
37
91
  context 'path not present' do
38
92
  it 'unsets path' do
39
- record.unset_path(path)
40
- expect(record.paths).to eq('/dir/path' => {})
93
+ record.unset_path(dir, 'path/file.rb')
94
+ expect(record.paths).to eq('/dir' => { 'path' => {} })
41
95
  end
42
96
  end
43
97
  end
44
98
 
45
99
  describe '#file_data' do
46
- context 'path is present' do
47
- before { record.set_path(:file, path) }
100
+ context 'with path in watched dir' do
101
+ context 'when path is present' do
102
+ before { record.update_file(dir, 'file.rb', mtime: 1.1) }
48
103
 
49
- it 'returns file data' do
50
- expect(record.file_data(path)).to eq(type: :file)
104
+ it 'returns file data' do
105
+ expect(record.file_data(dir, 'file.rb')).to eq(mtime: 1.1)
106
+ end
107
+ end
108
+
109
+ context 'path not present' do
110
+ it 'return empty hash' do
111
+ expect(record.file_data(dir, 'file.rb')).to be_empty
112
+ end
51
113
  end
52
114
  end
53
115
 
54
- context 'path not present' do
55
- it 'return empty hash' do
56
- expect(record.file_data(path)).to be_empty
116
+ context 'with path in subdir' do
117
+ context 'when path is present' do
118
+ before { record.update_file(dir, 'path/file.rb', mtime: 1.1) }
119
+
120
+ it 'returns file data' do
121
+ expected = { mtime: 1.1 }
122
+ expect(record.file_data(dir, 'path/file.rb')).to eq expected
123
+ end
124
+ end
125
+
126
+ context 'path not present' do
127
+ it 'return empty hash' do
128
+ expect(record.file_data(dir, 'path/file.rb')).to be_empty
129
+ end
57
130
  end
58
131
  end
59
132
  end
60
133
 
61
134
  describe '#dir_entries' do
62
- context 'path is present' do
63
- before { record.set_path(:file, path) }
135
+ context 'in watched dir' do
136
+ subject { record.dir_entries(dir, '.') }
137
+
138
+ context 'with no entries' do
139
+ it { should be_empty }
140
+ end
141
+
142
+ context 'with file.rb in record' do
143
+ before { record.update_file(dir, 'file.rb', mtime: 1.1) }
144
+ it { should eq('file.rb' => { mtime: 1.1 }) }
145
+ context 'when file is removed' do
146
+ before { record.update_file(dir, 'file.rb', mtime: 1.1) }
147
+ end
148
+ end
64
149
 
65
- it 'returns file path' do
66
- entries = record.dir_entries('/dir/path')
67
- expect(entries).to eq('file.rb' => { type: :file })
150
+ context 'with subdir/file.rb in record' do
151
+ before { record.update_file(dir, 'subdir/file.rb', mtime: 1.1) }
152
+ it { should eq('subdir' => {}) }
68
153
  end
69
154
  end
70
155
 
71
- context 'path not present' do
72
- it 'unsets path' do
73
- expect(record.dir_entries('/dir/path')).to eq({})
156
+ context 'in subdir /path' do
157
+ subject { record.dir_entries(dir, 'path') }
158
+
159
+ context 'with no entries' do
160
+ it { should be_empty }
161
+ end
162
+
163
+ context 'with path/file.rb already in record' do
164
+ before { record.update_file(dir, 'path/file.rb', mtime: 1.1) }
165
+ it { should eq('file.rb' => { mtime: 1.1 }) }
74
166
  end
75
167
  end
76
168
  end
77
169
 
78
170
  describe '#build' do
79
- let(:directories) { ['dir_path'] }
171
+ let(:dir1) { Pathname('/dir1') }
172
+ let(:dir2) { Pathname('/dir2') }
80
173
 
81
- let(:actor) do
82
- instance_double(Listen::Change, change: nil, terminate: true)
83
- end
174
+ let(:directories) { [dir1, dir2] }
84
175
 
85
176
  before do
86
177
  allow(listener).to receive(:directories) { directories }
87
- allow(listener).to receive(:sync).with(:change_pool) { actor }
178
+
179
+ allow(::File).to receive(:lstat) do |path|
180
+ fail "::File.lstat stub called with: #{path.inspect}"
181
+ end
182
+
183
+ allow(::Dir).to receive(:entries) do |path|
184
+ fail "::Dir.entries stub called with: #{path.inspect}"
185
+ end
186
+
187
+ allow(::Dir).to receive(:exist?) do |path|
188
+ fail "::Dir.exist? stub called with: #{path.inspect}"
189
+ end
88
190
  end
89
191
 
90
192
  it 're-inits paths' do
91
- record.set_path(:file, path)
92
- record.build
93
- expect(record.file_data(path)).to be_empty
94
- end
193
+ allow(::Dir).to receive(:entries) { [] }
95
194
 
96
- it 'calls change asynchronously on all directories to build record' do
97
- expect(actor).to receive(:change).
98
- with(:dir, 'dir_path', recursive: true, silence: true, build: true)
195
+ record.update_file(dir, 'path/file.rb', mtime: 1.1)
99
196
  record.build
197
+ expect(record.paths).to eq('/dir1' => {}, '/dir2' => {})
198
+ expect(record.file_data(dir, 'path/file.rb')).to be_empty
100
199
  end
101
- end
102
200
 
103
- describe '#still_building!' do
104
- let(:directories) { ['dir_path'] }
201
+ let(:foo_stat) { instance_double(::File::Stat, mtime: 1.0, mode: 0644) }
202
+ let(:bar_stat) { instance_double(::File::Stat, mtime: 2.3, mode: 0755) }
105
203
 
106
- let(:actor) do
107
- instance_double(Listen::Change, change: nil, terminate: true)
108
- end
204
+ context 'with no subdirs' do
109
205
 
110
- before do
111
- allow(listener).to receive(:directories) { directories }
112
- allow(listener).to receive(:sync).with(:change_pool) { actor }
206
+ before do
207
+ expect(::Dir).to receive(:entries).with('/dir1/.') { %w(foo bar) }
208
+ expect(::Dir).to receive(:exist?).with('/dir1/./foo') { false }
209
+ expect(::Dir).to receive(:exist?).with('/dir1/./bar') { false }
210
+ expect(::File).to receive(:lstat).with('/dir1/./foo') { foo_stat }
211
+ expect(::File).to receive(:lstat).with('/dir1/./bar') { bar_stat }
212
+
213
+ expect(::Dir).to receive(:entries).with('/dir2/.') { [] }
214
+ end
215
+
216
+ it 'builds record' do
217
+ record.build
218
+ expect(record.paths.keys).to eq %w( /dir1 /dir2 )
219
+ expect(record.paths['/dir1']).
220
+ to eq(
221
+ 'foo' => { mtime: 1.0, mode: 0644 },
222
+ 'bar' => { mtime: 2.3, mode: 0755 })
223
+ end
113
224
  end
114
225
 
115
- it 'keeps the build blocking longer' do
116
- record # To avoid initializing record in thread
226
+ context 'with subdir containing files' do
227
+ before do
228
+ expect(::Dir).to receive(:entries).with('/dir1/.') { %w(foo) }
229
+ expect(::Dir).to receive(:exist?).with('/dir1/./foo') { true }
117
230
 
118
- th = Thread.new do
119
- 10.times do
120
- sleep 0.05
121
- record.still_building!
122
- end
231
+ expect(::Dir).to receive(:entries).with('/dir1/foo') { %w(bar) }
232
+
233
+ expect(::Dir).to receive(:exist?).with('/dir1/foo/bar') { false }
234
+ expect(::File).to receive(:lstat).with('/dir1/foo/bar') { bar_stat }
235
+
236
+ expect(::Dir).to receive(:entries).with('/dir2/.') { [] }
123
237
  end
124
238
 
125
- started = Time.now
126
- record.build
127
- ended = Time.now
239
+ it 'builds record' do
240
+ record.build
241
+ expect(record.paths.keys).to eq %w( /dir1 /dir2 )
242
+ expect(record.paths['/dir1']).
243
+ to eq('foo' => { 'bar' => { mtime: 2.3, mode: 0755 } })
244
+ expect(record.paths['/dir2']).to eq({})
245
+ end
246
+ end
247
+
248
+ context 'with subdir containing dirs' do
249
+ before do
250
+ expect(::Dir).to receive(:entries).with('/dir1/.') { %w(foo) }
251
+ expect(::Dir).to receive(:exist?).with('/dir1/./foo') { true }
252
+
253
+ expect(::Dir).to receive(:entries).with('/dir1/foo') { %w(bar baz) }
128
254
 
129
- th.join
255
+ expect(::Dir).to receive(:exist?).with('/dir1/foo/bar') { true }
256
+ expect(::Dir).to receive(:entries).with('/dir1/foo/bar') { [] }
130
257
 
131
- expect(ended - started).to be > 0.5
258
+ expect(::Dir).to receive(:exist?).with('/dir1/foo/baz') { true }
259
+ expect(::Dir).to receive(:entries).with('/dir1/foo/baz') { [] }
260
+
261
+ expect(::Dir).to receive(:entries).with('/dir2/.') { [] }
262
+ end
263
+
264
+ it 'builds record' do
265
+ record.build
266
+ expect(record.paths.keys).to eq %w( /dir1 /dir2 )
267
+ expect(record.paths['/dir1']).
268
+ to eq(
269
+ 'foo' => {},
270
+ 'foo/bar' => {},
271
+ 'foo/baz' => {},
272
+ )
273
+ expect(record.paths['/dir2']).to eq({})
274
+ end
132
275
  end
133
276
  end
134
277
  end