listen 2.7.5 → 2.7.6

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 -1
  6. data/.yardopts +0 -0
  7. data/CHANGELOG.md +0 -0
  8. data/CONTRIBUTING.md +0 -0
  9. data/Gemfile +25 -4
  10. data/Guardfile +0 -0
  11. data/LICENSE.txt +0 -0
  12. data/README.md +18 -10
  13. data/Rakefile +0 -0
  14. data/lib/listen.rb +2 -4
  15. data/lib/listen/adapter.rb +13 -4
  16. data/lib/listen/adapter/base.rb +33 -16
  17. data/lib/listen/adapter/bsd.rb +21 -38
  18. data/lib/listen/adapter/darwin.rb +17 -25
  19. data/lib/listen/adapter/linux.rb +34 -52
  20. data/lib/listen/adapter/polling.rb +9 -25
  21. data/lib/listen/adapter/tcp.rb +27 -14
  22. data/lib/listen/adapter/windows.rb +67 -23
  23. data/lib/listen/change.rb +26 -23
  24. data/lib/listen/cli.rb +0 -0
  25. data/lib/listen/directory.rb +47 -58
  26. data/lib/listen/file.rb +66 -101
  27. data/lib/listen/listener.rb +214 -155
  28. data/lib/listen/queue_optimizer.rb +104 -0
  29. data/lib/listen/record.rb +15 -5
  30. data/lib/listen/silencer.rb +14 -10
  31. data/lib/listen/tcp.rb +0 -1
  32. data/lib/listen/tcp/broadcaster.rb +31 -26
  33. data/lib/listen/tcp/message.rb +2 -2
  34. data/lib/listen/version.rb +1 -1
  35. data/listen.gemspec +1 -1
  36. data/spec/acceptance/listen_spec.rb +151 -239
  37. data/spec/acceptance/tcp_spec.rb +125 -134
  38. data/spec/lib/listen/adapter/base_spec.rb +13 -30
  39. data/spec/lib/listen/adapter/bsd_spec.rb +7 -35
  40. data/spec/lib/listen/adapter/darwin_spec.rb +18 -30
  41. data/spec/lib/listen/adapter/linux_spec.rb +49 -55
  42. data/spec/lib/listen/adapter/polling_spec.rb +20 -35
  43. data/spec/lib/listen/adapter/tcp_spec.rb +25 -27
  44. data/spec/lib/listen/adapter/windows_spec.rb +7 -33
  45. data/spec/lib/listen/adapter_spec.rb +10 -10
  46. data/spec/lib/listen/change_spec.rb +55 -57
  47. data/spec/lib/listen/directory_spec.rb +105 -155
  48. data/spec/lib/listen/file_spec.rb +186 -73
  49. data/spec/lib/listen/listener_spec.rb +233 -216
  50. data/spec/lib/listen/record_spec.rb +60 -22
  51. data/spec/lib/listen/silencer_spec.rb +48 -75
  52. data/spec/lib/listen/tcp/broadcaster_spec.rb +78 -69
  53. data/spec/lib/listen/tcp/listener_spec.rb +28 -71
  54. data/spec/lib/listen/tcp/message_spec.rb +48 -14
  55. data/spec/lib/listen_spec.rb +3 -3
  56. data/spec/spec_helper.rb +6 -3
  57. data/spec/support/acceptance_helper.rb +250 -31
  58. data/spec/support/fixtures_helper.rb +6 -4
  59. data/spec/support/platform_helper.rb +2 -2
  60. metadata +5 -5
  61. data/lib/listen/tcp/listener.rb +0 -108
@@ -1,143 +1,256 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Listen::File do
4
- let(:registry) { double(Celluloid::Registry) }
5
- let(:listener) { double(Listen::Listener, registry: registry, options: {}) }
4
+ let(:async_record) do
5
+ instance_double(
6
+ Listen::Record,
7
+ set_path: true,
8
+ unset_path: true,
9
+ )
10
+ end
6
11
 
7
12
  let(:record) do
8
- double(Listen::Record, async: double(set_path: true, unset_path: true))
13
+ instance_double(
14
+ Listen::Record,
15
+ async: async_record,
16
+ file_data: record_data
17
+ )
9
18
  end
10
19
 
11
- let(:path) { Pathname.new(Dir.pwd) }
20
+ let(:path) { Pathname.pwd }
21
+ let(:file_path) { path + 'file.rb' }
22
+ let(:subject) { described_class.change(record, file_path) }
23
+
12
24
  around { |example| fixtures { example.run } }
13
- before { registry.stub(:[]).with(:record) { record } }
14
25
 
15
- describe '#change' do
16
- let(:file_path) { path.join('file.rb') }
17
- let(:file) { Listen::File.new(listener, file_path) }
26
+ before { allow(::File).to receive(:lstat) { fail 'Not stubbed!' } }
18
27
 
28
+ describe '#change' do
19
29
  let(:expected_data) do
20
- { type: 'File', mtime: kind_of(Float), mode: kind_of(Integer) }
30
+ { mtime: kind_of(Float), mode: kind_of(Integer) }
21
31
  end
