fsevents 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,105 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe FSEvents::Event do
4
+ before :each do
5
+ @id = stub('id')
6
+ @path = '.'
7
+ @stream = stub('stream')
8
+
9
+ @event = FSEvents::Event.new(@id, @path, @stream)
10
+ end
11
+
12
+ describe 'when initialized' do
13
+ it 'should accept an id, path, and stream' do
14
+ lambda { FSEvents::Event.new(@id, @path, @stream) }.should_not raise_error(ArgumentError)
15
+ end
16
+
17
+ it 'should require a stream' do
18
+ lambda { FSEvents::Event.new(@id, @path) }.should raise_error(ArgumentError)
19
+ end
20
+
21
+ it 'should require a path' do
22
+ lambda { FSEvents::Event.new(@id) }.should raise_error(ArgumentError)
23
+ end
24
+
25
+ it 'should require an id' do
26
+ lambda { FSEvents::Event.new }.should raise_error(ArgumentError)
27
+ end
28
+
29
+ it 'should store the id' do
30
+ FSEvents::Event.new(@id, @path, @stream).id.should == @id
31
+ end
32
+
33
+ it 'should store the path' do
34
+ FSEvents::Event.new(@id, @path, @stream).path.should == @path
35
+ end
36
+
37
+ it 'should store the stream' do
38
+ FSEvents::Event.new(@id, @path, @stream).stream.should == @stream
39
+ end
40
+ end
41
+
42
+ it 'should list files' do
43
+ @event.should respond_to(:files)
44
+ end
45
+
46
+ describe 'listing files' do
47
+ it 'should get files from the path' do
48
+ @event.files.sort.should == Dir["#{@path}/*"].sort
49
+ end
50
+ end
51
+
52
+ it 'should list modified files' do
53
+ @event.should respond_to(:modified_files)
54
+ end
55
+
56
+ describe 'listing modified files' do
57
+ before :each do
58
+ @now = Time.now
59
+ @stream.stubs(:last_event).returns(@now)
60
+ @files = Array.new(5) do |i|
61
+ file = stub("file #{i+1}")
62
+ File.stubs(:mtime).with(file).returns(@now + i - 2)
63
+ file
64
+ end
65
+ @event.stubs(:files).returns(@files)
66
+ end
67
+
68
+ it 'should get the file list' do
69
+ @event.expects(:files).returns(@files)
70
+ @event.modified_files
71
+ end
72
+
73
+ it 'should get the last event time from the stream' do
74
+ @stream.expects(:last_event).returns(@now)
75
+ @event.modified_files
76
+ end
77
+
78
+ it 'should return files modified after the last event time' do
79
+ expected_files = @files.values_at(3, 4)
80
+ modified_files = @event.modified_files
81
+
82
+ expected_files.each do |file|
83
+ modified_files.should include(file)
84
+ end
85
+ end
86
+
87
+ it 'should return files modified at the last event time' do
88
+ expected_files = @files.values_at(2)
89
+ modified_files = @event.modified_files
90
+
91
+ expected_files.each do |file|
92
+ modified_files.should include(file)
93
+ end
94
+ end
95
+
96
+ it 'should not return files not modified after the last event time' do
97
+ unexpected_files = @files.values_at(0, 1)
98
+ modified_files = @event.modified_files
99
+
100
+ unexpected_files.each do |file|
101
+ modified_files.should_not include(file)
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,4 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe FSEvents do
4
+ end
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,23 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ # this is my favorite way to require ever
10
+ begin
11
+ require 'mocha'
12
+ rescue LoadError
13
+ require 'rubygems'
14
+ gem 'mocha'
15
+ require 'mocha'
16
+ end
17
+
18
+ Spec::Runner.configure do |config|
19
+ config.mock_with :mocha
20
+ end
21
+
22
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
23
+ require 'fsevents'
@@ -0,0 +1,565 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe FSEvents::Stream do
4
+ before :each do
5
+ @path = '/tmp'
6
+ @stream = FSEvents::Stream.new(@path) {}
7
+ end
8
+
9
+ describe 'when initialized' do
10
+ it 'should accept a path and callback block' do
11
+ lambda { FSEvents::Stream.new(@path) {} }.should_not raise_error(ArgumentError)
12
+ end
13
+
14
+ it 'should not require a path' do
15
+ lambda { FSEvents::Stream.new() {} }.should_not raise_error(ArgumentError)
16
+ end
17
+
18
+ it 'should require a callback block' do
19
+ lambda { FSEvents::Stream.new(@path) }.should raise_error(ArgumentError)
20
+ end
21
+
22
+ it 'should accept a hash of options' do
23
+ lambda { FSEvents::Stream.new(@path, :flags => 27 ) {} }.should_not raise_error(ArgumentError)
24
+ end
25
+
26
+ it 'should accept an array of paths' do
27
+ lambda { FSEvents::Stream.new([@path, '/other/path']) {} }.should_not raise_error
28
+ end
29
+
30
+ it 'should accept an array of paths with options' do
31
+ lambda { FSEvents::Stream.new([@path, '/other/path'], :flags => 27) {} }.should_not raise_error
32
+ end
33
+
34
+ it 'should accept multiple paths' do
35
+ lambda { FSEvents::Stream.new(@path, '/other/path') {} }.should_not raise_error
36
+ end
37
+
38
+ it 'should accept multiple paths with options' do
39
+ lambda { FSEvents::Stream.new(@path, '/other/path', :flags => 27) {} }.should_not raise_error
40
+ end
41
+
42
+ it 'should store the callback block' do
43
+ callback = lambda {}
44
+ FSEvents::Stream.new(@path, &callback).callback.should == callback
45
+ end
46
+
47
+ describe 'handling options' do
48
+ before :each do
49
+ @options = {}
50
+ [:allocator, :context, :since, :latency, :flags].each do |opt|
51
+ @options[opt] = stub(opt.to_s)
52
+ end
53
+ @other_path = '/other/path'
54
+ end
55
+
56
+ it 'should store the allocator' do
57
+ FSEvents::Stream.new(@path, @options) {}.allocator.should == @options[:allocator]
58
+ end
59
+
60
+ it 'should default the allocator to KCFAllocatorDefault' do
61
+ @options.delete(:allocator)
62
+ FSEvents::Stream.new(@path, @options) {}.allocator.should == OSX::KCFAllocatorDefault
63
+ end
64
+
65
+ it 'should store the context' do
66
+ FSEvents::Stream.new(@path, @options) {}.context.should == @options[:context]
67
+ end
68
+
69
+ it 'should default the context to nil' do
70
+ @options.delete(:context)
71
+ FSEvents::Stream.new(@path, @options) {}.context.should == nil
72
+ end
73
+
74
+ it 'should store the path as an array' do
75
+ FSEvents::Stream.new(@path, @options) {}.paths.should == [@path]
76
+ end
77
+
78
+ it 'should store an array of paths as-is' do
79
+ FSEvents::Stream.new([@path, @other_path], @options) {}.paths.should == [@path, @other_path]
80
+ end
81
+
82
+ it 'should store multiple paths as an array' do
83
+ FSEvents::Stream.new(@path, @other_path, @options) {}.paths.should == [@path, @other_path]
84
+ end
85
+
86
+ it 'should default the path to the present working directory' do
87
+ FSEvents::Stream.new(@options) {}.paths.should == [Dir.pwd]
88
+ end
89
+
90
+ it "should store 'since' (event ID)" do
91
+ FSEvents::Stream.new(@path, @options) {}.since.should == @options[:since]
92
+ end
93
+
94
+ it "should default 'since' to KFSEventStreamEventIdSinceNow" do
95
+ @options.delete(:since)
96
+ FSEvents::Stream.new(@path, @options) {}.since.should == OSX::KFSEventStreamEventIdSinceNow
97
+ end
98
+
99
+ it 'should store the latency' do
100
+ FSEvents::Stream.new(@path, @options) {}.latency.should == @options[:latency]
101
+ end
102
+
103
+ it 'should default the latency to 1.0' do
104
+ @options.delete(:latency)
105
+ FSEvents::Stream.new(@path, @options) {}.latency.should == 1.0
106
+ end
107
+
108
+ it 'should store the flags' do
109
+ FSEvents::Stream.new(@path, @options) {}.flags.should == @options[:flags]
110
+ end
111
+
112
+ it 'should default the flags to 0' do
113
+ @options.delete(:flags)
114
+ FSEvents::Stream.new(@path, @options) {}.flags.should == 0
115
+ end
116
+ end
117
+ end
118
+
119
+ it 'should create a stream' do
120
+ @stream.should respond_to(:create)
121
+ end
122
+
123
+ describe 'when creating the stream' do
124
+ before :each do
125
+ @args = {}
126
+ [:allocator, :context, :paths, :since, :latency, :flags].each do |arg|
127
+ val = stub(arg.to_s)
128
+
129
+ @stream.stubs(arg).returns(val)
130
+ @args[arg] = val
131
+ end
132
+
133
+ @arg_placeholders = Array.new(7) { anything }
134
+
135
+ @stream_val = stub('stream')
136
+ OSX.stubs(:FSEventStreamCreate).returns(@stream_val)
137
+ end
138
+
139
+ it 'should create an FSEvent stream' do
140
+ OSX.expects(:FSEventStreamCreate).returns(@stream_val)
141
+ @stream.create
142
+ end
143
+
144
+ it 'should pass the allocator' do
145
+ args = @arg_placeholders
146
+ args[0] = @args[:allocator]
147
+ OSX.expects(:FSEventStreamCreate).with(*args).returns(@stream_val)
148
+ @stream.create
149
+ end
150
+
151
+ it 'should pass the stream callback' do
152
+ # stream_callback returns a different proc every time it's called
153
+ @stream.stubs(:stream_callback).returns(stub('stream callback'))
154
+ args = @arg_placeholders
155
+ args[1] = @stream.stream_callback
156
+ OSX.expects(:FSEventStreamCreate).with(*args).returns(@stream_val)
157
+ @stream.create
158
+ end
159
+
160
+ it 'should pass the context' do
161
+ args = @arg_placeholders
162
+ args[2] = @args[:context]
163
+ OSX.expects(:FSEventStreamCreate).with(*args).returns(@stream_val)
164
+ @stream.create
165
+ end
166
+
167
+ it 'should pass the paths' do
168
+ args = @arg_placeholders
169
+ args[3] = @args[:paths]
170
+ OSX.expects(:FSEventStreamCreate).with(*args).returns(@stream_val)
171
+ @stream.create
172
+ end
173
+
174
+ it "should pass 'since' (event ID)" do
175
+ args = @arg_placeholders
176
+ args[4] = @args[:since]
177
+ OSX.expects(:FSEventStreamCreate).with(*args).returns(@stream_val)
178
+ @stream.create
179
+ end
180
+
181
+ it 'should pass the latency' do
182
+ args = @arg_placeholders
183
+ args[5] = @args[:latency]
184
+ OSX.expects(:FSEventStreamCreate).with(*args).returns(@stream_val)
185
+ @stream.create
186
+ end
187
+
188
+ it 'should pass the flags' do
189
+ args = @arg_placeholders
190
+ args[6] = @args[:flags]
191
+ OSX.expects(:FSEventStreamCreate).with(*args).returns(@stream_val)
192
+ @stream.create
193
+ end
194
+
195
+ it 'should store the stream' do
196
+ @stream.create
197
+ @stream.stream.should == @stream_val
198
+ end
199
+
200
+ it 'should raise a StreamError exception if the stream could not be created' do
201
+ OSX.stubs(:FSEventStreamCreate).returns(nil)
202
+ lambda { @stream.create }.should raise_error(FSEvents::Stream::StreamError)
203
+ end
204
+
205
+ it 'should not raise a StreamError exception if the stream could be created' do
206
+ lambda { @stream.create }.should_not raise_error(FSEvents::Stream::StreamError)
207
+ end
208
+ end
209
+
210
+ it 'should have a stream callback' do
211
+ @stream.should respond_to(:stream_callback)
212
+ end
213
+
214
+ describe 'stream callback' do
215
+ it 'should return a proc' do
216
+ @stream.stream_callback.should be_kind_of(Proc)
217
+ end
218
+
219
+ describe 'proc' do
220
+ before :each do
221
+ @callback_arg_order = [:stream, :context, :event_count, :paths, :event_flags, :event_IDs]
222
+ @args_hash = {}
223
+ [:stream, :context].each do |arg|
224
+ @args_hash[arg] = stub(arg.to_s)
225
+ end
226
+ @args_hash[:event_count] = 0
227
+ [:paths, :event_flags, :event_IDs].each do |arg|
228
+ @args_hash[arg] = []
229
+ end
230
+ @args_hash[:paths].stubs(:regard_as)
231
+
232
+ @args = @args_hash.values_at(*@callback_arg_order)
233
+
234
+ @callback = stub('callback', :call => nil)
235
+ @stream.stubs(:callback).returns(@callback)
236
+
237
+ @proc = @stream.stream_callback
238
+ end
239
+
240
+ it 'should accept stream, context, event count, paths, event flags, and event IDs' do
241
+ lambda { @proc.call(*@args) }.should_not raise_error(ArgumentError)
242
+ end
243
+
244
+ it "should regard the paths as '*'" do
245
+ @args_hash[:paths].expects(:regard_as).with('*')
246
+ @proc.call(*@args)
247
+ end
248
+
249
+ it 'should call the stored callback' do
250
+ @callback.expects(:call)
251
+ @proc.call(*@args)
252
+ end
253
+
254
+ it 'should collect the paths and IDs, create Event objects, and pass them to the stored callback' do
255
+ event_count = 3
256
+ @args_hash[:event_count] = event_count
257
+ events = []
258
+ event_count.times do |i|
259
+ path = "/some/path/to/dir/number/#{i+1}"
260
+ id = i + 1
261
+ @args_hash[:paths].push path
262
+ @args_hash[:event_IDs].push id
263
+
264
+ event = stub("event #{path}")
265
+ FSEvents::Event.stubs(:new).with(id, path, @stream).returns(event)
266
+ events.push event
267
+ end
268
+ @args = @args_hash.values_at(*@callback_arg_order)
269
+ @callback.expects(:call).with(events)
270
+ @proc.call(*@args)
271
+ end
272
+
273
+ it "should update the stream's last event" do
274
+ @stream.expects(:update_last_event)
275
+ @proc.call(*@args)
276
+ end
277
+ end
278
+ end
279
+
280
+ it 'should create' do
281
+ FSEvents::Stream.should respond_to(:create)
282
+ end
283
+
284
+ describe 'when creating' do
285
+ before :each do
286
+ @other_path = '/other/path'
287
+ end
288
+
289
+ # This is just here for organization and use of the before block.
290
+ # I'd like to ensure that the block is passed to new, but mocha expecation apparently doesn't support that.
291
+ # So instead I stub new for some testing and then have something that actually uses new and sees the callback
292
+ # is the expected block.
293
+ describe do
294
+ before :each do
295
+ @stream.stubs(:create)
296
+ FSEvents::Stream.stubs(:new).returns(@stream)
297
+ end
298
+
299
+ it 'should accept arguments and a block' do
300
+ lambda { FSEvents::Stream.create(@path, @other_path, :flags => 27) {} }.should_not raise_error(ArgumentError)
301
+ end
302
+
303
+ it 'should initialize a new stream object' do
304
+ FSEvents::Stream.expects(:new).returns(@stream)
305
+ FSEvents::Stream.create(@path, @other_path, :flags => 27) {}
306
+ end
307
+
308
+ it 'should pass the arguments to the initialization' do
309
+ FSEvents::Stream.expects(:new).with(@path, @other_path, :flags => 27).returns(@stream)
310
+ FSEvents::Stream.create(@path, @other_path, :flags => 27) {}
311
+ end
312
+
313
+ it 'should make the resultant stream object create a stream' do
314
+ @stream.expects(:create)
315
+ FSEvents::Stream.create(@path, @other_path, :flags => 27) {}
316
+ end
317
+
318
+ it 'should return the stream object' do
319
+ FSEvents::Stream.create.should == @stream
320
+ end
321
+ end
322
+
323
+ it 'should pass the callback block' do
324
+ callback = lambda {}
325
+ FSEvents::Stream.create(@path, @other_path, :flags => 27, &callback).callback.should == callback
326
+ end
327
+ end
328
+
329
+ it 'should schedule itself' do
330
+ @stream.should respond_to(:schedule)
331
+ end
332
+
333
+ describe 'when scheduling' do
334
+ before :each do
335
+ OSX.stubs(:FSEventStreamScheduleWithRunLoop)
336
+ end
337
+
338
+ it 'should schedule the stream' do
339
+ OSX.expects(:FSEventStreamScheduleWithRunLoop)
340
+ @stream.schedule
341
+ end
342
+
343
+ it 'should pass the stream' do
344
+ OSX.expects(:FSEventStreamScheduleWithRunLoop).with(@stream.stream, anything, anything)
345
+ @stream.schedule
346
+ end
347
+
348
+ it "should use the 'get current' run loop" do
349
+ OSX.expects(:CFRunLoopGetCurrent)
350
+ @stream.schedule
351
+ end
352
+
353
+ it "should pass the 'get current' run loop" do
354
+ # CFRunLoopGetCurrent returns a different value every time it's called, so it's like testing Time.now
355
+ get_current_run_loop = OSX.CFRunLoopGetCurrent
356
+ OSX.stubs(:CFRunLoopGetCurrent).returns(get_current_run_loop)
357
+
358
+ OSX.expects(:FSEventStreamScheduleWithRunLoop).with(anything, get_current_run_loop, anything)
359
+ @stream.schedule
360
+ end
361
+
362
+ it 'should use the default mode' do
363
+ OSX.expects(:FSEventStreamScheduleWithRunLoop).with(anything, anything, OSX::KCFRunLoopDefaultMode)
364
+ @stream.schedule
365
+ end
366
+ end
367
+
368
+ it 'should start itself' do
369
+ @stream.should respond_to(:start)
370
+ end
371
+
372
+ describe 'when starting' do
373
+ before :each do
374
+ OSX.stubs(:FSEventStreamStart).returns(true)
375
+ end
376
+
377
+ it 'should start the stream' do
378
+ OSX.expects(:FSEventStreamStart).with(@stream.stream).returns(true)
379
+ @stream.start
380
+ end
381
+
382
+ it 'should raise a StreamError exception if the stream could not be started' do
383
+ OSX.stubs(:FSEventStreamStart).returns(nil)
384
+ lambda { @stream.start }.should raise_error(FSEvents::Stream::StreamError)
385
+ end
386
+
387
+ it 'should not raise a StreamError exception if the stream could be started' do
388
+ lambda { @stream.start }.should_not raise_error(FSEvents::Stream::StreamError)
389
+ end
390
+
391
+ it 'should update its last event' do
392
+ @stream.expects(:update_last_event)
393
+ @stream.start
394
+ end
395
+ end
396
+
397
+ it 'should update its last event' do
398
+ @stream.should respond_to(:update_last_event)
399
+ end
400
+
401
+ describe 'updating its last event' do
402
+ it 'should store the last event time' do
403
+ now = Time.now
404
+ Time.stubs(:now).returns(now)
405
+ @stream.update_last_event
406
+ @stream.last_event.should == now
407
+ end
408
+ end
409
+
410
+ it 'should start up' do
411
+ @stream.should respond_to(:startup)
412
+ end
413
+
414
+ describe 'when starting up' do
415
+ before :each do
416
+ @stream.stubs(:schedule)
417
+ @stream.stubs(:start)
418
+ end
419
+
420
+ it 'should schedule' do
421
+ @stream.expects(:schedule)
422
+ @stream.startup
423
+ end
424
+
425
+ it 'should start' do
426
+ @stream.expects(:start)
427
+ @stream.startup
428
+ end
429
+ end
430
+
431
+ it 'should watch' do
432
+ FSEvents::Stream.should respond_to(:watch)
433
+ end
434
+
435
+ describe 'when watching' do
436
+ before :each do
437
+ @other_path = '/other/path'
438
+ end
439
+
440
+ # This is just here for organization and use of the before block.
441
+ # I'd like to ensure that the block is passed to create, but mocha expecation apparently doesn't support that.
442
+ # So instead I stub create for some testing and then have something that actually uses create and sees the callback
443
+ # is the expected block.
444
+ describe do
445
+ before :each do
446
+ @stream.stubs(:startup)
447
+ FSEvents::Stream.stubs(:create).returns(@stream)
448
+ end
449
+
450
+ it 'should accept arguments and a block' do
451
+ lambda { FSEvents::Stream.watch(@path, @other_path, :flags => 27) {} }.should_not raise_error(ArgumentError)
452
+ end
453
+
454
+ it 'should create a stream object' do
455
+ FSEvents::Stream.expects(:create).returns(@stream)
456
+ FSEvents::Stream.watch(@path, @other_path, :flags => 27) {}
457
+ end
458
+
459
+ it 'should pass the arguments to the creation' do
460
+ FSEvents::Stream.expects(:create).with(@path, @other_path, :flags => 27).returns(@stream)
461
+ FSEvents::Stream.watch(@path, @other_path, :flags => 27) {}
462
+ end
463
+
464
+ it 'should start up the resultant stream object' do
465
+ @stream.expects(:startup)
466
+ FSEvents::Stream.watch(@path, @other_path, :flags => 27) {}
467
+ end
468
+
469
+ it 'should return the stream object' do
470
+ FSEvents::Stream.watch.should == @stream
471
+ end
472
+ end
473
+
474
+ it 'should pass the callback block' do
475
+ callback = lambda {}
476
+ FSEvents::Stream.watch(@path, @other_path, :flags => 27, &callback).callback.should == callback
477
+ end
478
+ end
479
+
480
+ it 'should stop itself' do
481
+ @stream.should respond_to(:stop)
482
+ end
483
+
484
+ describe 'when stopping' do
485
+ it 'should stop the stream' do
486
+ OSX.expects(:FSEventStreamStop).with(@stream.stream)
487
+ @stream.stop
488
+ end
489
+ end
490
+
491
+ it 'should invalidate itself' do
492
+ @stream.should respond_to(:invalidate)
493
+ end
494
+
495
+ describe 'when invalidating' do
496
+ it 'should invalidate the stream' do
497
+ OSX.expects(:FSEventStreamInvalidate).with(@stream.stream)
498
+ @stream.invalidate
499
+ end
500
+ end
501
+
502
+ it 'should release itself' do
503
+ @stream.should respond_to(:release)
504
+ end
505
+
506
+ describe 'when releasing' do
507
+ before :each do
508
+ OSX.stubs(:FSEventStreamRelease)
509
+ end
510
+
511
+ it 'should release the stream' do
512
+ OSX.expects(:FSEventStreamRelease).with(@stream.stream)
513
+ @stream.release
514
+ end
515
+
516
+ it 'should clear the stream' do
517
+ @stream.release
518
+ @stream.stream.should be_nil
519
+ end
520
+ end
521
+
522
+ it 'should shut down' do
523
+ @stream.should respond_to(:shutdown)
524
+ end
525
+
526
+ describe 'when shutting down' do
527
+ before :each do
528
+ @stream.stubs(:stop)
529
+ @stream.stubs(:invalidate)
530
+ @stream.stubs(:release)
531
+ end
532
+
533
+ it 'should stop' do
534
+ @stream.expects(:stop)
535
+ @stream.shutdown
536
+ end
537
+
538
+ it 'should invalidate' do
539
+ @stream.expects(:invalidate)
540
+ @stream.shutdown
541
+ end
542
+
543
+ it 'should release' do
544
+ @stream.expects(:release)
545
+ @stream.shutdown
546
+ end
547
+ end
548
+
549
+ it 'should run' do
550
+ @stream.should respond_to(:run)
551
+ end
552
+
553
+ describe 'running' do
554
+ it 'should enter the run loop' do
555
+ OSX.expects(:CFRunLoopRun)
556
+ @stream.run
557
+ end
558
+ end
559
+ end
560
+
561
+ describe FSEvents::Stream::StreamError do
562
+ it 'should be a type of StandardError' do
563
+ FSEvents::Stream::StreamError.should < StandardError
564
+ end
565
+ end