Merge lp:~mikemc/ubuntuone-client/clean-up-fsmonitor-in-tests into lp:ubuntuone-client

Proposed by Mike McCracken
Status: Merged
Approved by: dobey
Approved revision: 1379
Merged at revision: 1368
Proposed branch: lp:~mikemc/ubuntuone-client/clean-up-fsmonitor-in-tests
Merge into: lp:ubuntuone-client
Prerequisite: lp:~mikemc/ubuntuone-client/fix-networkstate-manythreads
Diff against target: 400 lines (+155/-46)
6 files modified
contrib/testing/testcase.py (+42/-1)
tests/syncdaemon/test_eq_inotify.py (+14/-2)
tests/syncdaemon/test_eventqueue.py (+9/-4)
tests/syncdaemon/test_fsm.py (+3/-2)
tests/syncdaemon/test_sync.py (+24/-9)
tests/syncdaemon/test_vm.py (+63/-28)
To merge this branch: bzr merge lp:~mikemc/ubuntuone-client/clean-up-fsmonitor-in-tests
Reviewer Review Type Date Requested Status
dobey (community) Approve
Review via email: mp+142800@code.launchpad.net

Commit message

- Clean up usage of Filesystem Monitor in test suite.

Description of the change

- Clean up usage of Filesystem Monitor in test suite.

All:
- Add fake FilesystemMonitor implementation to avoid unnecessary
  threads or IPC connection overhead for tests that don't inspect
  events.

test_eq_inotify specifically:
- Skip some tests which assume linux-specific inotify events
- Skip tests for UDF ancestors on non-linux, clarify skip message.
- Skip tests for non-utf8 names, as those are ignored in the fsevent daemon on darwin.

TO TEST: full test suite still not totally working on darwin, run
trial on the specific test files that are changed, eg:

% u1trial --reactor=twisted tests/syncdaemon/test_eq_inotify.py tests/syncdaemon/test_sync.py tests/syncdaemon/test_vm.py tests/syncdaemon/test_eventqueue.py

NOTE: several tests do still require the fsevents daemon (on darwin) to be running. Otherwise they will fail by timing out when connecting to it. This is a minor annoyance, and I'll file a bug about fixing it via e.g. skipIf, like we do with Squid.

