~didrocks/ubuntuone-client/dont-suffer-zg-crash

« back to all changes in this revision

Viewing changes to tests/syncdaemon/test_eq_inotify.py

  • Committer: Bazaar Package Importer
  • Author(s): Rodney Dawes
  • Date: 2009-06-30 12:00:00 UTC
  • Revision ID: james.westby@ubuntu.com-20090630120000-by806ovmw3193qe8
Tags: upstream-0.90.3
ImportĀ upstreamĀ versionĀ 0.90.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# tests.syncdaemon.test_eq_inotify
 
2
#
 
3
# Author: Facundo Batista <facundo@canonical.com>
 
4
#
 
5
# Copyright 2009 Canonical Ltd.
 
6
#
 
7
# This program is free software: you can redistribute it and/or modify it
 
8
# under the terms of the GNU General Public License version 3, as published
 
9
# by the Free Software Foundation.
 
10
#
 
11
# This program is distributed in the hope that it will be useful, but
 
12
# WITHOUT ANY WARRANTY; without even the implied warranties of
 
13
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 
14
# PURPOSE.  See the GNU General Public License for more details.
 
15
#
 
16
# You should have received a copy of the GNU General Public License along
 
17
# with this program.  If not, see <http://www.gnu.org/licenses/>.
 
18
 
 
19
'''Tests for the Event Queue part that uses inotify.'''
 
20
 
 
21
import os
 
22
import unittest
 
23
import functools
 
24
 
 
25
from twisted.internet import defer, reactor
 
26
from tests.syncdaemon.test_eventqueue import BaseEQTestCase
 
27
 
 
28
 
 
29
class WatchTests(BaseEQTestCase):
 
30
    '''Test the EQ API to add and remove watchs.'''
 
31
 
 
32
    def test_add_watch(self):
 
33
        '''Test that watchs can be added.'''
 
34
        # we should have what we asked for
 
35
        self.eq.inotify_add_watch(self.root_dir)
 
36
        self.assertTrue(self.root_dir in self.eq._watchs)
 
37
 
 
38
        # we shouldn't have other stuff
 
39
        self.assertTrue("not-added-dir" not in self.eq._watchs)
 
40
 
 
41
    def test_rm_watch(self):
 
42
        '''Test that watchs can be removed.'''
 
43
        # remove what we added
 
44
        self.eq.inotify_add_watch(self.root_dir)
 
45
        self.eq.inotify_rm_watch(self.root_dir)
 
46
        self.assertTrue(self.root_dir not in self.eq._watchs)
 
47
 
 
48
        # remove different stuff
 
49
        self.eq.inotify_add_watch(self.root_dir)
 
50
        self.assertRaises(ValueError,
 
51
                          self.eq.inotify_rm_watch, "not-added-dir")
 
52
 
 
53
 
 
54
class DynamicHitMe(object):
 
55
    '''Helper class to test a sequence of signals.'''
 
56
    def __init__(self, should_events, test_machinery):
 
57
        self.should_events = []
 
58
        for i, info in enumerate(should_events):
 
59
            self.should_events.append((i,) + info)
 
60
        self.final_step = self.should_events[-1][0]
 
61
        self.should_events.reverse()
 
62
        self.test_machinery = test_machinery
 
63
 
 
64
    def __getattr__(self, name):
 
65
        '''typical method faker'''
 
66
        if not name.startswith("handle_"):
 
67
            return
 
68
 
 
69
        asked_event = name[7:]
 
70
 
 
71
        # to what we should match
 
72
        test_info = self.should_events.pop()
 
73
        step = test_info[0]
 
74
        should_evtname = test_info[1]
 
75
        should_args = test_info[2:]
 
76
 
 
77
        def to_check(*asked_args):
 
78
            '''the function that actually be called'''
 
79
            if asked_args != should_args:
 
80
                self.test_machinery.finished_error(
 
81
                    "In step %d received wrong args (%r)" % (step, asked_args))
 
82
            else:
 
83
                if step == self.final_step:
 
84
                    self.test_machinery.finished_ok()
 
85
 
 
86
        if should_evtname != asked_event:
 
87
            msg = "Event %r asked in bad order (%d)" % (asked_event, step)
 
88
            self.test_machinery.finished_error(msg)
 
89
        else:
 
90
            return to_check
 
91
 
 
92
class BaseTwisted(BaseEQTestCase):
 
93
    '''Base class for twisted tests.'''
 
94
 
 
95
    # this timeout must be bigger than the one used in event_queue
 