22
32
 
23
- context 'path present in record' do
33
+ context 'with file in record' do
24
34
  let(:record_mtime) { nil }
25
35
  let(:record_md5) { nil }
26
36
  let(:record_mode) { nil }
27
37
 
28
38
  let(:record_data) do
29
- { type: 'File',
39
+ { type: :file,
30
40
  mtime: record_mtime,
31
41
  md5: record_md5,
32
42
  mode: record_mode }
33
43
  end
34
44
 
35
- before do
36
- record.stub_chain(:future, :file_data) { double(value: record_data) }
37
- end
45
+ context 'with non-existing file' do
46
+ before { allow(::File).to receive(:lstat) { fail Errno::ENOENT } }
47
+
48
+ it { should be :removed }
38
49
 
39
- context 'non-existing path' do
40
- it 'returns added' do
41
- expect(file.change).to eq :removed
42
- end
43
50
  it 'sets path in record' do
44
- expect(record.async).to receive(:unset_path).with(file_path)
45
- file.change
51
+ expect(async_record).to receive(:unset_path).with(file_path)
52
+ subject
46
53
  end
47
54
  end
48
55
 
49
- context 'existing path' do
50
- around do |example|
51
- touch file_path
52
- example.run
56
+ context 'with existing file' do
57
+ let(:stat_mtime) { Time.now.to_f - 1234.567 }
58
+ let(:stat_ctime) { Time.now.to_f - 1234.567 }
59
+ let(:stat_atime) { Time.now.to_f - 1234.567 }
60
+ let(:stat_mode) { 0640 }
61
+ let(:md5) { fail 'stub me (md5)' }
62
+
63
+ let(:stat) do
64
+ instance_double(
65
+ File::Stat,
66
+ mtime: stat_mtime,
67
+ atime: stat_atime,
68
+ ctime: stat_ctime,
69
+ mode: stat_mode
70
+ )
53
71
  end
54
72
 
55
- context 'old record path mtime' do
56
- let(:record_mtime) { (Time.now - 1).to_f }
73
+ before do
74
+ allow(::File).to receive(:lstat) { stat }
75
+ allow(Digest::MD5).to receive(:file) { double(:md5, digest: md5) }
76
+ end
57
77
 
58
- it 'returns modified' do
59
- expect(file.change).to eq :modified
60
- end
78
+ context 'with different mode in record' do
79
+ let(:record_mode) { 0722 }
61
80
 
62
- it 'sets path in record with expected data' do
63
- expect(record.async).to receive(:set_path).
64
- with(file_path, expected_data)
81
+ it { should be :modified }
65
82
 
66
- file.change
83
+ it 'sets path in record with expected data' do
84
+ expect(async_record).to receive(:set_path).
85
+ with(:file, file_path, expected_data)
86
+ subject
67
87
  end
68
88
  end
69
89
 
70
- context 'same record path mtime' do
71
- let(:record_mtime) { ::File.lstat(file_path).mtime.to_f }
72
- let(:record_mode) { ::File.lstat(file_path).mode }
90
+ context 'with same mode in record' do
91
+ let(:record_mode) { stat_mode }
92
+
93
+ # e.g. file was overwritten by earlier copy
94
+ context 'with earlier mtime than in record' do
95
+ let(:record_mtime) { stat_mtime.to_f - 123.45 }
73
96
 
74
- context 'same record path mode' do
75
- it 'returns nil' do
76
- expect(file.change).to be_nil
97
+ it { should be :modified }
98
+
99
+ it 'sets path in record with expected data' do
100
+ expect(async_record).to receive(:set_path).
101
+ with(:file, file_path, expected_data)
102
+ subject
77
103
  end
78
104
  end
79
105
 
80
- context 'diferent record path mode' do
81
- let(:record_mode) { 'foo' }
106
+ context 'with later mtime than in record' do
107
+ let(:record_mtime) { stat_mtime.to_f + 123.45 }
108
+
109
+ it { should be :modified }
82
110
 
83
- it 'returns modified' do
84
- expect(file.change).to eq :modified
111
+ it 'sets path in record with expected data' do
112
+ expect(async_record).to receive(:set_path).
113
+ with(:file, file_path, expected_data)
114
+ subject
85
115
  end
86
116
  end
87
117
 
88
- context 'same record path md5' do
89
- it 'returns nil' do
90
- expect(file.change).to be_nil
118
+ context 'with indentical mtime in record' do
119
+ let(:record_mtime) { stat_mtime.to_f }
120
+
121
+ context 'with accurate stat times' do
122
+ let(:stat_mtime) { Time.at(1401235714.123) }
123
+ let(:stat_atime) { Time.at(1401235714.123) }
124
+ let(:stat_ctime) { Time.at(1401235714.123) }
125
+ let(:record_mtime) { stat_mtime.to_f }
126
+ it { should be_nil }
91
127
  end
92
- end
93
128
 
94
- if darwin?
95
- context 'different record path md5' do
96
- let(:record_md5) { 'foo' }
97
- let(:expected_data) do
98
- { type: 'File',
99
- mtime: kind_of(Float),
100
- mode: kind_of(Integer),
101
- md5: kind_of(String) }
102
- end
129
+ context 'with inaccurate stat times' do
130
+ let(:stat_mtime) { Time.at(1401235714.0) }
131
+ let(:stat_atime) { Time.at(1401235714.0) }
132
+ let(:stat_ctime) { Time.at(1401235714.0) }
133
+
134
+ let(:record_mtime) { stat_mtime.to_f }
135
+
136
+ context 'with real mtime barely not within last second' do
137
+ before { allow(Time).to receive(:now) { now } }
103
138
 
104
- it 'returns modified' do
105
- expect(file.change).to eq :modified
139
+ # NOTE: if real mtime is ???14.99, the
140
+ # saved mtime is ???14.0
141
+ let(:now) { Time.at(1401235716.00) }
142
+ it { should be_nil }
106
143
  end
107
- it 'sets path in record with expected data' do
108
- expect(record.async).to receive(:set_path).
109
- with(file_path, expected_data)
110
144
 
111
- file.change
145
+ context 'with real mtime barely within last second' do
146
+ # NOTE: real mtime is in range (???14.0 .. ???14.999),
147
+ # so saved mtime at ???14.0 means it could be
148
+ # ???14.999, so ???15.999 could still be within 1 second
149
+ # range
150
+ let(:now) { Time.at(1401235715.999999) }
151
+
152
+ before { allow(Time).to receive(:now) { now } }
153
+
154
+ context 'without available md5' do
155
+ let(:md5) { fail Errno::ENOENT }
156
+
157
+ # Treat is as an ignored file, because chances are ... ...
158
+ # whatever is listening for changes won't be able to deal
159
+ # with the file either (e.g. because of permissions)
160
+ it { should be nil }
161
+
162
+ it 'should not unset record' do
163
+ expect(async_record).to_not receive(:unset_path)
164
+ end
165
+ end
166
+
167
+ context 'with available md5' do
168
+ let(:md5) { 'd41d8cd98f00b204e9800998ecf8427e' }
169
+
170
+ context 'with same md5 in record' do
171
+ let(:record_md5) { md5 }
172
+ it { should be_nil }
173
+ end
174
+
175
+ context 'with no md5 in record' do
176
+ let(:record_md5) { nil }
177
+ it { should be_nil }
178
+ end
179
+
180
+ context 'with different md5 in record' do
181
+ let(:record_md5) { 'foo' }
182
+ it { should be :modified }
183
+
184
+ it 'sets path in record with expected data' do
185
+ expect(async_record).to receive(:set_path).
186
+ with(:file, file_path, expected_data. merge(md5: md5))
187
+ subject
188
+ end
189
+ end
190
+ end
112
191
  end
113
192
  end
114
193
  end
115
-
116
194
  end
117
195
  end
118
196
  end
119
197
 
120
- context 'path not present in record' do
121
- before { record.stub_chain(:future, :file_data) { double(value: {}) } }
198
+ context 'with empty record' do
199
+ let(:record_data) { {} }
122
200
 
123
- context 'existing path' do
124
- around do |example|
125
- touch file_path
126
- example.run
201
+ context 'with existing path' do
202
+ let(:stat) do
203
+ instance_double(
204
+ File::Stat,
205
+ mtime: 1234,
206
+ mode: 0645
207
+ )
208
+ end
209
+
210
+ before do
211
+ allow(::File).to receive(:lstat) { stat }
127
212
  end
128
213
 
129
214
  it 'returns added' do
130
- expect(file.change).to eq :added
215
+ expect(subject).to eq :added
131
216
  end
132
217
 
133
218
  it 'sets path in record with expected data' do
134
- expect(record.async).to receive(:set_path).
135
- with(file_path, expected_data)
219
+ expect(async_record).to receive(:set_path).
220
+ with(:file, file_path, expected_data)
136
221
 
137
- file.change
222
+ subject
138
223
  end
139
224
  end
140
225
  end
141
226
  end
142
227
 
228
+ describe '#inaccurate_mac_time?' do
229
+ let(:stat) do
230
+ instance_double(File::Stat, mtime: mtime, atime: atime, ctime: ctime)
231
+ end
232
+
233
+ subject { Listen::File.inaccurate_mac_time?(stat) }
234
+
235
+ context 'with no accurate times' do
236
+ let(:mtime) { Time.at(1234567.0) }
237
+ let(:atime) { Time.at(1234567.0) }
238
+ let(:ctime) { Time.at(1234567.0) }
239
+ it { should be_truthy }
240
+ end
241
+
242
+ context 'with all accurate times' do
243
+ let(:mtime) { Time.at(1234567.89) }
244
+ let(:atime) { Time.at(1234567.89) }
245
+ let(:ctime) { Time.at(1234567.89) }
246
+ it { should be_falsey }
247
+ end
248
+
249
+ context 'with one accurate time' do
250
+ let(:mtime) { Time.at(1234567.0) }
251
+ let(:atime) { Time.at(1234567.89) }
252
+ let(:ctime) { Time.at(1234567.0) }
253
+ it { should be_falsey }
254
+ end
255
+ end
143
256
  end
@@ -1,227 +1,220 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Listen::Listener do
4
- let(:listener) { Listen::Listener.new(options) }
4
+ subject { described_class.new(options) }
5
5
  let(:options) { {} }
6
- let(:registry) { double(Celluloid::Registry, :[]= => true) }
6
+ let(:registry) { instance_double(Celluloid::Registry, :[]= => true) }
7
7
 
8
8
  let(:supervisor) do
9
- double(Celluloid::SupervisionGroup,
10
- add: true,
11
- pool: true)
9
+ instance_double(Celluloid::SupervisionGroup, add: true, pool: true)
12
10
  end
13
11
 
14
- let(:record) { double(Listen::Record, terminate: true, build: true) }
15
- let(:silencer) { double(Listen::Silencer, terminate: true) }
16
- let(:adapter_class) { double(:adapter_class, local_fs?: true) }
17
- let(:adapter) { double(Listen::Adapter::Base, class: adapter_class) }
18
- let(:change_pool) { double(Listen::Change, terminate: true) }
19
- let(:change_pool_async) { double('ChangePoolAsync') }
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) }
20
15
  before do
