Merge lp:~mikemc/ubuntuone-client/fix-dummy-sync-menu into lp:ubuntuone-client
- fix-dummy-sync-menu
- Merge into trunk
Status: | Superseded | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~mikemc/ubuntuone-client/fix-dummy-sync-menu | ||||
Merge into: | lp:ubuntuone-client | ||||
Diff against target: |
894 lines (+741/-8) (has conflicts) 9 files modified
tests/platform/sync_menu/test_common.py (+49/-0) tests/platform/sync_menu/test_linux.py (+350/-0) tests/status/test_aggregator.py (+14/-0) tests/syncdaemon/test_status_listener.py (+2/-2) ubuntuone/platform/sync_menu/common.py (+41/-0) ubuntuone/platform/sync_menu/linux.py (+253/-0) ubuntuone/status/aggregator.py (+28/-3) ubuntuone/syncdaemon/main.py (+2/-1) ubuntuone/syncdaemon/status_listener.py (+2/-2) Text conflict in tests/platform/sync_menu/test_linux.py Text conflict in ubuntuone/platform/sync_menu/common.py Text conflict in ubuntuone/platform/sync_menu/linux.py |
||||
To merge this branch: | bzr merge lp:~mikemc/ubuntuone-client/fix-dummy-sync-menu | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu One hackers | Pending | ||
Review via email: mp+126125@code.launchpad.net |
This proposal has been superseded by a proposal from 2012-09-27.
Commit message
- Fix dummy sync menu implementation for windows and darwin. (LP: #1055840)
Description of the change
- Fix dummy sync menu implementation for windows and darwin. (LP: #1055840)
Added a test to check that the dummy class has the same API, fixed missing start_timer function.
Removed code in dummy that won't be called.
Tests pass on darwin and linux (precise).
(On darwin, I only ran the specific tests that changed, since trunk tests have never worked.)
With this fix, an app built from trunk runs correctly on darwin again.
- 1322. By Mike McCracken
-
merge with default-
fs-monitor- fix - 1323. By Mike McCracken
-
merge with sync-menu-timer branch
- 1324. By Mike McCracken
-
change dummy to match new linux version
- 1325. By Mike McCracken
-
merge with trunk
- 1326. By Mike McCracken
-
change to reflect new API
- 1327. By Mike McCracken
-
merge with trunk
Unmerged revisions
Preview Diff
1 | === added file 'tests/platform/sync_menu/test_common.py' | |||
2 | --- tests/platform/sync_menu/test_common.py 1970-01-01 00:00:00 +0000 | |||
3 | +++ tests/platform/sync_menu/test_common.py 2012-09-26 18:35:23 +0000 | |||
4 | @@ -0,0 +1,49 @@ | |||
5 | 1 | # -*- coding: utf-8 *-* | ||
6 | 2 | # | ||
7 | 3 | # Copyright 2012 Canonical Ltd. | ||
8 | 4 | # | ||
9 | 5 | # This program is free software: you can redistribute it and/or modify it | ||
10 | 6 | # under the terms of the GNU General Public License version 3, as published | ||
11 | 7 | # by the Free Software Foundation. | ||
12 | 8 | # | ||
13 | 9 | # This program is distributed in the hope that it will be useful, but | ||
14 | 10 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
15 | 11 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
16 | 12 | # PURPOSE. See the GNU General Public License for more details. | ||
17 | 13 | # | ||
18 | 14 | # You should have received a copy of the GNU General Public License along | ||
19 | 15 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
20 | 16 | # | ||
21 | 17 | # In addition, as a special exception, the copyright holders give | ||
22 | 18 | # permission to link the code of portions of this program with the | ||
23 | 19 | # OpenSSL library under certain conditions as described in each | ||
24 | 20 | # individual source file, and distribute linked combinations | ||
25 | 21 | # including the two. | ||
26 | 22 | # You must obey the GNU General Public License in all respects | ||
27 | 23 | # for all of the code used other than OpenSSL. If you modify | ||
28 | 24 | # file(s) with this exception, you may extend this exception to your | ||
29 | 25 | # version of the file(s), but you are not obligated to do so. If you | ||
30 | 26 | # do not wish to do so, delete this exception statement from your | ||
31 | 27 | # version. If you delete this exception statement from all source | ||
32 | 28 | # files in the program, then also delete it here. | ||
33 | 29 | """Test the common dummy Sync Menu implementation for win32/darwin.""" | ||
34 | 30 | |||
35 | 31 | from collections import Callable | ||
36 | 32 | |||
37 | 33 | from twisted.trial.unittest import TestCase | ||
38 | 34 | |||
39 | 35 | from ubuntuone.platform.sync_menu import common | ||
40 | 36 | |||
41 | 37 | |||
42 | 38 | class SyncMenuDummyTestCase(TestCase): | ||
43 | 39 | """Test the SyncMenu.""" | ||
44 | 40 | |||
45 | 41 | def test_dummy_support(self): | ||
46 | 42 | """Can we create a Dummy with the same #args as the real obj.""" | ||
47 | 43 | dummy = common.UbuntuOneSyncMenu(1, 2) | ||
48 | 44 | self.assertIsInstance(dummy, common.UbuntuOneSyncMenu) | ||
49 | 45 | |||
50 | 46 | def test_dummy_has_update_transfers(self): | ||
51 | 47 | """Check that the dummy has the proper methods required by the API.""" | ||
52 | 48 | dummy = common.UbuntuOneSyncMenu(1, 2) | ||
53 | 49 | self.assertIsInstance(dummy.update_transfers, Callable) | ||
54 | 0 | 50 | ||
55 | === modified file 'tests/platform/sync_menu/test_linux.py' | |||
56 | --- tests/platform/sync_menu/test_linux.py 2012-09-24 13:09:52 +0000 | |||
57 | +++ tests/platform/sync_menu/test_linux.py 2012-09-26 18:35:23 +0000 | |||
58 | @@ -1,3 +1,4 @@ | |||
59 | 1 | <<<<<<< TREE | ||
60 | 1 | # -*- coding: utf-8 *-* | 2 | # -*- coding: utf-8 *-* |
61 | 2 | # | 3 | # |
62 | 3 | # Copyright 2012 Canonical Ltd. | 4 | # Copyright 2012 Canonical Ltd. |
63 | @@ -338,3 +339,352 @@ | |||
64 | 338 | self.assertEqual(item.property_get_int( | 339 | self.assertEqual(item.property_get_int( |
65 | 339 | linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE), | 340 | linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE), |
66 | 340 | percentage) | 341 | percentage) |
67 | 342 | ======= | ||
68 | 343 | # -*- coding: utf-8 *-* | ||
69 | 344 | # | ||
70 | 345 | # Copyright 2012 Canonical Ltd. | ||
71 | 346 | # | ||
72 | 347 | # This program is free software: you can redistribute it and/or modify it | ||
73 | 348 | # under the terms of the GNU General Public License version 3, as published | ||
74 | 349 | # by the Free Software Foundation. | ||
75 | 350 | # | ||
76 | 351 | # This program is distributed in the hope that it will be useful, but | ||
77 | 352 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
78 | 353 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
79 | 354 | # PURPOSE. See the GNU General Public License for more details. | ||
80 | 355 | # | ||
81 | 356 | # You should have received a copy of the GNU General Public License along | ||
82 | 357 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
83 | 358 | # | ||
84 | 359 | # In addition, as a special exception, the copyright holders give | ||
85 | 360 | # permission to link the code of portions of this program with the | ||
86 | 361 | # OpenSSL library under certain conditions as described in each | ||
87 | 362 | # individual source file, and distribute linked combinations | ||
88 | 363 | # including the two. | ||
89 | 364 | # You must obey the GNU General Public License in all respects | ||
90 | 365 | # for all of the code used other than OpenSSL. If you modify | ||
91 | 366 | # file(s) with this exception, you may extend this exception to your | ||
92 | 367 | # version of the file(s), but you are not obligated to do so. If you | ||
93 | 368 | # do not wish to do so, delete this exception statement from your | ||
94 | 369 | # version. If you delete this exception statement from all source | ||
95 | 370 | # files in the program, then also delete it here. | ||
96 | 371 | """Test the Sync Menu.""" | ||
97 | 372 | |||
98 | 373 | import time | ||
99 | 374 | from collections import Callable | ||
100 | 375 | |||
101 | 376 | from twisted.internet import defer | ||
102 | 377 | from twisted.trial.unittest import TestCase | ||
103 | 378 | |||
104 | 379 | from ubuntuone.platform import sync_menu | ||
105 | 380 | from ubuntuone.platform.sync_menu import linux | ||
106 | 381 | |||
107 | 382 | |||
108 | 383 | def fake_call_later(*args): | ||
109 | 384 | """Fake reactor.callLater.""" | ||
110 | 385 | |||
111 | 386 | |||
112 | 387 | class FakeStatusFrontend(object): | ||
113 | 388 | """Fake StatusFrontend.""" | ||
114 | 389 | |||
115 | 390 | def __init__(self): | ||
116 | 391 | self.recent_transfers_data = [] | ||
117 | 392 | self.uploading_data = [] | ||
118 | 393 | |||
119 | 394 | def recent_transfers(self): | ||
120 | 395 | """Return the fake recent transfers files.""" | ||
121 | 396 | return self.recent_transfers_data | ||
122 | 397 | |||
123 | 398 | def files_uploading(self): | ||
124 | 399 | """Return the fake files being upload.""" | ||
125 | 400 | return self.uploading_data | ||
126 | 401 | |||
127 | 402 | |||
128 | 403 | class FakeTimer(object): | ||
129 | 404 | """Fake Timer.""" | ||
130 | 405 | |||
131 | 406 | def __init__(self, delay): | ||
132 | 407 | self.delay = delay | ||
133 | 408 | self.callback = None | ||
134 | 409 | |||
135 | 410 | def addCallback(self, callback): | ||
136 | 411 | """Add callback.""" | ||
137 | 412 | self.callback = callback | ||
138 | 413 | |||
139 | 414 | |||
140 | 415 | class FakeSyncdaemonService(object): | ||
141 | 416 | """Fake SyncdaemonService.""" | ||
142 | 417 | |||
143 | 418 | |||
144 | 419 | class FakeSyncMenuApp(object): | ||
145 | 420 | """Fake SyncMenu.""" | ||
146 | 421 | |||
147 | 422 | data = {} | ||
148 | 423 | |||
149 | 424 | @classmethod | ||
150 | 425 | def new(cls, *args): | ||
151 | 426 | return FakeSyncMenuApp() | ||
152 | 427 | |||
153 | 428 | @classmethod | ||
154 | 429 | def clean(cls): | ||
155 | 430 | """Clear the values stored in data.""" | ||
156 | 431 | |||
157 | 432 | def set_menu(self, server): | ||
158 | 433 | """Set the menu for SyncMenu App.""" | ||
159 | 434 | self.data['server'] = server | ||
160 | 435 | |||
161 | 436 | def connect(self, signal, callback): | ||
162 | 437 | """Fake connect.""" | ||
163 | 438 | self.data['connect'] = (signal, callback) | ||
164 | 439 | |||
165 | 440 | |||
166 | 441 | class SyncMenuDummyTestCase(TestCase): | ||
167 | 442 | """Test the SyncMenu.""" | ||
168 | 443 | |||
169 | 444 | def test_dummy_support(self): | ||
170 | 445 | """Check that the Dummy object can be created properly.""" | ||
171 | 446 | dummy = linux.DummySyncMenu('random', 'args') | ||
172 | 447 | self.assertIsInstance(dummy, linux.DummySyncMenu) | ||
173 | 448 | |||
174 | 449 | def test_dummy_has_start_timer(self): | ||
175 | 450 | """Check that the dummy has the proper methods required by the API.""" | ||
176 | 451 | dummy = linux.DummySyncMenu('random', 'args') | ||
177 | 452 | self.assertIsInstance(dummy.start_timer, Callable) | ||
178 | 453 | |||
179 | 454 | |||
180 | 455 | class SyncMenuTestCase(TestCase): | ||
181 | 456 | """Test the SyncMenu.""" | ||
182 | 457 | |||
183 | 458 | skip = None if linux.use_syncmenu else "SyncMenu not installed." | ||
184 | 459 | |||
185 | 460 | @defer.inlineCallbacks | ||
186 | 461 | def setUp(self): | ||
187 | 462 | yield super(SyncMenuTestCase, self).setUp() | ||
188 | 463 | self.patch(linux.SyncMenu, "App", FakeSyncMenuApp) | ||
189 | 464 | FakeSyncMenuApp.clean() | ||
190 | 465 | self.syncdaemon_service = FakeSyncdaemonService() | ||
191 | 466 | self.status_frontend = FakeStatusFrontend() | ||
192 | 467 | self._paused = False | ||
193 | 468 | self.patch(sync_menu.UbuntuOneSyncMenu, "change_sync_status", | ||
194 | 469 | self._change_sync_status) | ||
195 | 470 | self.sync_menu = sync_menu.UbuntuOneSyncMenu(self.status_frontend, | ||
196 | 471 | self.syncdaemon_service) | ||
197 | 472 | |||
198 | 473 | def _change_sync_status(self, *args): | ||
199 | 474 | """Fake change_sync_status.""" | ||
200 | 475 | if self._paused: | ||
201 | 476 | self._paused = False | ||
202 | 477 | else: | ||
203 | 478 | self._paused = True | ||
204 | 479 | |||
205 | 480 | def test_init(self): | ||
206 | 481 | """Check that the menu is properly initialized.""" | ||
207 | 482 | self.assertIsInstance(FakeSyncMenuApp.data['server'], | ||
208 | 483 | linux.Dbusmenu.Server) | ||
209 | 484 | self.assertEqual(self.sync_menu.open_u1.get_parent(), | ||
210 | 485 | self.sync_menu.root_menu) | ||
211 | 486 | self.assertEqual(self.sync_menu.go_to_web.get_parent(), | ||
212 | 487 | self.sync_menu.root_menu) | ||
213 | 488 | self.assertEqual(self.sync_menu.more_storage.get_parent(), | ||
214 | 489 | self.sync_menu.root_menu) | ||
215 | 490 | self.assertEqual(self.sync_menu.get_help.get_parent(), | ||
216 | 491 | self.sync_menu.root_menu) | ||
217 | 492 | self.assertEqual(self.sync_menu.transfers.get_parent(), | ||
218 | 493 | self.sync_menu.root_menu) | ||
219 | 494 | |||
220 | 495 | self.assertEqual(self.sync_menu.open_u1.property_get( | ||
221 | 496 | linux.Dbusmenu.MENUITEM_PROP_LABEL), linux.OPEN_U1) | ||
222 | 497 | self.assertEqual(self.sync_menu.go_to_web.property_get( | ||
223 | 498 | linux.Dbusmenu.MENUITEM_PROP_LABEL), linux.GO_TO_WEB) | ||
224 | 499 | self.assertEqual(self.sync_menu.transfers.property_get( | ||
225 | 500 | linux.Dbusmenu.MENUITEM_PROP_LABEL), linux.TRANSFERS) | ||
226 | 501 | self.assertEqual(self.sync_menu.more_storage.property_get( | ||
227 | 502 | linux.Dbusmenu.MENUITEM_PROP_LABEL), linux.MORE_STORAGE) | ||
228 | 503 | self.assertEqual(self.sync_menu.get_help.property_get( | ||
229 | 504 | linux.Dbusmenu.MENUITEM_PROP_LABEL), linux.GET_HELP) | ||
230 | 505 | |||
231 | 506 | self.assertEqual(self.sync_menu.app.data['connect'], | ||
232 | 507 | ("notify::paused", self.sync_menu.change_sync_status)) | ||
233 | 508 | self.sync_menu.app.data['connect'][1]() | ||
234 | 509 | self.assertTrue(self._paused) | ||
235 | 510 | self.sync_menu.app.data['connect'][1]() | ||
236 | 511 | self.assertFalse(self._paused) | ||
237 | 512 | |||
238 | 513 | def test_open_u1(self): | ||
239 | 514 | """Check that the proper action is executed.""" | ||
240 | 515 | data = [] | ||
241 | 516 | |||
242 | 517 | self.patch(linux.glib, "spawn_command_line_async", data.append) | ||
243 | 518 | self.sync_menu.open_control_panel() | ||
244 | 519 | self.assertEqual(data, ['ubuntuone-installer']) | ||
245 | 520 | |||
246 | 521 | def test_go_to_web(self): | ||
247 | 522 | """Check that the proper action is executed.""" | ||
248 | 523 | data = [] | ||
249 | 524 | |||
250 | 525 | self.patch(linux.webbrowser, "open", data.append) | ||
251 | 526 | self.sync_menu.open_go_to_web() | ||
252 | 527 | self.assertEqual(data, [linux.DASHBOARD]) | ||
253 | 528 | |||
254 | 529 | def test_get_help(self): | ||
255 | 530 | """Check that the proper action is executed.""" | ||
256 | 531 | data = [] | ||
257 | 532 | |||
258 | 533 | self.patch(linux.webbrowser, "open", data.append) | ||
259 | 534 | self.sync_menu.open_web_help() | ||
260 | 535 | self.assertEqual(data, [linux.HELP_LINK]) | ||
261 | 536 | |||
262 | 537 | def test_more_storage(self): | ||
263 | 538 | """Check that the proper action is executed.""" | ||
264 | 539 | data = [] | ||
265 | 540 | |||
266 | 541 | self.patch(linux.webbrowser, "open", data.append) | ||
267 | 542 | self.sync_menu.open_get_more_storage() | ||
268 | 543 | self.assertEqual(data, [linux.GET_STORAGE_LINK]) | ||
269 | 544 | |||
270 | 545 | def test_empty_transfers(self): | ||
271 | 546 | """Check that the Transfers menu is empty.""" | ||
272 | 547 | self.assertEqual(self.sync_menu.transfers.get_children(), []) | ||
273 | 548 | |||
274 | 549 | def test_only_recent(self): | ||
275 | 550 | """Check that only recent transfers items are loaded.""" | ||
276 | 551 | data = ['file1', 'file2', 'file3'] | ||
277 | 552 | self.status_frontend.recent_transfers_data = data | ||
278 | 553 | self.sync_menu.transfers.update_progress() | ||
279 | 554 | children = self.sync_menu.transfers.get_children() | ||
280 | 555 | self.assertEqual(len(children), 3) | ||
281 | 556 | data.reverse() | ||
282 | 557 | for itemM, itemD in zip(children, data): | ||
283 | 558 | self.assertEqual(itemM.property_get( | ||
284 | 559 | linux.Dbusmenu.MENUITEM_PROP_LABEL), itemD) | ||
285 | 560 | |||
286 | 561 | def test_only_progress(self): | ||
287 | 562 | """Check that only progress items are loaded.""" | ||
288 | 563 | data = [ | ||
289 | 564 | ('file1', 3000, 400), | ||
290 | 565 | ('file2', 2000, 100), | ||
291 | 566 | ('file3', 5000, 4600)] | ||
292 | 567 | uploading_data = {} | ||
293 | 568 | for filename, size, written in data: | ||
294 | 569 | uploading_data[filename] = (size, written) | ||
295 | 570 | self.status_frontend.uploading_data = data | ||
296 | 571 | self.sync_menu.transfers.update_progress() | ||
297 | 572 | children = self.sync_menu.transfers.get_children() | ||
298 | 573 | self.assertEqual(len(children), 3) | ||
299 | 574 | data.reverse() | ||
300 | 575 | for item in children: | ||
301 | 576 | text = item.property_get(linux.Dbusmenu.MENUITEM_PROP_LABEL) | ||
302 | 577 | self.assertIn(text, uploading_data) | ||
303 | 578 | size, written = uploading_data[text] | ||
304 | 579 | percentage = written * 100 / size | ||
305 | 580 | self.assertEqual(item.property_get_int( | ||
306 | 581 | linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE), | ||
307 | 582 | percentage) | ||
308 | 583 | |||
309 | 584 | def test_full_transfers(self): | ||
310 | 585 | """Check that the transfers menu contains the maximum transfers.""" | ||
311 | 586 | # The api of recent transfers always returns a maximum of 5 items | ||
312 | 587 | data_recent = ['file1', 'file2', 'file3', 'file4', 'file5'] | ||
313 | 588 | self.status_frontend.recent_transfers_data = \ | ||
314 | 589 | data_recent | ||
315 | 590 | self.sync_menu.transfers.update_progress() | ||
316 | 591 | children = self.sync_menu.transfers.get_children() | ||
317 | 592 | self.assertEqual(len(children), 5) | ||
318 | 593 | data_recent.reverse() | ||
319 | 594 | for itemM, itemD in zip(children, data_recent): | ||
320 | 595 | self.assertEqual(itemM.property_get( | ||
321 | 596 | linux.Dbusmenu.MENUITEM_PROP_LABEL), itemD) | ||
322 | 597 | |||
323 | 598 | data_current = [ | ||
324 | 599 | ('file0', 1200, 600), | ||
325 | 600 | ('file1', 3000, 400), | ||
326 | 601 | ('file2', 2000, 100), | ||
327 | 602 | ('file3', 2500, 150), | ||
328 | 603 | ('file4', 1000, 600), | ||
329 | 604 | ('file5', 5000, 4600)] | ||
330 | 605 | uploading_data = {} | ||
331 | 606 | for filename, size, written in data_current: | ||
332 | 607 | uploading_data[filename] = (size, written) | ||
333 | 608 | self.status_frontend.uploading_data = data_current | ||
334 | 609 | self.sync_menu.transfers.update_progress() | ||
335 | 610 | children = self.sync_menu.transfers.get_children() | ||
336 | 611 | # The menu should only show 5 current transfers. | ||
337 | 612 | self.assertEqual(len(children), 10) | ||
338 | 613 | data_current.reverse() | ||
339 | 614 | for item in children[5:]: | ||
340 | 615 | text = item.property_get(linux.Dbusmenu.MENUITEM_PROP_LABEL) | ||
341 | 616 | self.assertIn(text, uploading_data) | ||
342 | 617 | size, written = uploading_data[text] | ||
343 | 618 | percentage = written * 100 / size | ||
344 | 619 | self.assertEqual(item.property_get_int( | ||
345 | 620 | linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE), | ||
346 | 621 | percentage) | ||
347 | 622 | |||
348 | 623 | def test_update_transfers(self): | ||
349 | 624 | """Check that everything is ok when updating the transfers value.""" | ||
350 | 625 | data_current = [ | ||
351 | 626 | ('file0', 1200, 600), | ||
352 | 627 | ('file1', 3000, 400), | ||
353 | 628 | ('file4', 1000, 600), | ||
354 | 629 | ('file5', 5000, 4600)] | ||
355 | 630 | uploading_data = {} | ||
356 | 631 | for filename, size, written in data_current: | ||
357 | 632 | uploading_data[filename] = (size, written) | ||
358 | 633 | self.status_frontend.uploading_data = data_current | ||
359 | 634 | self.sync_menu.transfers.update_progress() | ||
360 | 635 | children = self.sync_menu.transfers.get_children() | ||
361 | 636 | # The menu should only show 5 current transfers. | ||
362 | 637 | self.assertEqual(len(children), 4) | ||
363 | 638 | data_current.reverse() | ||
364 | 639 | for item in children: | ||
365 | 640 | text = item.property_get(linux.Dbusmenu.MENUITEM_PROP_LABEL) | ||
366 | 641 | self.assertIn(text, uploading_data) | ||
367 | 642 | size, written = uploading_data[text] | ||
368 | 643 | percentage = written * 100 / size | ||
369 | 644 | self.assertEqual(item.property_get_int( | ||
370 | 645 | linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE), | ||
371 | 646 | percentage) | ||
372 | 647 | |||
373 | 648 | data_recent = ['file5'] | ||
374 | 649 | self.status_frontend.recent_transfers_data = data_recent | ||
375 | 650 | self.sync_menu.transfers.update_progress() | ||
376 | 651 | children = self.sync_menu.transfers.get_children() | ||
377 | 652 | self.assertEqual(len(children), 5) | ||
378 | 653 | data_recent.reverse() | ||
379 | 654 | for itemM, itemD in zip(children, data_recent): | ||
380 | 655 | self.assertEqual(itemM.property_get( | ||
381 | 656 | linux.Dbusmenu.MENUITEM_PROP_LABEL), itemD) | ||
382 | 657 | |||
383 | 658 | data_current = [ | ||
384 | 659 | ('file0', 1200, 700), | ||
385 | 660 | ('file1', 3000, 600), | ||
386 | 661 | ('file4', 1000, 800)] | ||
387 | 662 | uploading_data = {} | ||
388 | 663 | for filename, size, written in data_current: | ||
389 | 664 | uploading_data[filename] = (size, written) | ||
390 | 665 | self.status_frontend.uploading_data = data_current | ||
391 | 666 | self.sync_menu.transfers.update_progress() | ||
392 | 667 | children = self.sync_menu.transfers.get_children() | ||
393 | 668 | # The menu should only show 5 current transfers. | ||
394 | 669 | self.assertEqual(len(children), 4) | ||
395 | 670 | data_current.reverse() | ||
396 | 671 | for item in children[5:]: | ||
397 | 672 | text = item.property_get(linux.Dbusmenu.MENUITEM_PROP_LABEL) | ||
398 | 673 | self.assertIn(text, uploading_data) | ||
399 | 674 | size, written = uploading_data[text] | ||
400 | 675 | percentage = written * 100 / size | ||
401 | 676 | self.assertEqual(item.property_get_int( | ||
402 | 677 | linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE), | ||
403 | 678 | percentage) | ||
404 | 679 | |||
405 | 680 | def test_update_transfers_delay(self): | ||
406 | 681 | """Check that the timer is being handle properly.""" | ||
407 | 682 | self.patch(linux.status.aggregator, "Timer", FakeTimer) | ||
408 | 683 | self.sync_menu.next_update = time.time() + 3 | ||
409 | 684 | self.sync_menu.update_transfers() | ||
410 | 685 | self.assertLess(self.sync_menu.timer.delay, 3) | ||
411 | 686 | self.sync_menu.timer = None | ||
412 | 687 | self.sync_menu.next_update = time.time() / 2 | ||
413 | 688 | self.sync_menu.update_transfers() | ||
414 | 689 | self.assertEqual(self.sync_menu.timer.delay, 0) | ||
415 | 690 | >>>>>>> MERGE-SOURCE | ||
416 | 341 | 691 | ||
417 | === modified file 'tests/status/test_aggregator.py' | |||
418 | --- tests/status/test_aggregator.py 2012-08-20 13:18:43 +0000 | |||
419 | +++ tests/status/test_aggregator.py 2012-09-26 18:35:23 +0000 | |||
420 | @@ -1314,6 +1314,20 @@ | |||
421 | 1314 | self.assertEqual({}, self.aggregator.to_do) | 1314 | self.assertEqual({}, self.aggregator.to_do) |
422 | 1315 | self.assertIdentical(None, self.aggregator.queue_done_timer) | 1315 | self.assertIdentical(None, self.aggregator.queue_done_timer) |
423 | 1316 | 1316 | ||
424 | 1317 | def test_register_listener(self): | ||
425 | 1318 | """Check that register_listener handles properly additions.""" | ||
426 | 1319 | |||
427 | 1320 | def fake_callback(): | ||
428 | 1321 | """Do nothing.""" | ||
429 | 1322 | |||
430 | 1323 | self.aggregator.register_listener(fake_callback) | ||
431 | 1324 | self.assertEqual(len(self.aggregator.listeners_callbacks), 1) | ||
432 | 1325 | |||
433 | 1326 | def test_register_listener_fail(self): | ||
434 | 1327 | """Check that register_listener handles properly additions.""" | ||
435 | 1328 | self.assertRaises(TypeError, self.aggregator.register_listener, []) | ||
436 | 1329 | self.assertEqual(len(self.aggregator.listeners_callbacks), 0) | ||
437 | 1330 | |||
438 | 1317 | def assertMiscCommandQueued(self, fc): | 1331 | def assertMiscCommandQueued(self, fc): |
439 | 1318 | """Assert that some command was queued.""" | 1332 | """Assert that some command was queued.""" |
440 | 1319 | self.assertEqual(len(self.aggregator.to_do), 1) | 1333 | self.assertEqual(len(self.aggregator.to_do), 1) |
441 | 1320 | 1334 | ||
442 | === modified file 'tests/syncdaemon/test_status_listener.py' | |||
443 | --- tests/syncdaemon/test_status_listener.py 2012-04-09 20:07:05 +0000 | |||
444 | +++ tests/syncdaemon/test_status_listener.py 2012-09-26 18:35:23 +0000 | |||
445 | @@ -84,7 +84,7 @@ | |||
446 | 84 | callback(args) | 84 | callback(args) |
447 | 85 | 85 | ||
448 | 86 | listener = Listener() | 86 | listener = Listener() |
450 | 87 | setattr(listener, 'handle_'+event, listener._handle_event) | 87 | setattr(listener, 'handle_' + event, listener._handle_event) |
451 | 88 | event_q.subscribe(listener) | 88 | event_q.subscribe(listener) |
452 | 89 | return listener | 89 | return listener |
453 | 90 | 90 | ||
454 | @@ -92,7 +92,7 @@ | |||
455 | 92 | class FakeStatusFrontend(object): | 92 | class FakeStatusFrontend(object): |
456 | 93 | """A fake status frontend.""" | 93 | """A fake status frontend.""" |
457 | 94 | 94 | ||
459 | 95 | def __init__(self): | 95 | def __init__(self, *args, **kwargs): |
460 | 96 | """Initialize this instance.""" | 96 | """Initialize this instance.""" |
461 | 97 | self.call_log = [] | 97 | self.call_log = [] |
462 | 98 | 98 | ||
463 | 99 | 99 | ||
464 | === modified file 'ubuntuone/platform/sync_menu/common.py' | |||
465 | --- ubuntuone/platform/sync_menu/common.py 2012-09-18 16:29:22 +0000 | |||
466 | +++ ubuntuone/platform/sync_menu/common.py 2012-09-26 18:35:23 +0000 | |||
467 | @@ -1,3 +1,4 @@ | |||
468 | 1 | <<<<<<< TREE | ||
469 | 1 | # -*- coding: utf-8 *-* | 2 | # -*- coding: utf-8 *-* |
470 | 2 | # | 3 | # |
471 | 3 | # Copyright 2012 Canonical Ltd. | 4 | # Copyright 2012 Canonical Ltd. |
472 | @@ -41,3 +42,43 @@ | |||
473 | 41 | 42 | ||
474 | 42 | def start_timer(self): | 43 | def start_timer(self): |
475 | 43 | """Empty start timer, this is not needed on windows.""" | 44 | """Empty start timer, this is not needed on windows.""" |
476 | 45 | ======= | ||
477 | 46 | # -*- coding: utf-8 *-* | ||
478 | 47 | # | ||
479 | 48 | # Copyright 2012 Canonical Ltd. | ||
480 | 49 | # | ||
481 | 50 | # This program is free software: you can redistribute it and/or modify it | ||
482 | 51 | # under the terms of the GNU General Public License version 3, as published | ||
483 | 52 | # by the Free Software Foundation. | ||
484 | 53 | # | ||
485 | 54 | # This program is distributed in the hope that it will be useful, but | ||
486 | 55 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
487 | 56 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
488 | 57 | # PURPOSE. See the GNU General Public License for more details. | ||
489 | 58 | # | ||
490 | 59 | # You should have received a copy of the GNU General Public License along | ||
491 | 60 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
492 | 61 | # | ||
493 | 62 | # In addition, as a special exception, the copyright holders give | ||
494 | 63 | # permission to link the code of portions of this program with the | ||
495 | 64 | # OpenSSL library under certain conditions as described in each | ||
496 | 65 | # individual source file, and distribute linked combinations | ||
497 | 66 | # including the two. | ||
498 | 67 | # You must obey the GNU General Public License in all respects | ||
499 | 68 | # for all of the code used other than OpenSSL. If you modify | ||
500 | 69 | # file(s) with this exception, you may extend this exception to your | ||
501 | 70 | # version of the file(s), but you are not obligated to do so. If you | ||
502 | 71 | # do not wish to do so, delete this exception statement from your | ||
503 | 72 | # version. If you delete this exception statement from all source | ||
504 | 73 | # files in the program, then also delete it here. | ||
505 | 74 | """Dummy implementation of sync_menu lib for win32 and darwin.""" | ||
506 | 75 | |||
507 | 76 | |||
508 | 77 | class UbuntuOneSyncMenu(object): | ||
509 | 78 | """Integrate U1 with the Ubuntu Sync Menu.""" | ||
510 | 79 | def __init__(self, status, syncdaemon_service): | ||
511 | 80 | """Match #args of linux syncmenu and do nothing.""" | ||
512 | 81 | |||
513 | 82 | def update_transfers(self): | ||
514 | 83 | """Do nothing.""" | ||
515 | 84 | >>>>>>> MERGE-SOURCE | ||
516 | 44 | 85 | ||
517 | === modified file 'ubuntuone/platform/sync_menu/linux.py' | |||
518 | --- ubuntuone/platform/sync_menu/linux.py 2012-09-24 12:47:05 +0000 | |||
519 | +++ ubuntuone/platform/sync_menu/linux.py 2012-09-26 18:35:23 +0000 | |||
520 | @@ -1,3 +1,4 @@ | |||
521 | 1 | <<<<<<< TREE | ||
522 | 1 | # -*- coding: utf-8 *-* | 2 | # -*- coding: utf-8 *-* |
523 | 2 | # | 3 | # |
524 | 3 | # Copyright 2012 Canonical Ltd. | 4 | # Copyright 2012 Canonical Ltd. |
525 | @@ -239,3 +240,255 @@ | |||
526 | 239 | 240 | ||
527 | 240 | 241 | ||
528 | 241 | UbuntuOneSyncMenu = UbuntuOneSyncMenuLinux if use_syncmenu else DummySyncMenu | 242 | UbuntuOneSyncMenu = UbuntuOneSyncMenuLinux if use_syncmenu else DummySyncMenu |
529 | 243 | ======= | ||
530 | 244 | # -*- coding: utf-8 *-* | ||
531 | 245 | # | ||
532 | 246 | # Copyright 2012 Canonical Ltd. | ||
533 | 247 | # | ||
534 | 248 | # This program is free software: you can redistribute it and/or modify it | ||
535 | 249 | # under the terms of the GNU General Public License version 3, as published | ||
536 | 250 | # by the Free Software Foundation. | ||
537 | 251 | # | ||
538 | 252 | # This program is distributed in the hope that it will be useful, but | ||
539 | 253 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
540 | 254 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
541 | 255 | # PURPOSE. See the GNU General Public License for more details. | ||
542 | 256 | # | ||
543 | 257 | # You should have received a copy of the GNU General Public License along | ||
544 | 258 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
545 | 259 | # | ||
546 | 260 | # In addition, as a special exception, the copyright holders give | ||
547 | 261 | # permission to link the code of portions of this program with the | ||
548 | 262 | # OpenSSL library under certain conditions as described in each | ||
549 | 263 | # individual source file, and distribute linked combinations | ||
550 | 264 | # including the two. | ||
551 | 265 | # You must obey the GNU General Public License in all respects | ||
552 | 266 | # for all of the code used other than OpenSSL. If you modify | ||
553 | 267 | # file(s) with this exception, you may extend this exception to your | ||
554 | 268 | # version of the file(s), but you are not obligated to do so. If you | ||
555 | 269 | # do not wish to do so, delete this exception statement from your | ||
556 | 270 | # version. If you delete this exception statement from all source | ||
557 | 271 | # files in the program, then also delete it here. | ||
558 | 272 | """Use SyncMenu lib to integrate U1 with the Systray Sync Icon.""" | ||
559 | 273 | |||
560 | 274 | import gettext | ||
561 | 275 | import logging | ||
562 | 276 | import time | ||
563 | 277 | import sys | ||
564 | 278 | import webbrowser | ||
565 | 279 | |||
566 | 280 | glib = None | ||
567 | 281 | try: | ||
568 | 282 | if 'gobject' in sys.modules and sys.modules['gobject'] is not None: | ||
569 | 283 | import glib as GLib | ||
570 | 284 | glib = GLib | ||
571 | 285 | else: | ||
572 | 286 | from gi.repository import GLib | ||
573 | 287 | glib = GLib | ||
574 | 288 | except ImportError: | ||
575 | 289 | pass | ||
576 | 290 | try: | ||
577 | 291 | from gi.repository import ( | ||
578 | 292 | Dbusmenu, | ||
579 | 293 | SyncMenu, | ||
580 | 294 | ) | ||
581 | 295 | use_syncmenu = True | ||
582 | 296 | except: | ||
583 | 297 | use_syncmenu = False | ||
584 | 298 | |||
585 | 299 | from ubuntuone.clientdefs import GETTEXT_PACKAGE | ||
586 | 300 | from ubuntuone import status | ||
587 | 301 | |||
588 | 302 | |||
589 | 303 | logger = logging.getLogger("ubuntuone.platform.SyncMenu") | ||
590 | 304 | |||
591 | 305 | Q_ = lambda string: gettext.dgettext(GETTEXT_PACKAGE, string) | ||
592 | 306 | |||
593 | 307 | OPEN_U1 = Q_("Open Ubuntu One") | ||
594 | 308 | GO_TO_WEB = Q_("Go to the Ubuntu One Website") | ||
595 | 309 | TRANSFERS = Q_("Current and Recent Transfers") | ||
596 | 310 | MORE_STORAGE = Q_("Get More Space") | ||
597 | 311 | GET_HELP = Q_("Get Help on the Web") | ||
598 | 312 | |||
599 | 313 | DELAY_BETWEEN_UPDATES = 3 | ||
600 | 314 | UBUNTUONE_LINK = u'https://one.ubuntu.com/' | ||
601 | 315 | DASHBOARD = UBUNTUONE_LINK + u'dashboard/' | ||
602 | 316 | HELP_LINK = UBUNTUONE_LINK + u'support/' | ||
603 | 317 | GET_STORAGE_LINK = UBUNTUONE_LINK + u'services/#storage_panel' | ||
604 | 318 | |||
605 | 319 | |||
606 | 320 | class UbuntuOneSyncMenuLinux(object): | ||
607 | 321 | """Integrate U1 with the Ubuntu Sync Menu.""" | ||
608 | 322 | |||
609 | 323 | def __init__(self, status, syncdaemon_service): | ||
610 | 324 | """Initialize menu.""" | ||
611 | 325 | self._syncdaemon_service = syncdaemon_service | ||
612 | 326 | self._paused = False | ||
613 | 327 | self.timer = None | ||
614 | 328 | self.next_update = time.time() | ||
615 | 329 | self.root_menu = Dbusmenu.Menuitem() | ||
616 | 330 | |||
617 | 331 | self.open_u1 = Dbusmenu.Menuitem() | ||
618 | 332 | self.open_u1.property_set(Dbusmenu.MENUITEM_PROP_LABEL, OPEN_U1) | ||
619 | 333 | |||
620 | 334 | self.go_to_web = Dbusmenu.Menuitem() | ||
621 | 335 | self.go_to_web.property_set(Dbusmenu.MENUITEM_PROP_LABEL, | ||
622 | 336 | GO_TO_WEB) | ||
623 | 337 | |||
624 | 338 | self.transfers = TransfersMenu(status) | ||
625 | 339 | self.transfers.property_set(Dbusmenu.MENUITEM_PROP_LABEL, | ||
626 | 340 | TRANSFERS) | ||
627 | 341 | |||
628 | 342 | self.more_storage = Dbusmenu.Menuitem() | ||
629 | 343 | self.more_storage.property_set(Dbusmenu.MENUITEM_PROP_LABEL, | ||
630 | 344 | MORE_STORAGE) | ||
631 | 345 | |||
632 | 346 | self.get_help = Dbusmenu.Menuitem() | ||
633 | 347 | self.get_help.property_set(Dbusmenu.MENUITEM_PROP_LABEL, | ||
634 | 348 | GET_HELP) | ||
635 | 349 | |||
636 | 350 | # Connect signals | ||
637 | 351 | self.open_u1.connect(Dbusmenu.MENUITEM_SIGNAL_ITEM_ACTIVATED, | ||
638 | 352 | self.open_control_panel) | ||
639 | 353 | self.go_to_web.connect(Dbusmenu.MENUITEM_SIGNAL_ITEM_ACTIVATED, | ||
640 | 354 | self.open_go_to_web) | ||
641 | 355 | self.get_help.connect(Dbusmenu.MENUITEM_SIGNAL_ITEM_ACTIVATED, | ||
642 | 356 | self.open_web_help) | ||
643 | 357 | self.more_storage.connect(Dbusmenu.MENUITEM_SIGNAL_ITEM_ACTIVATED, | ||
644 | 358 | self.open_get_more_storage) | ||
645 | 359 | |||
646 | 360 | # Add items | ||
647 | 361 | self.root_menu.child_append(self.open_u1) | ||
648 | 362 | self.root_menu.child_append(self.go_to_web) | ||
649 | 363 | self.root_menu.child_append(self.transfers) | ||
650 | 364 | self.root_menu.child_append(self.more_storage) | ||
651 | 365 | self.root_menu.child_append(self.get_help) | ||
652 | 366 | |||
653 | 367 | self.server = Dbusmenu.Server() | ||
654 | 368 | self.server.set_root(self.root_menu) | ||
655 | 369 | self.app = SyncMenu.App.new("ubuntuone-installer.desktop") | ||
656 | 370 | self.app.set_menu(self.server) | ||
657 | 371 | self.app.connect("notify::paused", self.change_sync_status) | ||
658 | 372 | |||
659 | 373 | def change_sync_status(self, *args): | ||
660 | 374 | """Triggered when the sync status is changed fromm the menu.""" | ||
661 | 375 | if self._paused: | ||
662 | 376 | self._syncdaemon_service.connect() | ||
663 | 377 | self._paused = False | ||
664 | 378 | else: | ||
665 | 379 | self._syncdaemon_service.disconnect() | ||
666 | 380 | self._paused = True | ||
667 | 381 | |||
668 | 382 | def open_control_panel(self, *args): | ||
669 | 383 | """Open the Ubuntu One Control Panel.""" | ||
670 | 384 | glib.spawn_command_line_async('ubuntuone-installer') | ||
671 | 385 | |||
672 | 386 | def open_go_to_web(self, *args): | ||
673 | 387 | """Open the Ubunto One Help Page""" | ||
674 | 388 | webbrowser.open(DASHBOARD) | ||
675 | 389 | |||
676 | 390 | def open_web_help(self, *args): | ||
677 | 391 | """Open the Ubunto One Help Page""" | ||
678 | 392 | webbrowser.open(HELP_LINK) | ||
679 | 393 | |||
680 | 394 | def open_get_more_storage(self, *args): | ||
681 | 395 | """Open the Ubunto One Help Page""" | ||
682 | 396 | webbrowser.open(GET_STORAGE_LINK) | ||
683 | 397 | |||
684 | 398 | def _timeout(self, result): | ||
685 | 399 | """The aggregating timer has expired, so update the UI.""" | ||
686 | 400 | self.next_update = int(time.time()) + DELAY_BETWEEN_UPDATES | ||
687 | 401 | self.transfers.update_progress() | ||
688 | 402 | self.timer = None | ||
689 | 403 | |||
690 | 404 | def update_transfers(self): | ||
691 | 405 | """Set up a timer if there isn't one ticking and update the ui.""" | ||
692 | 406 | if not self.timer: | ||
693 | 407 | logger.debug("Updating Transfers.") | ||
694 | 408 | delay = int(max(0, min(DELAY_BETWEEN_UPDATES, | ||
695 | 409 | self.next_update - time.time()))) | ||
696 | 410 | self.timer = status.aggregator.Timer(delay) | ||
697 | 411 | self.timer.addCallback(self._timeout) | ||
698 | 412 | |||
699 | 413 | |||
700 | 414 | class TransfersMenu(Dbusmenu.Menuitem): | ||
701 | 415 | """Menu that handles the recent and current transfers.""" | ||
702 | 416 | |||
703 | 417 | def __init__(self, status_frontend): | ||
704 | 418 | super(TransfersMenu, self).__init__() | ||
705 | 419 | self.status_frontend = status_frontend | ||
706 | 420 | self.uploading = {} | ||
707 | 421 | self.previous_transfers = [] | ||
708 | 422 | self._transfers_items = {} | ||
709 | 423 | self._uploading_items = {} | ||
710 | 424 | |||
711 | 425 | def update_progress(self): | ||
712 | 426 | """Update the list of recent transfers and current transfers.""" | ||
713 | 427 | current_transfers = self.status_frontend.recent_transfers() | ||
714 | 428 | uploading_data = {} | ||
715 | 429 | for filename, size, written in \ | ||
716 | 430 | self.status_frontend.files_uploading(): | ||
717 | 431 | uploading_data[filename] = (size, written) | ||
718 | 432 | |||
719 | 433 | temp_transfers = {} | ||
720 | 434 | if current_transfers != self.previous_transfers: | ||
721 | 435 | logger.debug("Update recent transfers with: %r", current_transfers) | ||
722 | 436 | for item_transfer in self._transfers_items: | ||
723 | 437 | self.child_delete(self._transfers_items[item_transfer]) | ||
724 | 438 | for item in current_transfers: | ||
725 | 439 | recent_file = Dbusmenu.Menuitem() | ||
726 | 440 | recent_file.property_set(Dbusmenu.MENUITEM_PROP_LABEL, | ||
727 | 441 | item) | ||
728 | 442 | self.child_add_position(recent_file, 0) | ||
729 | 443 | temp_transfers[item] = recent_file | ||
730 | 444 | self._transfers_items = temp_transfers | ||
731 | 445 | |||
732 | 446 | items_added = 0 | ||
733 | 447 | remove = [] | ||
734 | 448 | for item in self._uploading_items: | ||
735 | 449 | if item in uploading_data: | ||
736 | 450 | size, written = uploading_data[item] | ||
737 | 451 | percentage = written * 100 / size | ||
738 | 452 | upload_item = self._uploading_items[item] | ||
739 | 453 | upload_item.property_set_int( | ||
740 | 454 | SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE, | ||
741 | 455 | percentage) | ||
742 | 456 | logger.debug("Current transfer %s progress update: %r", | ||
743 | 457 | item, percentage) | ||
744 | 458 | items_added += 1 | ||
745 | 459 | else: | ||
746 | 460 | self.child_delete(self._uploading_items[item]) | ||
747 | 461 | remove.append(item) | ||
748 | 462 | for item in remove: | ||
749 | 463 | self._uploading_items.pop(item) | ||
750 | 464 | if items_added < 5: | ||
751 | 465 | for item in uploading_data: | ||
752 | 466 | if item not in self._uploading_items and items_added < 5: | ||
753 | 467 | size, written = uploading_data[item] | ||
754 | 468 | percentage = written * 100 / size | ||
755 | 469 | uploading_file = Dbusmenu.Menuitem() | ||
756 | 470 | uploading_file.property_set(Dbusmenu.MENUITEM_PROP_LABEL, | ||
757 | 471 | item) | ||
758 | 472 | uploading_file.property_set(Dbusmenu.MENUITEM_PROP_TYPE, | ||
759 | 473 | SyncMenu.PROGRESS_MENUITEM_TYPE) | ||
760 | 474 | uploading_file.property_set_int( | ||
761 | 475 | SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE, | ||
762 | 476 | percentage) | ||
763 | 477 | logger.debug("Current transfer %s created", item) | ||
764 | 478 | self.child_append(uploading_file) | ||
765 | 479 | self._uploading_items[item] = uploading_file | ||
766 | 480 | items_added += 1 | ||
767 | 481 | |||
768 | 482 | |||
769 | 483 | class DummySyncMenu(object): | ||
770 | 484 | """Dummy SyncMenu""" | ||
771 | 485 | |||
772 | 486 | def __init__(self, *args, **kwargs): | ||
773 | 487 | """Initialize menu.""" | ||
774 | 488 | |||
775 | 489 | def start_timer(self): | ||
776 | 490 | """Do nothing.""" | ||
777 | 491 | |||
778 | 492 | |||
779 | 493 | UbuntuOneSyncMenu = UbuntuOneSyncMenuLinux if use_syncmenu else DummySyncMenu | ||
780 | 494 | >>>>>>> MERGE-SOURCE | ||
781 | 242 | 495 | ||
782 | === modified file 'ubuntuone/status/aggregator.py' | |||
783 | --- ubuntuone/status/aggregator.py 2012-08-16 12:00:12 +0000 | |||
784 | +++ ubuntuone/status/aggregator.py 2012-09-26 18:35:23 +0000 | |||
785 | @@ -33,7 +33,7 @@ | |||
786 | 33 | import itertools | 33 | import itertools |
787 | 34 | import operator | 34 | import operator |
788 | 35 | import os | 35 | import os |
790 | 36 | from collections import deque | 36 | from collections import deque, Callable |
791 | 37 | 37 | ||
792 | 38 | import gettext | 38 | import gettext |
793 | 39 | 39 | ||
794 | @@ -41,7 +41,11 @@ | |||
795 | 41 | 41 | ||
796 | 42 | from ubuntuone.clientdefs import GETTEXT_PACKAGE | 42 | from ubuntuone.clientdefs import GETTEXT_PACKAGE |
797 | 43 | from ubuntuone.status.logger import logger | 43 | from ubuntuone.status.logger import logger |
799 | 44 | from ubuntuone.platform import session, notification | 44 | from ubuntuone.platform import ( |
800 | 45 | notification, | ||
801 | 46 | session, | ||
802 | 47 | sync_menu | ||
803 | 48 | ) | ||
804 | 45 | from ubuntuone.platform.messaging import Messaging | 49 | from ubuntuone.platform.messaging import Messaging |
805 | 46 | from ubuntuone.platform.launcher import UbuntuOneLauncher, DummyLauncher | 50 | from ubuntuone.platform.launcher import UbuntuOneLauncher, DummyLauncher |
806 | 47 | 51 | ||
807 | @@ -625,6 +629,7 @@ | |||
808 | 625 | self.progress = {} | 629 | self.progress = {} |
809 | 626 | self.to_do = {} | 630 | self.to_do = {} |
810 | 627 | self.recent_transfers = deque(maxlen=5) | 631 | self.recent_transfers = deque(maxlen=5) |
811 | 632 | self.listeners_callbacks = [] | ||
812 | 628 | 633 | ||
813 | 629 | def get_notification(self): | 634 | def get_notification(self): |
814 | 630 | """Create a new toggleable notification object.""" | 635 | """Create a new toggleable notification object.""" |
815 | @@ -655,6 +660,13 @@ | |||
816 | 655 | self.to_do = {} | 660 | self.to_do = {} |
817 | 656 | # pylint: enable=W0201 | 661 | # pylint: enable=W0201 |
818 | 657 | 662 | ||
819 | 663 | def register_listener(self, listener): | ||
820 | 664 | """Register a callable object to be notified.""" | ||
821 | 665 | if isinstance(listener, Callable): | ||
822 | 666 | self.listeners_callbacks.append(listener) | ||
823 | 667 | else: | ||
824 | 668 | raise TypeError("Callable object expected.") | ||
825 | 669 | |||
826 | 658 | def get_discovery_message(self): | 670 | def get_discovery_message(self): |
827 | 659 | """Get the text for the discovery bubble.""" | 671 | """Get the text for the discovery bubble.""" |
828 | 660 | lines = [] | 672 | lines = [] |
829 | @@ -716,6 +728,8 @@ | |||
830 | 716 | progress = float( | 728 | progress = float( |
831 | 717 | sum(self.progress.values())) / sum(self.to_do.values()) | 729 | sum(self.progress.values())) / sum(self.to_do.values()) |
832 | 718 | self.progress_bar.set_progress(progress) | 730 | self.progress_bar.set_progress(progress) |
833 | 731 | for listener in self.listeners_callbacks: | ||
834 | 732 | listener() | ||
835 | 719 | 733 | ||
836 | 720 | def download_started(self, command): | 734 | def download_started(self, command): |
837 | 721 | """A download just started.""" | 735 | """A download just started.""" |
838 | @@ -801,13 +815,24 @@ | |||
839 | 801 | class StatusFrontend(object): | 815 | class StatusFrontend(object): |
840 | 802 | """Frontend for the status aggregator, used by the StatusListener.""" | 816 | """Frontend for the status aggregator, used by the StatusListener.""" |
841 | 803 | 817 | ||
843 | 804 | def __init__(self, clock=reactor): | 818 | def __init__(self, clock=reactor, service=None): |
844 | 805 | """Initialize this instance.""" | 819 | """Initialize this instance.""" |
845 | 806 | self.aggregator = StatusAggregator(clock=clock) | 820 | self.aggregator = StatusAggregator(clock=clock) |
846 | 807 | self.notification = self.aggregator.get_notification() | 821 | self.notification = self.aggregator.get_notification() |
847 | 808 | self.messaging = Messaging() | 822 | self.messaging = Messaging() |
848 | 809 | self.quota_timer = None | 823 | self.quota_timer = None |
849 | 810 | 824 | ||
850 | 825 | self.syncdaemon_service = service | ||
851 | 826 | self.sync_menu = None | ||
852 | 827 | self.start_sync_menu() | ||
853 | 828 | |||
854 | 829 | def start_sync_menu(self): | ||
855 | 830 | """Create the sync menu and run the loop.""" | ||
856 | 831 | if self.syncdaemon_service is not None: | ||
857 | 832 | self.sync_menu = sync_menu.UbuntuOneSyncMenu(self, | ||
858 | 833 | self.syncdaemon_service) | ||
859 | 834 | self.aggregator.register_listener(self.sync_menu.update_transfers) | ||
860 | 835 | |||
861 | 811 | def recent_transfers(self): | 836 | def recent_transfers(self): |
862 | 812 | """Return a tuple with the recent transfers paths.""" | 837 | """Return a tuple with the recent transfers paths.""" |
863 | 813 | return list(self.aggregator.recent_transfers) | 838 | return list(self.aggregator.recent_transfers) |
864 | 814 | 839 | ||
865 | === modified file 'ubuntuone/syncdaemon/main.py' | |||
866 | --- ubuntuone/syncdaemon/main.py 2012-09-19 17:23:02 +0000 | |||
867 | +++ ubuntuone/syncdaemon/main.py 2012-09-26 18:35:23 +0000 | |||
868 | @@ -176,7 +176,8 @@ | |||
869 | 176 | 176 | ||
870 | 177 | def start_status_listener(self): | 177 | def start_status_listener(self): |
871 | 178 | """Start the status listener if it is configured to start.""" | 178 | """Start the status listener if it is configured to start.""" |
873 | 179 | self.status_listener = status_listener.get_listener(self.fs, self.vm) | 179 | self.status_listener = status_listener.get_listener(self.fs, self.vm, |
874 | 180 | self.external) | ||
875 | 180 | # subscribe to EQ, to be unsubscribed in shutdown | 181 | # subscribe to EQ, to be unsubscribed in shutdown |
876 | 181 | if self.status_listener: | 182 | if self.status_listener: |
877 | 182 | self.event_q.subscribe(self.status_listener) | 183 | self.event_q.subscribe(self.status_listener) |
878 | 183 | 184 | ||
879 | === modified file 'ubuntuone/syncdaemon/status_listener.py' | |||
880 | --- ubuntuone/syncdaemon/status_listener.py 2012-08-10 12:49:46 +0000 | |||
881 | +++ ubuntuone/syncdaemon/status_listener.py 2012-09-26 18:35:23 +0000 | |||
882 | @@ -48,10 +48,10 @@ | |||
883 | 48 | return True | 48 | return True |
884 | 49 | 49 | ||
885 | 50 | 50 | ||
887 | 51 | def get_listener(fsm, vm): | 51 | def get_listener(fsm, vm, syncdaemon_service=None): |
888 | 52 | """Return an instance of the status listener, or None if turned off.""" | 52 | """Return an instance of the status listener, or None if turned off.""" |
889 | 53 | if should_start_listener(): | 53 | if should_start_listener(): |
891 | 54 | status_frontend = StatusFrontend() | 54 | status_frontend = StatusFrontend(service=syncdaemon_service) |
892 | 55 | return StatusListener(fsm, vm, status_frontend) | 55 | return StatusListener(fsm, vm, status_frontend) |
893 | 56 | else: | 56 | else: |
894 | 57 | return None | 57 | return None |