96
    timeout = 2
 
97
 
 
98
    def setUp(self):
 
99
        '''Setup the test.'''
 
100
        BaseEQTestCase.setUp(self)
 
101
        # create the deferred for the tests
 
102
        self._deferred = defer.Deferred()
 
103
 
 
104
    def finished_ok(self):
 
105
        '''Called to indicate that the tests finished ok.'''
 
106
        self._deferred.callback(True)
 
107
 
 
108
    def finished_error(self, msg):
 
109
        '''Called to indicate that the tests finished badly.'''
 
110
        self._deferred.errback(Exception(msg))
 
111
 
 
112
 
 
113
class SignalingTests(BaseTwisted):
 
114
    '''Test the whole stuff to receive signals.'''
 
115
 
 
116
    def test_file_create_close(self):
 
117
        '''Test receiving the create and close signals on files.'''
 
118
        testfile = os.path.join(self.root_dir, "foo")
 
119
 
 
120
        # helper class, pylint: disable-msg=C0111
 
121
        class HitMe(object):
 
122
            # class-closure, cannot use self, pylint: disable-msg=E0213
 
123
            def __init__(innerself):
 
124
                innerself.hist = []
 
125
 
 
126
            def handle_FS_FILE_CREATE(innerself, path):
 
127
                if path != testfile:
 
128
                    self.finished_error("received a wrong path")
 
129
                else:
 
130
                    innerself.hist.append("create")
 
131
 
 
132
            def handle_FS_FILE_CLOSE_WRITE(innerself, path):
 
133
                if path != testfile:
 
134
                    self.finished_error("received a wrong path")
 
135
                else:
 
136
                    if innerself.hist == ["create"]:
 
137
                        os.remove(testfile)
 
138
                        self.finished_ok()
 
139
                    else:
 
140
                        msg = "Finished in bad condition: %s" % innerself.hist
 
141
                        self.finished_error(msg)
 
142
 
 
143
        self.eq.inotify_add_watch(self.root_dir)
 
144
        self.eq.subscribe(HitMe())
 
145
 
 
146
        # generate the event
 
147
        open(testfile, "w").close()
 
148
        return self._deferred
 
149
 
 
150
    def test_dir_create(self):
 
151
        '''Test receiving the create signal on dirs.'''
 
152
        testdir = os.path.join(self.root_dir, "foo")
 
153
 
 
154
        # helper class, pylint: disable-msg=C0111
 
155
        class HitMe(object):
 
156
            # class-closure, cannot use self, pylint: disable-msg=E0213
 
157
            def handle_FS_DIR_CREATE(innerself, path):
 
158
                if path != testdir:
 
159
                    self.finished_error("received a wrong path")
 
160
                else:
 
161
                    os.rmdir(testdir)
 
162
                    self.finished_ok()
 
163
 
 
164
        self.eq.inotify_add_watch(self.root_dir)
 
165
        self.eq.subscribe(HitMe())
 
166
 
 
167
        # generate the event
 
168
        os.mkdir(testdir)
 
169
        return self._deferred
 
170
 
 
171
    def test_file_delete(self):
 
172
        '''Test the delete signal on a file.'''
 
173
        testfile = os.path.join(self.root_dir, "foo")
 
174
        open(testfile, "w").close()
 
175
 
 
176
        # helper class, pylint: disable-msg=C0111
 
177
        class HitMe(object):
 
178
            # class-closure, cannot use self, pylint: disable-msg=E0213
 
179
            def handle_FS_FILE_DELETE(innerself, path):
 
180
                if path != testfile:
 
181
                    self.finished_error("received a wrong path")
 
182
                else:
 
183
                    self.finished_ok()
 
184
 
 
185
        self.eq.inotify_add_watch(self.root_dir)
 
186
        self.eq.subscribe(HitMe())
 
187
 
 
188
        # generate the event
 
189
        os.remove(testfile)
 
190
        return self._deferred
 
191
 
 
192
    def test_dir_delete(self):
 
193
        '''Test the delete signal on a dir.'''
 
194
        testdir = os.path.join(self.root_dir, "foo")
 
195
        os.mkdir(testdir)
 
196
 
 
197
        # helper class, pylint: disable-msg=C0111
 
198
        class HitMe(object):
 
199
            # class-closure, cannot use self, pylint: disable-msg=E0213
 
200
            def handle_FS_DIR_DELETE(innerself, path):
 
201
                if path != testdir:
 