21
- Celluloid::Registry.stub(:new) { registry }
22
- Celluloid::SupervisionGroup.stub(:run!) { supervisor }
23
- registry.stub(:[]).with(:silencer) { silencer }
24
- registry.stub(:[]).with(:adapter) { adapter }
25
- registry.stub(:[]).with(:record) { record }
26
- registry.stub(:[]).with(:change_pool) { change_pool }
27
-
16
+ allow(Celluloid::Registry).to receive(:new) { registry }
17
+ allow(Celluloid::SupervisionGroup).to receive(:run!) { supervisor }
18
+ allow(registry).to receive(:[]).with(:silencer) { silencer }
19
+ allow(registry).to receive(:[]).with(:adapter) { adapter }
20
+ allow(registry).to receive(:[]).with(:record) { record }
28
21
  end
29
22
 
30
23
  describe 'initialize' do
31
- it 'sets paused to false' do
32
- expect(listener).not_to be_paused
33
- end
24
+ it { should_not be_paused }
34
25
 
35
- it 'sets block' do
36
- block = proc {}
37
- listener = Listen::Listener.new('lib', &block)
38
- expect(listener.block).not_to be_nil
26
+ context 'with a block' do
27
+ describe 'block' do
28
+ subject { described_class.new('lib', &(proc {})) }
29
+ specify { expect(subject.block).to_not be_nil }
30
+ end
39
31
  end
