Merge lp:~mandel/ubuntuone-client/improve_watcher_tests into lp:ubuntuone-client
- improve_watcher_tests
- Merge into trunk
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 |
Related bugs: |
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.
- 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__'): |
looks good.