listen 2.7.6 → 2.7.7

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 (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