40
32
 
41
- it 'sets directories with realpath' do
42
- listener = Listen::Listener.new('lib', 'spec')
43
- expected = %w(lib spec).map { |dir| Pathname.pwd.join(dir) }
44
- expect(listener.directories).to eq expected
33
+ context 'with directories' do
34
+ describe 'directories' do
35
+ subject { described_class.new('lib', 'spec') }
36
+ expected = %w(lib spec).map { |dir| Pathname.pwd + dir }
37
+ specify { expect(subject.directories).to eq expected }
38
+ end
45
39
  end
46
40
  end
47
41
 
48
42
  describe 'options' do
49
- it 'sets default options' do
50
- expect(listener.options).to eq(
51
- debug: false,
52
- latency: nil,
53
- wait_for_delay: 0.1,
54
- force_polling: false,
55
- polling_fallback_message: nil)
56
- end
57
-
58
- it 'sets new options on initialize' do
59
- listener = Listen::Listener.new('lib',
60
- latency: 1.234,
61
- wait_for_delay: 0.85)
62
-
63
- expect(listener.options).to eq(
64
- debug: false,
65
- latency: 1.234,
66
- wait_for_delay: 0.85,
67
- force_polling: false,
68
- polling_fallback_message: nil)
43
+ context 'default options' do
44
+ it 'sets default options' do
45
+ expect(subject.options).
46
+ to eq(
47
+ debug: false,
48
+ latency: nil,
49
+ wait_for_delay: 0.1,
50
+ force_polling: false,
51
+ polling_fallback_message: nil)
52
+ end
53
+ end
54
+
55
+ context 'custom options' do
56
+ subject do
57
+ described_class.new('lib', latency: 1.234, wait_for_delay: 0.85)
58
+ end
59
+
60
+ it 'sets new options on initialize' do
61
+ expect(subject.options).
62
+ to eq(
63
+ debug: false,
64
+ latency: 1.234,
65
+ wait_for_delay: 0.85,
66
+ force_polling: false,
67
+ polling_fallback_message: nil)
68
+ end
69
69
  end
70
70
  end
71
71
 
72
72
  describe '#start' do
73
73
  before do
74
- adapter.stub_chain(:async, :start)
75
- silencer.stub(:silenced?) { false }
74
+ allow(subject).to receive(:_start_adapter)
75
+ allow(silencer).to receive(:silenced?) { false }
76
76
  end
77
77
 
78
78
  it 'registers silencer' do
79
79
  expect(supervisor).to receive(:add).
