fluid_cli 0.1.5 → 0.1.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/lib/fluid_cli/api.rb +9 -3
- data/lib/fluid_cli/http_request.rb +7 -1
- data/lib/fluid_cli/theme/syncer.rb +93 -13
- data/lib/fluid_cli/version.rb +1 -1
- data/lib/fluid_cli.rb +1 -0
- metadata +16 -97
- data/vendor/deps/listen/.github/release-drafter.yml +0 -17
- data/vendor/deps/listen/.github/workflows/development.yml +0 -67
- data/vendor/deps/listen/.github/workflows/push.yml +0 -12
- data/vendor/deps/listen/.gitignore +0 -28
- data/vendor/deps/listen/.rspec +0 -3
- data/vendor/deps/listen/.rubocop.yml +0 -283
- data/vendor/deps/listen/.yardopts +0 -11
- data/vendor/deps/listen/CHANGELOG.md +0 -1
- data/vendor/deps/listen/CONTRIBUTING.md +0 -45
- data/vendor/deps/listen/Gemfile +0 -33
- data/vendor/deps/listen/Guardfile +0 -26
- data/vendor/deps/listen/LICENSE.txt +0 -22
- data/vendor/deps/listen/README.md +0 -490
- data/vendor/deps/listen/Rakefile +0 -154
- data/vendor/deps/listen/bin/listen +0 -11
- data/vendor/deps/listen/lib/listen/adapter/base.rb +0 -129
- data/vendor/deps/listen/lib/listen/adapter/bsd.rb +0 -104
- data/vendor/deps/listen/lib/listen/adapter/config.rb +0 -31
- data/vendor/deps/listen/lib/listen/adapter/darwin.rb +0 -77
- data/vendor/deps/listen/lib/listen/adapter/linux.rb +0 -108
- data/vendor/deps/listen/lib/listen/adapter/polling.rb +0 -40
- data/vendor/deps/listen/lib/listen/adapter/windows.rb +0 -96
- data/vendor/deps/listen/lib/listen/adapter.rb +0 -43
- data/vendor/deps/listen/lib/listen/backend.rb +0 -40
- data/vendor/deps/listen/lib/listen/change.rb +0 -69
- data/vendor/deps/listen/lib/listen/cli.rb +0 -65
- data/vendor/deps/listen/lib/listen/directory.rb +0 -93
- data/vendor/deps/listen/lib/listen/error.rb +0 -11
- data/vendor/deps/listen/lib/listen/event/config.rb +0 -39
- data/vendor/deps/listen/lib/listen/event/loop.rb +0 -92
- data/vendor/deps/listen/lib/listen/event/processor.rb +0 -128
- data/vendor/deps/listen/lib/listen/event/queue.rb +0 -52
- data/vendor/deps/listen/lib/listen/file.rb +0 -95
- data/vendor/deps/listen/lib/listen/fsm.rb +0 -131
- data/vendor/deps/listen/lib/listen/listener/config.rb +0 -41
- data/vendor/deps/listen/lib/listen/listener.rb +0 -136
- data/vendor/deps/listen/lib/listen/logger.rb +0 -65
- data/vendor/deps/listen/lib/listen/monotonic_time.rb +0 -27
- data/vendor/deps/listen/lib/listen/options.rb +0 -24
- data/vendor/deps/listen/lib/listen/queue_optimizer.rb +0 -129
- data/vendor/deps/listen/lib/listen/record/entry.rb +0 -66
- data/vendor/deps/listen/lib/listen/record/symlink_detector.rb +0 -47
- data/vendor/deps/listen/lib/listen/record.rb +0 -122
- data/vendor/deps/listen/lib/listen/silencer/controller.rb +0 -50
- data/vendor/deps/listen/lib/listen/silencer.rb +0 -106
- data/vendor/deps/listen/lib/listen/thread.rb +0 -54
- data/vendor/deps/listen/lib/listen/version.rb +0 -5
- data/vendor/deps/listen/lib/listen.rb +0 -47
- data/vendor/deps/listen/listen.gemspec +0 -40
- data/vendor/deps/listen/spec/acceptance/listen_spec.rb +0 -320
- data/vendor/deps/listen/spec/lib/listen/adapter/base_spec.rb +0 -101
- data/vendor/deps/listen/spec/lib/listen/adapter/bsd_spec.rb +0 -13
- data/vendor/deps/listen/spec/lib/listen/adapter/config_spec.rb +0 -122
- data/vendor/deps/listen/spec/lib/listen/adapter/darwin_spec.rb +0 -82
- data/vendor/deps/listen/spec/lib/listen/adapter/linux_spec.rb +0 -199
- data/vendor/deps/listen/spec/lib/listen/adapter/polling_spec.rb +0 -83
- data/vendor/deps/listen/spec/lib/listen/adapter/windows_spec.rb +0 -13
- data/vendor/deps/listen/spec/lib/listen/adapter_spec.rb +0 -69
- data/vendor/deps/listen/spec/lib/listen/backend_spec.rb +0 -82
- data/vendor/deps/listen/spec/lib/listen/change_spec.rb +0 -102
- data/vendor/deps/listen/spec/lib/listen/cli_spec.rb +0 -116
- data/vendor/deps/listen/spec/lib/listen/directory_spec.rb +0 -284
- data/vendor/deps/listen/spec/lib/listen/event/config_spec.rb +0 -33
- data/vendor/deps/listen/spec/lib/listen/event/loop_spec.rb +0 -118
- data/vendor/deps/listen/spec/lib/listen/event/processor_spec.rb +0 -250
- data/vendor/deps/listen/spec/lib/listen/event/queue_spec.rb +0 -118
- data/vendor/deps/listen/spec/lib/listen/file_spec.rb +0 -254
- data/vendor/deps/listen/spec/lib/listen/fsm_spec.rb +0 -147
- data/vendor/deps/listen/spec/lib/listen/listener/config_spec.rb +0 -29
- data/vendor/deps/listen/spec/lib/listen/listener_spec.rb +0 -321
- data/vendor/deps/listen/spec/lib/listen/logger_spec.rb +0 -212
- data/vendor/deps/listen/spec/lib/listen/monotonic_time_spec.rb +0 -58
- data/vendor/deps/listen/spec/lib/listen/queue_optimizer_spec.rb +0 -111
- data/vendor/deps/listen/spec/lib/listen/record_spec.rb +0 -424
- data/vendor/deps/listen/spec/lib/listen/silencer/controller_spec.rb +0 -97
- data/vendor/deps/listen/spec/lib/listen/silencer_spec.rb +0 -109
- data/vendor/deps/listen/spec/lib/listen/thread_spec.rb +0 -133
- data/vendor/deps/listen/spec/lib/listen_spec.rb +0 -25
- data/vendor/deps/listen/spec/spec_helper.rb +0 -49
- data/vendor/deps/listen/spec/support/acceptance_helper.rb +0 -260
- data/vendor/deps/listen/spec/support/fixtures_helper.rb +0 -32
- data/vendor/deps/listen/spec/support/platform_helper.rb +0 -17
- data/vendor/deps/observer/.github/dependabot.yml +0 -6
- data/vendor/deps/observer/.github/workflows/test.yml +0 -33
- data/vendor/deps/observer/.gitignore +0 -8
- data/vendor/deps/observer/BSDL +0 -22
- data/vendor/deps/observer/COPYING +0 -56
- data/vendor/deps/observer/Gemfile +0 -9
- data/vendor/deps/observer/README.md +0 -139
- data/vendor/deps/observer/Rakefile +0 -10
- data/vendor/deps/observer/bin/console +0 -14
- data/vendor/deps/observer/bin/setup +0 -8
- data/vendor/deps/observer/lib/observer.rb +0 -229
- data/vendor/deps/observer/observer.gemspec +0 -32
- data/vendor/deps/observer/test/test_observer.rb +0 -66
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
include Listen
|
|
4
|
-
|
|
5
|
-
RSpec.describe Directory do
|
|
6
|
-
def fake_file_stat(name, options = {})
|
|
7
|
-
defaults = { directory?: false }
|
|
8
|
-
instance_double(::File::Stat, name, defaults.merge(options))
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def fake_dir_stat(name, options = {})
|
|
12
|
-
defaults = { directory?: true }
|
|
13
|
-
instance_double(::File::Stat, name, defaults.merge(options))
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def fake_children(exception, dir, *args, &block)
|
|
17
|
-
if block_given?
|
|
18
|
-
exception.send(:allow, dir).to receive(:children, &block)
|
|
19
|
-
else
|
|
20
|
-
exception.send(:allow, dir).to receive(:children).and_return(*args)
|
|
21
|
-
end
|
|
22
|
-
exception.send(:allow, dir).to receive(:exist?).and_return(true)
|
|
23
|
-
exception.send(:allow, dir).to receive(:directory?).and_return(true)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
let(:dir) { double(:dir) }
|
|
27
|
-
let(:file) { fake_path('file.rb') }
|
|
28
|
-
let(:file2) { fake_path('file2.rb') }
|
|
29
|
-
let(:subdir) { fake_path('subdir') }
|
|
30
|
-
|
|
31
|
-
let(:record) do
|
|
32
|
-
instance_double(
|
|
33
|
-
Record,
|
|
34
|
-
root: 'some_dir',
|
|
35
|
-
dir_entries: record_entries,
|
|
36
|
-
add_dir: true,
|
|
37
|
-
unset_path: true)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
let(:snapshot) { instance_double(Change, record: record, invalidate: nil) }
|
|
41
|
-
|
|
42
|
-
before do
|
|
43
|
-
allow(dir).to receive(:+).with('.') { dir }
|
|
44
|
-
allow(dir).to receive(:+).with('file.rb') { file }
|
|
45
|
-
allow(dir).to receive(:+).with('subdir') { subdir }
|
|
46
|
-
|
|
47
|
-
allow(file).to receive(:relative_path_from).with(dir) { 'file.rb' }
|
|
48
|
-
allow(file2).to receive(:relative_path_from).with(dir) { 'file2.rb' }
|
|
49
|
-
allow(subdir).to receive(:relative_path_from).with(dir) { 'subdir' }
|
|
50
|
-
|
|
51
|
-
allow(Pathname).to receive(:new).with('some_dir').and_return(dir)
|
|
52
|
-
allow(Pathname).to receive(:new).with('.').and_return(dir)
|
|
53
|
-
|
|
54
|
-
allow(::File).to receive(:lstat) do |*args|
|
|
55
|
-
fail "Not stubbed: File.lstat(#{args.map(&:inspect) * ','})"
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
context '#scan with recursive off' do
|
|
60
|
-
let(:options) { { recursive: false } }
|
|
61
|
-
|
|
62
|
-
context 'with file & subdir in record' do
|
|
63
|
-
let(:record_entries) do
|
|
64
|
-
{ 'file.rb' => { mtime: 1.1 }, 'subdir' => {} }.freeze
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
context 'with empty dir' do
|
|
68
|
-
before { fake_children(self, dir, []) }
|
|
69
|
-
|
|
70
|
-
it 'sets record dir path' do
|
|
71
|
-
expect(record).to receive(:add_dir).with('.')
|
|
72
|
-
described_class.scan(snapshot, '.', options)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
it "snapshots changes for file path and dir that doesn't exist" do
|
|
76
|
-
expect(snapshot).to receive(:invalidate).with(:file, 'file.rb', {})
|
|
77
|
-
|
|
78
|
-
expect(snapshot).to receive(:invalidate).
|
|
79
|
-
with(:dir, 'subdir', { recursive: false })
|
|
80
|
-
|
|
81
|
-
described_class.scan(snapshot, '.', options)
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
context 'when subdir is removed' do
|
|
86
|
-
before do
|
|
87
|
-
fake_children(self, dir, [file])
|
|
88
|
-
allow(::File).to receive(:lstat).with('file.rb').
|
|
89
|
-
and_return(fake_file_stat('file.rb'))
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
it 'notices subdir does not exist' do
|
|
93
|
-
expect(snapshot).to receive(:invalidate).
|
|
94
|
-
with(:dir, 'subdir', { recursive: false })
|
|
95
|
-
|
|
96
|
-
described_class.scan(snapshot, '.', options)
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
context 'when file.rb removed' do
|
|
101
|
-
before do
|
|
102
|
-
fake_children(self, dir, [subdir])
|
|
103
|
-
|
|
104
|
-
allow(::File).to receive(:lstat).with('subdir').
|
|
105
|
-
and_return(fake_dir_stat('subdir'))
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
it 'notices file was removed' do
|
|
109
|
-
expect(snapshot).to receive(:invalidate).with(:file, 'file.rb', {})
|
|
110
|
-
described_class.scan(snapshot, '.', options)
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
context 'when file.rb no longer exists after scan' do
|
|
115
|
-
before do
|
|
116
|
-
fake_children(self, dir, [file], [file2])
|
|
117
|
-
|
|
118
|
-
allow(::File).to receive(:lstat).with('file.rb').
|
|
119
|
-
and_raise(Errno::ENOENT)
|
|
120
|
-
|
|
121
|
-
allow(::File).to receive(:lstat).with('file2.rb').
|
|
122
|
-
and_return(fake_file_stat('file2.rb'))
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
it 'rescans' do
|
|
126
|
-
expect(snapshot).to receive(:invalidate).with(:file, 'file2.rb', {})
|
|
127
|
-
described_class.scan(snapshot, '.', options)
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
context 'when file2.rb is added' do
|
|
132
|
-
before do
|
|
133
|
-
fake_children(self, dir, [file, file2, subdir])
|
|
134
|
-
|
|
135
|
-
allow(::File).to receive(:lstat).with('file.rb').
|
|
136
|
-
and_return(fake_file_stat('file.rb'))
|
|
137
|
-
|
|
138
|
-
allow(::File).to receive(:lstat).with('file2.rb').
|
|
139
|
-
and_return(fake_file_stat('file2.rb'))
|
|
140
|
-
|
|
141
|
-
allow(::File).to receive(:lstat).with('subdir').
|
|
142
|
-
and_return(fake_dir_stat('subdir'))
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
it 'notices file removed and file2 changed' do
|
|
146
|
-
expect(snapshot).to receive(:invalidate).with(:file, 'file2.rb', {})
|
|
147
|
-
described_class.scan(snapshot, '.', options)
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
context 'with empty record' do
|
|
153
|
-
let(:record_entries) { {} }
|
|
154
|
-
|
|
155
|
-
context 'with non-existing dir path' do
|
|
156
|
-
before { fake_children(self, dir) { fail Errno::ENOENT } }
|
|
157
|
-
|
|
158
|
-
it 'reports no changes' do
|
|
159
|
-
expect(snapshot).to_not receive(:invalidate)
|
|
160
|
-
described_class.scan(snapshot, '.', options)
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
it 'unsets record dir path' do
|
|
164
|
-
expect(record).to receive(:unset_path).with('.')
|
|
165
|
-
described_class.scan(snapshot, '.', options)
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
context 'when network share is disconnected' do
|
|
170
|
-
before { fake_children(self, dir) { fail Errno::EHOSTDOWN } }
|
|
171
|
-
|
|
172
|
-
it 'reports no changes' do
|
|
173
|
-
expect(snapshot).to_not receive(:invalidate)
|
|
174
|
-
described_class.scan(snapshot, '.', options)
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
it 'unsets record dir path' do
|
|
178
|
-
expect(record).to receive(:unset_path).with('.')
|
|
179
|
-
described_class.scan(snapshot, '.', options)
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
context 'with file.rb in dir' do
|
|
184
|
-
before do
|
|
185
|
-
fake_children(self, dir, [file])
|
|
186
|
-
|
|
187
|
-
allow(::File).to receive(:lstat).with('file.rb').
|
|
188
|
-
and_return(fake_file_stat('file.rb'))
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
it 'snapshots changes for file & file2 paths' do
|
|
192
|
-
expect(snapshot).to receive(:invalidate).
|
|
193
|
-
with(:file, 'file.rb', {})
|
|
194
|
-
|
|
195
|
-
expect(snapshot).to_not receive(:invalidate).
|
|
196
|
-
with(:file, 'file2.rb', {})
|
|
197
|
-
|
|
198
|
-
expect(snapshot).to_not receive(:invalidate).
|
|
199
|
-
with(:dir, 'subdir', recursive: false)
|
|
200
|
-
|
|
201
|
-
described_class.scan(snapshot, '.', options)
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
context '#scan with recursive on' do
|
|
208
|
-
let(:options) { { recursive: true } }
|
|
209
|
-
|
|
210
|
-
context 'with file.rb & subdir in record' do
|
|
211
|
-
let(:record_entries) do
|
|
212
|
-
{ 'file.rb' => { mtime: 1.1 }, 'subdir' => {} }
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
context 'with empty dir' do
|
|
216
|
-
before { fake_children(self, dir, []) }
|
|
217
|
-
|
|
218
|
-
it 'snapshots changes for file & subdir path' do
|
|
219
|
-
expect(snapshot).to receive(:invalidate).with(:file, 'file.rb', {})
|
|
220
|
-
|
|
221
|
-
expect(snapshot).to receive(:invalidate).
|
|
222
|
-
with(:dir, 'subdir', { recursive: true })
|
|
223
|
-
|
|
224
|
-
described_class.scan(snapshot, '.', options)
|
|
225
|
-
end
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
context 'with subdir2 path present' do
|
|
229
|
-
let(:subdir2) { fake_path('subdir2', children: []) }
|
|
230
|
-
|
|
231
|
-
before do
|
|
232
|
-
fake_children(self, dir, [subdir2])
|
|
233
|
-
allow(subdir2).to receive(:relative_path_from).with(dir) { 'subdir2' }
|
|
234
|
-
|
|
235
|
-
allow(::File).to receive(:lstat).with('subdir2').
|
|
236
|
-
and_return(fake_dir_stat('subdir2'))
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
it 'snapshots changes for file, file2 & subdir paths' do
|
|
240
|
-
expect(snapshot).to receive(:invalidate).with(:file, 'file.rb', {})
|
|
241
|
-
|
|
242
|
-
expect(snapshot).to receive(:invalidate).
|
|
243
|
-
with(:dir, 'subdir', { recursive: true })
|
|
244
|
-
|
|
245
|
-
expect(snapshot).to receive(:invalidate).
|
|
246
|
-
with(:dir, 'subdir2', { recursive: true })
|
|
247
|
-
|
|
248
|
-
described_class.scan(snapshot, '.', options)
|
|
249
|
-
end
|
|
250
|
-
end
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
context 'with empty record' do
|
|
254
|
-
let(:record_entries) { {} }
|
|
255
|
-
|
|
256
|
-
context 'with non-existing dir' do
|
|
257
|
-
before do
|
|
258
|
-
fake_children(self, dir) { fail Errno::ENOENT }
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
it 'reports no changes' do
|
|
262
|
-
expect(snapshot).to_not receive(:invalidate)
|
|
263
|
-
described_class.scan(snapshot, '.', options)
|
|
264
|
-
end
|
|
265
|
-
end
|
|
266
|
-
|
|
267
|
-
context 'with subdir present in dir' do
|
|
268
|
-
before do
|
|
269
|
-
fake_children(self, dir, [subdir])
|
|
270
|
-
fake_children(self, subdir, [])
|
|
271
|
-
allow(::File).to receive(:lstat).with('subdir').
|
|
272
|
-
and_return(fake_dir_stat('subdir'))
|
|
273
|
-
end
|
|
274
|
-
|
|
275
|
-
it 'snapshots changes for subdir' do
|
|
276
|
-
expect(snapshot).to receive(:invalidate).
|
|
277
|
-
with(:dir, 'subdir', { recursive: true })
|
|
278
|
-
|
|
279
|
-
described_class.scan(snapshot, '.', options)
|
|
280
|
-
end
|
|
281
|
-
end
|
|
282
|
-
end
|
|
283
|
-
end
|
|
284
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'listen/event/config'
|
|
4
|
-
|
|
5
|
-
RSpec.describe Listen::Event::Config do
|
|
6
|
-
let(:listener) { instance_double(Listen::Listener) }
|
|
7
|
-
let(:event_queue) { instance_double(Listen::Event::Queue) }
|
|
8
|
-
let(:queue_optimizer) { instance_double(Listen::QueueOptimizer) }
|
|
9
|
-
let(:wait_for_delay) { 1.234 }
|
|
10
|
-
|
|
11
|
-
context 'with a given block' do
|
|
12
|
-
let(:myblock) { instance_double(Proc) }
|
|
13
|
-
|
|
14
|
-
subject do
|
|
15
|
-
described_class.new(
|
|
16
|
-
listener,
|
|
17
|
-
event_queue,
|
|
18
|
-
queue_optimizer,
|
|
19
|
-
wait_for_delay) do |*args|
|
|
20
|
-
myblock.call(*args)
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
it 'calls the block' do
|
|
25
|
-
expect(myblock).to receive(:call).with(:foo, :bar)
|
|
26
|
-
subject.call(:foo, :bar)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
it 'is callable' do
|
|
30
|
-
expect(subject).to be_callable
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'thread'
|
|
4
|
-
require 'listen/event/config'
|
|
5
|
-
require 'listen/event/loop'
|
|
6
|
-
|
|
7
|
-
RSpec.describe Listen::Event::Loop do
|
|
8
|
-
let(:config) { instance_double(Listen::Event::Config, 'config') }
|
|
9
|
-
let(:processor) { instance_double(Listen::Event::Processor, 'processor') }
|
|
10
|
-
let(:thread) { instance_double(Thread, 'thread') }
|
|
11
|
-
|
|
12
|
-
let(:reasons) { instance_double(::Queue, 'reasons') }
|
|
13
|
-
let(:ready) { instance_double(::Queue, 'ready') }
|
|
14
|
-
|
|
15
|
-
let(:blocks) do
|
|
16
|
-
{
|
|
17
|
-
thread_block: proc { fail 'thread block stub called' },
|
|
18
|
-
}
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
subject { described_class.new(config) }
|
|
22
|
-
|
|
23
|
-
# TODO: this is hideous
|
|
24
|
-
before do
|
|
25
|
-
allow(::Queue).to receive(:new).and_return(reasons, ready)
|
|
26
|
-
allow(Listen::Event::Processor).to receive(:new).with(config, reasons).
|
|
27
|
-
and_return(processor)
|
|
28
|
-
|
|
29
|
-
allow(Thread).to receive(:new) do |*args, &block|
|
|
30
|
-
fail 'Unstubbed call:'\
|
|
31
|
-
" Thread.new(#{args.map(&:inspect) * ','},&#{block.inspect})"
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
allow(config).to receive(:min_delay_between_events).and_return(1.234)
|
|
35
|
-
|
|
36
|
-
allow(thread).to receive(:name=)
|
|
37
|
-
allow(Thread).to receive(:new) do |*_, &block|
|
|
38
|
-
blocks[:thread_block] = block
|
|
39
|
-
thread
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
allow(Kernel).to receive(:sleep) do |*args|
|
|
43
|
-
fail "stub called: sleep(#{args.map(&:inspect) * ','})"
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
describe '#start' do
|
|
48
|
-
it 'is started' do
|
|
49
|
-
expect(processor).to receive(:loop_for).with(1.234)
|
|
50
|
-
expect(Thread).to receive(:new) do |&block|
|
|
51
|
-
block.call
|
|
52
|
-
thread
|
|
53
|
-
end
|
|
54
|
-
subject.start
|
|
55
|
-
expect(subject).to be_started
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
context 'when start is called again' do
|
|
59
|
-
it 'returns silently' do
|
|
60
|
-
expect(processor).to receive(:loop_for).with(1.234)
|
|
61
|
-
expect(Thread).to receive(:new) do |&block|
|
|
62
|
-
block.call
|
|
63
|
-
thread
|
|
64
|
-
end
|
|
65
|
-
subject.start
|
|
66
|
-
expect { subject.start }.to_not raise_exception
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
context 'when state change to :started takes longer than 5 seconds' do
|
|
71
|
-
before do
|
|
72
|
-
expect(Thread).to receive(:new) { thread }
|
|
73
|
-
expect_any_instance_of(::ConditionVariable).to receive(:wait) { } # return immediately
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
it 'raises Error::NotStarted' do
|
|
77
|
-
expect do
|
|
78
|
-
subject.start
|
|
79
|
-
end.to raise_exception(::Listen::Error::NotStarted, "thread didn't start in 5.0 seconds (in state: :starting)")
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
context 'when set up / started' do
|
|
85
|
-
before do
|
|
86
|
-
allow(thread).to receive(:alive?).and_return(true)
|
|
87
|
-
allow(config).to receive(:min_delay_between_events).and_return(1.234)
|
|
88
|
-
|
|
89
|
-
allow(processor).to receive(:loop_for).with(1.234)
|
|
90
|
-
|
|
91
|
-
expect(Thread).to receive(:new) do |&block|
|
|
92
|
-
block.call
|
|
93
|
-
thread
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
subject.start
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
describe '#stop' do
|
|
100
|
-
before do
|
|
101
|
-
allow(thread).to receive(:join)
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
it 'frees the thread' do
|
|
105
|
-
subject.stop
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
it 'waits for the thread to finish' do
|
|
109
|
-
expect(thread).to receive(:join)
|
|
110
|
-
subject.stop
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
it 'sets the reason for waking up' do
|
|
114
|
-
subject.stop
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
end
|
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'listen/event/processor'
|
|
4
|
-
require 'listen/event/config'
|
|
5
|
-
|
|
6
|
-
RSpec.describe Listen::Event::Processor do
|
|
7
|
-
let(:event_queue) { instance_double(::Queue, 'event_queue') }
|
|
8
|
-
let(:event) { instance_double(::Array, 'event') }
|
|
9
|
-
let(:listener) { instance_double(Listen::Listener, 'listener') }
|
|
10
|
-
let(:config) { instance_double(Listen::Event::Config, 'config', listener: listener) }
|
|
11
|
-
let(:reasons) { instance_double(::Queue, 'reasons') }
|
|
12
|
-
|
|
13
|
-
subject { described_class.new(config, reasons) }
|
|
14
|
-
|
|
15
|
-
# This is to simulate events over various points in time
|
|
16
|
-
let(:sequence) do
|
|
17
|
-
{}
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
let(:state) do
|
|
21
|
-
{ monotonic_time: 0.0 }
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def status_for_time(time)
|
|
25
|
-
# find the status of the listener for a given point in time
|
|
26
|
-
previous_state_timestamps = sequence.keys.reject { |k| k > time }
|
|
27
|
-
last_state_before_given_time = previous_state_timestamps.max
|
|
28
|
-
sequence[last_state_before_given_time]
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
before do
|
|
32
|
-
allow(config).to receive(:event_queue).and_return(event_queue)
|
|
33
|
-
|
|
34
|
-
allow(listener).to receive(:stopped?) do
|
|
35
|
-
status_for_time(state[:monotonic_time]) == :stopped
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
allow(listener).to receive(:paused?) do
|
|
39
|
-
status_for_time(state[:monotonic_time]) == :paused
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
allow(Listen::MonotonicTime).to receive(:now) do
|
|
43
|
-
state[:monotonic_time]
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
describe '#loop_for' do
|
|
48
|
-
before do
|
|
49
|
-
allow(reasons).to receive(:empty?).and_return(true)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
context 'when stopped' do
|
|
53
|
-
before do
|
|
54
|
-
sequence[0.0] = :stopped
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
context 'with pending changes' do
|
|
58
|
-
before do
|
|
59
|
-
allow(event_queue).to receive(:empty?).and_return(false)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
it 'does not change the event queue' do
|
|
63
|
-
expect(event_queue).to receive(:pop).and_return(event)
|
|
64
|
-
subject.loop_for(1)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
it 'does not sleep' do
|
|
68
|
-
expect(config).to_not receive(:sleep)
|
|
69
|
-
expect(event_queue).to receive(:pop).and_return(event)
|
|
70
|
-
t = Listen::MonotonicTime.now
|
|
71
|
-
subject.loop_for(1)
|
|
72
|
-
diff = Listen::MonotonicTime.now - t
|
|
73
|
-
expect(diff).to be < 0.02
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
context 'when not stopped' do
|
|
79
|
-
before do
|
|
80
|
-
allow(event_queue).to receive(:empty?).and_return(true)
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
context 'when initially paused' do
|
|
84
|
-
before do
|
|
85
|
-
sequence[0.0] = :paused
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
context 'when stopped after sleeping' do
|
|
89
|
-
before do
|
|
90
|
-
sequence[0.2] = :stopped
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
it 'sleeps, waiting to be woken up' do
|
|
94
|
-
expect(config).to receive(:sleep).once { state[:monotonic_time] = 0.6 }
|
|
95
|
-
expect(event_queue).to receive(:pop).and_return(event)
|
|
96
|
-
subject.loop_for(1)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
it 'breaks' do
|
|
100
|
-
expect(event_queue).to receive(:pop).and_return(event)
|
|
101
|
-
allow(config).to receive(:sleep).once { state[:monotonic_time] = 0.6 }
|
|
102
|
-
expect(config).to_not receive(:call)
|
|
103
|
-
subject.loop_for(1)
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
context 'when still paused after sleeping' do
|
|
108
|
-
context 'when there were no events before' do
|
|
109
|
-
before do
|
|
110
|
-
sequence[1.0] = :stopped
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
it 'sleeps for latency to possibly later optimize some events' do
|
|
114
|
-
# pretend we were woken up at 0.6 seconds since start
|
|
115
|
-
allow(config).to receive(:sleep).
|
|
116
|
-
with(anything) { |*_args| state[:monotonic_time] += 0.6 }
|
|
117
|
-
|
|
118
|
-
# pretend we slept for latency (now: 1.6 seconds since start)
|
|
119
|
-
allow(config).to receive(:sleep).
|
|
120
|
-
with(1.0) { |*_args| state[:monotonic_time] += 1.0 }
|
|
121
|
-
|
|
122
|
-
expect(event_queue).to receive(:pop).and_return(event)
|
|
123
|
-
subject.loop_for(1)
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
context 'when there were no events for ages' do
|
|
128
|
-
before do
|
|
129
|
-
sequence[3.5] = :stopped # in the future to break from the loop
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
it 'still does not process events because it is paused' do
|
|
133
|
-
# pretend we were woken up at 0.6 seconds since start
|
|
134
|
-
allow(config).to receive(:sleep).
|
|
135
|
-
with(anything) { |*_args| state[:monotonic_time] += 2.0 }
|
|
136
|
-
|
|
137
|
-
# second loop starts here (no sleep, coz recent events, but no
|
|
138
|
-
# processing coz paused
|
|
139
|
-
|
|
140
|
-
# pretend we were woken up at 3.6 seconds since start
|
|
141
|
-
allow(listener).to receive(:wait_for_state).
|
|
142
|
-
with(:initializing, :backend_started, :processing_events, :stopped) do |*_args|
|
|
143
|
-
state[:monotonic_time] += 3.0
|
|
144
|
-
raise ScriptError, 'done'
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
expect(event_queue).to receive(:pop).and_return(event)
|
|
148
|
-
expect { subject.loop_for(1) }.to raise_exception(ScriptError, 'done')
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
context 'when initially processing' do
|
|
155
|
-
before do
|
|
156
|
-
sequence[0.0] = :processing
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
context 'when event queue is empty' do
|
|
160
|
-
before do
|
|
161
|
-
allow(event_queue).to receive(:empty?).and_return(true)
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
context 'when stopped after sleeping' do
|
|
165
|
-
before do
|
|
166
|
-
sequence[0.2] = :stopped
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
it 'sleeps, waiting to be woken up' do
|
|
170
|
-
expect(event_queue).to receive(:pop).and_return(event)
|
|
171
|
-
expect(config).to receive(:sleep).
|
|
172
|
-
once { |*_args| state[:monotonic_time] = 0.6 }
|
|
173
|
-
|
|
174
|
-
subject.loop_for(1)
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
it 'breaks' do
|
|
178
|
-
allow(config).to receive(:sleep).
|
|
179
|
-
once { |*_args| state[:monotonic_time] = 0.6 }
|
|
180
|
-
|
|
181
|
-
expect(config).to_not receive(:call)
|
|
182
|
-
expect(event_queue).to receive(:pop).and_return(event)
|
|
183
|
-
subject.loop_for(1)
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
context 'when event queue has events' do
|
|
189
|
-
context 'when there were events ages ago' do
|
|
190
|
-
before do
|
|
191
|
-
sequence[3.5] = :stopped # in the future to break from the loop
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
it 'processes events' do
|
|
195
|
-
allow(event_queue).to receive(:empty?).
|
|
196
|
-
and_return(false, false, true)
|
|
197
|
-
|
|
198
|
-
# resets latency check
|
|
199
|
-
expect(config).to receive(:callable?).and_return(true)
|
|
200
|
-
|
|
201
|
-
change = [:file, :modified, 'foo', 'bar']
|
|
202
|
-
resulting_changes = { modified: ['foo'], added: [], removed: [] }
|
|
203
|
-
allow(event_queue).to receive(:pop).and_return(change).exactly(4)
|
|
204
|
-
|
|
205
|
-
allow(config).to receive(:optimize_changes).with([change, change, change]).
|
|
206
|
-
and_return(resulting_changes)
|
|
207
|
-
|
|
208
|
-
final_changes = [['foo'], [], []]
|
|
209
|
-
allow(config).to receive(:call) do |*changes|
|
|
210
|
-
state[:monotonic_time] = 4.0 # stopped
|
|
211
|
-
expect(changes).to eq(final_changes)
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
allow(listener).to receive(:wait_for_state).
|
|
215
|
-
with(:initializing, :backend_started, :processing_events, :stopped)
|
|
216
|
-
|
|
217
|
-
subject.instance_variable_set(:@_remember_time_of_first_unprocessed_event, -3)
|
|
218
|
-
subject.loop_for(1)
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
end
|
|
222
|
-
end
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
describe '_process_changes' do
|
|
227
|
-
context 'when it raises an exception derived from StandardError' do
|
|
228
|
-
before do
|
|
229
|
-
allow(event_queue).to receive(:empty?).and_return(true)
|
|
230
|
-
allow(config).to receive(:callable?).and_return(true)
|
|
231
|
-
resulting_changes = { modified: ['foo'], added: [], removed: [] }
|
|
232
|
-
allow(config).to receive(:optimize_changes).with(anything).and_return(resulting_changes)
|
|
233
|
-
expect(config).to receive(:call).and_raise(ArgumentError, "bang!")
|
|
234
|
-
expect(config).to receive(:call).and_return(nil)
|
|
235
|
-
expect(config).to receive(:call).and_raise("error!")
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
it 'rescues and logs exception and continues' do
|
|
239
|
-
expect(Listen.logger).to receive(:error).with(/Exception rescued in _process_changes:\nArgumentError: bang!/)
|
|
240
|
-
expect(Listen.logger).to receive(:error).with(/Exception rescued in _process_changes:\nRuntimeError: error!/)
|
|
241
|
-
expect(Listen.logger).to receive(:debug).with(/Callback \(exception\) took/)
|
|
242
|
-
expect(Listen.logger).to receive(:debug).with(/Callback took/)
|
|
243
|
-
expect(Listen.logger).to receive(:debug).with(/Callback \(exception\) took/)
|
|
244
|
-
subject.send(:_process_changes, event)
|
|
245
|
-
subject.send(:_process_changes, event)
|
|
246
|
-
subject.send(:_process_changes, event)
|
|
247
|
-
end
|
|
248
|
-
end
|
|
249
|
-
end
|
|
250
|
-
end
|