Merge lp:~mandel/ubuntuone-client/improve_watcher_tests into lp:ubuntuone-client

Proposed by Manuel de la Peña
Status: Merged
Approved by: Natalia Bidart
Approved revision: 1092
Merged at revision: 1075
Proposed branch: lp:~mandel/ubuntuone-client/improve_watcher_tests
Merge into: lp:ubuntuone-client
Prerequisite: lp:~nataliabidart/ubuntuone-client/cleanup-os-helper
Diff against target: 468 lines (+136/-130)
2 files modified
tests/platform/windows/test_filesystem_notifications.py (+121/-103)
ubuntuone/platform/windows/filesystem_notifications.py (+15/-27)
To merge this branch: bzr merge lp:~mandel/ubuntuone-client/improve_watcher_tests
Reviewer Review Type Date Requested Status
Natalia Bidart (community) Approve
Guillermo Gonzalez linux Approve
Review via email: mp+69671@code.launchpad.net

Commit message

Improve the tests that are used in the watcher code so that it does not longer uses mocker.

Description of the change

Improve the tests that are used in the watcher code so that it does not longer uses mocker.

To post a comment you must log in.
1088. By Manuel de la Peña

Remove unused wd.

1089. By Manuel de la Peña

Merged with trunk.

1090. By Manuel de la Peña

Went back to use get_wd.

Revision history for this message
Guillermo Gonzalez (verterok) wrote :

looks good.

review: Approve (linux)
1091. By Alejandro J. Cura

Verify the events were processed in the right thread

1092. By Manuel de la Peña