80
- with(Listen::Silencer, as: :silencer, args: listener)
80
+ with(Listen::Silencer, as: :silencer, args: subject)
81
81
 
82
- listener.start
82
+ subject.start
83
83
  end
84
84
 
85
85
  it 'supervises change_pool' do
86
86
  expect(supervisor).to receive(:pool).
87
- with(Listen::Change, as: :change_pool, args: listener)
87
+ with(Listen::Change, as: :change_pool, args: subject)
88
88
 
89
- listener.start
89
+ subject.start
90
90
  end
91
91
 
92
92
  it 'supervises adaper' do
93
- Listen::Adapter.stub(:select) { Listen::Adapter::Polling }
93
+ allow(Listen::Adapter).to receive(:select) { Listen::Adapter::Polling }
94
94
  expect(supervisor).to receive(:add).
95
- with(Listen::Adapter::Polling, as: :adapter, args: listener)
95
+ with(Listen::Adapter::Polling, as: :adapter, args: subject)
96
96
 
97
- listener.start
97
+ subject.start
98
98
  end
99
99
 
100
100
  it 'supervises record' do
101
101
  expect(supervisor).to receive(:add).
102
- with(Listen::Record, as: :record, args: listener)
102
+ with(Listen::Record, as: :record, args: subject)
103
103
 
104
- listener.start
104
+ subject.start
105
105
  end
106
106
 
107
107
  it 'builds record' do
108
108
  expect(record).to receive(:build)
109
- listener.start
109
+ subject.start
110
110
  end
111
111
 
112
112
  it 'sets paused to false' do
113
- listener.start
114
- expect(listener.paused).to be_false
113
+ subject.start
114
+ expect(subject).to_not be_paused
115
115
  end
116
116
 
117
- it 'starts adapter asynchronously' do
118
- async_stub = double
119
- expect(adapter).to receive(:async) { async_stub }
120
- expect(async_stub).to receive(:start)
121
- listener.start
122
- end
123
-
124
- it 'starts adapter asynchronously' do
125
- async_stub = double
126
- expect(adapter).to receive(:async) { async_stub }
127
- expect(async_stub).to receive(:start)
128
- listener.start
117
+ it 'starts adapter' do
118
+ expect(subject).to receive(:_start_adapter)
119
+ subject.start
129
120
  end
130
121
 
131
122
  it 'calls block on changes' do
132
- foo = double(Pathname, to_s: 'foo', exist?: true, directory?: false)
133
- listener.changes = [{ modified: foo }]
134
- block_stub = double('block')
135
- listener.block = block_stub
123
+ foo = instance_double(Pathname, to_s: 'foo', exist?: true)
124
+
125
+ block_stub = instance_double(Proc)
126
+ subject.block = block_stub
136
127
  expect(block_stub).to receive(:call).with(['foo'], [], [])
137
- listener.start
128
+ subject.start
129
+ subject.queue(:file, :modified, foo)
138
130
  sleep 0.25
139
131
  end
140
132
  end
141
133
 
142
134
  describe '#stop' do
135
+ before do
136
+ allow(Celluloid::SupervisionGroup).to receive(:run!) { supervisor }
137
+ subject.start
138
+ end
139
+
143
140
  it 'terminates supervisor' do
144
- listener.supervisor = supervisor
145
141
  expect(supervisor).to receive(:terminate)
146
- listener.stop
142
+ subject.stop
147
143
  end
148
144
  end
149
145
 
150
146
  describe '#pause' do
147
+ before { subject.start }
151
148
  it 'sets paused to true' do
152
- listener.pause
153
- expect(listener.paused).to be_true
149
+ subject.pause
150
+ expect(subject).to be_paused
154
151
  end
155
152
  end
156
153
 
157
154
  describe '#unpause' do
158
- it 'builds record' do
159
- expect(record).to receive(:build)
160
- listener.unpause
155
+ before do
156
+ subject.start
157
+ subject.pause
161
158
  end
162
159
 
163
160
  it 'sets paused to false' do
164
- record.stub(:build)
165
- listener.unpause
166
- expect(listener.paused).to be_false
161
+ subject.unpause
162
+ expect(subject).to_not be_paused
167
163
  end
168
164
  end
169
165
 
170
166
  describe '#paused?' do
167
+ before { subject.start }
171
168
  it 'returns true when paused' do
172
- listener.paused = true
173
- expect(listener).to be_paused
169
+ subject.paused = true
170
+ expect(subject).to be_paused
174
171
  end
175
172
  it 'returns false when not paused (nil)' do
176
- listener.paused = nil
177
- expect(listener).not_to be_paused
173
+ subject.paused = nil
174
+ expect(subject).not_to be_paused
178
175
  end
179
176
  it 'returns false when not paused (false)' do
180
- listener.paused = false
181
- expect(listener).not_to be_paused
177
+ subject.paused = false
178
+ expect(subject).not_to be_paused
182
179
  end
183
180
  end
184
181
 
185
182
  describe '#listen?' do
186
- it 'returns true when not paused (false)' do
187
- listener.paused = false
188
- listener.stopping = false
189
- expect(listener.listen?).to be_true
183
+ context 'when processing' do
184
+ before { subject.start }
185
+ it { should be_processing }
190
186
  end
