listen 2.7.5 → 2.7.6

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