202
                    self.finished_error("received a wrong path")
 
203
                else:
 
204
                    self.finished_ok()
 
205
 
 
206
        self.eq.inotify_add_watch(self.root_dir)
 
207
        self.eq.subscribe(HitMe())
 
208
 
 
209
        # generate the event
 
210
        os.rmdir(testdir)
 
211
        return self._deferred
 
212
 
 
213
    def test_symlink(self):
 
214
        '''Test that symlinks are ignored.'''
 
215
        testdir = os.path.join(self.root_dir, "foo")
 
216
        os.mkdir(testdir)
 
217
        fromfile = os.path.join(self.root_dir, "from")
 
218
        open(fromfile, "w").close()
 
219
        symlpath = os.path.join(testdir, "syml")
 
220
 
 
221
        class DontHitMe(object):
 
222
            '''we shouldn't be called'''
 
223
            # class-closure, cannot use self, pylint: disable-msg=E0213
 
224
            def handle_default(innerself, *a):
 
225
                '''Something here? Error!'''
 
226
                self.finished_error("don't hit me! received %s" % (a,))
 
227
 
 
228
        def confirm():
 
229
            '''check result.'''
 
230
            self.finished_ok()
 
231
 
 
232
        # set up everything and freeze
 
233
        self.eq.inotify_add_watch(testdir)
 
234
        self.eq.subscribe(DontHitMe())
 
235
 
 
236
        os.symlink(fromfile, symlpath)
 
237
        reactor.callLater(.1, confirm)
 
238
        return self._deferred
 
239
 
 
240
    def test_file_moved_from(self):
 
241
        '''Test receiving the delete signal on a file when moved_from.'''
 
242
        fromfile = os.path.join(self.root_dir, "foo")
 
243
        helpdir = os.path.join(self.root_dir, "dir")
 
244
        tofile = os.path.join(helpdir, "foo")
 
245
        open(fromfile, "w").close()
 
246
        os.mkdir(helpdir)
 
247
 
 
248
        # helper class, pylint: disable-msg=C0111
 
249
        class HitMe(object):
 
250
            # class-closure, cannot use self, pylint: disable-msg=E0213
 
251
            def handle_FS_FILE_DELETE(innerself, path):
 
252
                if path != fromfile:
 
253
                    self.finished_error("received a wrong path")
 
254
                else:
 
255
                    os.remove(tofile)
 
256
                    os.rmdir(helpdir)
 
257
                    self.finished_ok()
 
258
 
 
259
        self.eq.inotify_add_watch(self.root_dir)
 
260
        self.eq.subscribe(HitMe())
 
261
 
 
262
        # generate the event
 
263
        os.rename(fromfile, tofile)
 
264
        return self._deferred
 
265
 
 
266
    def test_dir_moved_from(self):
 
267
        '''Test receiving the delete signal on a dir when it's moved_from.'''
 
268
        fromdir = os.path.join(self.root_dir, "foo")
 
269
        helpdir = os.path.join(self.root_dir, "dir")
 
270
        todir = os.path.join(helpdir, "foo")
 
271
        os.mkdir(fromdir)
 
272
        os.mkdir(helpdir)
 
273
 
 
274
        # helper class, pylint: disable-msg=C0111
 
275
        class HitMe(object):
 
276
            # class-closure, cannot use self, pylint: disable-msg=E0213
 
277
            def handle_FS_DIR_DELETE(innerself, path):
 
278
                if path != fromdir:
 
279
                    self.finished_error("received a wrong path")
 
280
                else:
 
281
                    os.rmdir(todir)
 
282
                    os.rmdir(helpdir)
 
283
                    self.finished_ok()
 
284
 
 
285
        self.eq.inotify_add_watch(self.root_dir)
 
286
        self.eq.subscribe(HitMe())
 
287
 
 
288
        # generate the event
 
289
        os.rename(fromdir, todir)
 
290
        return self._deferred
 
291
 
 
292
    def test_file_moved_to(self):
 
293
        '''Test receiving the create signal on a file when it's moved_to.'''
 
294
        fromfile = os.path.join(self.root_dir, "dir", "foo")
 
295
        tofile = os.path.join(self.root_dir, "foo")
 
296
        helpdir = os.path.join(self.root_dir, "dir")
 
297
        os.mkdir(helpdir)
 
298
        open(fromfile, "w").close()
 
299
 
 
300
        # helper class, pylint: disable-msg=C0111
 
301
        class HitMe(object):
 
302
            # class-closure, cannot use self, pylint: disable-msg=E0213
 