191
- it 'returns false when not paused (nil)' do
192
- listener.paused = nil
193
- listener.stopping = false
194
- expect(listener.listen?).to be_false
195
- end
196
- it 'returns false when paused' do
197
- listener.paused = true
198
- listener.stopping = false
199
- expect(listener.listen?).to be_false
187
+
188
+ context 'when stopped' do
189
+ it { should_not be_processing }
200
190
  end
201
- it 'returns false when stopped' do
202
- listener.paused = false
203
- listener.stopping = true
204
- expect(listener.listen?).to be_false
191
+
192
+ context 'when paused' do
193
+ before do
194
+ subject.start
195
+ subject.pause
196
+ end
197
+ it { should_not be_processing }
205
198
  end
206
199
  end
207
200
 
208
201
  describe '#ignore' do
209
- let(:new_silencer) { double(Listen::Silencer) }
210
- before { Celluloid::Actor.stub(:[]=) }
202
+ let(:new_silencer) { instance_double(Listen::Silencer) }
203
+ before { allow(Celluloid::Actor).to receive(:[]=) }
211
204
 
212
205
  it 'resets silencer actor' do
213
- expect(Listen::Silencer).to receive(:new).with(listener) { new_silencer }
206
+ expect(Listen::Silencer).to receive(:new).with(subject) { new_silencer }
214
207
  expect(registry).to receive(:[]=).with(:silencer, new_silencer)
215
- listener.ignore(/foo/)
208
+ subject.ignore(/foo/)
216
209
  end
217
210
 
218
211
  context 'with existing ignore options' do
219
212
  let(:options) { { ignore: /bar/ } }
220
213
 
221
214
  it 'adds up to existing ignore options' do
222
- expect(Listen::Silencer).to receive(:new).with(listener)
223
- listener.ignore(/foo/)
224
- expect(listener.options).to include(ignore: [/bar/, /foo/])
215
+ expect(Listen::Silencer).to receive(:new).with(subject)
216
+ subject.ignore(/foo/)
217
+ expect(subject.options).to include(ignore: [/bar/, /foo/])
225
218
  end
226
219
  end
227
220
 
@@ -229,31 +222,31 @@ describe Listen::Listener do
229
222
  let(:options) { { ignore: [/bar/] } }
230
223
 
231
224
  it 'adds up to existing ignore options' do
232
- expect(Listen::Silencer).to receive(:new).with(listener)
233
- listener.ignore(/foo/)
234
- expect(listener.options).to include(ignore: [[/bar/], /foo/])
225
+ expect(Listen::Silencer).to receive(:new).with(subject)
226
+ subject.ignore(/foo/)
227
+ expect(subject.options).to include(ignore: [[/bar/], /foo/])
235
228
  end
236
229
  end
237
230
  end
238
231
 
239
232
  describe '#ignore!' do
240
- let(:new_silencer) { double(Listen::Silencer) }
241
- before { Celluloid::Actor.stub(:[]=) }
233
+ let(:new_silencer) { instance_double(Listen::Silencer) }
234
+ before { allow(Celluloid::Actor).to receive(:[]=) }
242
235
 
243
236
  it 'resets silencer actor' do
244
- expect(Listen::Silencer).to receive(:new).with(listener) { new_silencer }
237
+ expect(Listen::Silencer).to receive(:new).with(subject) { new_silencer }
245
238
  expect(registry).to receive(:[]=).with(:silencer, new_silencer)
246
- listener.ignore!(/foo/)
247
- expect(listener.options).to include(ignore!: /foo/)
239
+ subject.ignore!(/foo/)
240
+ expect(subject.options).to include(ignore!: /foo/)
248
241
  end
249
242
 
250
243
  context 'with existing ignore! options' do
251
244
  let(:options) { { ignore!: /bar/ } }
252
245
 
253
246
  it 'overwrites existing ignore options' do
254
- expect(Listen::Silencer).to receive(:new).with(listener)
255
- listener.ignore!([/foo/])
256
- expect(listener.options).to include(ignore!: [/foo/])
247
+ expect(Listen::Silencer).to receive(:new).with(subject)
248
+ subject.ignore!([/foo/])
249
+ expect(subject.options).to include(ignore!: [/foo/])
257
250
  end
258
251
  end
259
252
 
@@ -261,166 +254,190 @@ describe Listen::Listener do
261
254
  let(:options) { { ignore: /bar/ } }
262
255
 
263
256
  it 'deletes ignore options' do
264
- expect(Listen::Silencer).to receive(:new).with(listener)
265
- listener.ignore!([/foo/])
266
- expect(listener.options).to_not include(ignore: /bar/)
257
+ expect(Listen::Silencer).to receive(:new).with(subject)
258
+ subject.ignore!([/foo/])
259
+ expect(subject.options).to_not include(ignore: /bar/)
267
260
  end
268
261
  end
269
262
  end
270
263
 
271
264
  describe '#only' do