To post a comment you must log in.
Revision history for this message
dobey (dobey) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'contrib/testing/testcase.py'
2--- contrib/testing/testcase.py 2012-10-22 17:18:47 +0000
3+++ contrib/testing/testcase.py 2013-01-10 22:19:25 +0000
4@@ -217,12 +217,52 @@
5 return {RECENT_TRANSFERS: [], UPLOADING: []}
6
7
8+class FakeMonitor(object):
9+ """A fake FilesystemMonitor."""
10+
11+ def __init__(self, eq, fs, ignore_config=None, timeout=1):
12+ """Do nothing."""
13+
14+ def freeze_begin(self, path):
15+ """Succeed quietly."""
16+
17+ def freeze_rollback(self):
18+ """Succeed quietly."""
19+
20+ def freeze_commit(self, events):
21+ """Never report dirty commits for fake events."""
22+ return defer.succeed(False)
23+
24+ def add_to_mute_filter(self, event, **info):
25+ """Do nothing."""
26+
27+ def rm_from_mute_filter(self, event, **info):
28+ """Do nothing."""
29+
30+ def add_watches_to_udf_ancestors(self, volume):
31+ """Just report success."""
32+ return defer.succeed(True)
33+
34+ def add_watch(self, dirpath):
35+ """Just report success."""
36+ defer.succeed(True)
37+
38+ def rm_watch(self, dirpath):
39+ """Just report success."""
40+ return defer.succeed(True)
41+
42+ def shutdown(self):
43+ """Just report success."""
44+ return defer.succeed(True)
45+
46+
47 class FakeMain(main.Main):
48 """ A fake Main class to setup the tests """
49
50 _fake_AQ_class = FakeActionQueue
51 _fake_AQ_params = ()
52 _sync_class = None
53+ _monitor_class = FakeMonitor
54
55 # don't call Main.__init__ we take care of creating a fake main and
56 # all its attributes. pylint: disable=W0231
57@@ -238,7 +278,8 @@
58 self.vm = volume_manager.VolumeManager(self)
59 self.fs = fs_manager.FileSystemManager(
60 self.data_dir, self.partials_dir, self.vm, self.db)
61- self.event_q = event_queue.EventQueue(self.fs)
62+ self.event_q = event_queue.EventQueue(self.fs,
63+ monitor_class=self._monitor_class)
64 self.fs.register_eq(self.event_q)
65 self.action_q = self._fake_AQ_class(self.event_q, self,
66 *self._fake_AQ_params)
67
68=== modified file 'tests/syncdaemon/test_eq_inotify.py'
69--- tests/syncdaemon/test_eq_inotify.py 2012-04-09 20:07:05 +0000
70+++ tests/syncdaemon/test_eq_inotify.py 2013-01-10 22:19:25 +0000
71@@ -34,15 +34,17 @@
72 import functools
73 import logging
74 import os
75+import sys
76
77 from twisted.internet import defer, reactor
78 from ubuntuone.devtools.handlers import MementoHandler
79-from ubuntuone.devtools.testcases import skipIfOS
80+from ubuntuone.devtools.testcases import skipIfOS, skipIfNotOS
81
82 from contrib.testing.testcase import (
83 BaseTwistedTestCase,
84 FakeMain,
85 Listener,
86+ skip_if_darwin_missing_fs_event,
87 skip_if_win32_missing_fs_event,
88 )
89 from tests.syncdaemon.test_eventqueue import BaseEQTestCase
90@@ -182,6 +184,9 @@
91 # this timeout must be bigger than the one used in event_queue
92 timeout = 2
93
94+ # use the default FSMonitor
95+ _monitor_class = None
96+
97 @defer.inlineCallbacks
98 def setUp(self):
99 """Setup the test."""
100@@ -330,6 +335,7 @@
101 yield self._deferred
102
103 @skip_if_win32_missing_fs_event
104+ @skip_if_darwin_missing_fs_event
105 @defer.inlineCallbacks
106 def test_commit_middle_events(self):
107 """Commit behaviour when something happened in the middle."""
108@@ -396,6 +402,7 @@
109 yield self._deferred
110
111 @skip_if_win32_missing_fs_event
112+ @skip_if_darwin_missing_fs_event
113 @defer.inlineCallbacks
114 def test_selective(self):
115 """Check that it's frozen only for a path."""
116@@ -633,6 +640,8 @@
117 make_dir(root_dir)
118 open_file(fromfile, "w").close()
119 self.eq.add_to_mute_filter("FS_FILE_CREATE", path=tofile)
120+ if sys.platform == 'darwin':
121+ self.eq.add_to_mute_filter("FS_FILE_OPEN", path=tofile)
122 self.eq.add_to_mute_filter("FS_FILE_CLOSE_WRITE", path=tofile)
123
124 yield self.eq.add_watch(root_dir)
125@@ -646,7 +655,7 @@
126 yield self._deferred
127
128
129-@skip_if_win32_missing_fs_event
130+@skipIfNotOS('linux2', "Only Linux watches UDF ancestors")
131 class AncestorsUDFTestCase(BaseTwistedTestCase):
132 """Events over UDF's ancestor are properly handled."""
133
134@@ -661,6 +670,7 @@
135 self.data_dir = self.mktemp('data_dir')
136 self.shares_dir = self.mktemp('shares_dir')
137 self.partials_dir = self.mktemp('partials_dir')
138+ self.patch(FakeMain, '_monitor_class', None)
139 self.main = FakeMain(self.root_dir, self.shares_dir,
140 self.data_dir, self.partials_dir)
141 self.addCleanup(self.main.shutdown)
142@@ -988,6 +998,7 @@
143
144
145 @skipIfOS('win32', "we can't create files with invalid utf8 byte sequences.")
146+@skipIfOS('darwin', "fsevents daemon ignores events with invalid filenames")
147 class NonUTF8NamesTests(BaseTwisted):
148 """Test the non-utf8 name handling."""
149
150@@ -1160,6 +1171,7 @@
151
152
153 @skip_if_win32_missing_fs_event
154+@skip_if_darwin_missing_fs_event
155 class SignalingTests(BaseTwisted):
156 """Test the whole stuff to receive signals."""
157
158
159=== modified file 'tests/syncdaemon/test_eventqueue.py'
160--- tests/syncdaemon/test_eventqueue.py 2012-07-26 22:00:58 +0000
161+++ tests/syncdaemon/test_eventqueue.py 2013-01-10 22:19:25 +0000
162@@ -36,7 +36,9 @@
163 from twisted.internet import defer
164 from twisted.trial.unittest import TestCase
165
166-from contrib.testing.testcase import BaseTwistedTestCase, FakeVolumeManager
167+from contrib.testing.testcase import (BaseTwistedTestCase,
168+ FakeMonitor,
169+ FakeVolumeManager)
170 from ubuntuone.platform.filesystem_notifications.monitor import FilesystemMonitor
171 from ubuntuone.syncdaemon import (
172 event_queue,
173@@ -49,6 +51,8 @@
174 class BaseEQTestCase(BaseTwistedTestCase):
175 """ Setup an EQ for test. """
176
177+ _monitor_class = FakeMonitor
178+
179 @defer.inlineCallbacks
180 def setUp(self):
181 """Setup the test."""
182@@ -66,7 +70,8 @@
183 share_id='', is_dir=True)
184 self.fs.set_by_path(path=self.root_dir,
185 local_hash=None, server_hash=None)
186- self.eq = event_queue.EventQueue(self.fs)
187+ self.eq = event_queue.EventQueue(self.fs,
188+ monitor_class=self._monitor_class)
189 self.eq.listener_map = {}
190 self.addCleanup(self.eq.shutdown)
191 self.fs.register_eq(self.eq)
192@@ -395,7 +400,7 @@
193 return d
194
195
196-class FakeMonitor(object):
197+class SimpleFakeMonitor(object):
198 """A fake FilesystemMonitor."""
199
200 def __init__(self, *args):
201@@ -431,7 +436,7 @@
202 @defer.inlineCallbacks
203 def test_shutdown_defers(self):
204 """The shutdown method in eq defers on the shutdown of the monitor."""
205- self.patch(event_queue, "FilesystemMonitor", FakeMonitor)
206+ self.patch(event_queue, "FilesystemMonitor", SimpleFakeMonitor)
207 eq = event_queue.EventQueue(None)
208 d = eq.shutdown()
209 self.assertFalse(d.called, "shutdown is fired after the monitor.")
210
211=== modified file 'tests/syncdaemon/test_fsm.py'
212--- tests/syncdaemon/test_fsm.py 2012-11-02 13:49:39 +0000
213+++ tests/syncdaemon/test_fsm.py 2013-01-10 22:19:25 +0000
214@@ -42,6 +42,7 @@
215 BaseTwistedTestCase,
216 FakeVolumeManager,
217 FakeMain,
218+ FakeMonitor,
219 Listener,
220 skip_if_win32_and_uses_metadata_older_than_5,
221 skip_if_win32_and_uses_readonly,
222@@ -116,7 +117,7 @@
223 self.addCleanup(self.db.shutdown)
224 self.fsm = FileSystemManager(self.fsmdir, self.partials_dir,
225 FakeVolumeManager(self.root_dir), self.db)
226- self.eq = EventQueue(self.fsm)
227+ self.eq = EventQueue(self.fsm, monitor_class=FakeMonitor)
228 self.addCleanup(self.eq.shutdown)
229 self.fsm.register_eq(self.eq)
230 self.share = yield self.create_share('share', u'share_name')
231@@ -4424,4 +4425,4 @@
232 result = self.fsm.get_paths_by_pattern(search)
233 self.assertNotEqual(result, expected)
234 expected = sorted(expected)
235- self.assertEqual(result, expected)
236\ No newline at end of file
237+ self.assertEqual(result, expected)
238
239=== modified file 'tests/syncdaemon/test_sync.py'
240--- tests/syncdaemon/test_sync.py 2012-10-02 19:52:03 +0000
241+++ tests/syncdaemon/test_sync.py 2013-01-10 22:19:25 +0000
242@@ -297,19 +297,21 @@
243 yield self.fsm.vm.add_share(share)
244 defer.returnValue(share)
245
246-
247-class TestSync(BaseSync):
248- """Test for Sync."""
249-
250+class TestUsingRealFSMonitor(BaseSync):
251+ """Class for tests that require a real FS monitor."""
252 timeout = 3
253
254 @defer.inlineCallbacks
255 def setUp(self):
256- """Set up."""
257- yield super(TestSync, self).setUp()
258- self.sync = Sync(main=self.main)
259- self.fsm = self.main.fs
260- self.handler.setLevel(logging.DEBUG)
261+ """Override self.main from BaseSync."""
262+ yield super(TestUsingRealFSMonitor, self).setUp()
263+ # FakeMain sends _monitor_class to EventQueue, which
264+ # uses platform default monitor when given None:
265+ self.patch(FakeMain, "_monitor_class", None)
266+ self.main = FakeMain(root_dir=self.root, shares_dir=self.shares,
267+ data_dir=self.data,
268+ partials_dir=self.partials_dir)
269+ self.addCleanup(self.main.shutdown)
270
271 @skipIfOS('win32', 'In windows we can not unlink opened files.')
272 def test_deleting_open_files_is_no_cause_for_despair(self):
273@@ -331,6 +333,19 @@
274 d.addCallback(cb)
275 return d
276
277+class TestSync(BaseSync):
278+ """Test for Sync."""
279+
280+ timeout = 3
281+
282+ @defer.inlineCallbacks
283+ def setUp(self):
284+ """Set up."""
285+ yield super(TestSync, self).setUp()
286+ self.sync = Sync(main=self.main)
287+ self.fsm = self.main.fs
288+ self.handler.setLevel(logging.DEBUG)
289+
290 def test_handle_AQ_DOWNLOAD_DOES_NOT_EXIST(self):
291 """handle_AQ_DOWNLOAD_DOES_NOT_EXIST."""
292 self.called = False
293
294=== modified file 'tests/syncdaemon/test_vm.py'
295--- tests/syncdaemon/test_vm.py 2012-08-06 19:18:57 +0000
296+++ tests/syncdaemon/test_vm.py 2013-01-10 22:19:25 +0000
297@@ -1468,34 +1468,6 @@
298 'watch for %r should be present.' % udf.path)
299
300 @defer.inlineCallbacks
301- def test_add_udf_with_content(self):
302- """Test for VolumeManager.add_udf with content on disk."""
303- # create a sync instance
304- from ubuntuone.syncdaemon import sync
305- sync = sync.Sync(self.main)
306- suggested_path = u"~/suggested_path"
307- udf = self._create_udf(suggested_path=suggested_path,
308- subscribed=True)
309- # create some files inside it
310- make_dir(udf.path)
311- for i in range(10):
312- with open_file(os.path.join(udf.path, 'file_%d' % (i,)), 'wb') as f:
313- f.write(os.urandom(10))
314- self.assertEqual(len(os.listdir(udf.path)), 10)
315- # patch the fake action queue to intercept make_file calls
316- called = []
317- self.main.action_q.make_file = lambda *a: called.append(a)
318- yield self.vm.add_udf(udf)
319- self.assertEqual(len(called), 10)
320- # check that the UDF is in the fsm metadata
321- mdobj = self.main.fs.get_by_path(udf.path)
322- self.assertEqual(mdobj.node_id, udf.node_id)
323- self.assertEqual(mdobj.share_id, udf.volume_id)
324- # check that there is a watch in the UDF
325- self.assertIn(udf.path, self.watches,
326- 'watch for %r should be present.' % udf.path)
327-
328- @defer.inlineCallbacks
329 def test_add_udf_calls_AQ(self):
330 """Test that VolumeManager.add_udf calls AQ.rescan_from_scratch."""
331 udf = self._create_udf(subscribed=True)
332@@ -1927,6 +1899,69 @@
333 self.assertEqual(expected, actual)
334
335
336+class VolumeManagerOpTestsRequiringRealFSMonitor(BaseVolumeManagerTests):
337+ """Tests of UDF/Volumes operations which require a real FSMonitor."""
338+
339+ @defer.inlineCallbacks
340+ def setUp(self):
341+ """Setup the test."""
342+ yield super(VolumeManagerOpTestsRequiringRealFSMonitor, self).setUp()
343+ # FakeMain sends _monitor_class to EventQueue, which
344+ # uses platform default monitor when given None:
345+ self.patch(FakeMain, "_monitor_class", None)
346+ self.main = FakeMain(root_dir=self.root_dir,
347+ shares_dir=self.shares_dir,
348+ data_dir=self.data_dir,
349+ partials_dir=self.partials_dir)
350+ # re-do the patching from super that alters main or its
351+ # event_q:
352+ orig_add_watch = self.main.event_q.add_watch
353+
354+ def fake_add_watch(path):
355+ self.watches.add(path)
356+ return orig_add_watch(path)
357+
358+ orig_rm_watch = self.main.event_q.rm_watch
359+
360+ def fake_rm_watch(path):
361+ self.watches.remove(path)
362+ return orig_rm_watch(path)
363+
364+ self.patch(self.main.event_q, 'add_watch', fake_add_watch)
365+ self.patch(self.main.event_q, 'rm_watch', fake_rm_watch)
366+ self.vm = self.main.vm
367+ self.main.event_q.push('SYS_INIT_DONE')
368+ self.addCleanup(self.main.shutdown)
369+
370+ @defer.inlineCallbacks
371+ def test_add_udf_with_content(self):
372+ """Test for VolumeManager.add_udf with content on disk."""
373+ # create a sync instance
374+ from ubuntuone.syncdaemon import sync
375+ sync = sync.Sync(self.main)
376+ suggested_path = u"~/suggested_path"
377+ udf = self._create_udf(suggested_path=suggested_path,
378+ subscribed=True)
379+ # create some files inside it
380+ make_dir(udf.path)
381+ for i in range(10):
382+ with open_file(os.path.join(udf.path, 'file_%d' % (i,)), 'wb') as f:
383+ f.write(os.urandom(10))
384+ self.assertEqual(len(os.listdir(udf.path)), 10)
385+ # patch the fake action queue to intercept make_file calls
386+ called = []
387+ self.main.action_q.make_file = lambda *a: called.append(a)
388+ yield self.vm.add_udf(udf)
389+ self.assertEqual(len(called), 10)
390+ # check that the UDF is in the fsm metadata
391+ mdobj = self.main.fs.get_by_path(udf.path)
392+ self.assertEqual(mdobj.node_id, udf.node_id)
393+ self.assertEqual(mdobj.share_id, udf.volume_id)
394+ # check that there is a watch in the UDF
395+ self.assertIn(udf.path, self.watches,
396+ 'watch for %r should be present.' % udf.path)
397+
398+
399 class VolumeManagerOperationsTests(BaseVolumeManagerTests):
400 """Test UDF/Volumes operations."""
401

Subscribers

People subscribed via source and target branches