303
            def handle_FS_FILE_CREATE(innerself, path):
 
304
                if path != tofile:
 
305
                    self.finished_error("received a wrong path")
 
306
                else:
 
307
                    os.remove(tofile)
 
308
                    os.rmdir(helpdir)
 
309
                    self.finished_ok()
 
310
 
 
311
        self.eq.inotify_add_watch(self.root_dir)
 
312
        self.eq.subscribe(HitMe())
 
313
 
 
314
        # generate the event
 
315
        os.rename(fromfile, tofile)
 
316
        return self._deferred
 
317
 
 
318
    def test_dir_moved_to(self):
 
319
        '''Test receiving the create signal on a file when it's moved_to.'''
 
320
        fromdir = os.path.join(self.root_dir, "dir", "foo")
 
321
        todir = os.path.join(self.root_dir, "foo")
 
322
        helpdir = os.path.join(self.root_dir, "dir")
 
323
        os.mkdir(helpdir)
 
324
        os.mkdir(fromdir)
 
325
 
 
326
        # helper class, pylint: disable-msg=C0111
 
327
        class HitMe(object):
 
328
            # class-closure, cannot use self, pylint: disable-msg=E0213
 
329
            def handle_FS_DIR_CREATE(innerself, path):
 
330
                if path != todir:
 
331
                    self.finished_error("received a wrong path")
 
332
                else:
 
333
                    os.rmdir(todir)
 
334
                    os.rmdir(helpdir)
 
335
                    self.finished_ok()
 
336
 
 
337
        self.eq.inotify_add_watch(self.root_dir)
 
338
        self.eq.subscribe(HitMe())
 
339
 
 
340
        # generate the event
 
341
        os.rename(fromdir, todir)
 
342
        return self._deferred
 
343
 
 
344
    def test_lots_of_changes(self):
 
345
        '''Test doing several operations on files.'''
 
346
        helpdir = os.path.join(self.root_dir, "dir")
 
347
        os.mkdir(helpdir)
 
348
        mypath = functools.partial(os.path.join, self.root_dir)
 
349
 
 
350
        self.eq.inotify_add_watch(self.root_dir)
 
351
 
 
352
        should_events = [
 
353
            ("FS_FILE_CREATE", mypath("foo")),
 
354
            ("FS_FILE_CLOSE_WRITE", mypath("foo")),
 
355
            ("FS_FILE_DELETE", mypath("foo")),
 
356
            ("FS_FILE_CREATE", mypath("bar")),
 
357
            ("FS_FILE_CLOSE_WRITE", mypath("bar")),
 
358
            ("FS_FILE_CREATE", mypath("foo")),
 
359
            ("FS_FILE_CLOSE_WRITE", mypath("foo")),
 
360
            ("FS_FILE_DELETE", mypath("bar")),
 
361
            ("FS_FILE_DELETE", mypath("foo")),
 
362
            ("FS_FILE_CREATE", mypath("bar")),
 
363
            ("FS_FILE_CLOSE_WRITE", mypath("bar")),
 
364
            ("FS_FILE_DELETE", mypath("bar")),
 
365
        ]
 
366
        self.eq.subscribe(DynamicHitMe(should_events, self))
 
367
 
 
368
        # generate the events
 
369
        open(mypath("foo"), "w").close()
 
370
        os.rename(mypath("foo"), mypath("dir", "foo"))
 
371
        open(mypath("bar"), "w").close()
 
372
        os.rename(mypath("dir", "foo"), mypath("foo"))
 
373
        os.rename(mypath("bar"), mypath("dir", "bar"))
 
374
        os.remove(mypath("foo"))
 
375
        os.rename(mypath("dir", "bar"), mypath("bar"))
 
376
        os.remove(mypath("bar"))
 
377
        return self._deferred
 
378
 
 
379
    def test_file_moved_inside(self):
 
380
        '''Test the synthesis of the FILE_MOVE event.'''
 
381
        fromfile = os.path.join(self.root_dir, "foo")
 
382
        self.fs.create(fromfile, "")
 
383
        self.fs.set_node_id(fromfile, "from_node_id")
 
384
        tofile = os.path.join(self.root_dir, "bar")
 
385
        self.fs.create(tofile, "")
 
386
        self.fs.set_node_id(tofile, "to_node_id")
 
387
        open(fromfile, "w").close()
 
388
 
 
389
        # helper class, pylint: disable-msg=C0111
 
390
        class HitMe(object):
 