272
- let(:new_silencer) { double(Listen::Silencer) }
273
- before { Celluloid::Actor.stub(:[]=) }
265
+ let(:new_silencer) { instance_double(Listen::Silencer) }
266
+ before { allow(Celluloid::Actor).to receive(:[]=) }
274
267
 
275
268
  it 'resets silencer actor' do
276
- expect(Listen::Silencer).to receive(:new).with(listener) { new_silencer }
269
+ expect(Listen::Silencer).to receive(:new).with(subject) { new_silencer }
277
270
  expect(registry).to receive(:[]=).with(:silencer, new_silencer)
278
- listener.only(/foo/)
271
+ subject.only(/foo/)
279
272
  end
280
273
 
281
274
  context 'with existing only options' do
282
275
  let(:options) { { only: /bar/ } }
283
276
 
284
277
  it 'overwrites existing ignore options' do
285
- expect(Listen::Silencer).to receive(:new).with(listener)
286
- listener.only([/foo/])
287
- expect(listener.options).to include(only: [/foo/])
278
+ expect(Listen::Silencer).to receive(:new).with(subject)
279
+ subject.only([/foo/])
280
+ expect(subject.options).to include(only: [/foo/])
288
281
  end
289
282
  end
290
283
  end
291
284
 
292
285
  describe '_wait_for_changes' do
293
286
  it 'gets two changes and calls the block once' do
294
- silencer.stub(:silenced?) { false }
295
-
296
- fake_time = 0
297
- listener.stub(:sleep) do |sec|
298
- fake_time += sec
299
- listener.stopping = true if fake_time > 1
300
- end
287
+ allow(silencer).to receive(:silenced?) { false }
301
288
 
302
- listener.block = proc do |modified, added, _|
289
+ subject.block = proc do |modified, added, _|
303
290
  expect(modified).to eql(['foo.txt'])
304
291
  expect(added).to eql(['bar.txt'])
305
292
  end
306
293
 
307
- foo = double(Pathname, to_s: 'foo.txt', exist?: true, directory?: false)
308
- bar = double(Pathname, to_s: 'bar.txt', exist?: true, directory?: false)
309
-
310
- i = 0
311
- listener.stub(:_pop_changes) do
312
- i += 1
313
- case i
314
- when 1
315
- []
316
- when 2
317
- [{ modified: foo }]
318
- when 3
319
- [{ added: bar }]
320
- else
321
- []
322
- end
323
- end
324
-
325
- listener.send :_wait_for_changes
294
+ foo = instance_double(
295
+ Pathname,
296
+ to_s: 'foo.txt',
297
+ exist?: true,
298
+ directory?: false)
299
+
300
+ bar = instance_double(
301
+ Pathname,
302
+ to_s: 'bar.txt',
303
+ exist?: true,
304
+ directory?: false)
305
+
306
+ subject.start
307
+ subject.queue(:file, :modified, foo, {})
308
+ subject.queue(:file, :added, bar, {})
309
+ sleep 0.25
326
310
  end
327
311
  end
328
312
 
329
313
  describe '_smoosh_changes' do
330
314
  it 'recognizes rename from temp file' do
331
- path = double(Pathname, to_s: 'foo', exist?: true, directory?: false)
315
+ path = instance_double(
316
+ Pathname,
317
+ to_s: 'foo',
318
+ exist?: true,
319
+ directory?: false)
320
+
332
321
  changes = [
333
- { modified: path },
334
- { removed: path },
335
- { added: path },
336
- { modified: path }
322
+ [:file, :modified, path],
323
+ [:file, :removed, path],
324
+ [:file, :added, path],
325
+ [:file, :modified, path]
337
326
  ]
338
- silencer.stub(:silenced?) { false }
339
- smooshed = listener.send :_smoosh_changes, changes
327
+ allow(silencer).to receive(:silenced?) { false }
328
+ smooshed = subject.send :_smoosh_changes, changes
340
329
  expect(smooshed).to eq(modified: ['foo'], added: [], removed: [])
341
330
  end
342
331
 
343
332
  it 'recognizes deleted temp file' do
344
- path = double(Pathname, to_s: 'foo', exist?: false, directory?: false)
333
+ path = instance_double(
334
+ Pathname,
335
+ to_s: 'foo',
336
+ exist?: false,
337
+ directory?: false)
338
+
345
339
  changes = [
346
- { added: path },
347
- { modified: path },
348
- { removed: path },
349
- { modified: path }
340
+ [:file, :added, path],
341
+ [:file, :modified, path],
342
+ [:file, :removed, path],
343
+ [:file, :modified, path]
350
344
  ]
351
- silencer.stub(:silenced?) { false }
352
- smooshed = listener.send :_smoosh_changes, changes
345
+ allow(silencer).to receive(:silenced?) { false }
346
+ smooshed = subject.send :_smoosh_changes, changes
353
347
  expect(smooshed).to eq(modified: [], added: [], removed: [])
354
348
  end
355
349
 
356
350
  it 'recognizes double move as modification' do
357
351
  # e.g. "mv foo x && mv x foo" is like "touch foo"
