Merge lp:~mandel/ubuntuone-client/windows_sdtool into lp:ubuntuone-client
- windows_sdtool
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Natalia Bidart |
Approved revision: | 1061 |
Merged at revision: | 1019 |
Proposed branch: | lp:~mandel/ubuntuone-client/windows_sdtool |
Merge into: | lp:ubuntuone-client |
Diff against target: |
1771 lines (+1721/-3) 4 files modified
tests/platform/windows/test_ipc.py (+1/-1) tests/platform/windows/test_tools.py (+1043/-0) ubuntuone/platform/windows/ipc_client.py (+107/-2) ubuntuone/platform/windows/tools.py (+570/-0) |
To merge this branch: | bzr merge lp:~mandel/ubuntuone-client/windows_sdtool |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Natalia Bidart (community) | Approve | ||
Alejandro J. Cura (community) | Approve | ||
Review via email: mp+65493@code.launchpad.net |
Commit message
Provides an SDTool implementation that works on windows.
Description of the change
Provides an SDTool implementation that works on windows. In order to run the tests please do:
python C:\Python27\
- 1056. By Manuel de la Peña
-
Added the tests to ensure that the sdtool works as expected.
- 1057. By Manuel de la Peña
-
Merged with previous changes made on linux.
- 1058. By Manuel de la Peña
-
Fixed a number of lint issues
- 1059. By Manuel de la Peña
-
Use inlineCallbacks in the connect method.
- 1060. By Manuel de la Peña
-
Added forgotten test file.
- 1061. By Manuel de la Peña
-
Fixed lint issues and comments from reviews.
Natalia Bidart (nataliabidart) : | # |
Alejandro J. Cura (alecu) wrote : | # |
As we discussed on IRC, it would make sense to have the functions that create and return a deferred use inlineCallbacks instead. Please fix this and remove the self.bus that's not defined anywhere.
Good branch otherwise, thanks!
Natalia Bidart (nataliabidart) wrote : | # |
Marking as Needs Fixing to track what Alejandro asked.
Alejandro J. Cura (alecu) wrote : | # |
I'm changing the review to approve, pending the fixes that nessita will check.
Natalia Bidart (nataliabidart) wrote : | # |
Filed bug #801685 so Manuel can make the improvements later, without blocking this branch.
Preview Diff
1 | === modified file 'tests/platform/windows/test_ipc.py' |
2 | --- tests/platform/windows/test_ipc.py 2011-06-22 14:30:55 +0000 |
3 | +++ tests/platform/windows/test_ipc.py 2011-06-23 16:46:29 +0000 |
4 | @@ -2131,6 +2131,7 @@ |
5 | return d |
6 | |
7 | def test_enable_bandwidth_throttling(self): |
8 | + """Enable bandwidth throttling.""" |
9 | reply_handler = lambda: None |
10 | error_handler = lambda: None |
11 | |
12 | @@ -2148,7 +2149,6 @@ |
13 | d.addCallback(test_execution) |
14 | # pylint: enable=E1101 |
15 | return d |
16 | - """Enable bandwidth throttling.""" |
17 | |
18 | def test_disable_bandwidth_throttling(self): |
19 | """Disable bandwidth throttling.""" |
20 | |
21 | === added file 'tests/platform/windows/test_tools.py' |
22 | --- tests/platform/windows/test_tools.py 1970-01-01 00:00:00 +0000 |
23 | +++ tests/platform/windows/test_tools.py 2011-06-23 16:46:29 +0000 |
24 | @@ -0,0 +1,1043 @@ |
25 | +# tests.platform.windows.test_tools - test tools for SyncDaemon |
26 | +# |
27 | +# Author: Manuel de la Pena <manuel@canonical.com> |
28 | +# |
29 | +# Copyright 2011 Canonical Ltd. |
30 | +# |
31 | +# This program is free software: you can redistribute it and/or modify it |
32 | +# under the terms of the GNU General Public License version 3, as published |
33 | +# by the Free Software Foundation. |
34 | +# |
35 | +# This program is distributed in the hope that it will be useful, but |
36 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
37 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
38 | +# PURPOSE. See the GNU General Public License for more details. |
39 | +# |
40 | +# You should have received a copy of the GNU General Public License along |
41 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
42 | + |
43 | +from mocker import Mocker, ANY |
44 | + |
45 | +from twisted.trial.unittest import TestCase |
46 | +from twisted.internet import defer, reactor |
47 | +from twisted.spread.pb import ( |
48 | + PBServerFactory, |
49 | + Root) |
50 | + |
51 | +from ubuntuone.platform.windows.tools import SyncDaemonTool |
52 | +from ubuntuone.platform.windows.ipc import ( |
53 | + Config, |
54 | + Events, |
55 | + Folders, |
56 | + FileSystem, |
57 | + PublicFiles, |
58 | + Shares, |
59 | + Status, |
60 | + SyncDaemon, |
61 | + NAMED_PIPE_URL) |
62 | + |
63 | + |
64 | +class SaveProtocolServerFactory(PBServerFactory): |
65 | + """A PBServerFactory that saves the latest connected client.""" |
66 | + |
67 | + protocolInstance = None |
68 | + |
69 | + def clientConnectionMade(self, protocol): |
70 | + """Keep track of the given protocol.""" |
71 | + self.protocolInstance = protocol |
72 | + |
73 | + |
74 | +class FakeRoot(Root): |
75 | + """Root object that exposes the diff referenceable objects.""" |
76 | + |
77 | + def __init__(self, status, events, sync_daemon, file_system, shares, |
78 | + config, folders, public_files): |
79 | + """Create a new instance that will expose the objects.""" |
80 | + self._status = status |
81 | + self._events = events |
82 | + self._sync_daemon = sync_daemon |
83 | + self._file_system = file_system |
84 | + self._shares = shares |
85 | + self._config = config |
86 | + self._folders = folders |
87 | + self._public_files = public_files |
88 | + |
89 | + def remote_get_status(self): |
90 | + """Return the status remote object.""" |
91 | + return self._status |
92 | + |
93 | + def remote_get_events(self): |
94 | + """Return the events remote object.""" |
95 | + return self._events |
96 | + |
97 | + def remote_get_sync_daemon(self): |
98 | + """Return the sync daemon remote object.""" |
99 | + return self._sync_daemon |
100 | + |
101 | + def remote_get_file_system(self): |
102 | + """Return the file system remote object.""" |
103 | + return self._file_system |
104 | + |
105 | + def remote_get_shares(self): |
106 | + """Return the shares remote object.""" |
107 | + return self._shares |
108 | + |
109 | + def remote_get_config(self): |
110 | + """Return the config remote object.""" |
111 | + return self._config |
112 | + |
113 | + def remote_get_folders(self): |
114 | + """Return the folders remote object.""" |
115 | + return self._folders |
116 | + |
117 | + def remote_get_public_files(self): |
118 | + """Return the public files remote object.""" |
119 | + return self._public_files |
120 | + |
121 | + |
122 | +class TestSyncDaemonTool(TestCase): |
123 | + """Various utility methods to test/play with the SyncDaemon.""" |
124 | + |
125 | + def setUp(self): |
126 | + """Set up the different tests.""" |
127 | + super(TestSyncDaemonTool, self).setUp() |
128 | + self.pipe = NAMED_PIPE_URL % 'test_stdtool' |
129 | + self.mocker = Mocker() |
130 | + self.root = self.mocker.mock() |
131 | + self.status = Status(None, None, None) |
132 | + self.status.syncdaemon_status = self.root |
133 | + self.events = Events(None) |
134 | + self.events.events = self.root |
135 | + self.daemon = SyncDaemon(None, None, None, None) |
136 | + self.daemon.service = self.root |
137 | + self.fs = FileSystem(None, None) |
138 | + self.fs.syncdaemon_filesystem = self.root |
139 | + self.shares = Shares(None, None) |
140 | + self.shares.syncdaemon_shares = self.root |
141 | + self.config = Config(None, None) |
142 | + self.config.syncdaemon_config = self.root |
143 | + self.folders = Folders(None, None) |
144 | + self.folders.syncdaemon_folders = self.root |
145 | + self.files = PublicFiles(None, None) |
146 | + self.files.syncdaemon_public_files = self.root |
147 | + self.is_running = self.mocker.replace( |
148 | + 'ubuntuone.platform.windows.tools.is_running') |
149 | + # start pb |
150 | + self.syncdaemon_root = FakeRoot(self.status, self.events, self.daemon, |
151 | + self.fs, self.shares, self.config, |
152 | + self.folders, self.files) |
153 | + self.server_factory = SaveProtocolServerFactory(self.syncdaemon_root) |
154 | + # pylint: disable=E1101 |
155 | + self.listener = reactor.listenPipe(self.pipe, self.server_factory) |
156 | + self.sdtool = SyncDaemonTool(named_pipe=self.pipe) |
157 | + |
158 | + def tearDown(self): |
159 | + """Clean reactor.""" |
160 | + if self.server_factory.protocolInstance is not None: |
161 | + self.server_factory.protocolInstance.transport.loseConnection() |
162 | + return defer.gatherResults([self._tearDownServer(), |
163 | + self._tearDownClient()]) |
164 | + |
165 | + def _tearDownServer(self): |
166 | + """Teardown the server.""" |
167 | + return defer.maybeDeferred(self.listener.stopListening) |
168 | + |
169 | + def _tearDownClient(self): |
170 | + """Tear down the client.""" |
171 | + self.sdtool.client.disconnect() |
172 | + return defer.succeed(None) |
173 | + |
174 | + @defer.inlineCallbacks |
175 | + def _connect(self): |
176 | + """Connect to the running service.""" |
177 | + yield self.sdtool.client.connect(self.pipe) |
178 | + defer.returnValue(self.sdtool) |
179 | + |
180 | + def test_wait_connected(self): |
181 | + """Test that we wait until we connect to the server.""" |
182 | + |
183 | + def test_get_current_downloads(self): |
184 | + """Test gettting the current downloads.""" |
185 | + downloads = [dict(name='name', id='id'), |
186 | + dict(name='name', id='id')] |
187 | + |
188 | + @defer.inlineCallbacks |
189 | + def test_execution(sdtool): |
190 | + """Actual test.""" |
191 | + self.root.current_downloads() |
192 | + self.mocker.result(downloads) |
193 | + self.mocker.replay() |
194 | + result = yield sdtool.get_current_downloads() |
195 | + self.mocker.verify() |
196 | + self.assertEqual(downloads, result) |
197 | + |
198 | + d = self._connect() |
199 | + # pylint: disable=E1101 |
200 | + d.addCallback(test_execution) |
201 | + # pylint: enable=E1101 |
202 | + return d |
203 | + |
204 | + def test_get_current_uploads(self): |
205 | + """Test that we get all the current uploads.""" |
206 | + uploads = [dict(name='name', id='id'), |
207 | + dict(name='name', id='id')] |
208 | + |
209 | + @defer.inlineCallbacks |
210 | + def test_execution(sdtool): |
211 | + """Actual test.""" |
212 | + self.root.current_uploads() |
213 | + self.mocker.replay() |
214 | + self.mocker.result(uploads) |
215 | + result = yield sdtool.get_current_uploads() |
216 | + self.mocker.verify() |
217 | + self.assertEqual(uploads, result) |
218 | + |
219 | + d = self._connect() |
220 | + # pylint: disable=E1101 |
221 | + d.addCallback(test_execution) |
222 | + # pylint: enable=E1101 |
223 | + return d |
224 | + |
225 | + def test_wait_for_nirvana(self, last_event_interval=5, verbose=False): |
226 | + """Test that we can reach nirvana.""" |
227 | + last_event_interval = 5 |
228 | + |
229 | + @defer.inlineCallbacks |
230 | + def test_execution(sdtool): |
231 | + """Actual test.""" |
232 | + self.root.wait_for_nirvana(last_event_interval, ANY, ANY) |
233 | + self.mocker.replay() |
234 | + yield sdtool.wait_for_nirvana() |
235 | + self.mocker.verify() |
236 | + |
237 | + d = self._connect() |
238 | + # pylint: disable=E1101 |
239 | + d.addCallback(test_execution) |
240 | + # pylint: enable=E1101 |
241 | + return d |
242 | + |
243 | + def test_accept_share(self): |
244 | + """Test that we do accept the share.""" |
245 | + share_id = 'share_id' |
246 | + |
247 | + @defer.inlineCallbacks |
248 | + def test_execution(sdtool): |
249 | + """Actual test.""" |
250 | + self.root.accept_share(share_id, ANY, ANY) |
251 | + self.mocker.replay() |
252 | + yield sdtool.accept_share(share_id) |
253 | + self.mocker.verify() |
254 | + |
255 | + d = self._connect() |
256 | + # pylint: disable=E1101 |
257 | + d.addCallback(test_execution) |
258 | + # pylint: enable=E1101 |
259 | + return d |
260 | + |
261 | + def test_reject_share(self): |
262 | + """Test that we do reject the share.""" |
263 | + share_id = 'share_id' |
264 | + |
265 | + @defer.inlineCallbacks |
266 | + def test_execution(sdtool): |
267 | + """Actual test.""" |
268 | + self.root.reject_share(share_id, ANY, ANY) |
269 | + self.mocker.replay() |
270 | + yield sdtool.reject_share(share_id) |
271 | + self.mocker.verify() |
272 | + |
273 | + d = self._connect() |
274 | + # pylint: disable=E1101 |
275 | + d.addCallback(test_execution) |
276 | + # pylint: enable=E1101 |
277 | + return d |
278 | + |
279 | + def test_subscribe_share(self): |
280 | + """Test that we subscribe to the share.""" |
281 | + share_id = 'share_id' |
282 | + |
283 | + @defer.inlineCallbacks |
284 | + def test_execution(sdtool): |
285 | + """Actual test.""" |
286 | + self.root.subscribe(share_id) |
287 | + self.mocker.replay() |
288 | + yield sdtool.subscribe_share(share_id) |
289 | + self.mocker.verify() |
290 | + |
291 | + d = self._connect() |
292 | + # pylint: disable=E1101 |
293 | + d.addCallback(test_execution) |
294 | + # pylint: enable=E1101 |
295 | + return d |
296 | + |
297 | + def test_unsubscribe_share(self): |
298 | + """Unsubscribe from a share given its id.""" |
299 | + share_id = 'share_id' |
300 | + |
301 | + @defer.inlineCallbacks |
302 | + def test_execution(sdtool): |
303 | + """Actual test.""" |
304 | + self.root.unsubscribe(share_id) |
305 | + self.mocker.replay() |
306 | + yield sdtool.unsubscribe_share(share_id) |
307 | + self.mocker.verify() |
308 | + |
309 | + d = self._connect() |
310 | + # pylint: disable=E1101 |
311 | + d.addCallback(test_execution) |
312 | + # pylint: enable=E1101 |
313 | + return d |
314 | + |
315 | + def test_get_shares(self): |
316 | + """Test that we get the list of shares.""" |
317 | + shares = [dict(name='name', id='id'), |
318 | + dict(name='name', id='id')] |
319 | + |
320 | + @defer.inlineCallbacks |
321 | + def test_execution(sdtool): |
322 | + """Actual test.""" |
323 | + self.root.get_shares() |
324 | + self.mocker.result(shares) |
325 | + self.mocker.replay() |
326 | + yield sdtool.get_shares() |
327 | + self.mocker.verify() |
328 | + |
329 | + d = self._connect() |
330 | + # pylint: disable=E1101 |
331 | + d.addCallback(test_execution) |
332 | + # pylint: enable=E1101 |
333 | + return d |
334 | + |
335 | + def test_refresh_shares(self): |
336 | + """Test that we can refresh the shares.""" |
337 | + |
338 | + @defer.inlineCallbacks |
339 | + def test_execution(sdtool): |
340 | + """Actual test.""" |
341 | + self.root.refresh_shares() |
342 | + self.mocker.replay() |
343 | + yield sdtool.refresh_shares() |
344 | + self.mocker.verify() |
345 | + |
346 | + d = self._connect() |
347 | + # pylint: disable=E1101 |
348 | + d.addCallback(test_execution) |
349 | + # pylint: enable=E1101 |
350 | + return d |
351 | + |
352 | + def test_offer_share(self): |
353 | + """Test that we can indeed offer a share.""" |
354 | + path = 'path' |
355 | + username = 'username' |
356 | + name = 'name' |
357 | + access_level = 'level' |
358 | + |
359 | + @defer.inlineCallbacks |
360 | + def test_execution(sdtool): |
361 | + """Actual test.""" |
362 | + self.root.create_share(path, username, name, access_level) |
363 | + self.mocker.replay() |
364 | + yield sdtool.offer_share(path, username, name, access_level) |
365 | + self.mocker.verify() |
366 | + |
367 | + d = self._connect() |
368 | + # pylint: disable=E1101 |
369 | + d.addCallback(test_execution) |
370 | + # pylint: enable=E1101 |
371 | + return d |
372 | + |
373 | + def test_list_shared(self): |
374 | + """Test that we can list the shared.""" |
375 | + shares = [dict(name='name', id='id'), |
376 | + dict(name='name', id='id')] |
377 | + |
378 | + @defer.inlineCallbacks |
379 | + def test_execution(sdtool): |
380 | + """Actual test.""" |
381 | + self.root.get_shared() |
382 | + self.mocker.result(shares) |
383 | + self.mocker.replay() |
384 | + yield sdtool.list_shared() |
385 | + self.mocker.verify() |
386 | + |
387 | + d = self._connect() |
388 | + # pylint: disable=E1101 |
389 | + d.addCallback(test_execution) |
390 | + # pylint: enable=E1101 |
391 | + return d |
392 | + |
393 | + def test_create_folder(self): |
394 | + """Test that we can create a folder.""" |
395 | + path = 'path' |
396 | + |
397 | + @defer.inlineCallbacks |
398 | + def test_execution(sdtool): |
399 | + """Actual test.""" |
400 | + self.root.create(path) |
401 | + self.mocker.replay() |
402 | + yield sdtool.create_folder(path) |
403 | + self.mocker.verify() |
404 | + |
405 | + d = self._connect() |
406 | + # pylint: disable=E1101 |
407 | + d.addCallback(test_execution) |
408 | + # pylint: enable=E1101 |
409 | + return d |
410 | + |
411 | + def test_delete_folder(self): |
412 | + """Test that we can delete a folder.""" |
413 | + folder_id = 'folder_id' |
414 | + |
415 | + @defer.inlineCallbacks |
416 | + def test_execution(sdtool): |
417 | + """Actual test.""" |
418 | + self.root.delete(folder_id) |
419 | + self.mocker.replay() |
420 | + yield sdtool.delete_folder(folder_id) |
421 | + self.mocker.verify() |
422 | + |
423 | + d = self._connect() |
424 | + # pylint: disable=E1101 |
425 | + d.addCallback(test_execution) |
426 | + # pylint: enable=E1101 |
427 | + return d |
428 | + |
429 | + def test_subscribe_folder(self): |
430 | + """Test that we can subscribe to a folder.""" |
431 | + folder_id = 'folder_id' |
432 | + |
433 | + @defer.inlineCallbacks |
434 | + def test_execution(sdtool): |
435 | + """Actual test.""" |
436 | + self.root.subscribe(folder_id ) |
437 | + self.mocker.replay() |
438 | + yield sdtool.subscribe_folder(folder_id ) |
439 | + self.mocker.verify() |
440 | + |
441 | + d = self._connect() |
442 | + # pylint: disable=E1101 |
443 | + d.addCallback(test_execution) |
444 | + # pylint: enable=E1101 |
445 | + return d |
446 | + |
447 | + def test_unsubscribe_folder(self): |
448 | + """Test that we can unsubscribe to a folder.""" |
449 | + folder_id = 'folder_id' |
450 | + |
451 | + @defer.inlineCallbacks |
452 | + def test_execution(sdtool): |
453 | + """Actual test.""" |
454 | + self.root.unsubscribe(folder_id) |
455 | + self.mocker.replay() |
456 | + yield sdtool.unsubscribe_folder(folder_id) |
457 | + self.mocker.verify() |
458 | + |
459 | + d = self._connect() |
460 | + # pylint: disable=E1101 |
461 | + d.addCallback(test_execution) |
462 | + # pylint: enable=E1101 |
463 | + return d |
464 | + |
465 | + def test_get_folders(self): |
466 | + """Test that we can get the folders.""" |
467 | + folders = [dict(name='name', id='id'), |
468 | + dict(name='name', id='id')] |
469 | + |
470 | + @defer.inlineCallbacks |
471 | + def test_execution(sdtool): |
472 | + """Actual test.""" |
473 | + self.root.get_folders() |
474 | + self.mocker.result(folders) |
475 | + self.mocker.replay() |
476 | + yield sdtool.get_folders() |
477 | + self.mocker.verify() |
478 | + |
479 | + d = self._connect() |
480 | + # pylint: disable=E1101 |
481 | + d.addCallback(test_execution) |
482 | + # pylint: enable=E1101 |
483 | + return d |
484 | + |
485 | + def test_get_folder_info(self): |
486 | + """Test that we can get the info of a folder.""" |
487 | + path = 'path' |
488 | + |
489 | + @defer.inlineCallbacks |
490 | + def test_execution(client): |
491 | + """Actual test.""" |
492 | + self.root.get_info(path) |
493 | + self.mocker.replay() |
494 | + yield client.get_folder_info(path) |
495 | + self.mocker.verify() |
496 | + |
497 | + d = self._connect() |
498 | + # pylint: disable=E1101 |
499 | + d.addCallback(test_execution) |
500 | + # pylint: enable=E1101 |
501 | + return d |
502 | + |
503 | + def test_get_metadata(self): |
504 | + """Test that we can get the metadata.""" |
505 | + path = 'path' |
506 | + |
507 | + @defer.inlineCallbacks |
508 | + def test_execution(sdtool): |
509 | + """Actual test.""" |
510 | + self.root.get_metadata(path) |
511 | + self.mocker.replay() |
512 | + yield sdtool.get_metadata(path) |
513 | + self.mocker.verify() |
514 | + |
515 | + d = self._connect() |
516 | + # pylint: disable=E1101 |
517 | + d.addCallback(test_execution) |
518 | + # pylint: enable=E1101 |
519 | + return d |
520 | + |
521 | + def test_change_public_access(self): |
522 | + """Test that we can change the public access.""" |
523 | + path = 'path' |
524 | + share_id = 'share_id' |
525 | + node_id = 'node_id' |
526 | + is_public = False |
527 | + metadata = dict(share_id=share_id, node_id=node_id) |
528 | + |
529 | + @defer.inlineCallbacks |
530 | + def test_execution(sdtool): |
531 | + """Actual test.""" |
532 | + self.root.get_metadata(path) |
533 | + self.mocker.result(metadata) |
534 | + self.root.change_public_access(share_id, node_id, is_public) |
535 | + self.mocker.replay() |
536 | + yield sdtool.change_public_access(path, is_public) |
537 | + self.mocker.verify() |
538 | + |
539 | + d = self._connect() |
540 | + # pylint: disable=E1101 |
541 | + d.addCallback(test_execution) |
542 | + # pylint: enable=E1101 |
543 | + return d |
544 | + |
545 | + def test_quit(self): |
546 | + """Test that we can quit.""" |
547 | + |
548 | + @defer.inlineCallbacks |
549 | + def test_execution(sdtool): |
550 | + """Actual test.""" |
551 | + self.is_running() |
552 | + self.mocker.result(True) |
553 | + self.root.quit(None, None) |
554 | + self.mocker.replay() |
555 | + yield sdtool.quit() |
556 | + self.mocker.verify() |
557 | + |
558 | + d = self._connect() |
559 | + # pylint: disable=E1101 |
560 | + d.addCallback(test_execution) |
561 | + # pylint: enable=E1101 |
562 | + return d |
563 | + |
564 | + def test_connect(self): |
565 | + """Test we can connect.""" |
566 | + |
567 | + @defer.inlineCallbacks |
568 | + def test_execution(sdtool): |
569 | + """Actual test.""" |
570 | + self.root.connect() |
571 | + self.mocker.replay() |
572 | + yield sdtool.connect() |
573 | + self.mocker.verify() |
574 | + |
575 | + d = self._connect() |
576 | + # pylint: disable=E1101 |
577 | + d.addCallback(test_execution) |
578 | + # pylint: enable=E1101 |
579 | + return d |
580 | + |
581 | + def test_disconnect(self): |
582 | + """Test we can disconnect.""" |
583 | + |
584 | + @defer.inlineCallbacks |
585 | + def test_execution(sdtool): |
586 | + """Actual test.""" |
587 | + self.root.disconnect() |
588 | + self.mocker.replay() |
589 | + yield sdtool.disconnect() |
590 | + self.mocker.verify() |
591 | + |
592 | + d = self._connect() |
593 | + # pylint: disable=E1101 |
594 | + d.addCallback(test_execution) |
595 | + # pylint: enable=E1101 |
596 | + return d |
597 | + |
598 | + def test_get_status(self): |
599 | + """Test we can get the status.""" |
600 | + status = dict(is_error=False, is_online=True, is_connected=True) |
601 | + |
602 | + @defer.inlineCallbacks |
603 | + def test_execution(sdtool): |
604 | + """Actual test.""" |
605 | + self.root.current_status() |
606 | + self.mocker.result(status) |
607 | + self.mocker.replay() |
608 | + yield sdtool.get_status() |
609 | + self.mocker.verify() |
610 | + |
611 | + d = self._connect() |
612 | + # pylint: disable=E1101 |
613 | + d.addCallback(test_execution) |
614 | + # pylint: enable=E1101 |
615 | + return d |
616 | + |
617 | + def test_waiting(self): |
618 | + """Test that we get the info of what is waitig.""" |
619 | + |
620 | + @defer.inlineCallbacks |
621 | + def test_execution(sdtool): |
622 | + """Actual test.""" |
623 | + self.root.waiting() |
624 | + self.mocker.result({}) |
625 | + self.mocker.replay() |
626 | + yield sdtool.waiting() |
627 | + self.mocker.verify() |
628 | + |
629 | + d = self._connect() |
630 | + # pylint: disable=E1101 |
631 | + d.addCallback(test_execution) |
632 | + # pylint: enable=E1101 |
633 | + return d |
634 | + |
635 | + def test_waiting_metadata(self): |
636 | + """Test that we can get the waiting metadata.""" |
637 | + |
638 | + @defer.inlineCallbacks |
639 | + def test_execution(sdtool): |
640 | + """Actual test.""" |
641 | + self.root.waiting_metadata() |
642 | + self.mocker.replay() |
643 | + yield sdtool.waiting_metadata() |
644 | + self.mocker.verify() |
645 | + |
646 | + d = self._connect() |
647 | + # pylint: disable=E1101 |
648 | + d.addCallback(test_execution) |
649 | + # pylint: enable=E1101 |
650 | + return d |
651 | + |
652 | + def test_waiting_content(self): |
653 | + """Test that we get the waiting content.""" |
654 | + |
655 | + @defer.inlineCallbacks |
656 | + def test_execution(sdtool): |
657 | + """Actual test.""" |
658 | + self.root.waiting_content() |
659 | + self.mocker.replay() |
660 | + yield sdtool.waiting_content() |
661 | + self.mocker.verify() |
662 | + |
663 | + d = self._connect() |
664 | + # pylint: disable=E1101 |
665 | + d.addCallback(test_execution) |
666 | + # pylint: enable=E1101 |
667 | + return d |
668 | + |
669 | + def test_get_throttling_limits(self): |
670 | + """Test that we get the throttling limits.""" |
671 | + |
672 | + @defer.inlineCallbacks |
673 | + def test_execution(sdtool): |
674 | + """Actual test.""" |
675 | + self.root.get_throttling_limits(ANY, ANY) |
676 | + self.mocker.replay() |
677 | + yield sdtool.get_throttling_limits() |
678 | + self.mocker.verify() |
679 | + |
680 | + d = self._connect() |
681 | + # pylint: disable=E1101 |
682 | + d.addCallback(test_execution) |
683 | + # pylint: enable=E1101 |
684 | + return d |
685 | + |
686 | + def test_set_throttling_limits(self): |
687 | + """Test that we can set the limits.""" |
688 | + download = 'download' |
689 | + upload = 'upload' |
690 | + |
691 | + @defer.inlineCallbacks |
692 | + def test_execution(sdtool): |
693 | + """Actual test.""" |
694 | + self.root.set_throttling_limits(download, upload, ANY, ANY) |
695 | + self.mocker.replay() |
696 | + yield sdtool.set_throttling_limits(download, upload) |
697 | + self.mocker.verify() |
698 | + |
699 | + d = self._connect() |
700 | + # pylint: disable=E1101 |
701 | + d.addCallback(test_execution) |
702 | + # pylint: enable=E1101 |
703 | + return d |
704 | + |
705 | + def test_is_throttling_enabled(self): |
706 | + """Test that we get the bool stating if they are active.""" |
707 | + |
708 | + @defer.inlineCallbacks |
709 | + def test_execution(sdtool): |
710 | + """Actual test.""" |
711 | + self.root.bandwidth_throttling_enabled(ANY, ANY) |
712 | + self.mocker.result(True) |
713 | + self.mocker.replay() |
714 | + result = yield sdtool.is_throttling_enabled() |
715 | + self.mocker.verify() |
716 | + self.assertTrue(result) |
717 | + |
718 | + d = self._connect() |
719 | + # pylint: disable=E1101 |
720 | + d.addCallback(test_execution) |
721 | + # pylint: enable=E1101 |
722 | + return d |
723 | + |
724 | + def test_enable_throttling_true(self): |
725 | + """Test that we can set them.""" |
726 | + |
727 | + @defer.inlineCallbacks |
728 | + def test_execution(sdtool): |
729 | + """Actual test.""" |
730 | + self.root.enable_bandwidth_throttling(ANY, ANY) |
731 | + self.mocker.replay() |
732 | + yield sdtool.enable_throttling(True) |
733 | + self.mocker.verify() |
734 | + |
735 | + d = self._connect() |
736 | + # pylint: disable=E1101 |
737 | + d.addCallback(test_execution) |
738 | + # pylint: enable=E1101 |
739 | + return d |
740 | + |
741 | + def test_enable_throttling_false(self): |
742 | + """Test that we can set them.""" |
743 | + |
744 | + @defer.inlineCallbacks |
745 | + def test_execution(sdtool): |
746 | + """Actual test.""" |
747 | + self.root.disable_bandwidth_throttling(ANY, ANY) |
748 | + self.mocker.replay() |
749 | + yield sdtool.enable_throttling(False) |
750 | + self.mocker.verify() |
751 | + |
752 | + d = self._connect() |
753 | + # pylint: disable=E1101 |
754 | + d.addCallback(test_execution) |
755 | + # pylint: enable=E1101 |
756 | + return d |
757 | + |
758 | + def test_is_autoconnect_enabled(self): |
759 | + """Test that we can read the config.""" |
760 | + |
761 | + @defer.inlineCallbacks |
762 | + def test_execution(sdtool): |
763 | + """Actual test.""" |
764 | + self.root.autoconnect_enabled() |
765 | + self.mocker.replay() |
766 | + self.mocker.result(True) |
767 | + result = yield sdtool.is_autoconnect_enabled() |
768 | + self.mocker.verify() |
769 | + self.assertTrue(result) |
770 | + |
771 | + d = self._connect() |
772 | + # pylint: disable=E1101 |
773 | + d.addCallback(test_execution) |
774 | + # pylint: enable=E1101 |
775 | + return d |
776 | + |
777 | + def test_enable_autoconnect(self): |
778 | + """Test that we can enable the autoconnect.""" |
779 | + enabled = False |
780 | + |
781 | + @defer.inlineCallbacks |
782 | + def test_execution(sdtool): |
783 | + """Actual test.""" |
784 | + self.root.set_autoconnect_enabled(enabled) |
785 | + self.mocker.replay() |
786 | + yield sdtool.enable_autoconnect(enabled) |
787 | + self.mocker.verify() |
788 | + |
789 | + d = self._connect() |
790 | + # pylint: disable=E1101 |
791 | + d.addCallback(test_execution) |
792 | + # pylint: enable=E1101 |
793 | + return d |
794 | + |
795 | + def test_is_show_all_notifications_enabled(self): |
796 | + """Test if we can read the info.""" |
797 | + |
798 | + @defer.inlineCallbacks |
799 | + def test_execution(sdtool): |
800 | + """Actual test.""" |
801 | + self.root.show_all_notifications_enabled() |
802 | + self.mocker.result(True) |
803 | + self.mocker.replay() |
804 | + result = yield sdtool.is_show_all_notifications_enabled() |
805 | + self.mocker.verify() |
806 | + self.assertTrue(result) |
807 | + |
808 | + d = self._connect() |
809 | + # pylint: disable=E1101 |
810 | + d.addCallback(test_execution) |
811 | + # pylint: enable=E1101 |
812 | + return d |
813 | + |
814 | + def test_enable_show_all_notifications_true(self): |
815 | + """Enable/disable show_all_notifications.""" |
816 | + |
817 | + @defer.inlineCallbacks |
818 | + def test_execution(sdtool): |
819 | + """Actual test.""" |
820 | + self.root.enable_show_all_notifications() |
821 | + self.mocker.replay() |
822 | + yield sdtool.enable_show_all_notifications(True) |
823 | + self.mocker.verify() |
824 | + |
825 | + d = self._connect() |
826 | + # pylint: disable=E1101 |
827 | + d.addCallback(test_execution) |
828 | + # pylint: enable=E1101 |
829 | + return d |
830 | + |
831 | + def test_enable_show_all_notifications_false(self): |
832 | + """Enable/disable show_all_notifications.""" |
833 | + |
834 | + @defer.inlineCallbacks |
835 | + def test_execution(sdtool): |
836 | + """Actual test.""" |
837 | + self.root.disable_show_all_notifications() |
838 | + self.mocker.replay() |
839 | + yield sdtool.enable_show_all_notifications(False) |
840 | + self.mocker.verify() |
841 | + |
842 | + d = self._connect() |
843 | + # pylint: disable=E1101 |
844 | + d.addCallback(test_execution) |
845 | + # pylint: enable=E1101 |
846 | + return d |
847 | + |
848 | + def test_is_share_autosubscribe_enabled(self): |
849 | + """Test that we can get the config.""" |
850 | + |
851 | + @defer.inlineCallbacks |
852 | + def test_execution(sdtool): |
853 | + """Actual test.""" |
854 | + self.root.share_autosubscribe_enabled() |
855 | + self.mocker.result(True) |
856 | + self.mocker.replay() |
857 | + result = yield sdtool.is_share_autosubscribe_enabled() |
858 | + self.mocker.verify() |
859 | + self.assertTrue(result) |
860 | + |
861 | + d = self._connect() |
862 | + # pylint: disable=E1101 |
863 | + d.addCallback(test_execution) |
864 | + # pylint: enable=E1101 |
865 | + return d |
866 | + |
867 | + def test_enable_share_autosubscribe_true(self): |
868 | + """Enable/disable share_autosubscribe.""" |
869 | + |
870 | + @defer.inlineCallbacks |
871 | + def test_execution(sdtool): |
872 | + """Actual test.""" |
873 | + self.root.enable_share_autosubscribe() |
874 | + self.mocker.replay() |
875 | + yield sdtool.enable_share_autosubscribe(True) |
876 | + self.mocker.verify() |
877 | + |
878 | + d = self._connect() |
879 | + # pylint: disable=E1101 |
880 | + d.addCallback(test_execution) |
881 | + # pylint: enable=E1101 |
882 | + return d |
883 | + |
884 | + def test_enable_share_autosubscribe_false(self): |
885 | + """Enable/disable share_autosubscribe.""" |
886 | + |
887 | + @defer.inlineCallbacks |
888 | + def test_execution(sdtool): |
889 | + """Actual test.""" |
890 | + self.root.disable_share_autosubscribe() |
891 | + self.mocker.replay() |
892 | + yield sdtool.enable_share_autosubscribe(False) |
893 | + self.mocker.verify() |
894 | + |
895 | + d = self._connect() |
896 | + # pylint: disable=E1101 |
897 | + d.addCallback(test_execution) |
898 | + # pylint: enable=E1101 |
899 | + return d |
900 | + |
901 | + def test_is_udf_autosubscribe_enabled(self): |
902 | + """Test that we can get the config value.""" |
903 | + |
904 | + @defer.inlineCallbacks |
905 | + def test_execution(sdtool): |
906 | + """Actual test.""" |
907 | + self.root.udf_autosubscribe_enabled() |
908 | + self.mocker.result(True) |
909 | + self.mocker.replay() |
910 | + result = yield sdtool.is_udf_autosubscribe_enabled() |
911 | + self.mocker.verify() |
912 | + self.assertTrue(result) |
913 | + |
914 | + d = self._connect() |
915 | + # pylint: disable=E1101 |
916 | + d.addCallback(test_execution) |
917 | + # pylint: enable=E1101 |
918 | + return d |
919 | + |
920 | + def test_enable_udf_autosubscribe_true(self): |
921 | + """Test that we can get the config.""" |
922 | + |
923 | + @defer.inlineCallbacks |
924 | + def test_execution(sdtool): |
925 | + """Actual test.""" |
926 | + self.root.enable_udf_autosubscribe() |
927 | + self.mocker.replay() |
928 | + yield sdtool.enable_udf_autosubscribe(True) |
929 | + self.mocker.verify() |
930 | + |
931 | + d = self._connect() |
932 | + # pylint: disable=E1101 |
933 | + d.addCallback(test_execution) |
934 | + # pylint: enable=E1101 |
935 | + return d |
936 | + |
937 | + def test_enable_udf_autosubscribe_false(self): |
938 | + """Test that we can set the config.""" |
939 | + |
940 | + @defer.inlineCallbacks |
941 | + def test_execution(sdtool): |
942 | + """Actual test.""" |
943 | + self.root.disable_udf_autosubscribe() |
944 | + self.mocker.replay() |
945 | + yield sdtool.enable_udf_autosubscribe(False) |
946 | + self.mocker.verify() |
947 | + |
948 | + d = self._connect() |
949 | + # pylint: disable=E1101 |
950 | + d.addCallback(test_execution) |
951 | + # pylint: enable=E1101 |
952 | + return d |
953 | + |
954 | + def test_refresh_volumes(self): |
955 | + """Test that we can refresh the volumes.""" |
956 | + |
957 | + @defer.inlineCallbacks |
958 | + def test_execution(sdtool): |
959 | + """Actual test.""" |
960 | + self.root.refresh_volumes() |
961 | + self.mocker.replay() |
962 | + yield sdtool.refresh_volumes() |
963 | + self.mocker.verify() |
964 | + |
965 | + d = self._connect() |
966 | + # pylint: disable=E1101 |
967 | + d.addCallback(test_execution) |
968 | + # pylint: enable=E1101 |
969 | + return d |
970 | + |
971 | + def test_rescan_from_scratch(self): |
972 | + """Test that we can rescan from scratch.""" |
973 | + volume_id = 'volume_id' |
974 | + |
975 | + @defer.inlineCallbacks |
976 | + def test_execution(sdtool): |
977 | + """Actual test.""" |
978 | + self.root.rescan_from_scratch(volume_id) |
979 | + self.mocker.replay() |
980 | + yield sdtool.rescan_from_scratch(volume_id) |
981 | + self.mocker.verify() |
982 | + |
983 | + d = self._connect() |
984 | + # pylint: disable=E1101 |
985 | + d.addCallback(test_execution) |
986 | + # pylint: enable=E1101 |
987 | + return d |
988 | + |
989 | + def test_get_dirty_nodes(self): |
990 | + """Test that we get the dirty nodes.""" |
991 | + dirty_nodes = ['node_1', 'node_2', 'node_3'] |
992 | + |
993 | + @defer.inlineCallbacks |
994 | + def test_execution(sdtool): |
995 | + """Actual test.""" |
996 | + self.root.get_dirty_nodes() |
997 | + self.mocker.result(dirty_nodes) |
998 | + self.mocker.replay() |
999 | + result = yield sdtool.get_dirty_nodes() |
1000 | + self.mocker.verify() |
1001 | + self.assertEqual(dirty_nodes, result) |
1002 | + |
1003 | + d = self._connect() |
1004 | + # pylint: disable=E1101 |
1005 | + d.addCallback(test_execution) |
1006 | + # pylint: enable=E1101 |
1007 | + return d |
1008 | + |
1009 | + def test_get_root_dir(self): |
1010 | + """Test that we get the root dir.""" |
1011 | + root = '/path/to/root' |
1012 | + |
1013 | + @defer.inlineCallbacks |
1014 | + def test_execution(sdtool): |
1015 | + """Actual test.""" |
1016 | + self.root.get_rootdir() |
1017 | + self.mocker.result(root) |
1018 | + self.mocker.replay() |
1019 | + result = yield sdtool.get_root_dir() |
1020 | + self.mocker.verify() |
1021 | + self.assertEqual(root, result) |
1022 | + |
1023 | + d = self._connect() |
1024 | + # pylint: disable=E1101 |
1025 | + d.addCallback(test_execution) |
1026 | + # pylint: enable=E1101 |
1027 | + return d |
1028 | + |
1029 | + def test_get_shares_dir(self): |
1030 | + """Test that we get the shares dir.""" |
1031 | + shares_dir = '/path/to/shares' |
1032 | + |
1033 | + @defer.inlineCallbacks |
1034 | + def test_execution(sdtool): |
1035 | + """Actual test.""" |
1036 | + self.root.get_sharesdir() |
1037 | + self.mocker.result(shares_dir) |
1038 | + self.mocker.replay() |
1039 | + result = yield sdtool.get_shares_dir() |
1040 | + self.mocker.verify() |
1041 | + self.assertEqual(shares_dir, result) |
1042 | + |
1043 | + d = self._connect() |
1044 | + # pylint: disable=E1101 |
1045 | + d.addCallback(test_execution) |
1046 | + # pylint: enable=E1101 |
1047 | + return d |
1048 | + |
1049 | + def test_get_shares_dir_link(self): |
1050 | + """Test that we get the link dir.""" |
1051 | + link = 'path/to/link' |
1052 | + |
1053 | + @defer.inlineCallbacks |
1054 | + def test_execution(sdtool): |
1055 | + """Actual test.""" |
1056 | + self.root.get_sharesdir_link() |
1057 | + self.mocker.result(link) |
1058 | + self.mocker.replay() |
1059 | + result = yield sdtool.get_shares_dir_link() |
1060 | + self.mocker.verify() |
1061 | + self.assertEqual(link, result) |
1062 | + |
1063 | + d = self._connect() |
1064 | + # pylint: disable=E1101 |
1065 | + d.addCallback(test_execution) |
1066 | + # pylint: enable=E1101 |
1067 | + return d |
1068 | |
1069 | === modified file 'ubuntuone/platform/windows/ipc_client.py' |
1070 | --- ubuntuone/platform/windows/ipc_client.py 2011-05-04 13:39:41 +0000 |
1071 | +++ ubuntuone/platform/windows/ipc_client.py 2011-06-23 16:46:29 +0000 |
1072 | @@ -18,13 +18,26 @@ |
1073 | """Client lib to simplify the ipc client code.""" |
1074 | |
1075 | import logging |
1076 | + |
1077 | from functools import wraps |
1078 | -from twisted.spread.pb import Referenceable |
1079 | -from ubuntuone.platform.windows.ipc import RemoteMeta |
1080 | +from win32api import GetUserNameEx |
1081 | +from win32con import NameSamCompatible |
1082 | +from twisted.internet import defer, reactor |
1083 | +from twisted.spread.pb import Referenceable, PBClientFactory |
1084 | + |
1085 | +from ubuntuone.platform.windows.ipc import RemoteMeta, NAMED_PIPE_URL |
1086 | |
1087 | logger = logging.getLogger("ubuntuone.SyncDaemon.Client") |
1088 | |
1089 | |
1090 | +class SyncDaemonClientError(Exception): |
1091 | + """Error ocurred when trying to be a client.""" |
1092 | + |
1093 | + |
1094 | +class SyncDaemonClientConnectionError(SyncDaemonClientError): |
1095 | + """Error ocurrend when trying to connect.""" |
1096 | + |
1097 | + |
1098 | def remote(function): |
1099 | """Decorate the function to make the remote call.""" |
1100 | @wraps(function) |
1101 | @@ -660,3 +673,95 @@ |
1102 | @signal |
1103 | def on_public_files_list_error(self, error): |
1104 | """Emit the PublicFilesListError signal.""" |
1105 | + |
1106 | +class UbuntuOneClient(object): |
1107 | + """Root object that provides access to all the remote objects.""" |
1108 | + |
1109 | + def __init__(self): |
1110 | + """Create a new instance.""" |
1111 | + self.status = None |
1112 | + self.events = None |
1113 | + self.sync_daemon = None |
1114 | + self.file_system = None |
1115 | + self.shares = None |
1116 | + self.config = None |
1117 | + self.folders = None |
1118 | + self.public_files = None |
1119 | + self.factory = None |
1120 | + self.client = None |
1121 | + |
1122 | + @defer.inlineCallbacks |
1123 | + def _request_remote_objects(self, root): |
1124 | + """Request all the diff remote objects used for the communication.""" |
1125 | + status = yield root.callRemote('get_status') |
1126 | + self.status = StatusClient(status) |
1127 | + |
1128 | + events = yield root.callRemote('get_events') |
1129 | + self.events = EventsClient(events) |
1130 | + |
1131 | + sync_daemon = yield root.callRemote('get_sync_daemon') |
1132 | + self.sync_daemon = SyncDaemonClient(sync_daemon) |
1133 | + |
1134 | + file_system = yield root.callRemote('get_file_system') |
1135 | + self.file_system = FileSystemClient(file_system) |
1136 | + |
1137 | + shares = yield root.callRemote('get_shares') |
1138 | + self.shares = SharesClient(shares) |
1139 | + |
1140 | + config = yield root.callRemote('get_config') |
1141 | + self.config = ConfigClient(config) |
1142 | + |
1143 | + folders = yield root.callRemote('get_folders') |
1144 | + self.folders = FoldersClient(folders) |
1145 | + |
1146 | + public_files = yield root.callRemote('get_public_files') |
1147 | + self.public_files = PublicFilesClient(public_files) |
1148 | + |
1149 | + defer.returnValue(self) |
1150 | + |
1151 | + @defer.inlineCallbacks |
1152 | + def connect(self, pipe=None): |
1153 | + """Connect to the sso service.""" |
1154 | + # pylint: disable=W0702 |
1155 | + try: |
1156 | + if pipe is None: |
1157 | + pipe = NAMED_PIPE_URL % GetUserNameEx(NameSamCompatible) |
1158 | + # got the pipe, lets try and connect to it and get the diff |
1159 | + # remote objects for the wrappers |
1160 | + self.factory = PBClientFactory() |
1161 | + # the reactor does have a connectTCP method |
1162 | + # pylint: disable=E1101 |
1163 | + self.client = reactor.connectPipe(pipe, self.factory) |
1164 | + # pylint: enable=E1101 |
1165 | + yield self.factory.getRootObject() |
1166 | + yield self._request_remote_objects() |
1167 | + except: |
1168 | + raise SyncDaemonClientConnectionError( |
1169 | + 'Could not connect to the syncdaemon on pipe %.', |
1170 | + pipe) |
1171 | + # pylint: disable=W0702 |
1172 | + |
1173 | + @defer.inlineCallbacks |
1174 | + def register_to_signals(self): |
1175 | + """Register the different clients to the signals.""" |
1176 | + for client in [self.status, self.events, self.sync_daemon, self.shares, |
1177 | + self.folders, self.public_files]: |
1178 | + register = getattr(client, 'register_to_signals', None) |
1179 | + if register is not None: |
1180 | + yield register() |
1181 | + defer.returnValue(self) |
1182 | + |
1183 | + @defer.inlineCallbacks |
1184 | + def unregister_to_signals(self): |
1185 | + """Unregister from the diff signals.""" |
1186 | + for client in [self.status, self.events, self.sync_daemon, self.shares, |
1187 | + self.folders, self.public_files]: |
1188 | + unregister = getattr(client, 'unregister_to_signals', None) |
1189 | + if unregister is not None: |
1190 | + yield unregister() |
1191 | + defer.returnValue(self) |
1192 | + |
1193 | + def disconnect(self): |
1194 | + """Disconnect from the process.""" |
1195 | + if self.client: |
1196 | + self.client.disconnect() |
1197 | |
1198 | === added file 'ubuntuone/platform/windows/tools.py' |
1199 | --- ubuntuone/platform/windows/tools.py 1970-01-01 00:00:00 +0000 |
1200 | +++ ubuntuone/platform/windows/tools.py 2011-06-23 16:46:29 +0000 |
1201 | @@ -0,0 +1,570 @@ |
1202 | +# ubuntuone.syncdaemon.tools - tools for SyncDaemon |
1203 | +# |
1204 | +# Author: Guillermo Gonzalez <guillermo.gonzalez@canonical.com> |
1205 | +# Manuel de la Pena <manuel@canonical.com> |
1206 | +# |
1207 | +# Copyright 2011 Canonical Ltd. |
1208 | +# |
1209 | +# This program is free software: you can redistribute it and/or modify it |
1210 | +# under the terms of the GNU General Public License version 3, as published |
1211 | +# by the Free Software Foundation. |
1212 | +# |
1213 | +# This program is distributed in the hope that it will be useful, but |
1214 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
1215 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1216 | +# PURPOSE. See the GNU General Public License for more details. |
1217 | +# |
1218 | +# You should have received a copy of the GNU General Public License along |
1219 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
1220 | + |
1221 | +"""SyncDaemon Tools.""" |
1222 | + |
1223 | +import logging |
1224 | +import time |
1225 | +import sys |
1226 | + |
1227 | +from win32api import GetUserNameEx |
1228 | +from win32con import NameSamCompatible |
1229 | +from win32pipe import CallNamedPipe |
1230 | +from twisted.internet import defer, reactor |
1231 | + |
1232 | +from ubuntuone.syncdaemon.config import get_user_config |
1233 | +from ubuntuone.platform.windows.ipc import NAMED_PIPE_URL |
1234 | +from ubuntuone.platform.windows.ipc_client import UbuntuOneClient |
1235 | + |
1236 | + |
1237 | +def is_running(bus=None): |
1238 | + """Check if there is a syncdaemon instance running. |
1239 | + |
1240 | + Running means the name is registered in the given bus. |
1241 | + |
1242 | + """ |
1243 | + pipe = NAMED_PIPE_URL % GetUserNameEx(NameSamCompatible) |
1244 | + try: |
1245 | + CallNamedPipe(pipe, '', 512, 0) |
1246 | + return True |
1247 | + except: |
1248 | + return False |
1249 | + |
1250 | + |
1251 | +class SyncDaemonTool(object): |
1252 | + """Various utility methods to test/play with the SyncDaemon.""" |
1253 | + |
1254 | + def __init__(self, named_pipe=None): |
1255 | + self.named_pipe = named_pipe |
1256 | + self.client = UbuntuOneClient() |
1257 | + self.last_event = 0 |
1258 | + self.delayed_call = None |
1259 | + self.log = logging.getLogger('ubuntuone.SyncDaemon.SDTool') |
1260 | + |
1261 | + |
1262 | + def _get_dict(self, a_dict): |
1263 | + """Converts a dict returned by the IPC to a dict of strings.""" |
1264 | + str_dict = {} |
1265 | + for key in a_dict: |
1266 | + str_dict[key] = unicode(a_dict[key]) |
1267 | + return str_dict |
1268 | + |
1269 | + def wait_connected(self): |
1270 | + """Wait until syncdaemon is connected to the server.""" |
1271 | + self.log.debug('wait_connected') |
1272 | + d = defer.Deferred() |
1273 | + |
1274 | + def check_connection_status(): |
1275 | + """Check if the daemon is up and running.""" |
1276 | + # check if the syncdaemon is running |
1277 | + # catch all errors, pylint: disable-msg=W0703 |
1278 | + try: |
1279 | + self.client.connect(pipe=self.named_pipe) |
1280 | + d.callback(True) |
1281 | + except Exception, e: |
1282 | + self.log.debug('Not connected: %s', e) |
1283 | + d.errback() |
1284 | + |
1285 | + reactor.callLater(.5, check_connection_status) |
1286 | + return d |
1287 | + |
1288 | + def get_current_downloads(self): |
1289 | + """Return a deferred that will be fired with the current downloads.""" |
1290 | + d = defer.Deferred() |
1291 | + |
1292 | + def reply_handler(downloads, d): |
1293 | + """Current downloads callback.""" |
1294 | + downloads_str = [] |
1295 | + for download in downloads: |
1296 | + downloads_str.append(self._get_dict(download)) |
1297 | + d.callback(downloads_str) |
1298 | + |
1299 | + downloads_d = self.client.status.current_downloads() |
1300 | + downloads_d.addCallback(lambda downloads: reply_handler(downloads, d)) |
1301 | + return d |
1302 | + |
1303 | + def wait_all_downloads(self, verbose=False): |
1304 | + """Wait until there is no more pending downloads.""" |
1305 | + self.log.debug('wait_all_downloads') |
1306 | + d = self.get_current_downloads() |
1307 | + |
1308 | + def reply_handler(downloads): |
1309 | + """Check if the are downloads in progress. |
1310 | + |
1311 | + If so, reschelude a new check if there is at least one. |
1312 | + |
1313 | + """ |
1314 | + if verbose: |
1315 | + sys.stdout.write(', %s' % str(len(downloads))) |
1316 | + sys.stdout.flush() |
1317 | + if len(downloads) > 0: |
1318 | + self.log.debug('wait_all_downloads: %d', len(downloads)) |
1319 | + return self.get_current_downloads() |
1320 | + else: |
1321 | + self.log.debug('wait_all_downloads: No more downloads') |
1322 | + return True |
1323 | + |
1324 | + if verbose: |
1325 | + sys.stdout.write('\nchecking current downloads') |
1326 | + sys.stdout.flush() |
1327 | + d.addCallback(reply_handler) |
1328 | + return d |
1329 | + |
1330 | + def get_current_uploads(self): |
1331 | + """Return a deferred that will be called with the current uploads.""" |
1332 | + d = defer.Deferred() |
1333 | + |
1334 | + def reply_handler(uploads, d): |
1335 | + """Reply handler.""" |
1336 | + uploads_str = [] |
1337 | + for upload in uploads: |
1338 | + uploads_str.append(self._get_dict(upload)) |
1339 | + d.callback(uploads_str) |
1340 | + |
1341 | + uploads_d = self.client.status.current_uploads() |
1342 | + uploads_d.addCallback(lambda uploads: reply_handler(uploads, d)) |
1343 | + return d |
1344 | + |
1345 | + def wait_all_uploads(self, verbose=False): |
1346 | + """Wait until there is no more pending uploads.""" |
1347 | + self.log.debug('wait_all_uploads') |
1348 | + d = self.get_current_uploads() |
1349 | + |
1350 | + def reply_handler(uploads): |
1351 | + """Check if the are downloads in progress. |
1352 | + |
1353 | + If so, reschelude a new check if there is at least one. |
1354 | + |
1355 | + """ |
1356 | + if verbose: |
1357 | + sys.stdout.write(', %s' % str(len(uploads))) |
1358 | + sys.stdout.flush() |
1359 | + if len(uploads) > 0: |
1360 | + self.log.debug('wait_all_uploads: %d', len(uploads)) |
1361 | + return self.get_current_uploads() |
1362 | + else: |
1363 | + self.log.debug('wait_all_uploads: No more uploads') |
1364 | + return True |
1365 | + |
1366 | + if verbose: |
1367 | + sys.stdout.write('\nchecking current uploads') |
1368 | + sys.stdout.flush() |
1369 | + |
1370 | + d.addCallback(reply_handler) |
1371 | + return d |
1372 | + |
1373 | + def wait_no_more_events(self, last_event_interval, verbose=False): |
1374 | + """Wait until no more events are fired by the syncdaemon.""" |
1375 | + self.log.debug('wait_no_more_events') |
1376 | + d = defer.Deferred() |
1377 | + |
1378 | + def check_last_event(): |
1379 | + """Check time! |
1380 | + |
1381 | + Check if the daemon is connected and didn't received event |
1382 | + in the last_event_interval. |
1383 | + """ |
1384 | + current_time = time.time() |
1385 | + if self.last_event and \ |
1386 | + current_time - self.last_event < last_event_interval: |
1387 | + # keep it running in case this is the last event |
1388 | + self.log.debug('rescheduling wait_no_more_events') |
1389 | + if not self.delayed_call.active(): |
1390 | + self.delayed_call = reactor.callLater(last_event_interval, |
1391 | + check_last_event) |
1392 | + else: |
1393 | + self.delayed_call.reset(last_event_interval) |
1394 | + else: |
1395 | + self.log.debug('wait_no_more_events: No more events!') |
1396 | + d.callback(True) |
1397 | + |
1398 | + if verbose: |
1399 | + sys.stdout.write("Listening events") |
1400 | + sys.stdout.flush() |
1401 | + |
1402 | + def event_handler(event_dict): |
1403 | + """Update last_event and run checks.""" |
1404 | + self.last_event = time.time() |
1405 | + self.log.debug('wait_no_more_events - new event: %s - %s', |
1406 | + event_dict['event_name'], str(self.last_event)) |
1407 | + if verbose: |
1408 | + sys.stdout.write('.') |
1409 | + sys.stdout.flush() |
1410 | + if self.delayed_call.active(): |
1411 | + self.delayed_call.reset(last_event_interval) |
1412 | + |
1413 | + self.client.events.on_event_cb = event_handler |
1414 | + |
1415 | + def cleanup(result): |
1416 | + """Remove the signal handler.""" |
1417 | + self.client.events.on_event_cb = None |
1418 | + return result |
1419 | + d.addBoth(cleanup) |
1420 | + |
1421 | + # in case the daemon already reached nirvana |
1422 | + self.delayed_call = reactor.callLater(last_event_interval, |
1423 | + check_last_event) |
1424 | + return d |
1425 | + |
1426 | + def wait_for_nirvana(self, last_event_interval=5, verbose=False): |
1427 | + """Wait until the syncdaemon reachs nirvana. |
1428 | + |
1429 | + This is when there are: |
1430 | + - the syncdaemon is connected |
1431 | + - 0 transfers inprogress |
1432 | + - no more events are fired in the event queue |
1433 | + @param last_event_interval: the seconds to wait to determine that there |
1434 | + is no more events in the queue and the daemon reached nirvana |
1435 | + """ |
1436 | + self.log.debug('wait_for_nirvana') |
1437 | + return self.client.sync_daemon.wait_for_nirvana(last_event_interval) |
1438 | + |
1439 | + def accept_share(self, share_id): |
1440 | + """Accept the share with id: share_id.""" |
1441 | + self.log.debug('accept_share(%s)', share_id) |
1442 | + self.client.shares.on_share_answer_response = lambda info:\ |
1443 | + info['volume_id']==share_id |
1444 | + return self.client.shares.accept_share(share_id) |
1445 | + |
1446 | + def reject_share(self, share_id): |
1447 | + """Reject the share with id: share_id.""" |
1448 | + self.log.debug('reject_share(%s)', share_id) |
1449 | + self.client.shares.on_share_answer_response = lambda info:\ |
1450 | + info['volume_id']==share_id |
1451 | + return self.client.shares.reject_share(share_id) |
1452 | + |
1453 | + def subscribe_share(self, share_id): |
1454 | + """Subscribe to a share given its id.""" |
1455 | + self.log.debug('subscribe_share: %r', share_id) |
1456 | + return self.client.shares.subscribe(share_id) |
1457 | + |
1458 | + def unsubscribe_share(self, share_id): |
1459 | + """Unsubscribe from a share given its id.""" |
1460 | + self.log.debug('unsubscribe_share: %r', share_id) |
1461 | + return self.client.shares.unsubscribe(share_id) |
1462 | + |
1463 | + def get_shares(self): |
1464 | + """Get the list of shares (accepted or not).""" |
1465 | + self.log.debug('get_shares') |
1466 | + d = defer.Deferred() |
1467 | + |
1468 | + def reply_handler(results): |
1469 | + """Get_shares reply handler.""" |
1470 | + shares = [] |
1471 | + for result in results: |
1472 | + shares.append(self._get_dict(result)) |
1473 | + self.log.debug('shares: %r', shares) |
1474 | + d.callback(shares) |
1475 | + |
1476 | + shares_d = self.client.shares.get_shares() |
1477 | + shares_d.addCallback(reply_handler) |
1478 | + return d |
1479 | + |
1480 | + def refresh_shares(self): |
1481 | + """Call refresh_shares method via DBus. |
1482 | + |
1483 | + Request a refresh of share list to the server. |
1484 | + |
1485 | + """ |
1486 | + self.log.debug('refresh_shares') |
1487 | + return self.client.shares.refresh_shares() |
1488 | + |
1489 | + def offer_share(self, path, username, name, access_level): |
1490 | + """Offer a share at the specified path to user with id: username.""" |
1491 | + self.log.debug('offer_share(%s, %s, %s, %s)', |
1492 | + path, username, name, access_level) |
1493 | + return self.client.shares.create_share(path, username, name, |
1494 | + access_level) |
1495 | + |
1496 | + def list_shared(self): |
1497 | + """Get the list of the shares "shared"/created/offered.""" |
1498 | + self.log.debug('list_shared') |
1499 | + d = defer.Deferred() |
1500 | + |
1501 | + def reply_handler(results): |
1502 | + """Get_shares reply handler.""" |
1503 | + shares = [] |
1504 | + for result in results: |
1505 | + shares.append(self._get_dict(result)) |
1506 | + self.log.debug('shared: %r', shares) |
1507 | + d.callback(shares) |
1508 | + |
1509 | + shared_d = self.client.shares.get_shared() |
1510 | + shared_d.addCallback(reply_handler) |
1511 | + return d |
1512 | + |
1513 | + def wait_for_signals(self, signal_ok, signal_error, |
1514 | + dbus_iface=None): |
1515 | + """Wait for one of the specified signals, return a deferred. |
1516 | + |
1517 | + @param signal_ok: this will fire the deferred's callback |
1518 | + @param signal_error: the will fire the deferred's errback |
1519 | + @param dbus_iface: the interface the signal belongs to |
1520 | + """ |
1521 | + raise NotImplementedError('Not implemented yet!') |
1522 | + |
1523 | + def create_folder(self, path): |
1524 | + """Create a user defined folder in the specified path.""" |
1525 | + self.log.debug('create_folder') |
1526 | + return self.client.folders.create(path) |
1527 | + |
1528 | + def delete_folder(self, folder_id): |
1529 | + """Delete a user defined folder given its id.""" |
1530 | + self.log.debug('delete_folder') |
1531 | + return self.client.folders.delete(folder_id) |
1532 | + |
1533 | + def subscribe_folder(self, folder_id): |
1534 | + """Subscribe to a user defined folder given its id.""" |
1535 | + self.log.debug('subscribe_folder') |
1536 | + return self.client.folders.subscribe(folder_id) |
1537 | + |
1538 | + def unsubscribe_folder(self, folder_id): |
1539 | + """Unsubscribe from a user defined folder given its id.""" |
1540 | + self.log.debug('unsubscribe_folder') |
1541 | + return self.client.folders.unsubscribe(folder_id) |
1542 | + |
1543 | + def get_folders(self): |
1544 | + """Return the list of folders (a list of dicts).""" |
1545 | + self.log.debug('get_folders') |
1546 | + d = defer.Deferred() |
1547 | + |
1548 | + def reply_handler(results): |
1549 | + """Get_folders reply handler.""" |
1550 | + folders = [] |
1551 | + for result in results: |
1552 | + folders.append(self._get_dict(result)) |
1553 | + self.log.debug('folders: %r', folders) |
1554 | + d.callback(folders) |
1555 | + |
1556 | + folders_d = self.client.folders.get_folders() |
1557 | + folders_d.addCallback(reply_handler) |
1558 | + return d |
1559 | + |
1560 | + def get_folder_info(self, path): |
1561 | + """Call the get_info method for a UDF path.""" |
1562 | + self.log.debug('get_info') |
1563 | + return self.client.folders.get_info(path) |
1564 | + |
1565 | + def get_metadata(self, path): |
1566 | + """Call the exposed mtehod FileSystem.get_metadata using DBus.""" |
1567 | + self.log.debug('get_metadata(%s)', path) |
1568 | + return self.client.file_system.get_metadata(path) |
1569 | + |
1570 | + @defer.inlineCallbacks |
1571 | + def change_public_access(self, path, is_public): |
1572 | + """Change the public access for a given path.""" |
1573 | + self.log.debug('change_public_access(%s)', path) |
1574 | + metadata = yield self.client.file_system.get_metadata(path) |
1575 | + file_info = yield self.client.public_files.change_public_access( |
1576 | + metadata['share_id'], |
1577 | + metadata['node_id'], |
1578 | + is_public) |
1579 | + defer.returnValue(file_info) |
1580 | + |
1581 | + def quit(self): |
1582 | + """Quit the syncdaemon.""" |
1583 | + self.log.debug('quit') |
1584 | + # avoid triggering dbus activation while calling quit |
1585 | + if not is_running(): |
1586 | + return defer.succeed(None) |
1587 | + |
1588 | + def check(r): |
1589 | + """Wait 0.5 sec to return, to allow syncdaemon to shutdown.""" |
1590 | + d1 = defer.Deferred() |
1591 | + reactor.callLater(0.5, d1.callback, r) |
1592 | + return d1 |
1593 | + |
1594 | + d = self.client.sync_daemon.quit() |
1595 | + d.addCallback(check) |
1596 | + return d |
1597 | + |
1598 | + def wait_for_signal(self, signal_name, filter): |
1599 | + """Wait for the specified DBus signal (the first received). |
1600 | + |
1601 | + @param signal_name: the signal name |
1602 | + @param filter: a callable to filter signal, must return True, and is |
1603 | + used to fire the deferred callback. |
1604 | + |
1605 | + """ |
1606 | + raise NotImplementedError('Not implemented.') |
1607 | + |
1608 | + def connect(self): |
1609 | + """Connect syncdaemon.""" |
1610 | + return self.client.sync_daemon.connect() |
1611 | + |
1612 | + def disconnect(self): |
1613 | + """Disconnect syncdaemon.""" |
1614 | + return self.client.sync_daemon.disconnect() |
1615 | + |
1616 | + def get_status(self): |
1617 | + """Get the current_status dict.""" |
1618 | + |
1619 | + d = defer.Deferred() |
1620 | + def reply_handler(status): |
1621 | + """The reply handler""" |
1622 | + state_dict = self._get_dict(status) |
1623 | + state_dict['is_connected'] = bool(state_dict['is_connected']) |
1624 | + state_dict['is_online'] = bool(state_dict['is_online']) |
1625 | + state_dict['is_error'] = bool(state_dict['is_error']) |
1626 | + d.callback(state_dict) |
1627 | + status_d = self.client.status.current_status() |
1628 | + status_d.addCallback(reply_handler) |
1629 | + status_d.addErrback(d.errback) |
1630 | + return d |
1631 | + |
1632 | + def waiting(self): |
1633 | + """Return a description of the waiting queue elements.""" |
1634 | + return self.client.status.waiting() |
1635 | + |
1636 | + def waiting_metadata(self): |
1637 | + """Return a description of the waiting metadata queue elements.""" |
1638 | + return self.client.status.waiting_metadata() |
1639 | + |
1640 | + def waiting_content(self): |
1641 | + """Return the waiting content queue elements.""" |
1642 | + return self.client.status.waiting_content() |
1643 | + |
1644 | + def start(self): |
1645 | + """Start syncdaemon if it's not running.""" |
1646 | + if not is_running(self.bus): |
1647 | + raise Exception('Not implemented yet!') |
1648 | + else: |
1649 | + return defer.succeed(None) |
1650 | + |
1651 | + def get_throttling_limits(self): |
1652 | + """Return a dict with the read and write limits.""" |
1653 | + return self.client.config.get_throttling_limits() |
1654 | + |
1655 | + def set_throttling_limits(self, read_limit, write_limit): |
1656 | + """Set the read and write limits.""" |
1657 | + return self.client.config.set_throttling_limits(read_limit, |
1658 | + write_limit) |
1659 | + |
1660 | + def is_throttling_enabled(self): |
1661 | + """Check if throttling is enabled.""" |
1662 | + return self.client.config.bandwidth_throttling_enabled() |
1663 | + |
1664 | + def enable_throttling(self, enabled): |
1665 | + """Enable/disable throttling.""" |
1666 | + if enabled: |
1667 | + return self.client.config.enable_bandwidth_throttling() |
1668 | + else: |
1669 | + return self.client.config.disable_bandwidth_throttling() |
1670 | + |
1671 | + def is_files_sync_enabled(self): |
1672 | + """Check if files sync is enabled.""" |
1673 | + self.log.debug('is_files_sync_enabled') |
1674 | + return get_user_config().get_files_sync_enabled() |
1675 | + |
1676 | + @defer.inlineCallbacks |
1677 | + def enable_files_sync(self, enabled): |
1678 | + """Enable/disable files sync.""" |
1679 | + config = get_user_config() |
1680 | + was_enabled = config.get_files_sync_enabled() |
1681 | + self.log.debug('enable_files_sync: enable? %r was enabled? %r', |
1682 | + enabled, was_enabled) |
1683 | + if was_enabled: |
1684 | + yield self.client.config.set_files_sync_enabled(enabled) |
1685 | + config.set_files_sync_enabled(enabled) |
1686 | + if not enabled: |
1687 | + # User requested the service to be disabled |
1688 | + self.quit() |
1689 | + else: |
1690 | + if enabled: |
1691 | + config.set_files_sync_enabled(True) |
1692 | + config.save() |
1693 | + self.start() |
1694 | + |
1695 | + def is_autoconnect_enabled(self): |
1696 | + """Check if autoconnect is enabled.""" |
1697 | + return self.client.config.autoconnect_enabled() |
1698 | + |
1699 | + def enable_autoconnect(self, enabled): |
1700 | + """Enable/disable autoconnect.""" |
1701 | + return self.client.config.set_autoconnect_enabled(enabled) |
1702 | + |
1703 | + def is_show_all_notifications_enabled(self): |
1704 | + """Check if show_all_notifications is enabled.""" |
1705 | + return self.client.config.show_all_notifications_enabled() |
1706 | + |
1707 | + def enable_show_all_notifications(self, enabled): |
1708 | + """Enable/disable show_all_notifications.""" |
1709 | + if enabled: |
1710 | + return self.client.config.enable_show_all_notifications() |
1711 | + else: |
1712 | + return self.client.config.disable_show_all_notifications() |
1713 | + |
1714 | + def is_share_autosubscribe_enabled(self): |
1715 | + """Check if share_autosubscribe is enabled.""" |
1716 | + return self.client.config.share_autosubscribe_enabled() |
1717 | + |
1718 | + def enable_share_autosubscribe(self, enabled): |
1719 | + """Enable/disable share_autosubscribe.""" |
1720 | + if enabled: |
1721 | + return self.client.config.enable_share_autosubscribe() |
1722 | + else: |
1723 | + return self.client.config.disable_share_autosubscribe() |
1724 | + |
1725 | + def is_udf_autosubscribe_enabled(self): |
1726 | + """Check if udf_autosubscribe is enabled.""" |
1727 | + return self.client.config.udf_autosubscribe_enabled() |
1728 | + |
1729 | + def enable_udf_autosubscribe(self, enabled): |
1730 | + """Enable/disable udf_autosubscribe.""" |
1731 | + if enabled: |
1732 | + return self.client.config.enable_udf_autosubscribe() |
1733 | + else: |
1734 | + return self.client.config.disable_udf_autosubscribe() |
1735 | + |
1736 | + def refresh_volumes(self): |
1737 | + """Call refresh_volumes method via DBus. |
1738 | + |
1739 | + Request the volumes list to the server. |
1740 | + """ |
1741 | + self.log.debug('refresh_volumes') |
1742 | + return self.client.folders.refresh_volumes() |
1743 | + |
1744 | + def rescan_from_scratch(self, volume_id): |
1745 | + """Call rescan_from_scratch via DBus. |
1746 | + |
1747 | + Request a rescan from scratch for volume_id. |
1748 | + """ |
1749 | + self.log.debug('rescan_from_scratch %r', volume_id) |
1750 | + return self.client.sync_daemon.rescan_from_scratch(volume_id) |
1751 | + |
1752 | + def get_dirty_nodes(self): |
1753 | + """Call get_dirty_nodes via DBus. |
1754 | + |
1755 | + Return the list of dirty nodes. |
1756 | + """ |
1757 | + self.log.debug('get_dirty_nodes') |
1758 | + return self.client.file_system.get_dirty_nodes() |
1759 | + |
1760 | + def get_root_dir(self): |
1761 | + """Return the root directory.""" |
1762 | + return self.client.sync_daemon.get_rootdir() |
1763 | + |
1764 | + def get_shares_dir(self): |
1765 | + """Return the shares directory.""" |
1766 | + return self.client.sync_daemon.get_sharesdir() |
1767 | + |
1768 | + def get_shares_dir_link(self): |
1769 | + """Return the shares link directory.""" |
1770 | + return self.client.sync_daemon.get_sharesdir_link() |
1771 | + |
* In order to really handle all the possible errors in UbuntuOneClient .connect, please add to it the inlineCallbacks decorator and change:
d = self.factory. getRootObject( )
d. addCallback( self._request_ remote_ objects)
return d
for:
yield self.factory. getRootObject( ) remote_ objects
yield self._request_
* We need tests for the SDTool...