391
            # class-closure, cannot use self, pylint: disable-msg=E0213
 
392
            def handle_FS_FILE_MOVE(innerself, path_from, path_to):
 
393
                if path_from != fromfile:
 
394
                    self.finished_error("received a wrong path in from")
 
395
                elif path_to != tofile:
 
396
                    self.finished_error("received a wrong path in to")
 
397
                else:
 
398
                    os.remove(tofile)
 
399
                    self.finished_ok()
 
400
 
 
401
        self.eq.inotify_add_watch(self.root_dir)
 
402
        self.eq.subscribe(HitMe())
 
403
 
 
404
        # generate the event
 
405
        os.rename(fromfile, tofile)
 
406
        return self._deferred
 
407
 
 
408
    def test_dir_moved_inside(self):
 
409
        '''Test the synthesis of the DIR_MOVE event.'''
 
410
        fromdir = os.path.join(self.root_dir, "foo")
 
411
        self.fs.create(fromdir, "")
 
412
        self.fs.set_node_id(fromdir, "from_node_id")
 
413
        todir = os.path.join(self.root_dir, "bar")
 
414
        self.fs.create(todir, "")
 
415
        self.fs.set_node_id(todir, "to_node_id")
 
416
        os.mkdir(fromdir)
 
417
 
 
418
        # helper class, pylint: disable-msg=C0111
 
419
        class HitMe(object):
 
420
            # class-closure, cannot use self, pylint: disable-msg=E0213
 
421
            def handle_FS_DIR_MOVE(innerself, path_from, path_to):
 
422
                if path_from != fromdir:
 
423
                    self.finished_error("received a wrong path in from")
 
424
                elif path_to != todir:
 
425
                    self.finished_error("received a wrong path in to")
 
426
                else:
 
427
                    os.rmdir(todir)
 
428
                    self.finished_ok()
 
429
 
 
430
        self.eq.inotify_add_watch(self.root_dir)
 
431
        self.eq.subscribe(HitMe())
 
432
 
 
433
        # generate the event
 
434
        os.rename(fromdir, todir)
 
435
        return self._deferred
 
436
 
 
437
    def test_file_moved_inside_mixed(self):
 
438
        '''Test the synthesis of the FILE_MOVE event with more events.'''
 
439
        helpdir = os.path.join(self.root_dir, "dir")
 
440
        os.mkdir(helpdir)
 
441
        mypath = functools.partial(os.path.join, self.root_dir)
 
442
        self.fs.create(mypath('foo'), "")
 
443
        self.fs.set_node_id(mypath('foo'), "foo_node_id")
 
444
        self.fs.create(mypath('bar'), "")
 
445
        self.fs.set_node_id(mypath('bar'), "bar_node_id")
 
446
 
 
447
 
 
448
        self.eq.inotify_add_watch(self.root_dir)
 
449
 
 
450
        should_events = [
 
451
            ("FS_FILE_CREATE", mypath("foo")),
 
452
            ("FS_FILE_CLOSE_WRITE", mypath("foo")),
 
453
            ("FS_FILE_CREATE", mypath("bar")),
 
454
            ("FS_FILE_CLOSE_WRITE", mypath("bar")),
 
455
            ("FS_FILE_DELETE", mypath("foo")),
 
456
            ("FS_FILE_CREATE", mypath("foo")),
 
457
            ("FS_FILE_MOVE", mypath("bar"), mypath("baz")),
 
458
            ("FS_FILE_DELETE", mypath("foo")),
 
459
            ("FS_FILE_DELETE", mypath("baz")),
 
460
        ]
 
461
        self.eq.subscribe(DynamicHitMe(should_events, self))
 
462
 
 
463
        # generate the events
 
464
        open(mypath("foo"), "w").close()
 
465
        open(mypath("bar"), "w").close()
 
466
        os.rename(mypath("foo"), mypath("dir", "foo"))
 
467
        os.rename(mypath("dir", "foo"), mypath("foo"))
 
468
        os.rename(mypath("bar"), mypath("baz"))
 
469
        os.remove(mypath("foo"))
 
470
        os.remove(mypath("baz"))
 
471
        return self._deferred
 
472
 
 
473
    def test_dir_with_contents_moved_outside(self):
 
474
        ''' test the move of a dir outside the watched diresctory.'''
 
475
        root = os.path.join(self.root_dir, "watched_root")
 
476
        os.mkdir(root)
 
477
        trash = os.path.join(self.root_dir, "trash")
 
