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

Proposed by Manuel de la Peña
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
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\Scripts\u1trial tests\platform\windows\test_tools.py

To post a comment you must log in.
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

* 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()
            yield self._request_remote_objects

* We need tests for the SDTool...

review: Needs Fixing
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.

Revision history for this message
Natalia Bidart (nataliabidart) :
review: Approve
Revision history for this message
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!

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

Marking as Needs Fixing to track what Alejandro asked.

review: Needs Fixing
Revision history for this message
Alejandro J. Cura (alecu) wrote :

I'm changing the review to approve, pending the fixes that nessita will check.

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

Filed bug #801685 so Manuel can make the improvements later, without blocking this branch.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'tests/platform/windows/test_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+

Subscribers

People subscribed via source and target branches