Merge lp:~mandel/ubuntuone-client/improve-fs-notifications-logging into lp:ubuntuone-client
- improve-fs-notifications-logging
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Manuel de la Peña |
Approved revision: | 1155 |
Merged at revision: | 1151 |
Proposed branch: | lp:~mandel/ubuntuone-client/improve-fs-notifications-logging |
Merge into: | lp:ubuntuone-client |
Diff against target: |
259 lines (+90/-6) 2 files modified
tests/platform/windows/test_filesystem_notifications.py (+74/-2) ubuntuone/platform/windows/filesystem_notifications.py (+16/-4) |
To merge this branch: | bzr merge lp:~mandel/ubuntuone-client/improve-fs-notifications-logging |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Natalia Bidart (community) | Approve | ||
Diego Sarmentero (community) | Approve | ||
Review via email: mp+79243@code.launchpad.net |
Commit message
Fixes: lp:872254
* Improves the logging used in the fs events on Windows.
* Adds a logging assertion in the tests to ensure that things are done correctly.
Description of the change
Fixes: lp:872254
* Improves the logging used in the fs events on Windows.
* Adds a logging assertion in the tests to ensure that things are done correctly.
Natalia Bidart (nataliabidart) wrote : | # |
* The following code is not needed since the memento handler accepts multiple strings in one single call:
119 + def _assert_logs(self, events):
120 + """Assert the debug logs."""
121 + logs = self._get_
122 + for msg in logs:
123 + self.assertTrue
you just have to do:
* I also think that all the new helper functions could be improve using list comprehensions, for example:
86 + def _get_event_
87 + """Return the pushing event logs."""
88 + logs = []
89 + for e in events:
90 + logs.append(
91 + return logs
can be better read as:
86 + def _get_event_
87 + """Return the pushing event logs."""
88 + msg = 'Pushing event %s to processor.'
89 + return [msg % e for e in events]
Anyways, if we use the former to create generators instead of lists in memory, we can improve all the helpers having only one like this:
def _assert_logs(self, events):
logs = []
msg = 'Pushing event %r to processor.'
logs.extend(msg % e for e in events)
msg = 'Got from ReadDirectoryCh
logs.extend(msg % [(WINDOWS_
for actions in self.raw_events)
msg = 'Is path %r a dir? %r'
logs.
self.
* Also, always user %r to log variables, not %s, since the latter can fail for non-ascii strings.
* For every addHanlder, you need to queue a removeHandler cleanup function.
* The rest looks good!
- 1152. By Manuel de la Peña
-
Done hte follwing:
* use %r for logging.
* simplify the use of helper functions to list comprehentions.
Manuel de la Peña (mandel) wrote : | # |
> * The following code is not needed since the memento handler accepts multiple
> strings in one single call:
>
> 119 + def _assert_logs(self, events):
> 120 + """Assert the debug logs."""
> 121 + logs = self._get_
> 122 + for msg in logs:
> 123 + self.assertTrue
>
> you just have to do:
>
> self.assertTrue
>
> * I also think that all the new helper functions could be improve using list
> comprehensions, for example:
>
> 86 + def _get_event_
> 87 + """Return the pushing event logs."""
> 88 + logs = []
> 89 + for e in events:
> 90 + logs.append(
> 91 + return logs
>
> can be better read as:
>
> 86 + def _get_event_
> 87 + """Return the pushing event logs."""
> 88 + msg = 'Pushing event %s to processor.'
> 89 + return [msg % e for e in events]
>
> Anyways, if we use the former to create generators instead of lists in memory,
> we can improve all the helpers having only one like this:
>
> def _assert_logs(self, events):
> logs = []
>
> msg = 'Pushing event %r to processor.'
> logs.extend(msg % e for e in events)
>
> msg = 'Got from ReadDirectoryCh
> logs.extend(msg % [(WINDOWS_
> for actions in self.raw_events)
>
> msg = 'Is path %r a dir? %r'
> logs.extend(is_dir % data for data in self.paths_checked)
>
> self.assertTrue
>
> * Also, always user %r to log variables, not %s, since the latter can fail for
> non-ascii strings.
>
> * For every addHanlder, you need to queue a removeHandler cleanup function.
>
> * The rest looks good!
All comments make sense and therefore I have removed all the helper functions, the only issue I found with the comments it he one regarding 'self.assertTru
facundobatista> mandel, pong
<mandel> facundobatista, can you give me a hand with the memento handler?
<mandel> facundobatista, there is something I quite not get
<facundobatista> mandel, yes
<mandel> facundobatista, what does check_debug do when I pass a collection of expected logs messages?
<facundobatista> mandel, it checks that *all* those messages are included in a single log line with debug level
<mandel> facundobatista, ok, is there a method to check that all those logs messages have been added, but not in a single line?
<mandel> or should I loop though them and do a check_dbug per message?
<facundobatista> mandel, there's no method... the idea behind those multiple messages, is that you care to "some stuff being logged" (like the word "Starting", and the share id, etc), but you don't care the exact log wording
<facundobatista> mandel, give me one example of what you're wanting
<mandel> facundobatista, let me past the example
<mandel> facundobatista, here: http://
<mandel> facundobatista, I want to ensure that what I passed is indeed present ...
Natalia Bidart (nataliabidart) wrote : | # |
This code will not be executed if something fails before it runs:
# clean logger
should be:
# clean logger
(you need to move it up right after the log adding)
* While you're at it, can you please change this call:
# clean behind us by removing the file
to be
# clean behind us by removing the file
(you need to move it up right after the file creation)
* The proper styling for this:
# a map of the actions to names so that we have better logs.
WINDOWS_
1: 'IN_CREATE',
2: 'IN_DELETE',
3: 'IN_MODIFY',
4: 'IN_MOVED_FROM',
5: 'IN_MOVED_TO'}
should be:
# a map of the actions to names so that we have better logs.
WINDOWS_
1: 'IN_CREATE',
2: 'IN_DELETE',
3: 'IN_MODIFY',
4: 'IN_MOVED_FROM',
5: 'IN_MOVED_TO',
}
You have my +1 with those changes!
- 1153. By Manuel de la Peña
-
Made changes according to code review.
- 1154. By Manuel de la Peña
-
Move the cleanup as close as possible to the file creation.
Natalia Bidart (nataliabidart) wrote : | # |
The adding of:
# clean logger
needs to be done right after the addHandler, since if the actions() calls fails, the logger is not removed.
The rest looks good!
Manuel de la Peña (mandel) wrote : | # |
> The adding of:
>
> # clean logger
> self.addCleanup
>
> needs to be done right after the addHandler, since if the actions() calls
> fails, the logger is not removed.
>
True, and sorted out!!
> The rest looks good!
- 1155. By Manuel de la Peña
-
Add the clean up just after the things that we need to clean.
Preview Diff
1 | === modified file 'tests/platform/windows/test_filesystem_notifications.py' |
2 | --- tests/platform/windows/test_filesystem_notifications.py 2011-10-07 13:53:28 +0000 |
3 | +++ tests/platform/windows/test_filesystem_notifications.py 2011-10-18 13:36:35 +0000 |
4 | @@ -17,6 +17,7 @@ |
5 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
6 | """Test the filesystem notifications on windows.""" |
7 | |
8 | +import logging |
9 | import os |
10 | import tempfile |
11 | import thread |
12 | @@ -24,8 +25,10 @@ |
13 | import itertools |
14 | |
15 | from twisted.internet import defer |
16 | +from win32file import FILE_NOTIFY_INFORMATION |
17 | |
18 | from contrib.testing.testcase import BaseTwistedTestCase |
19 | +from ubuntuone.devtools.handlers import MementoHandler |
20 | from ubuntuone.platform.windows import os_helper |
21 | from ubuntuone.platform.windows.pyinotify import ( |
22 | ProcessEvent, |
23 | @@ -47,6 +50,7 @@ |
24 | FILE_NOTIFY_CHANGE_LAST_WRITE, |
25 | FILE_NOTIFY_CHANGE_SECURITY, |
26 | FILE_NOTIFY_CHANGE_LAST_ACCESS, |
27 | + WINDOWS_ACTIONS_NAMES, |
28 | ) |
29 | |
30 | #create a rever mapping to use it in the tests. |
31 | @@ -114,6 +118,34 @@ |
32 | FILE_NOTIFY_CHANGE_LAST_WRITE | \ |
33 | FILE_NOTIFY_CHANGE_SECURITY | \ |
34 | FILE_NOTIFY_CHANGE_LAST_ACCESS |
35 | + self.memento = MementoHandler() |
36 | + self.memento.setLevel(logging.DEBUG) |
37 | + self.raw_events = [] |
38 | + self.paths_checked = [] |
39 | + old_is_dir = Watch._path_is_dir |
40 | + |
41 | + def file_notify_information_wrapper(buf, data): |
42 | + """Wrapper that gets the events and adds them to the list.""" |
43 | + events = FILE_NOTIFY_INFORMATION(buf, data) |
44 | + # we want to append the list because that is what will be logged. |
45 | + # If we use extend we wont have the same logging because it will |
46 | + # group all events in a single lists which is not what the COM API |
47 | + # does. |
48 | + str_events = [(WINDOWS_ACTIONS_NAMES[action], path) for action, path in |
49 | + events] |
50 | + self.raw_events.append(str_events) |
51 | + return events |
52 | + |
53 | + def path_is_dir_wrapper(watch, path): |
54 | + """Wrapper that gets the checked paths.""" |
55 | + result = old_is_dir(watch, path) |
56 | + self.paths_checked.append((path, result)) |
57 | + return result |
58 | + |
59 | + self.patch(filesystem_notifications, 'FILE_NOTIFY_INFORMATION', |
60 | + file_notify_information_wrapper) |
61 | + self.patch(filesystem_notifications.Watch, '_path_is_dir', |
62 | + path_is_dir_wrapper) |
63 | |
64 | @defer.inlineCallbacks |
65 | def _perform_operations(self, path, mask, auto_add, actions, |
66 | @@ -123,6 +155,10 @@ |
67 | manager = WatchManager(handler) |
68 | yield manager.add_watch(os_helper.get_windows_valid_path(path), mask, |
69 | auto_add=auto_add) |
70 | + # change the logger so that we can check the logs if we wanted |
71 | + manager._wdm[0].log.addHandler(self.memento) |
72 | + # clean logger later |
73 | + self.addCleanup(manager._wdm[0].log.removeHandler, self.memento) |
74 | # execution the actions |
75 | actions() |
76 | # process the recorded events |
77 | @@ -136,6 +172,10 @@ |
78 | manager = WatchManager() |
79 | manager.add_watch(os_helper.get_windows_valid_path(path), mask, |
80 | auto_add=auto_add) |
81 | + # change the logger so that we can check the logs if we wanted |
82 | + manager._wdm[0].log.addHandler(self.memento) |
83 | + # clean logger later |
84 | + self.addCleanup(manager._wdm[0].log.removeHandler, self.memento) |
85 | # execution the actions |
86 | actions() |
87 | # process the recorded events |
88 | @@ -143,6 +183,18 @@ |
89 | events = self.handler.processed_events |
90 | return events |
91 | |
92 | + def _assert_logs(self, events): |
93 | + """Assert the debug logs.""" |
94 | + logs = [] |
95 | + msg = 'Is path %r a dir? %s' |
96 | + logs.extend([msg % data for data in self.paths_checked]) |
97 | + msg = 'Got from ReadDirectoryChangesW %r.' |
98 | + logs.extend([msg % actions for actions in self.raw_events]) |
99 | + msg = 'Pushing event %r to processor.' |
100 | + logs.extend([msg % e for e in events]) |
101 | + for msg in logs: |
102 | + self.assertTrue(self.memento.check_debug(msg)) |
103 | + |
104 | @defer.inlineCallbacks |
105 | def test_file_create(self): |
106 | """Test that the correct event is returned on a file create.""" |
107 | @@ -166,6 +218,8 @@ |
108 | self.assertEqual('.', event.path) |
109 | self.assertEqual(os.path.join(self.basedir, file_name), event.pathname) |
110 | self.assertEqual(0, event.wd) |
111 | + # assert the logging |
112 | + self._assert_logs(events) |
113 | |
114 | @defer.inlineCallbacks |
115 | def test_dir_create(self): |
116 | @@ -186,6 +240,8 @@ |
117 | self.assertEqual('.', event.path) |
118 | self.assertEqual(os.path.join(self.basedir, dir_name), event.pathname) |
119 | self.assertEqual(0, event.wd) |
120 | + # assert the logging |
121 | + self._assert_logs(events) |
122 | |
123 | @defer.inlineCallbacks |
124 | def test_file_remove(self): |
125 | @@ -208,6 +264,8 @@ |
126 | self.assertEqual('.', event.path) |
127 | self.assertEqual(os.path.join(self.basedir, file_name), event.pathname) |
128 | self.assertEqual(0, event.wd) |
129 | + # assert the logging |
130 | + self._assert_logs(events) |
131 | |
132 | @defer.inlineCallbacks |
133 | def test_dir_remove(self): |
134 | @@ -229,6 +287,8 @@ |
135 | self.assertEqual('.', event.path) |
136 | self.assertEqual(os.path.join(self.basedir, dir_name), event.pathname) |
137 | self.assertEqual(0, event.wd) |
138 | + # assert the logging |
139 | + self._assert_logs(events) |
140 | |
141 | @defer.inlineCallbacks |
142 | def test_file_write(self): |
143 | @@ -236,6 +296,8 @@ |
144 | file_name = os.path.join(self.basedir, 'test_file_write') |
145 | # create the file before recording |
146 | fd = open(file_name, 'w') |
147 | + # clean behind us by removing the file |
148 | + self.addCleanup(os.remove, file_name) |
149 | |
150 | def write_file(): |
151 | """Action for the test.""" |
152 | @@ -252,8 +314,8 @@ |
153 | self.assertEqual('.', event.path) |
154 | self.assertEqual(os.path.join(self.basedir, file_name), event.pathname) |
155 | self.assertEqual(0, event.wd) |
156 | - # clean behind us by removeing the file |
157 | - os.remove(file_name) |
158 | + # assert the logging |
159 | + self._assert_logs(events) |
160 | |
161 | @defer.inlineCallbacks |
162 | def test_file_moved_to_watched_dir_same_watcher(self): |
163 | @@ -296,6 +358,8 @@ |
164 | self.assertEqual(0, move_to_event.wd) |
165 | # assert that both cookies are the same |
166 | self.assertEqual(move_from_event.cookie, move_to_event.cookie) |
167 | + # assert the logging |
168 | + self._assert_logs(events) |
169 | |
170 | @defer.inlineCallbacks |
171 | def test_file_moved_to_not_watched_dir(self): |
172 | @@ -323,6 +387,8 @@ |
173 | self.assertEqual(os.path.join(self.basedir, from_file_name), |
174 | event.pathname) |
175 | self.assertEqual(0, event.wd) |
176 | + # assert the logging |
177 | + self._assert_logs(events) |
178 | |
179 | @defer.inlineCallbacks |
180 | def test_file_move_from_not_watched_dir(self): |
181 | @@ -351,6 +417,8 @@ |
182 | self.assertEqual(os.path.join(self.basedir, to_file_name), |
183 | event.pathname) |
184 | self.assertEqual(0, event.wd) |
185 | + # assert the logging |
186 | + self._assert_logs(events) |
187 | |
188 | @defer.inlineCallbacks |
189 | def test_dir_moved_to_watched_dir_same_watcher(self): |
190 | @@ -391,6 +459,8 @@ |
191 | self.assertEqual(0, move_to_event.wd) |
192 | # assert that both cookies are the same |
193 | self.assertEqual(move_from_event.cookie, move_to_event.cookie) |
194 | + # assert the logging |
195 | + self._assert_logs(events) |
196 | |
197 | @defer.inlineCallbacks |
198 | def test_dir_moved_to_not_watched_dir(self): |
199 | @@ -414,6 +484,8 @@ |
200 | self.assertEqual('.', event.path) |
201 | self.assertEqual(os.path.join(self.basedir, dir_name), event.pathname) |
202 | self.assertEqual(0, event.wd) |
203 | + # assert the logging |
204 | + self._assert_logs(events) |
205 | |
206 | @defer.inlineCallbacks |
207 | def test_dir_move_from_not_watched_dir(self): |
208 | |
209 | === modified file 'ubuntuone/platform/windows/filesystem_notifications.py' |
210 | --- ubuntuone/platform/windows/filesystem_notifications.py 2011-10-14 20:02:23 +0000 |
211 | +++ ubuntuone/platform/windows/filesystem_notifications.py 2011-10-18 13:36:35 +0000 |
212 | @@ -90,7 +90,17 @@ |
213 | 2: IN_DELETE, |
214 | 3: IN_MODIFY, |
215 | 4: IN_MOVED_FROM, |
216 | - 5: IN_MOVED_TO} |
217 | + 5: IN_MOVED_TO, |
218 | +} |
219 | + |
220 | +# a map of the actions to names so that we have better logs. |
221 | +WINDOWS_ACTIONS_NAMES = { |
222 | + 1: 'IN_CREATE', |
223 | + 2: 'IN_DELETE', |
224 | + 3: 'IN_MODIFY', |
225 | + 4: 'IN_MOVED_FROM', |
226 | + 5: 'IN_MOVED_TO', |
227 | +} |
228 | |
229 | # translates quickly the event and it's is_dir state to our standard events |
230 | NAME_TRANSLATIONS = { |
231 | @@ -188,7 +198,7 @@ |
232 | else: |
233 | # path does not exists, was it in the internal list? |
234 | is_dir = path in self._subdirs |
235 | - self.log.debug('is path %r a dir? %s', path, is_dir) |
236 | + self.log.debug('Is path %r a dir? %s', path, is_dir) |
237 | return is_dir |
238 | |
239 | def _process_events(self, events): |
240 | @@ -239,7 +249,7 @@ |
241 | # add the event only if we do not have an exclude filter or |
242 | # the exclude filter returns False, that is, the event will not |
243 | # be excluded |
244 | - self.log.debug('Event is %s.', event) |
245 | + self.log.debug('Pushing event %r to processor.', event) |
246 | self._processor(event) |
247 | |
248 | def _call_deferred(self, f, *args): |
249 | @@ -310,7 +320,9 @@ |
250 | data = GetOverlappedResult(handle, self._overlapped, True) |
251 | # lets ead the data and store it in the results |
252 | events = FILE_NOTIFY_INFORMATION(buf, data) |
253 | - self.log.debug('Events from ReadDirectoryChangesW are %s', events) |
254 | + self.log.debug('Got from ReadDirectoryChangesW %r.', |
255 | + [(WINDOWS_ACTIONS_NAMES[action], path) for action, path in |
256 | + events]) |
257 | reactor.callFromThread(self._process_events, events) |
258 | |
259 | @is_valid_windows_path(path_indexes=[1]) |
+1