478
        os.mkdir(trash)
 
479
 
 
480
        testdir = os.path.join(root, "testdir")
 
481
        self.eq.fs.create(testdir, '')
 
482
        self.eq.fs.set_node_id(testdir, 'testdir_id')
 
483
        os.mkdir(testdir)
 
484
        testfile = os.path.join(testdir, "testfile")
 
485
        self.eq.fs.create(testfile, '')
 
486
        self.eq.fs.set_node_id(testfile, 'testfile_id')
 
487
        open(testfile, 'w').close()
 
488
 
 
489
        paths = [testdir, testfile]
 
490
        # helper class, pylint: disable-msg=C0111
 
491
        class HitMe(object):
 
492
            # class-closure, cannot use self, pylint: disable-msg=E0213
 
493
            def handle_FS_DIR_DELETE(innerself, path):
 
494
                expected = paths.pop()
 
495
                if path != expected:
 
496
                    self.finished_error("received a wrong path, expected:"
 
497
                                        " %s was: %s " % (expected, path))
 
498
                elif len(paths) == 0:
 
499
                    self.finished_ok()
 
500
 
 
501
            def handle_FS_FILE_DELETE(innerself, path):
 
502
                self.assertEqual(paths.pop(), path)
 
503
 
 
504
        self.eq.inotify_add_watch(root)
 
505
        self.eq.subscribe(HitMe())
 
506
 
 
507
        # generate the event
 
508
        os.rename(testdir, os.path.join(trash, os.path.basename(testdir)))
 
509
        return self._deferred
 
510
 
 
511
    def test_creation_inside_a_moved_directory(self):
 
512
        '''Test that renaming a directory is supported.'''
 
513
        testdir = os.path.join(self.root_dir, "testdir")
 
514
        self.eq.fs.create(testdir, '')
 
515
        self.eq.fs.set_node_id(testdir, 'testdir_id')
 
516
        os.mkdir(testdir)
 
517
        newdirname = os.path.join(self.root_dir, "newdir")
 
518
 
 
519
        # helper class, pylint: disable-msg=C0111
 
520
        class HitMe(object):
 
521
            # class-closure, cannot use self, pylint: disable-msg=E0213
 
522
            def handle_FS_FILE_CREATE(innerself, path):
 
523
                if path != newfilepath:
 
524
                    self.finished_error("received a wrong path")
 
525
                else:
 
526
                    os.remove(newfilepath)
 
527
                    os.rmdir(newdirname)
 
528
                    self.finished_ok()
 
529
 
 
530
        self.eq.inotify_add_watch(self.root_dir)
 
531
        self.eq.inotify_add_watch(testdir)
 
532
        self.eq.subscribe(HitMe())
 
533
 
 
534
        # rename the dir
 
535
        os.rename(testdir, newdirname)
 
536
 
 
537
        # generate the event
 
538
        newfilepath = os.path.join(newdirname, "afile")
 
539
        open(newfilepath, "w").close()
 
540
        return self._deferred
 
541
 
 
542
    def test_outside_file_moved_to(self):
 
543
        '''Test receiving the create signal on a file when it's moved_to.'''
 
544
        fromfile = os.path.join(self.root_dir, "foo")
 
545
        root_dir = os.path.join(self.root_dir, "my_files")
 
546
        tofile = os.path.join(root_dir, "foo")
 
547
        mypath = functools.partial(os.path.join, root_dir)
 
548
        os.mkdir(root_dir)
 
549
        open(fromfile, "w").close()
 
550
 
 
551
        should_events = [
 
552
            ("FS_FILE_CREATE", mypath("foo")),
 
553
            ("FS_FILE_CLOSE_WRITE", mypath("foo")),
 
554
        ]
 
555
        self.eq.subscribe(DynamicHitMe(should_events, self))
 
556
        self.eq.inotify_add_watch(root_dir)
 
557
 
 
558
        # generate the event
 
559
        os.rename(fromfile, tofile)
 
560
        return self._deferred
 
561
 
 
562
    def test_outside_dir_with_contents_moved_to(self):
 
563
        '''Test receiving the create signal on a file when it's moved_to.'''
 
564
        fromdir = os.path.join(self.root_dir, "foo_dir")
 
565
        fromfile = os.path.join(fromdir, "foo")
 
566
        root_dir = os.path.join(self.root_dir, "my_files")
 
567
        mypath = functools.partial(os.path.join, root_dir)
 
568
        todir = os.path.join(root_dir, "foo_dir")
 
