em-dir-watcher 0.1.0 → 0.9.1
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.
- data/LICENSE +2 -1
- data/README.md +126 -9
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/examples/monitor.rb +11 -3
- data/lib/em-dir-watcher/invokers/subprocess_invoker.rb +100 -0
- data/lib/em-dir-watcher/monitor.rb +47 -0
- data/lib/em-dir-watcher/platform/linux.rb +51 -0
- data/lib/em-dir-watcher/platform/mac/ffi_fsevents_watcher.rb +80 -0
- data/lib/em-dir-watcher/platform/mac/rubycocoa_watcher.rb +51 -0
- data/lib/em-dir-watcher/platform/mac.rb +50 -0
- data/lib/em-dir-watcher/platform/windows/monitor.rb +0 -2
- data/lib/em-dir-watcher/platform/windows/path_to_ruby_exe.rb +0 -3
- data/lib/em-dir-watcher/platform/windows.rb +29 -7
- data/lib/em-dir-watcher/tree.rb +216 -0
- data/lib/em-dir-watcher.rb +4 -9
- data/test/helper.rb +9 -1
- data/test/test_monitor.rb +161 -0
- data/test/test_tree.rb +440 -0
- data/testloop +11 -0
- metadata +16 -8
- data/.document +0 -5
- data/lib/em-dir-watcher/platform/nix.rb +0 -107
- data/lib/em-dir-watcher/platform/windows/monitor-nix.rb +0 -29
- data/test/test_em-dir-watcher.rb +0 -7
data/test/test_tree.rb
ADDED
@@ -0,0 +1,440 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'eventmachine'
|
4
|
+
|
5
|
+
class TestTreeFileList < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
FileUtils.rm_rf TEST_DIR
|
9
|
+
FileUtils.mkdir_p TEST_DIR
|
10
|
+
end
|
11
|
+
|
12
|
+
should "should be empty for an empty directory" do
|
13
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
14
|
+
assert_equal "", @tree.full_file_list.join(", ").strip
|
15
|
+
end
|
16
|
+
|
17
|
+
should "should return a single file" do
|
18
|
+
FileUtils.touch File.join(TEST_DIR, 'foo')
|
19
|
+
|
20
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
21
|
+
assert_equal "foo", @tree.full_file_list.join(", ").strip
|
22
|
+
end
|
23
|
+
|
24
|
+
should "should return a file in a subdirectory" do
|
25
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar')
|
26
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'foo')
|
27
|
+
|
28
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
29
|
+
assert_equal "bar/foo", @tree.full_file_list.join(", ").strip
|
30
|
+
end
|
31
|
+
|
32
|
+
should "should return a sorted list of files" do
|
33
|
+
FileUtils.touch File.join(TEST_DIR, 'aa')
|
34
|
+
FileUtils.touch File.join(TEST_DIR, 'zz')
|
35
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar')
|
36
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'foo')
|
37
|
+
|
38
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
39
|
+
assert_equal "aa, bar/foo, zz", @tree.full_file_list.join(", ").strip
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
class TestTreeInclusions < Test::Unit::TestCase
|
45
|
+
|
46
|
+
def setup
|
47
|
+
FileUtils.rm_rf TEST_DIR
|
48
|
+
FileUtils.mkdir_p TEST_DIR
|
49
|
+
|
50
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar')
|
51
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar', 'boo')
|
52
|
+
|
53
|
+
FileUtils.touch File.join(TEST_DIR, 'aa')
|
54
|
+
FileUtils.touch File.join(TEST_DIR, 'biz')
|
55
|
+
FileUtils.touch File.join(TEST_DIR, 'zz')
|
56
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'foo')
|
57
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'biz')
|
58
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'biz.html')
|
59
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'boo', 'bizzz')
|
60
|
+
|
61
|
+
@list = ['aa', 'biz', 'zz', 'bar/foo', 'bar/biz', 'bar/biz.html', 'bar/boo/bizzz'].sort
|
62
|
+
end
|
63
|
+
|
64
|
+
should "ignore files not included by path" do
|
65
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR, ['/bar']
|
66
|
+
assert_equal join(['bar/foo', 'bar/biz', 'bar/biz.html', 'bar/boo/bizzz'].sort), join(@tree.full_file_list)
|
67
|
+
end
|
68
|
+
|
69
|
+
should "ignore files not included by extension glob" do
|
70
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR, ['*.html']
|
71
|
+
assert_equal join(['bar/biz.html'].sort), join(@tree.full_file_list)
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
class TestTreeExclusions < Test::Unit::TestCase
|
77
|
+
|
78
|
+
def setup
|
79
|
+
FileUtils.rm_rf TEST_DIR
|
80
|
+
FileUtils.mkdir_p TEST_DIR
|
81
|
+
|
82
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar')
|
83
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar', 'boo')
|
84
|
+
|
85
|
+
FileUtils.touch File.join(TEST_DIR, 'aa')
|
86
|
+
FileUtils.touch File.join(TEST_DIR, 'biz')
|
87
|
+
FileUtils.touch File.join(TEST_DIR, 'zz')
|
88
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'foo')
|
89
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'biz')
|
90
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'biz.html')
|
91
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'boo', 'bizzz')
|
92
|
+
|
93
|
+
@list = ['aa', 'biz', 'zz', 'bar/foo', 'bar/biz', 'bar/biz.html', 'bar/boo/bizzz'].sort
|
94
|
+
end
|
95
|
+
|
96
|
+
should "ignore a single file excluded by path" do
|
97
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR, nil, ['bar/biz']
|
98
|
+
assert_equal join(@list - ['bar/biz']), join(@tree.full_file_list)
|
99
|
+
end
|
100
|
+
|
101
|
+
should "ignore files excluded by name" do
|
102
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR, nil, ['biz']
|
103
|
+
assert_equal join(@list - ['biz', 'bar/biz']), join(@tree.full_file_list)
|
104
|
+
end
|
105
|
+
|
106
|
+
should "ignore files excluded by name glob" do
|
107
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR, nil, ['biz*']
|
108
|
+
assert_equal join(@list - ['biz', 'bar/biz', 'bar/biz.html', 'bar/boo/bizzz']), join(@tree.full_file_list)
|
109
|
+
end
|
110
|
+
|
111
|
+
should "ignore a directory excluded by name glob" do
|
112
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR, nil, ['bo*']
|
113
|
+
assert_equal join(@list - ['bar/boo/bizzz']), join(@tree.full_file_list)
|
114
|
+
end
|
115
|
+
|
116
|
+
should "ignore a files and directories excluded by regexp" do
|
117
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR, nil, [/b/]
|
118
|
+
assert_equal join(['aa', 'zz']), join(@tree.full_file_list)
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
class TestTreeInclusionsWithExclusions < Test::Unit::TestCase
|
124
|
+
|
125
|
+
def setup
|
126
|
+
FileUtils.rm_rf TEST_DIR
|
127
|
+
FileUtils.mkdir_p TEST_DIR
|
128
|
+
|
129
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar')
|
130
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar', 'boo')
|
131
|
+
|
132
|
+
FileUtils.touch File.join(TEST_DIR, 'aa')
|
133
|
+
FileUtils.touch File.join(TEST_DIR, 'biz')
|
134
|
+
FileUtils.touch File.join(TEST_DIR, 'zz')
|
135
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'foo')
|
136
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'biz')
|
137
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'biz.html')
|
138
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'boo', 'bizzz')
|
139
|
+
|
140
|
+
@list = ['aa', 'biz', 'zz', 'bar/foo', 'bar/biz', 'bar/biz.html', 'bar/boo/bizzz'].sort
|
141
|
+
end
|
142
|
+
|
143
|
+
should "ignore files that match both inclusions and exclusions" do
|
144
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR, ['/bar'], ['*.html']
|
145
|
+
assert_equal join(['bar/foo', 'bar/biz', 'bar/boo/bizzz'].sort), join(@tree.full_file_list)
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
class TestTreeRefreshing < Test::Unit::TestCase
|
151
|
+
|
152
|
+
def setup
|
153
|
+
FileUtils.rm_rf TEST_DIR
|
154
|
+
FileUtils.mkdir_p TEST_DIR
|
155
|
+
|
156
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar')
|
157
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar', 'boo')
|
158
|
+
|
159
|
+
FileUtils.touch File.join(TEST_DIR, 'aa')
|
160
|
+
FileUtils.touch File.join(TEST_DIR, 'biz')
|
161
|
+
FileUtils.touch File.join(TEST_DIR, 'zz')
|
162
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'foo')
|
163
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'biz')
|
164
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'biz.html')
|
165
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'boo', 'bizzz')
|
166
|
+
|
167
|
+
@list = ['aa', 'biz', 'zz', 'bar/foo', 'bar/biz', 'bar/biz.html', 'bar/boo/bizzz'].sort
|
168
|
+
end
|
169
|
+
|
170
|
+
should "no changes when nothing has changed" do
|
171
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
172
|
+
changed_paths = @tree.refresh!
|
173
|
+
assert_equal "", join(changed_paths)
|
174
|
+
assert_equal join(@list), join(@tree.full_file_list)
|
175
|
+
end
|
176
|
+
|
177
|
+
should "single file modification" do
|
178
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
179
|
+
sleep 1
|
180
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'foo')
|
181
|
+
changed_paths = @tree.refresh!
|
182
|
+
assert_equal join(['bar/foo']), join(changed_paths)
|
183
|
+
end
|
184
|
+
|
185
|
+
should "single file deletion" do
|
186
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
187
|
+
FileUtils.rm File.join(TEST_DIR, 'bar', 'biz')
|
188
|
+
changed_paths = @tree.refresh!
|
189
|
+
assert_equal join(['bar/biz']), join(changed_paths)
|
190
|
+
end
|
191
|
+
|
192
|
+
should "single directory deletion" do
|
193
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
194
|
+
FileUtils.rm_rf File.join(TEST_DIR, 'bar')
|
195
|
+
changed_paths = @tree.refresh!
|
196
|
+
assert_equal join(['bar/foo', 'bar/biz', 'bar/biz.html', 'bar/boo/bizzz'].sort), join(changed_paths)
|
197
|
+
end
|
198
|
+
|
199
|
+
should "single file creation" do
|
200
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
201
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'miz')
|
202
|
+
changed_paths = @tree.refresh!
|
203
|
+
assert_equal join(['bar/miz']), join(changed_paths)
|
204
|
+
end
|
205
|
+
|
206
|
+
should "single directory creation" do
|
207
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
208
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar', 'koo')
|
209
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'koo', 'aaa')
|
210
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'koo', 'zzz')
|
211
|
+
changed_paths = @tree.refresh!
|
212
|
+
assert_equal join(['bar/koo/aaa', 'bar/koo/zzz'].sort), join(changed_paths)
|
213
|
+
end
|
214
|
+
|
215
|
+
should "not report changes on empty directory creation" do
|
216
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
217
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar', 'koo')
|
218
|
+
changed_paths = @tree.refresh!
|
219
|
+
assert_equal "", join(changed_paths)
|
220
|
+
end
|
221
|
+
|
222
|
+
should "files turned into a directory" do
|
223
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
224
|
+
FileUtils.rm File.join(TEST_DIR, 'bar', 'foo')
|
225
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar', 'foo')
|
226
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'foo', 'aaa')
|
227
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'foo', 'zzz')
|
228
|
+
changed_paths = @tree.refresh!
|
229
|
+
assert_equal join(['bar/foo', 'bar/foo/aaa', 'bar/foo/zzz'].sort), join(changed_paths)
|
230
|
+
end
|
231
|
+
|
232
|
+
should "directory turned into a file" do
|
233
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
234
|
+
FileUtils.rm_rf File.join(TEST_DIR, 'bar', 'boo')
|
235
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'boo')
|
236
|
+
changed_paths = @tree.refresh!
|
237
|
+
assert_equal join(['bar/boo/bizzz', 'bar/boo'].sort), join(changed_paths)
|
238
|
+
end
|
239
|
+
|
240
|
+
should "avoid traversing excluded directories" do
|
241
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR, nil, ['death']
|
242
|
+
FileUtils.ln_s(TEST_DIR, File.join(TEST_DIR, 'death')) unless EMDirWatcher::PLATFORM == 'Windows'
|
243
|
+
FileUtils.rm_rf File.join(TEST_DIR, 'bar', 'foo')
|
244
|
+
changed_paths = @tree.refresh!
|
245
|
+
assert_equal "bar/foo", join(changed_paths)
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|
249
|
+
|
250
|
+
class TestTreeScopedRefresh < Test::Unit::TestCase
|
251
|
+
|
252
|
+
def setup
|
253
|
+
FileUtils.rm_rf TEST_DIR
|
254
|
+
FileUtils.mkdir_p TEST_DIR
|
255
|
+
|
256
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar')
|
257
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar', 'boo')
|
258
|
+
|
259
|
+
FileUtils.touch File.join(TEST_DIR, 'aa')
|
260
|
+
FileUtils.touch File.join(TEST_DIR, 'biz')
|
261
|
+
FileUtils.touch File.join(TEST_DIR, 'zz')
|
262
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'foo')
|
263
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'biz')
|
264
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'biz.html')
|
265
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'boo', 'bizzz')
|
266
|
+
|
267
|
+
@list = ['aa', 'biz', 'zz', 'bar/foo', 'bar/biz', 'bar/biz.html', 'bar/boo/bizzz'].sort
|
268
|
+
end
|
269
|
+
|
270
|
+
unless EMDirWatcher::PLATFORM == 'Windows'
|
271
|
+
should "fail with an exception when faced with an endless symlink loop" do
|
272
|
+
assert_raises Errno::ELOOP do
|
273
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
274
|
+
FileUtils.ln_s(TEST_DIR, File.join(TEST_DIR, 'bar', 'death'))
|
275
|
+
FileUtils.rm_rf File.join(TEST_DIR, 'bar', 'foo')
|
276
|
+
changed_paths = @tree.refresh! File.join(TEST_DIR, 'bar')
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
should "report file deletion in inner directory when the scope specifies the directory" do
|
282
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
283
|
+
FileUtils.ln_s(TEST_DIR, File.join(TEST_DIR, 'death')) unless EMDirWatcher::PLATFORM == 'Windows'
|
284
|
+
FileUtils.rm_rf File.join(TEST_DIR, 'bar', 'foo')
|
285
|
+
changed_paths = @tree.refresh! File.join(TEST_DIR, 'bar')
|
286
|
+
assert_equal "bar/foo", join(changed_paths)
|
287
|
+
end
|
288
|
+
|
289
|
+
should "report file deletion in inner directory when the scope specifies the file" do
|
290
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
291
|
+
FileUtils.ln_s(TEST_DIR, File.join(TEST_DIR, 'death')) unless EMDirWatcher::PLATFORM == 'Windows'
|
292
|
+
FileUtils.rm_rf File.join(TEST_DIR, 'bar', 'foo')
|
293
|
+
changed_paths = @tree.refresh! File.join(TEST_DIR, 'bar', 'foo')
|
294
|
+
assert_equal "bar/foo", join(changed_paths)
|
295
|
+
end
|
296
|
+
|
297
|
+
should "not refresh the whole directory when the scope specifies a single file" do
|
298
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
299
|
+
FileUtils.ln_s(TEST_DIR, File.join(TEST_DIR, 'bar', 'death')) unless EMDirWatcher::PLATFORM == 'Windows'
|
300
|
+
FileUtils.rm_rf File.join(TEST_DIR, 'bar', 'foo')
|
301
|
+
changed_paths = @tree.refresh! File.join(TEST_DIR, 'bar', 'foo')
|
302
|
+
assert_equal "bar/foo", join(changed_paths)
|
303
|
+
end
|
304
|
+
|
305
|
+
should "report file deletion in a subtree when the scope specifies a directory" do
|
306
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
307
|
+
FileUtils.ln_s(TEST_DIR, File.join(TEST_DIR, 'death')) unless EMDirWatcher::PLATFORM == 'Windows'
|
308
|
+
FileUtils.rm_rf File.join(TEST_DIR, 'bar', 'boo', 'bizzz')
|
309
|
+
changed_paths = @tree.refresh! File.join(TEST_DIR, 'bar')
|
310
|
+
assert_equal "bar/boo/bizzz", join(changed_paths)
|
311
|
+
end
|
312
|
+
|
313
|
+
should "report removed files when doing cascaded scoped refreshes" do
|
314
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
315
|
+
FileUtils.rm_rf File.join(TEST_DIR, 'bar')
|
316
|
+
|
317
|
+
changed_paths = @tree.refresh! File.join(TEST_DIR, 'bar', 'boo')
|
318
|
+
assert_equal "bar/boo/bizzz", join(changed_paths)
|
319
|
+
|
320
|
+
changed_paths = @tree.refresh! File.join(TEST_DIR, 'bar')
|
321
|
+
assert_equal "bar/biz, bar/biz.html, bar/foo", join(changed_paths)
|
322
|
+
end
|
323
|
+
|
324
|
+
end
|
325
|
+
|
326
|
+
class TestTreeScopedNonRecursiveRefresh < Test::Unit::TestCase
|
327
|
+
|
328
|
+
def setup
|
329
|
+
FileUtils.rm_rf TEST_DIR
|
330
|
+
FileUtils.mkdir_p TEST_DIR
|
331
|
+
FileUtils.rm_rf ALT_TEST_DIR
|
332
|
+
FileUtils.mkdir_p ALT_TEST_DIR
|
333
|
+
|
334
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar')
|
335
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar', 'boo')
|
336
|
+
|
337
|
+
FileUtils.touch File.join(TEST_DIR, 'aa')
|
338
|
+
FileUtils.touch File.join(TEST_DIR, 'biz')
|
339
|
+
FileUtils.touch File.join(TEST_DIR, 'zz')
|
340
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'foo')
|
341
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'biz')
|
342
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'biz.html')
|
343
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'boo', 'bizzz')
|
344
|
+
|
345
|
+
@list = ['aa', 'biz', 'zz', 'bar/foo', 'bar/biz', 'bar/biz.html', 'bar/boo/bizzz'].sort
|
346
|
+
end
|
347
|
+
|
348
|
+
should "not report changes in a child directory" do
|
349
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
350
|
+
FileUtils.rm_rf File.join(TEST_DIR, 'bar', 'boo', 'bizzz')
|
351
|
+
changed_paths = @tree.refresh! File.join(TEST_DIR, 'bar'), false
|
352
|
+
assert_equal "", join(changed_paths)
|
353
|
+
end
|
354
|
+
|
355
|
+
should "report removed files" do
|
356
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
357
|
+
FileUtils.rm_rf File.join(TEST_DIR, 'bar', 'foo')
|
358
|
+
changed_paths = @tree.refresh! File.join(TEST_DIR, 'bar'), false
|
359
|
+
assert_equal "bar/foo", join(changed_paths)
|
360
|
+
end
|
361
|
+
|
362
|
+
should "report added files" do
|
363
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
364
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'coo')
|
365
|
+
changed_paths = @tree.refresh! File.join(TEST_DIR, 'bar'), false
|
366
|
+
assert_equal "bar/coo", join(changed_paths)
|
367
|
+
end
|
368
|
+
|
369
|
+
should "report entire subtree of a removed directory when the scope specifies that directory" do
|
370
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
371
|
+
FileUtils.rm_rf File.join(TEST_DIR, 'bar')
|
372
|
+
changed_paths = @tree.refresh! File.join(TEST_DIR, 'bar'), false
|
373
|
+
assert_equal "bar/biz, bar/biz.html, bar/boo/bizzz, bar/foo", join(changed_paths)
|
374
|
+
end
|
375
|
+
|
376
|
+
should "report entire subtree of an added directory when the scope specifies that directory" do
|
377
|
+
FileUtils.mv File.join(TEST_DIR, 'bar'), ALT_TEST_DIR
|
378
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
379
|
+
FileUtils.mv File.join(ALT_TEST_DIR, 'bar'), TEST_DIR
|
380
|
+
changed_paths = @tree.refresh! File.join(TEST_DIR, 'bar'), false
|
381
|
+
assert_equal "bar/biz, bar/biz.html, bar/boo/bizzz, bar/foo", join(changed_paths)
|
382
|
+
end
|
383
|
+
|
384
|
+
should "report entire subtree of a removed directory when the scope specifies a parent directory" do
|
385
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
386
|
+
FileUtils.rm_rf File.join(TEST_DIR, 'bar')
|
387
|
+
changed_paths = @tree.refresh! File.join(TEST_DIR), false
|
388
|
+
assert_equal "bar/biz, bar/biz.html, bar/boo/bizzz, bar/foo", join(changed_paths)
|
389
|
+
end
|
390
|
+
|
391
|
+
should "report entire subtree of an added directory when the scope specifies a parent directory" do
|
392
|
+
FileUtils.mv File.join(TEST_DIR, 'bar'), ALT_TEST_DIR
|
393
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
394
|
+
FileUtils.mv File.join(ALT_TEST_DIR, 'bar'), TEST_DIR
|
395
|
+
changed_paths = @tree.refresh! File.join(TEST_DIR), false
|
396
|
+
assert_equal "bar/biz, bar/biz.html, bar/boo/bizzz, bar/foo", join(changed_paths)
|
397
|
+
end
|
398
|
+
|
399
|
+
end
|
400
|
+
|
401
|
+
unless EMDirWatcher::PLATFORM == 'Windows'
|
402
|
+
class TestTreeSymlinkHandling < Test::Unit::TestCase
|
403
|
+
|
404
|
+
def setup
|
405
|
+
FileUtils.rm_rf TEST_DIR
|
406
|
+
FileUtils.rm_rf ALT_TEST_DIR
|
407
|
+
FileUtils.mkdir_p TEST_DIR
|
408
|
+
|
409
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar')
|
410
|
+
FileUtils.mkdir File.join(TEST_DIR, 'bar', 'boo')
|
411
|
+
|
412
|
+
FileUtils.touch File.join(TEST_DIR, 'aa')
|
413
|
+
FileUtils.touch File.join(TEST_DIR, 'biz')
|
414
|
+
FileUtils.touch File.join(TEST_DIR, 'zz')
|
415
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'foo')
|
416
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'biz')
|
417
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'biz.html')
|
418
|
+
FileUtils.touch File.join(TEST_DIR, 'bar', 'boo', 'bizzz')
|
419
|
+
|
420
|
+
FileUtils.ln_s TEST_DIR, ALT_TEST_DIR
|
421
|
+
|
422
|
+
@list = ['aa', 'biz', 'zz', 'bar/foo', 'bar/biz', 'bar/biz.html', 'bar/boo/bizzz'].sort
|
423
|
+
end
|
424
|
+
|
425
|
+
should "handle referencing root scope via symlink" do
|
426
|
+
@tree = EMDirWatcher::Tree.new TEST_DIR
|
427
|
+
FileUtils.rm_rf File.join(TEST_DIR, 'bar', 'foo')
|
428
|
+
changed_paths = @tree.refresh! ALT_TEST_DIR
|
429
|
+
assert_equal "bar/foo", join(changed_paths)
|
430
|
+
end
|
431
|
+
|
432
|
+
should "handle referencing root scope by real path when monitoring a symlinked path" do
|
433
|
+
@tree = EMDirWatcher::Tree.new ALT_TEST_DIR
|
434
|
+
FileUtils.rm_rf File.join(ALT_TEST_DIR, 'bar', 'foo')
|
435
|
+
changed_paths = @tree.refresh! TEST_DIR
|
436
|
+
assert_equal "bar/foo", join(changed_paths)
|
437
|
+
end
|
438
|
+
|
439
|
+
end
|
440
|
+
end
|
data/testloop
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#! /bin/bash
|
2
|
+
N=20
|
3
|
+
test -n "$1" && N=$1
|
4
|
+
: >/tmp/failures
|
5
|
+
for (( i = 1; i <= N; i++ )); do
|
6
|
+
echo "*** ATTEMP $i ***"
|
7
|
+
rake test || echo "failed $i" >>/tmp/failures
|
8
|
+
done
|
9
|
+
cat /tmp/failures
|
10
|
+
F=$(cat /tmp/failures | wc -l)
|
11
|
+
echo "Failed $F of $N test runs ($(( F*100/N ))%)"
|
metadata
CHANGED
@@ -4,17 +4,18 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
+
- 9
|
7
8
|
- 1
|
8
|
-
|
9
|
-
version: 0.1.0
|
9
|
+
version: 0.9.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Andrey Tarantsov
|
13
|
+
- Mikhail Gusarov
|
13
14
|
autorequire:
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-
|
18
|
+
date: 2010-08-09 00:00:00 +07:00
|
18
19
|
default_executable:
|
19
20
|
dependencies: []
|
20
21
|
|
@@ -28,7 +29,6 @@ extra_rdoc_files:
|
|
28
29
|
- LICENSE
|
29
30
|
- README.md
|
30
31
|
files:
|
31
|
-
- .document
|
32
32
|
- .gitignore
|
33
33
|
- LICENSE
|
34
34
|
- README.md
|
@@ -36,13 +36,20 @@ files:
|
|
36
36
|
- VERSION
|
37
37
|
- examples/monitor.rb
|
38
38
|
- lib/em-dir-watcher.rb
|
39
|
-
- lib/em-dir-watcher/
|
39
|
+
- lib/em-dir-watcher/invokers/subprocess_invoker.rb
|
40
|
+
- lib/em-dir-watcher/monitor.rb
|
41
|
+
- lib/em-dir-watcher/platform/linux.rb
|
42
|
+
- lib/em-dir-watcher/platform/mac.rb
|
43
|
+
- lib/em-dir-watcher/platform/mac/ffi_fsevents_watcher.rb
|
44
|
+
- lib/em-dir-watcher/platform/mac/rubycocoa_watcher.rb
|
40
45
|
- lib/em-dir-watcher/platform/windows.rb
|
41
|
-
- lib/em-dir-watcher/platform/windows/monitor-nix.rb
|
42
46
|
- lib/em-dir-watcher/platform/windows/monitor.rb
|
43
47
|
- lib/em-dir-watcher/platform/windows/path_to_ruby_exe.rb
|
48
|
+
- lib/em-dir-watcher/tree.rb
|
44
49
|
- test/helper.rb
|
45
|
-
- test/
|
50
|
+
- test/test_monitor.rb
|
51
|
+
- test/test_tree.rb
|
52
|
+
- testloop
|
46
53
|
has_rdoc: true
|
47
54
|
homepage: http://github.com/mockko/em-dir-watcher
|
48
55
|
licenses: []
|
@@ -75,5 +82,6 @@ specification_version: 3
|
|
75
82
|
summary: Directory watching support for EventMachine (fssm / win32-changenotify)
|
76
83
|
test_files:
|
77
84
|
- test/helper.rb
|
78
|
-
- test/
|
85
|
+
- test/test_monitor.rb
|
86
|
+
- test/test_tree.rb
|
79
87
|
- examples/monitor.rb
|
data/.document
DELETED
@@ -1,107 +0,0 @@
|
|
1
|
-
|
2
|
-
module EMDirWatcher
|
3
|
-
module Platform
|
4
|
-
module NIX
|
5
|
-
|
6
|
-
# monkey-patch to set latency to 0
|
7
|
-
module FssmFSEventsMonkeyPatch
|
8
|
-
def patched_add_handler(handler, preload=true)
|
9
|
-
@handlers[handler.path.to_s] = handler
|
10
|
-
|
11
|
-
fsevent = Rucola::FSEvents.new(handler.path.to_s, {:latency => 0.0}) do |events|
|
12
|
-
events.each do |event|
|
13
|
-
handler.refresh(event.path)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
fsevent.create_stream
|
18
|
-
handler.refresh(nil, true) if preload
|
19
|
-
fsevent.start
|
20
|
-
@fsevents << fsevent
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
class Watcher
|
25
|
-
|
26
|
-
attr_accessor :handler, :active
|
27
|
-
|
28
|
-
class << self
|
29
|
-
def run_fssm_watcher path, globs
|
30
|
-
require 'fssm'
|
31
|
-
if FSSM::Support.backend == 'FSEvents'
|
32
|
-
FSSM::Backends::FSEvents.send(:include, FssmFSEventsMonkeyPatch)
|
33
|
-
FSSM::Backends::FSEvents.send(:alias_method, :add_handler, :patched_add_handler)
|
34
|
-
end
|
35
|
-
$stdout.sync = true
|
36
|
-
report = proc { |b,r|
|
37
|
-
puts File.join(b, r)
|
38
|
-
}
|
39
|
-
FSSM.monitor path, globs do
|
40
|
-
create &report
|
41
|
-
delete &report
|
42
|
-
update &report
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def initialize path, globs, &handler
|
48
|
-
@path = path
|
49
|
-
@globs = globs
|
50
|
-
@handler = handler
|
51
|
-
@active = true
|
52
|
-
|
53
|
-
setup_listener
|
54
|
-
end
|
55
|
-
|
56
|
-
def stop
|
57
|
-
@active = false
|
58
|
-
kill
|
59
|
-
end
|
60
|
-
|
61
|
-
def kill
|
62
|
-
if @io
|
63
|
-
Process.kill 'TERM', @io.pid
|
64
|
-
Process.waitpid @io.pid
|
65
|
-
@io = nil
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def setup_listener
|
70
|
-
io = open('|-', 'r')
|
71
|
-
if io.nil?
|
72
|
-
Watcher.run_fssm_watcher @path, @globs
|
73
|
-
exit
|
74
|
-
end
|
75
|
-
@io = io
|
76
|
-
|
77
|
-
@connection = EM.watch io do |conn|
|
78
|
-
class << conn
|
79
|
-
attr_accessor :watcher
|
80
|
-
|
81
|
-
def notify_readable
|
82
|
-
path = @io.readline.strip
|
83
|
-
@watcher.handler.call path
|
84
|
-
rescue EOFError
|
85
|
-
detach
|
86
|
-
@watcher.kill # waitpid to cleanup zombie
|
87
|
-
if @watcher.active
|
88
|
-
EM.next_tick do
|
89
|
-
@watcher.setup_listener
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def unbind
|
95
|
-
@watcher.kill
|
96
|
-
end
|
97
|
-
end
|
98
|
-
conn.watcher = self
|
99
|
-
conn.notify_readable = true
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
@@ -1,29 +0,0 @@
|
|
1
|
-
|
2
|
-
# this is only used when debugging Windows code on a Mac
|
3
|
-
|
4
|
-
$stdout.sync = true
|
5
|
-
|
6
|
-
require 'rubygems'
|
7
|
-
require 'socket'
|
8
|
-
require 'fssm'
|
9
|
-
|
10
|
-
port = ARGV[0].to_i
|
11
|
-
dir = ARGV[1]
|
12
|
-
|
13
|
-
socket = TCPSocket.open('127.0.0.1', port)
|
14
|
-
|
15
|
-
report_change = proc do |a, b|
|
16
|
-
path = File.join(a, b)
|
17
|
-
socket.puts path
|
18
|
-
end
|
19
|
-
|
20
|
-
begin
|
21
|
-
FSSM.monitor dir do
|
22
|
-
update(&report_change)
|
23
|
-
create(&report_change)
|
24
|
-
delete(&report_change)
|
25
|
-
end
|
26
|
-
ensure
|
27
|
-
cn.close
|
28
|
-
socket.close
|
29
|
-
end
|
data/test/test_em-dir-watcher.rb
DELETED