Added missing docs.

Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Looks good!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'tests/platform/windows/test_filesystem_notifications.py'
2--- tests/platform/windows/test_filesystem_notifications.py 2011-07-28 13:56:40 +0000
3+++ tests/platform/windows/test_filesystem_notifications.py 2011-07-28 19:33:25 +0000
4@@ -1,5 +1,6 @@
5 #
6-# Author: Manuel de la Pena <manuel@canonical.com>
7+# Authors: Manuel de la Pena <manuel@canonical.com>
8+# Alejandro J. Cura <alecu@canonical.com>
9 #
10 # Copyright 2011 Canonical Ltd.
11 #
12@@ -18,13 +19,14 @@
13
14 import os
15 import tempfile
16+import thread
17 import time
18
19-from mocker import MockerTestCase, ANY
20-
21 from contrib.testing.testcase import BaseTwistedTestCase
22+from ubuntuone.platform.windows import os_helper
23 from ubuntuone.platform.windows.pyinotify import ProcessEvent
24 from ubuntuone.platform.windows.filesystem_notifications import (
25+ Watch,
26 WatchManager,
27 Notifier,
28 FILE_NOTIFY_CHANGE_FILE_NAME,
29@@ -40,28 +42,43 @@
30 class TestCaseHandler(ProcessEvent):
31 """ProcessEvent used for test cases."""
32
33- def my_init(self, **kwargs):
34+ thread_id = None
35+
36+ def my_init(self, main_thread_id=None, **kwargs):
37 """Init the event notifier."""
38 self.processed_events = []
39+ self.main_thread_id = main_thread_id
40
41 def process_IN_CREATE(self, event):
42+ """Process events and added to the list."""
43 self.processed_events.append(event)
44+ self._verify_thread_id()
45
46 def process_IN_DELETE(self, event):
47+ """Process events and added to the list."""
48 self.processed_events.append(event)
49+ self._verify_thread_id()
50
51 def process_default(self, event):
52 """Process events and added to the list."""
53 self.processed_events.append(event)
54+ self._verify_thread_id()
55+
56+ def _verify_thread_id(self):
57+ """Verify that the event was processed in the correct thread."""
58+ if self.main_thread_id:
59+ assert self.main_thread_id == thread.get_ident()
60
61
62 class TestWatch(BaseTwistedTestCase):
63 """Test the watch so that it returns the same events as pyinotify."""
64
65+ timeout = 10
66+
67 def setUp(self):
68 """Set infor for the tests."""
69 super(TestWatch, self).setUp()
70- self.basedir = os.path.abspath(self.mktemp('test_root'))
71+ self.basedir = self.mktemp('test_root')
72 self.mask = FILE_NOTIFY_CHANGE_FILE_NAME | \
73 FILE_NOTIFY_CHANGE_DIR_NAME | \
74 FILE_NOTIFY_CHANGE_ATTRIBUTES | \
75@@ -70,18 +87,20 @@
76 FILE_NOTIFY_CHANGE_SECURITY | \
77 FILE_NOTIFY_CHANGE_LAST_ACCESS
78
79+ self.handler = TestCaseHandler(main_thread_id=thread.get_ident())
80+
81 def _perform_operations(self, path, mask, auto_add, actions, number_events):
82 """Perform the file operations and returns the recorded events."""
83 manager = WatchManager()
84- manager.add_watch(path, mask, auto_add=auto_add)
85- handler = TestCaseHandler()
86- notifier = Notifier(manager, handler)
87+ manager.add_watch(os_helper.get_windows_valid_path(path), mask,
88+ auto_add=auto_add)
89+ notifier = Notifier(manager, self.handler)
90 # execution the actions
91 actions()
92 # process the recorded events
93- while not len(handler.processed_events) >= number_events:
94+ while not len(self.handler.processed_events) >= number_events:
95 notifier.process_events()
96- events = handler.processed_events
97+ events = self.handler.processed_events
98 notifier.stop()
99 return events
100
101@@ -89,15 +108,15 @@
102 time_out):
103 """Perform the file operations and returns the recorded events."""
104 manager = WatchManager()
105- manager.add_watch(path, mask, auto_add=auto_add)
106- handler = TestCaseHandler()
107- notifier = Notifier(manager, handler)
108+ manager.add_watch(os_helper.get_windows_valid_path(path), mask,
109+ auto_add=auto_add)
110+ notifier = Notifier(manager, self.handler)
111 # execution the actions
112 actions()
113 # process the recorded events
114 time.sleep(time_out)
115 notifier.process_events()
116- events = handler.processed_events
117+ events = self.handler.processed_events
118 notifier.stop()
119 return events
120
121@@ -371,9 +390,10 @@
122 """Test that the exclude filter works as expectd."""
123 manager = WatchManager()
124 # add a watch that will always exclude all actions
125- manager.add_watch(self.basedir, self.mask, auto_add=True,
126- exclude_filter=lambda x: True )
127- handler = TestCaseHandler()
128+ manager.add_watch(os_helper.get_windows_valid_path(self.basedir),
129+ self.mask, auto_add=True,
130+ exclude_filter=lambda x: True )
131+ handler = TestCaseHandler(main_thread_id=thread.get_ident())
132 notifier = Notifier(manager, handler)
133 # execution the actions
134 file_name = os.path.join(self.basedir, 'test_file_create')
135@@ -398,30 +418,40 @@
136 open_dir, 2)
137 self.assertEqual(0, len(events))
138
139-TestWatch.skip = 'To be fixed by mandel and alecu.'
140-
141-
142-class TestWatchManager(BaseTwistedTestCase, MockerTestCase):
143+
144+class FakeEvent():
145+ """A fake event."""
146+
147+ def __init__(self, path):
148+ """Create an event with the given path."""
149+ self.pathname = path
150+
151+
152+class TestWatchManager(BaseTwistedTestCase):
153 """Test the watch manager."""
154
155 def setUp(self):
156 """Set each of the tests."""
157 super(TestWatchManager, self).setUp()
158- self.watch_factory = self.mocker.mock()
159- self.watch = self.mocker.mock()
160- self.manager = WatchManager(watch_factory=self.watch_factory)
161+ self.path = u'\\\\?\\C:\\path' # a valid windows path
162+ self.watch = Watch(1, self.path, None, True)
163+ self.manager = WatchManager()
164+ self.manager._wdm = {1: self.watch}
165
166 def test_stop(self):
167 """Test that the different watches are stoppe."""
168- self.manager._wdm = {1: self.watch}
169- self.watch.path
170- self.watch.stop_watching()
171- self.mocker.replay()
172+ self.was_called = False
173+
174+ def stop_watching():
175+ """Fake stop watch."""
176+ self.was_called = True
177+
178+ self.watch.stop_watching = stop_watching
179 self.manager.stop()
180+ self.assertTrue(self.was_called, 'The watch stop was not called.')
181
182 def test_get_present_watch(self):
183 """Test that we can get a Watch using is wd."""
184- self.manager._wdm = {1: self.watch}
185 self.assertEqual(self.watch, self.manager.get_watch(1))
186
187 def test_get_missing_watch(self):
188@@ -430,112 +460,100 @@
189
190 def test_delete_present_watch(self):
191 """Test that we can remove a present watch."""
192- self.manager._wdm = {1: self.watch}
193- self.watch.stop_watching()
194- self.mocker.replay()
195- self.mocker.replay()
196+ self.was_called = False
197+
198+ def stop_watching():
199+ """Fake stop watch."""
200+ self.was_called = True
201+
202+ self.watch.stop_watching = stop_watching
203 self.manager.del_watch(1)
204+ self.assertRaises(KeyError, self.manager.get_watch, (1,))
205
206 def test_add_single_watch(self):
207 """Test the addition of a new single watch."""
208- wd = 0
209- path = 'path'
210+ self.was_called = False
211+
212+ def start_watching(*args):
213+ """Fake stop watch."""
214+ self.was_called = True
215+
216+ Watch.start_watching = start_watching
217+ self.manager._wdm = {}
218+
219 mask = 'bit_mask'
220 auto_add = True
221- events_queue = self.manager._events_queue
222 exclude_filter = lambda x: False
223 proc_fun = lambda x: None
224- # expect the factory to be called with the passed values
225- valid_path = u'\\\\?\\C:\\path' # a valid windows path
226- self.watch_factory(wd, valid_path, mask, auto_add,
227- events_queue=events_queue, exclude_filter=exclude_filter,
228- proc_fun=proc_fun)
229- self.mocker.result(self.watch)
230- self.watch.start_watching()
231- self.mocker.replay()
232- self.manager.add_watch(path, mask, proc_fun=proc_fun, auto_add=auto_add,
233+ self.manager.add_watch(self.path, mask, proc_fun=proc_fun, auto_add=auto_add,
234 exclude_filter=exclude_filter)
235 self.assertEqual(1, len(self.manager._wdm))
236+ self.assertTrue(self.was_called, 'The watch start was not called.')
237+ self.assertEqual(self.path, self.manager._wdm[0]._path)
238+ self.assertEqual(mask, self.manager._wdm[0]._mask)
239+ self.assertEqual(auto_add, self.manager._wdm[0]._auto_add)
240+ self.assertEqual(proc_fun, self.manager._wdm[0]._proc_fun)
241+ self.assertEqual(exclude_filter, self.manager._wdm[0].exclude_filter)
242
243 def test_update_present_watch(self):
244 """Test the update of a present watch."""
245- self.manager._wdm = {1: self.watch}
246+ self.stop_was_called = False
247+ self.start_was_called = False
248+
249+ def stop_watching():
250+ """Fake stop watch."""
251+ self.stop_was_called = True
252+
253+ def start_watching():
254+ """Fake stop watch."""
255+ self.start_was_called = True
256+
257+ proc_fun = lambda x: None
258 mask = 'mask'
259- proc_fun = lambda x: None
260- self.watch.path
261- self.watch.stop_watching()
262- self.watch.update(mask, proc_fun=proc_fun, auto_add=False)
263- self.watch.start_watching()
264- self.mocker.replay()
265+ self.watch.stop_watching = stop_watching
266+ self.watch.start_watching = start_watching
267 self.manager.update_watch(1, mask, proc_fun)
268-
269- def test_get_wath_present_wd(self):
270- """Test that the correct path is returned."""
271- self.manager._wdm = {1: self.watch}
272- path = 'path'
273- self.watch.path
274- self.mocker.result(path)
275- self.mocker.replay()
276- self.assertEqual(path, self.manager.get_path(1))
277-
278- def test_get_wath_missing_wd(self):
279- """Test that the correct path is returned."""
280+ self.assertEqual(self.watch._mask, mask)
281+ self.assertEqual(self.watch._proc_fun, proc_fun)
282+ self.assertTrue(self.stop_was_called, 'The watch stop was not called.')
283+ self.assertTrue(self.start_was_called,
284+ 'The watch start was not called.')
285+
286+ def test_get_watch_present_wd(self):
287+ """Test that the correct path is returned."""
288+ self.assertEqual(self.path, self.manager.get_path(1))
289+
290+ def test_get_watch_missing_wd(self):
291+ """Test that the correct path is returned."""
292+ self.manager._wdm = {}
293 self.assertEqual(None, self.manager.get_path(1))
294
295 def test_get_wd_exact_path(self):
296 """Test the wd is returned when there is a watch for the path."""
297- self.manager._wdm = {1: self.watch}
298- path = 'path'
299- self.watch.auto_add
300- self.mocker.result(True)
301- self.watch.path
302- self.mocker.result(path)
303- self.mocker.replay()
304- self.assertEqual(1, self.manager.get_wd(path))
305+ self.assertEqual(1, self.manager.get_wd(self.path))
306
307 def test_get_wd_child_path(self):
308 """Test the wd is returned when we have a child path."""
309- self.manager._wdm = {1: self.watch}
310- path = 'path'
311- self.watch.path
312- self.mocker.result(path)
313- self.watch.auto_add
314- self.mocker.result(True)
315- self.mocker.replay()
316- self.assertEqual(1, self.manager.get_wd(os.path.join(path, 'child')))
317+ child = os.path.join(self.path, 'child')
318+ self.assertEqual(1, self.manager.get_wd(child))
319
320 def test_rm_present_wd(self):
321 """Test the removal of a present watch."""
322- self.manager._wdm = {1: self.watch}
323- self.watch.stop_watching()
324- self.mocker.replay()
325 self.manager.rm_watch(1)
326 self.assertEqual(None, self.manager._wdm.get(1))
327
328 def test_rm_root_path(self):
329 """Test the removal of a root path."""
330- self.manager._wdm = {1: self.watch}
331- path = 'path'
332- self.watch.path
333- self.mocker.result(path)
334- self.watch.auto_add
335- self.mocker.result(True)
336- self.watch.exclude_filter = ANY
337- self.mocker.replay()
338- self.manager.rm_path(path)
339+ self.manager.rm_path(self.path)
340 self.assertEqual(self.watch, self.manager._wdm.get(1))
341+ event = FakeEvent(self.path)
342+ self.assertTrue(self.watch.exclude_filter(event))
343
344 def test_rm_child_path(self):
345 """Test the removal of a child path."""
346- self.manager._wdm = {1: self.watch}
347- path = 'path'
348- self.watch.path
349- self.mocker.result(path)
350- self.watch.auto_add
351- self.mocker.result(True)
352- self.watch.exclude_filter = ANY
353- self.mocker.replay()
354- self.manager.rm_path(os.path.join(path, 'child'))
355+ child = os.path.join(self.path, u'child')
356+ event = FakeEvent(child)
357+ self.manager.rm_path(child)
358 self.assertEqual(self.watch, self.manager._wdm[1])
359-
360-TestWatchManager.skip = 'To be fixed by mandel and alecu.'
361+ # assert that the path is ignored
362+ self.assertTrue(self.watch.exclude_filter(event))
363
364=== modified file 'ubuntuone/platform/windows/filesystem_notifications.py'
365--- ubuntuone/platform/windows/filesystem_notifications.py 2011-07-28 15:35:45 +0000
366+++ ubuntuone/platform/windows/filesystem_notifications.py 2011-07-28 19:33:25 +0000
367@@ -119,8 +119,7 @@
368 FILE_NOTIFY_CHANGE_LAST_ACCESS
369
370
371-# The implementation of the code that is provided as the pyinotify
372-# substitute
373+# The implementation of the code that is provided as the pyinotify substitute
374 class Watch(object):
375 """Implement the same functions as pyinotify.Watch."""
376
377@@ -157,6 +156,11 @@
378 events_queue = Queue()
379 self.events_queue = events_queue
380
381+ def _is_excluded(self, event):
382+ """Return if an event is ignored."""
383+ return (self.exclude_filter and self.exclude_filter(event)) or\
384+ event.mask == IN_OPEN | IN_ISDIR
385+
386 # XXX: confirm is using this decorator is correct!!! (nessita)
387 @is_valid_windows_path(path_indexes=[1])
388 def _path_is_dir(self, path):
389@@ -177,12 +181,6 @@
390 self._subdirs.append(path)
391 return is_dir
392
393- def _is_excluded(self, event):
394- """Return if an event is ignored."""
395- return (self.exclude_filter and self.exclude_filter(event)) or\
396- event.mask == IN_OPEN | IN_ISDIR
397-
398-
399 def _process_events(self):
400 """Process the events form the queue."""
401 # we transform the events to be the same as the one in pyinotify
402@@ -195,27 +193,21 @@
403 continue
404 # map the windows events to the pyinotify ones, tis is dirty but
405 # makes the multiplatform better, linux was first :P
406- is_dir = self._path_is_dir(file_name)
407- if os.path.exists(file_name):
408- is_dir = os.path.isdir(file_name)
409- else:
410- # we removed the path, we look in the internal list
411- if file_name in self._subdirs:
412- is_dir = True
413- self._subdirs.remove(file_name)
414+ syncdaemon_path = get_syncdaemon_valid_path(
415+ os.path.join(self._path, file_name))
416+ is_dir = self._path_is_dir(os.path.join(self._path, file_name))
417 if is_dir:
418 self._subdirs.append(file_name)
419 mask = WINDOWS_ACTIONS[action]
420 head, tail = os.path.split(file_name)
421 if is_dir:
422 mask |= IN_ISDIR
423- syncdaemon_path = get_syncdaemon_valid_path(self._path)
424 event_raw_data = {
425 'wd': self._descriptor,
426 'dir': is_dir,
427 'mask': mask,
428 'name': tail,
429- 'path': head.replace(syncdaemon_path, '.')}
430+ 'path': '.'}
431 # by the way in which the win api fires the events we know for
432 # sure that no move events will be added in the wrong order, this
433 # is kind of hacky, I dont like it too much
434@@ -229,7 +221,7 @@
435 event = Event(event_raw_data)
436 # FIXME: event deduces the pathname wrong and we need to manually
437 # set it
438- event.pathname = file_name
439+ event.pathname = syncdaemon_path
440 # add the event only if we do not have an exclude filter or
441 # the exclude filter returns False, that is, the event will not
442 # be excluded
443@@ -288,14 +280,9 @@
444 # add the diff events to the q so that the can be processed no
445 # matter the speed.
446 for action, file_name in results:
447- file_name = os.path.join(self._path, file_name)
448- # the COM api returns unicode files which although later in
449- # the future is something that we want, atm syncdaemon expects
450- # paths to be a byte sequence encoded with utf8
451- full_filename = get_syncdaemon_valid_path(file_name)
452- self._raw_events_queue.put((full_filename, action))
453- self.log.debug('Added %s to raw events queue.',
454- (full_filename, action))
455+ self._raw_events_queue.put((file_name, action))
456+ self.log.debug('Added %r to raw events queue.',
457+ (file_name, action))
458
459 def start_watching(self):
460 """Tell the watch to start processing events."""
461@@ -414,6 +401,7 @@
462 self._wd_count += 1
463 self.log.debug('Watch count increased to %s', self._wd_count)
464
465+ # XXX: Add path validation!!! (nessita)
466 def add_watch(self, path, mask, proc_fun=None, auto_add=False,
467 quiet=True, exclude_filter=None):
468 if hasattr(path, '__iter__'):

Subscribers

People subscribed via source and target branches