569
        os.mkdir(root_dir)
 
570
        os.mkdir(fromdir)
 
571
        open(fromfile, "w").close()
 
572
 
 
573
        should_events = [
 
574
            ("FS_DIR_CREATE", mypath("foo_dir")),
 
575
        ]
 
576
        self.eq.subscribe(DynamicHitMe(should_events, self))
 
577
        self.eq.inotify_add_watch(root_dir)
 
578
 
 
579
        # generate the event
 
580
        os.rename(fromdir, todir)
 
581
        return self._deferred
 
582
 
 
583
class FreezeTests(BaseTwisted):
 
584
    '''Test the freeze mechanism.'''
 
585
 
 
586
    def test_api(self):
 
587
        '''API for freeze/freeze_commit stuff.'''
 
588
        # bad args
 
589
        self.assertRaises(TypeError, self.eq.freeze_begin)
 
590
        self.assertRaises(TypeError, self.eq.freeze_begin, 1, 2)
 
591
        self.assertRaises(TypeError, self.eq.freeze_commit)
 
592
        self.assertRaises(TypeError, self.eq.freeze_commit, 1, 2)
 
593
        self.assertRaises(TypeError, self.eq.freeze_rollback, 1)
 
594
 
 
595
        # nothing frozen
 
596
        self.assertRaises(ValueError, self.eq.freeze_commit, [])
 
597
        self.assertRaises(ValueError, self.eq.freeze_rollback)
 
598
 
 
599
        # freeze, no-double-freeze, freeze_commit, no post-commit or rollback
 
600
        self.eq.freeze_begin(1)
 
601
        self.assertRaises(ValueError, self.eq.freeze_begin, 1)
 
602
        self.eq.freeze_commit([])
 
603
        self.assertRaises(ValueError, self.eq.freeze_commit, [])
 
604
        self.assertRaises(ValueError, self.eq.freeze_rollback)
 
605
 
 
606
        # freeze, rollback, no post-commit or rollback
 
607
        self.eq.freeze_begin(1)
 
608
        self.assertRaises(ValueError, self.eq.freeze_begin, 1)
 
609
        self.eq.freeze_rollback()
 
610
        self.assertRaises(ValueError, self.eq.freeze_commit, [])
 
611
        self.assertRaises(ValueError, self.eq.freeze_rollback)
 
612
 
 
613
    def test_commit_no_middle_events(self):
 
614
        '''Commit behaviour when nothing happened in the middle.'''
 
615
        testdir = os.path.join(self.root_dir, "foo")
 
616
        os.mkdir(testdir)
 
617
 
 
618
        # helper class, pylint: disable-msg=C0111
 
619
        class HitMe(object):
 
620
            # class-closure, cannot use self, pylint: disable-msg=E0213
 
621
            def handle_FS_DIR_DELETE(innerself, path):
 
622
                if path != "foobar":
 
623
                    self.finished_error("received a wrong path")
 
624
                else:
 
625
                    self.finished_ok()
 
626
 
 
627
        def freeze_commit():
 
628
            '''release with handcrafted event and check result.'''
 
629
            d = self.eq.freeze_commit([("FS_DIR_DELETE", "foobar")])
 
630
 
 
631
            def check(dirty):
 
632
                '''check dirty'''
 
633
                if dirty:
 
634
                    self.finished_error("should not be dirty here")
 
635
            d.addCallback(check)
 
636
 
 
637
        # set up everything and freeze
 
638
        self.eq.inotify_add_watch(testdir)
 
639
        self.eq.subscribe(HitMe())
 
640
        self.eq.freeze_begin(testdir)
 
641
 
 
642
        reactor.callLater(.1, freeze_commit)
 
643
        return self._deferred
 
644
 
 
645
    def test_commit_middle_events(self):
 
646
        '''Commit behaviour when something happened in the middle.'''
 
647
        testdir = os.path.join(self.root_dir, "foo")
 
648
        testfile = os.path.join(testdir, "bar")
 
649
        os.mkdir(testdir)
 
650
 
 
651
        class DontHitMe(object):
 
652
            '''we shouldn't be called'''
 
653
            # class-closure, cannot use self, pylint: disable-msg=E0213
 
654
            def handle_default(innerself, *a):
 
655
                '''Something here? Error!'''
 
656
                self.finished_error("don't hit me! received %s" % (a,))
 
657
 
 
658
        def freeze_commit():
 
659
            '''release and check result.'''
 
