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.
- checksums.yaml +4 -4
- data/.gitignore +0 -0
- data/.rspec +0 -0
- data/.rubocop.yml +0 -0
- data/.travis.yml +0 -1
- data/.yardopts +0 -0
- data/CHANGELOG.md +0 -0
- data/CONTRIBUTING.md +0 -0
- data/Gemfile +25 -4
- data/Guardfile +0 -0
- data/LICENSE.txt +0 -0
- data/README.md +18 -10
- data/Rakefile +0 -0
- data/lib/listen.rb +2 -4
- data/lib/listen/adapter.rb +13 -4
- data/lib/listen/adapter/base.rb +33 -16
- data/lib/listen/adapter/bsd.rb +21 -38
- data/lib/listen/adapter/darwin.rb +17 -25
- data/lib/listen/adapter/linux.rb +34 -52
- data/lib/listen/adapter/polling.rb +9 -25
- data/lib/listen/adapter/tcp.rb +27 -14
- data/lib/listen/adapter/windows.rb +67 -23
- data/lib/listen/change.rb +26 -23
- data/lib/listen/cli.rb +0 -0
- data/lib/listen/directory.rb +47 -58
- data/lib/listen/file.rb +66 -101
- data/lib/listen/listener.rb +214 -155
- data/lib/listen/queue_optimizer.rb +104 -0
- data/lib/listen/record.rb +15 -5
- data/lib/listen/silencer.rb +14 -10
- data/lib/listen/tcp.rb +0 -1
- data/lib/listen/tcp/broadcaster.rb +31 -26
- data/lib/listen/tcp/message.rb +2 -2
- data/lib/listen/version.rb +1 -1
- data/listen.gemspec +1 -1
- data/spec/acceptance/listen_spec.rb +151 -239
- data/spec/acceptance/tcp_spec.rb +125 -134
- data/spec/lib/listen/adapter/base_spec.rb +13 -30
- data/spec/lib/listen/adapter/bsd_spec.rb +7 -35
- data/spec/lib/listen/adapter/darwin_spec.rb +18 -30
- data/spec/lib/listen/adapter/linux_spec.rb +49 -55
- data/spec/lib/listen/adapter/polling_spec.rb +20 -35
- data/spec/lib/listen/adapter/tcp_spec.rb +25 -27
- data/spec/lib/listen/adapter/windows_spec.rb +7 -33
- data/spec/lib/listen/adapter_spec.rb +10 -10
- data/spec/lib/listen/change_spec.rb +55 -57
- data/spec/lib/listen/directory_spec.rb +105 -155
- data/spec/lib/listen/file_spec.rb +186 -73
- data/spec/lib/listen/listener_spec.rb +233 -216
- data/spec/lib/listen/record_spec.rb +60 -22
- data/spec/lib/listen/silencer_spec.rb +48 -75
- data/spec/lib/listen/tcp/broadcaster_spec.rb +78 -69
- data/spec/lib/listen/tcp/listener_spec.rb +28 -71
- data/spec/lib/listen/tcp/message_spec.rb +48 -14
- data/spec/lib/listen_spec.rb +3 -3
- data/spec/spec_helper.rb +6 -3
- data/spec/support/acceptance_helper.rb +250 -31
- data/spec/support/fixtures_helper.rb +6 -4
- data/spec/support/platform_helper.rb +2 -2
- metadata +5 -5
- 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(:
|
5
|
-
|
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
|
-
|
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.
|
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
|
-
|
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
|
-
{
|
30
|
+
{ mtime: kind_of(Float), mode: kind_of(Integer) }
|
21
31
|
end
|
22
32
|
|
23
|
-
context '
|
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:
|
39
|
+
{ type: :file,
|
30
40
|
mtime: record_mtime,
|
31
41
|
md5: record_md5,
|
32
42
|
mode: record_mode }
|
33
43
|
end
|
34
44
|
|
35
|
-
|
36
|
-
|
37
|
-
|
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(
|
45
|
-
|
51
|
+
expect(async_record).to receive(:unset_path).with(file_path)
|
52
|
+
subject
|
46
53
|
end
|
47
54
|
end
|
48
55
|
|
49
|
-
context 'existing
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
56
|
-
|
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
|
-
|
59
|
-
|
60
|
-
end
|
78
|
+
context 'with different mode in record' do
|
79
|
+
let(:record_mode) { 0722 }
|
61
80
|
|
62
|
-
it
|
63
|
-
expect(record.async).to receive(:set_path).
|
64
|
-
with(file_path, expected_data)
|
81
|
+
it { should be :modified }
|
65
82
|
|
66
|
-
|
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
|
71
|
-
let(:
|
72
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
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 '
|
81
|
-
let(:
|
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 '
|
84
|
-
expect(
|
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 '
|
89
|
-
|
90
|
-
|
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
|
-
|
95
|
-
|
96
|
-
let(:
|
97
|
-
let(:
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
105
|
-
|
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
|
-
|
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 '
|
121
|
-
|
198
|
+
context 'with empty record' do
|
199
|
+
let(:record_data) { {} }
|
122
200
|
|
123
|
-
context 'existing path' do
|
124
|
-
|
125
|
-
|
126
|
-
|
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(
|
215
|
+
expect(subject).to eq :added
|
131
216
|
end
|
132
217
|
|
133
218
|
it 'sets path in record with expected data' do
|
134
|
-
expect(
|
135
|
-
with(file_path, expected_data)
|
219
|
+
expect(async_record).to receive(:set_path).
|
220
|
+
with(:file, file_path, expected_data)
|
136
221
|
|
137
|
-
|
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
|
-
|
4
|
+
subject { described_class.new(options) }
|
5
5
|
let(:options) { {} }
|
6
|
-
let(:registry) {
|
6
|
+
let(:registry) { instance_double(Celluloid::Registry, :[]= => true) }
|
7
7
|
|
8
8
|
let(:supervisor) do
|
9
|
-
|
10
|
-
add: true,
|
11
|
-
pool: true)
|
9
|
+
instance_double(Celluloid::SupervisionGroup, add: true, pool: true)
|
12
10
|
end
|
13
11
|
|
14
|
-
let(:record) {
|
15
|
-
let(:silencer) {
|
16
|
-
let(:
|
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.
|
22
|
-
Celluloid::SupervisionGroup.
|
23
|
-
registry.
|
24
|
-
registry.
|
25
|
-
registry.
|
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
|
32
|
-
expect(listener).not_to be_paused
|
33
|
-
end
|
24
|
+
it { should_not be_paused }
|
34
25
|
|
35
|
-
|
36
|
-
block
|
37
|
-
|
38
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
75
|
-
silencer.
|
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:
|
80
|
+
with(Listen::Silencer, as: :silencer, args: subject)
|
81
81
|
|
82
|
-
|
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:
|
87
|
+
with(Listen::Change, as: :change_pool, args: subject)
|
88
88
|
|
89
|
-
|
89
|
+
subject.start
|
90
90
|
end
|
91
91
|
|
92
92
|
it 'supervises adaper' do
|
93
|
-
Listen::Adapter.
|
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:
|
95
|
+
with(Listen::Adapter::Polling, as: :adapter, args: subject)
|
96
96
|
|
97
|
-
|
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:
|
102
|
+
with(Listen::Record, as: :record, args: subject)
|
103
103
|
|
104
|
-
|
104
|
+
subject.start
|
105
105
|
end
|
106
106
|
|
107
107
|
it 'builds record' do
|
108
108
|
expect(record).to receive(:build)
|
109
|
-
|
109
|
+
subject.start
|
110
110
|
end
|
111
111
|
|
112
112
|
it 'sets paused to false' do
|
113
|
-
|
114
|
-
expect(
|
113
|
+
subject.start
|
114
|
+
expect(subject).to_not be_paused
|
115
115
|
end
|
116
116
|
|
117
|
-
it 'starts adapter
|
118
|
-
|
119
|
-
|
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 =
|
133
|
-
|
134
|
-
block_stub =
|
135
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
153
|
-
expect(
|
149
|
+
subject.pause
|
150
|
+
expect(subject).to be_paused
|
154
151
|
end
|
155
152
|
end
|
156
153
|
|
157
154
|
describe '#unpause' do
|
158
|
-
|
159
|
-
|
160
|
-
|
155
|
+
before do
|
156
|
+
subject.start
|
157
|
+
subject.pause
|
161
158
|
end
|
162
159
|
|
163
160
|
it 'sets paused to false' do
|
164
|
-
|
165
|
-
|
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
|
-
|
173
|
-
expect(
|
169
|
+
subject.paused = true
|
170
|
+
expect(subject).to be_paused
|
174
171
|
end
|
175
172
|
it 'returns false when not paused (nil)' do
|
176
|
-
|
177
|
-
expect(
|
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
|
-
|
181
|
-
expect(
|
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
|
-
|
187
|
-
|
188
|
-
|
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
|
-
|
192
|
-
|
193
|
-
|
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
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
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) {
|
210
|
-
before { Celluloid::Actor.
|
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(
|
206
|
+
expect(Listen::Silencer).to receive(:new).with(subject) { new_silencer }
|
214
207
|
expect(registry).to receive(:[]=).with(:silencer, new_silencer)
|
215
|
-
|
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(
|
223
|
-
|
224
|
-
expect(
|
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(
|
233
|
-
|
234
|
-
expect(
|
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) {
|
241
|
-
before { Celluloid::Actor.
|
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(
|
237
|
+
expect(Listen::Silencer).to receive(:new).with(subject) { new_silencer }
|
245
238
|
expect(registry).to receive(:[]=).with(:silencer, new_silencer)
|
246
|
-
|
247
|
-
expect(
|
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(
|
255
|
-
|
256
|
-
expect(
|
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(
|
265
|
-
|
266
|
-
expect(
|
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) {
|
273
|
-
before { Celluloid::Actor.
|
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(
|
269
|
+
expect(Listen::Silencer).to receive(:new).with(subject) { new_silencer }
|
277
270
|
expect(registry).to receive(:[]=).with(:silencer, new_silencer)
|
278
|
-
|
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(
|
286
|
-
|
287
|
-
expect(
|
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.
|
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
|
-
|
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 =
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
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 =
|
315
|
+
path = instance_double(
|
316
|
+
Pathname,
|
317
|
+
to_s: 'foo',
|
318
|
+
exist?: true,
|
319
|
+
directory?: false)
|
320
|
+
|
332
321
|
changes = [
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
322
|
+
[:file, :modified, path],
|
323
|
+
[:file, :removed, path],
|
324
|
+
[:file, :added, path],
|
325
|
+
[:file, :modified, path]
|
337
326
|
]
|
338
|
-
silencer.
|
339
|
-
smooshed =
|
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 =
|
333
|
+
path = instance_double(
|
334
|
+
Pathname,
|
335
|
+
to_s: 'foo',
|
336
|
+
exist?: false,
|
337
|
+
directory?: false)
|
338
|
+
|
345
339
|
changes = [
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
340
|
+
[:file, :added, path],
|
341
|
+
[:file, :modified, path],
|
342
|
+
[:file, :removed, path],
|
343
|
+
[:file, :modified, path]
|
350
344
|
]
|
351
|
-
silencer.
|
352
|
-
smooshed =
|
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 =
|
352
|
+
path = instance_double(
|
353
|
+
Pathname,
|
354
|
+
to_s: 'foo',
|
355
|
+
exist?: true,
|
356
|
+
directory?: false)
|
357
|
+
|
359
358
|
changes = [
|
360
|
-
|
361
|
-
|
359
|
+
[:file, :removed, path],
|
360
|
+
[:file, :added, path]
|
362
361
|
]
|
363
|
-
silencer.
|
364
|
-
smooshed =
|
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 =
|
372
|
-
|
373
|
-
|
374
|
-
|
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 =
|
380
|
-
|
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
|
-
|
383
|
-
|
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,
|
401
|
+
twice.with(foo, :file) { false }
|
388
402
|
|
389
|
-
expect(silencer).to receive(:silenced?).with(bar,
|
390
|
-
smooshed =
|
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 =
|
398
|
-
|
399
|
-
|
400
|
-
|
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
|
-
|
405
|
-
|
424
|
+
[:file, :moved_from, ignored, cookie: 4321],
|
425
|
+
[:file, :moved_to, foo , cookie: 4321]
|
406
426
|
]
|
407
|
-
expect(silencer).to receive(:silenced?).with(ignored,
|
408
|
-
expect(silencer).to receive(:silenced?).with(foo,
|
409
|
-
smooshed =
|
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 =
|
417
|
-
|
418
|
-
|
419
|
-
|
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
|