358
- path = double(Pathname, to_s: 'foo', exist?: true, directory?: false)
352
+ path = instance_double(
353
+ Pathname,
354
+ to_s: 'foo',
355
+ exist?: true,
356
+ directory?: false)
357
+
359
358
  changes = [
360
- { removed: path },
361
- { added: path }
359
+ [:file, :removed, path],
360
+ [:file, :added, path]
362
361
  ]
363
- silencer.stub(:silenced?) { false }
364
- smooshed = listener.send :_smoosh_changes, changes
362
+ allow(silencer).to receive(:silenced?) { false }
363
+ smooshed = subject.send :_smoosh_changes, changes
365
364
  expect(smooshed).to eq(modified: ['foo'], added: [], removed: [])
366
365
  end
367
366
 
368
367
  context 'with cookie' do
369
368
 
370
369
  it 'recognizes single moved_to as add' do
371
- foo = double(Pathname, to_s: 'foo', exist?: true, directory?: false)
372
- changes = [{ moved_to: foo , cookie: 4321 }]
373
- expect(silencer).to receive(:silenced?).with(foo, 'File') { false }
374
- smooshed = listener.send :_smoosh_changes, changes
370
+ foo = instance_double(
371
+ Pathname,
372
+ to_s: 'foo',
373
+ exist?: true,
374
+ directory?: false)
375
+
376
+ changes = [[:file, :moved_to, foo, cookie: 4321]]
377
+ expect(silencer).to receive(:silenced?).with(foo, :file) { false }
378
+ smooshed = subject.send :_smoosh_changes, changes
375
379
  expect(smooshed).to eq(modified: [], added: ['foo'], removed: [])
376
380
  end
377
381
 
378
382
  it 'recognizes related moved_to as add' do
379
- foo = double(:foo, to_s: 'foo', exist?: true, directory?: false)
380
- bar = double(:bar, to_s: 'bar', exist?: true, directory?: false)
383
+ foo = instance_double(
384
+ Pathname,
385
+ to_s: 'foo',
386
+ exist?: true,
387
+ directory?: false)
388
+
389
+ bar = instance_double(
390
+ Pathname,
391
+ to_s: 'bar',
392
+ exist?: true,
393
+ directory?: false)
394
+
381
395
  changes = [
382
- { moved_from: foo , cookie: 4321 },
383
- { moved_to: bar, cookie: 4321 }
396
+ [:file, :moved_from, foo , cookie: 4321],
397
+ [:file, :moved_to, bar, cookie: 4321]
384
398
  ]
385
399
 
386
400
  expect(silencer).to receive(:silenced?).
387
- twice.with(foo, 'File') { false }
401
+ twice.with(foo, :file) { false }
388
402
 
389
- expect(silencer).to receive(:silenced?).with(bar, 'File') { false }
390
- smooshed = listener.send :_smoosh_changes, changes
403
+ expect(silencer).to receive(:silenced?).with(bar, :file) { false }
404
+ smooshed = subject.send :_smoosh_changes, changes
391
405
  expect(smooshed).to eq(modified: [], added: ['bar'], removed: [])
392
406
  end
393
407
 
394
408
  # Scenario with workaround for editors using rename()
395
409
  it 'recognizes related moved_to with ignored moved_from as modify' do
396
410
 
397
- ignored = double(Pathname,
398
- to_s: 'foo',
399
- exist?: true,
400
- directory?: false)
411
+ ignored = instance_double(
412
+ Pathname,
413
+ to_s: 'foo',
414
+ exist?: true,
415
+ directory?: false)
416
+
417
+ foo = instance_double(
418
+ Pathname,
419
+ to_s: 'foo',
420
+ exist?: true,
421
+ directory?: false)
401
422
 
402
- foo = double(Pathname, to_s: 'foo', exist?: true, directory?: false)
403
423
  changes = [
404
- { moved_from: ignored, cookie: 4321 },
405
- { moved_to: foo , cookie: 4321 }
424
+ [:file, :moved_from, ignored, cookie: 4321],
425
+ [:file, :moved_to, foo , cookie: 4321]
406
426
  ]
407
- expect(silencer).to receive(:silenced?).with(ignored, 'File') { true }
408
- expect(silencer).to receive(:silenced?).with(foo, 'File') { false }
409
- smooshed = listener.send :_smoosh_changes, changes
427
+ expect(silencer).to receive(:silenced?).with(ignored, :file) { true }
428
+ expect(silencer).to receive(:silenced?).with(foo, :file) { false }
429
+ smooshed = subject.send :_smoosh_changes, changes
410
430
  expect(smooshed).to eq(modified: ['foo'], added: [], removed: [])
411
431
  end
412
432
  end
413
433
 
414
434
  context 'with no cookie' do
415
435
  it 'recognizes properly ignores files' do
416
- ignored = double(Pathname,
417
- to_s: 'foo',
418
- exist?: true,
419
- directory?: false)
420
-
421
- changes = [{ modified: ignored }]
422
- expect(silencer).to receive(:silenced?).with(ignored, 'File') { true }
423
- smooshed = listener.send :_smoosh_changes, changes
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
424
441
  expect(smooshed).to eq(modified: [], added: [], removed: [])
425
442
  end
426
443
  end