660
            d = self.eq.freeze_commit([("FS_DIR_DELETE", "foobar")])
 
661
            def check(dirty):
 
662
                '''check dirty'''
 
663
                if not dirty:
 
664
                    self.finished_error("it *should* be dirty here")
 
665
                else:
 
666
                    self.finished_ok()
 
667
            d.addCallback(check)
 
668
 
 
669
        # set up everything and freeze
 
670
        self.eq.inotify_add_watch(testdir)
 
671
        self.eq.subscribe(DontHitMe())
 
672
        self.eq.freeze_begin(testdir)
 
673
 
 
674
        open(testfile, "w").close()
 
675
        reactor.callLater(.1, freeze_commit)
 
676
        return self._deferred
 
677
 
 
678
    def test_rollback(self):
 
679
        '''Check rollback.'''
 
680
        testdir = os.path.join(self.root_dir, "foo")
 
681
        testfile = os.path.join(testdir, "bar")
 
682
        os.mkdir(testdir)
 
683
 
 
684
        # helper class, pylint: disable-msg=C0111
 
685
        class HitMe(object):
 
686
            # class-closure, cannot use self, pylint: disable-msg=E0213
 
687
            def handle_FS_DIR_DELETE(innerself, path):
 
688
                if path != "foobar":
 
689
                    self.finished_error("received a wrong path")
 
690
                else:
 
691
                    self.finished_ok()
 
692
 
 
693
 
 
694
        def freeze_rollback():
 
695
            '''release with handcrafted event and check result.'''
 
696
            self.eq.freeze_rollback()
 
697
            self.eq.freeze_begin(testdir)
 
698
            reactor.callLater(.1,
 
699
                        self.eq.freeze_commit, [("FS_DIR_DELETE", "foobar")])
 
700
 
 
701
        # set up everything and freeze
 
702
        self.eq.inotify_add_watch(testdir)
 
703
        self.eq.subscribe(HitMe())
 
704
        self.eq.freeze_begin(testdir)
 
705
 
 
706
        # don't matter if had changes, rollback cleans them
 
707
        open(testfile, "w").close()
 
708
        reactor.callLater(.1, freeze_rollback)
 
709
        return self._deferred
 
710
 
 
711
    def test_selective(self):
 
712
        '''Check that it's frozen only for a path.'''
 
713
        testdir = os.path.join(self.root_dir, "foo")
 
714
        os.mkdir(testdir)
 
715
        testfile = os.path.join(self.root_dir, "bar")
 
716
 
 
717
        # helper class, pylint: disable-msg=C0111
 
718
        class HitMe(object):
 
719
            # class-closure, cannot use self, pylint: disable-msg=E0213
 
720
            def __init__(innerself):
 
721
                innerself.hist = []
 
722
 
 
723
            def handle_FS_FILE_CREATE(innerself, path):
 
724
                if path != testfile:
 
725
                    self.finished_error("received a wrong path")
 
726
                else:
 
727
                    innerself.hist.append("create")
 
728
 
 
729
            def handle_FS_FILE_CLOSE_WRITE(innerself, path):
 
730
                if path != testfile:
 
731
                    self.finished_error("received a wrong path")
 
732
                else:
 
733
                    if innerself.hist == ["create"]:
 
734
                        os.remove(testfile)
 
735
                        self.finished_ok()
 
736
                    else:
 
737
                        msg = "Finished in bad condition: %s" % innerself.hist
 
738
                        self.finished_error(msg)
 
739
 
 
740
        # set up everything
 
741
        self.eq.inotify_add_watch(self.root_dir)
 
742
        self.eq.inotify_add_watch(testdir)
 
743
        self.eq.subscribe(HitMe())
 
744
 
 
745
        # only freeze one path
 
746
        self.eq.freeze_begin(testdir)
 
747
 
 
748
        # generate events in the nonfrozen path
 
749
        open(testfile, "w").close()
 
750
 
 
751
        return self._deferred
 
752
 
 
753
 
 
754
def test_suite():
 
755
    # pylint: disable-msg=C0111
 
756
    return unittest.TestLoader().loadTestsFromName(__name__)
 
757
 
 
758
def Xtest_suite():
 
759
    # pylint: disable-msg=C0111
 
760
    loader = unittest.TestLoader()
 
761
    suite = unittest.TestSuite()
 
762
    suite.addTests(loader.loadTestsFromTestCase(SignalingTests))
 
763
    return suite
 
764
 
 
765
if __name__ == "__main__":
 
766
    unittest.main()