Merge lp:~dobey/ubuntuone-client/update-4-0 into lp:ubuntuone-client/stable-4-0
- update-4-0
- Merge into stable-4-0
Proposed by
dobey
on 2012-10-02
| Status: | Merged |
|---|---|
| Merged at revision: | 1265 |
| Proposed branch: | lp:~dobey/ubuntuone-client/update-4-0 |
| Merge into: | lp:ubuntuone-client/stable-4-0 |
| Diff against target: |
783 lines (+243/-113) 14 files modified
tests/platform/messaging/test_linux.py (+9/-1) tests/platform/sync_menu/test_common.py (+49/-0) tests/platform/sync_menu/test_linux.py (+63/-18) tests/status/test_aggregator.py (+18/-2) tests/syncdaemon/test_main.py (+0/-14) tests/syncdaemon/test_status_listener.py (+2/-2) ubuntuone/platform/__init__.py (+4/-3) ubuntuone/platform/filesystem_notifications/monitor/__init__.py (+2/-2) ubuntuone/platform/messaging/linux.py (+7/-2) ubuntuone/platform/sync_menu/common.py (+6/-11) ubuntuone/platform/sync_menu/linux.py (+50/-28) ubuntuone/status/aggregator.py (+28/-15) ubuntuone/syncdaemon/main.py (+3/-13) ubuntuone/syncdaemon/status_listener.py (+2/-2) |
| To merge this branch: | bzr merge lp:~dobey/ubuntuone-client/update-4-0 |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Roberto Alsina (community) | 2012-10-02 | Approve on 2012-10-02 | |
|
Review via email:
|
|||
Commit Message
[Rodney Dawes]
Start the control panel, not the old installer which no longer exists.
Disable the session inhibitor to prevent bug #737620 from happening.
[Mike McCracken]
- Fix dummy sync menu implementation for windows and darwin. (LP: #1055840)
- Make fsevents root daemon the default fs monitor for darwin, leave other platforms alone.
- Send 'darwin' to server as platform name in auth. (LP: #1037435)
[Diego Sarmentero]
- Sort items based on the written value, to prioritize the files being transferred (LP: #1052956).
- Using a timer event that doesn't fire continuously if another update timer is in progress (LP: #1052922).
Description of the Change
To post a comment you must log in.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
| 1 | === modified file 'tests/platform/messaging/test_linux.py' |
| 2 | --- tests/platform/messaging/test_linux.py 2012-05-02 20:14:50 +0000 |
| 3 | +++ tests/platform/messaging/test_linux.py 2012-10-02 20:28:28 +0000 |
| 4 | @@ -39,7 +39,7 @@ |
| 5 | from twisted.internet import defer |
| 6 | from twisted.trial.unittest import TestCase |
| 7 | from ubuntuone.devtools.testcases import skipIf |
| 8 | - |
| 9 | +from ubuntuone.platform.messaging import linux |
| 10 | from ubuntuone.platform.messaging.linux import ( |
| 11 | Messaging, |
| 12 | _server_callback, |
| 13 | @@ -160,3 +160,11 @@ |
| 14 | actual_callback = messaging.create_callback() |
| 15 | actual_callback(messaging.indicators[-1]) |
| 16 | self.assertEquals(0, len(messaging.indicators)) |
| 17 | + |
| 18 | + def test_open_u1(self): |
| 19 | + """Check that the proper action is executed.""" |
| 20 | + data = [] |
| 21 | + |
| 22 | + self.patch(linux.glib, "spawn_command_line_async", data.append) |
| 23 | + _server_callback(None) |
| 24 | + self.assertEqual(data, ['ubuntuone-control-panel-qt']) |
| 25 | |
| 26 | === added file 'tests/platform/sync_menu/test_common.py' |
| 27 | --- tests/platform/sync_menu/test_common.py 1970-01-01 00:00:00 +0000 |
| 28 | +++ tests/platform/sync_menu/test_common.py 2012-10-02 20:28:28 +0000 |
| 29 | @@ -0,0 +1,49 @@ |
| 30 | +# -*- coding: utf-8 *-* |
| 31 | +# |
| 32 | +# Copyright 2012 Canonical Ltd. |
| 33 | +# |
| 34 | +# This program is free software: you can redistribute it and/or modify it |
| 35 | +# under the terms of the GNU General Public License version 3, as published |
| 36 | +# by the Free Software Foundation. |
| 37 | +# |
| 38 | +# This program is distributed in the hope that it will be useful, but |
| 39 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
| 40 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
| 41 | +# PURPOSE. See the GNU General Public License for more details. |
| 42 | +# |
| 43 | +# You should have received a copy of the GNU General Public License along |
| 44 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
| 45 | +# |
| 46 | +# In addition, as a special exception, the copyright holders give |
| 47 | +# permission to link the code of portions of this program with the |
| 48 | +# OpenSSL library under certain conditions as described in each |
| 49 | +# individual source file, and distribute linked combinations |
| 50 | +# including the two. |
| 51 | +# You must obey the GNU General Public License in all respects |
| 52 | +# for all of the code used other than OpenSSL. If you modify |
| 53 | +# file(s) with this exception, you may extend this exception to your |
| 54 | +# version of the file(s), but you are not obligated to do so. If you |
| 55 | +# do not wish to do so, delete this exception statement from your |
| 56 | +# version. If you delete this exception statement from all source |
| 57 | +# files in the program, then also delete it here. |
| 58 | +"""Test the common dummy Sync Menu implementation for win32/darwin.""" |
| 59 | + |
| 60 | +from collections import Callable |
| 61 | + |
| 62 | +from twisted.trial.unittest import TestCase |
| 63 | + |
| 64 | +from ubuntuone.platform.sync_menu import common |
| 65 | + |
| 66 | + |
| 67 | +class SyncMenuDummyTestCase(TestCase): |
| 68 | + """Test the SyncMenu.""" |
| 69 | + |
| 70 | + def test_dummy_support(self): |
| 71 | + """Can we create a Dummy with the same #args as the real obj.""" |
| 72 | + dummy = common.UbuntuOneSyncMenu(1, 2) |
| 73 | + self.assertIsInstance(dummy, common.UbuntuOneSyncMenu) |
| 74 | + |
| 75 | + def test_dummy_has_update_transfers(self): |
| 76 | + """Check that the dummy has the proper methods required by the API.""" |
| 77 | + dummy = common.UbuntuOneSyncMenu(1, 2) |
| 78 | + self.assertIsInstance(dummy.update_transfers, Callable) |
| 79 | |
| 80 | === modified file 'tests/platform/sync_menu/test_linux.py' |
| 81 | --- tests/platform/sync_menu/test_linux.py 2012-09-21 14:09:05 +0000 |
| 82 | +++ tests/platform/sync_menu/test_linux.py 2012-10-02 20:28:28 +0000 |
| 83 | @@ -28,6 +28,7 @@ |
| 84 | # files in the program, then also delete it here. |
| 85 | """Test the Sync Menu.""" |
| 86 | |
| 87 | +import time |
| 88 | from collections import Callable |
| 89 | |
| 90 | from twisted.internet import defer |
| 91 | @@ -57,18 +58,20 @@ |
| 92 | return self.uploading_data |
| 93 | |
| 94 | |
| 95 | -class FakeStatusListener(object): |
| 96 | - """Fake StatusListener.""" |
| 97 | - |
| 98 | - def __init__(self): |
| 99 | - self.status_frontend = FakeStatusFrontend() |
| 100 | - |
| 101 | - |
| 102 | -class FakeMain(object): |
| 103 | - """Fake Main.""" |
| 104 | - |
| 105 | - def __init__(self): |
| 106 | - self.status_listener = FakeStatusListener() |
| 107 | +class FakeTimer(object): |
| 108 | + """Fake Timer.""" |
| 109 | + |
| 110 | + def __init__(self, delay): |
| 111 | + self.delay = delay |
| 112 | + self.callback = None |
| 113 | + |
| 114 | + def addCallback(self, callback): |
| 115 | + """Add callback.""" |
| 116 | + self.callback = callback |
| 117 | + |
| 118 | + |
| 119 | +class FakeSyncdaemonService(object): |
| 120 | + """Fake SyncdaemonService.""" |
| 121 | |
| 122 | |
| 123 | class FakeSyncMenuApp(object): |
| 124 | @@ -101,10 +104,10 @@ |
| 125 | dummy = linux.DummySyncMenu('random', 'args') |
| 126 | self.assertIsInstance(dummy, linux.DummySyncMenu) |
| 127 | |
| 128 | - def test_dummy_has_start_timer(self): |
| 129 | + def test_dummy_has_update_transfers(self): |
| 130 | """Check that the dummy has the proper methods required by the API.""" |
| 131 | dummy = linux.DummySyncMenu('random', 'args') |
| 132 | - self.assertIsInstance(dummy.start_timer, Callable) |
| 133 | + self.assertIsInstance(dummy.update_transfers, Callable) |
| 134 | |
| 135 | |
| 136 | class SyncMenuTestCase(TestCase): |
| 137 | @@ -117,12 +120,13 @@ |
| 138 | yield super(SyncMenuTestCase, self).setUp() |
| 139 | self.patch(linux.SyncMenu, "App", FakeSyncMenuApp) |
| 140 | FakeSyncMenuApp.clean() |
| 141 | - self.main = FakeMain() |
| 142 | - self.status_frontend = self.main.status_listener.status_frontend |
| 143 | + self.syncdaemon_service = FakeSyncdaemonService() |
| 144 | + self.status_frontend = FakeStatusFrontend() |
| 145 | self._paused = False |
| 146 | self.patch(sync_menu.UbuntuOneSyncMenu, "change_sync_status", |
| 147 | self._change_sync_status) |
| 148 | - self.sync_menu = sync_menu.UbuntuOneSyncMenu(self.main) |
| 149 | + self.sync_menu = sync_menu.UbuntuOneSyncMenu(self.status_frontend, |
| 150 | + self.syncdaemon_service) |
| 151 | |
| 152 | def _change_sync_status(self, *args): |
| 153 | """Fake change_sync_status.""" |
| 154 | @@ -170,7 +174,7 @@ |
| 155 | |
| 156 | self.patch(linux.glib, "spawn_command_line_async", data.append) |
| 157 | self.sync_menu.open_control_panel() |
| 158 | - self.assertEqual(data, ['ubuntuone-installer']) |
| 159 | + self.assertEqual(data, ['ubuntuone-control-panel-qt']) |
| 160 | |
| 161 | def test_go_to_web(self): |
| 162 | """Check that the proper action is executed.""" |
| 163 | @@ -330,3 +334,44 @@ |
| 164 | self.assertEqual(item.property_get_int( |
| 165 | linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE), |
| 166 | percentage) |
| 167 | + |
| 168 | + def test_transfers_order(self): |
| 169 | + """Check that the proper transfers are shown first.""" |
| 170 | + data_current = [ |
| 171 | + ('file0', 1200, 610), |
| 172 | + ('file1', 3000, 400), |
| 173 | + ('file2', 2000, 100), |
| 174 | + ('file3', 2500, 150), |
| 175 | + ('file4', 2500, 950), |
| 176 | + ('file5', 3500, 550), |
| 177 | + ('file6', 1000, 600), |
| 178 | + ('file7', 5000, 4600)] |
| 179 | + expected = [ |
| 180 | + ('file7', 5000, 4600), |
| 181 | + ('file4', 2500, 950), |
| 182 | + ('file0', 1200, 610), |
| 183 | + ('file6', 1000, 600), |
| 184 | + ('file5', 3500, 550)] |
| 185 | + self.status_frontend.uploading_data = data_current |
| 186 | + self.sync_menu.transfers.update_progress() |
| 187 | + children = self.sync_menu.transfers.get_children() |
| 188 | + # The menu should only show 5 current transfers. |
| 189 | + self.assertEqual(len(children), 5) |
| 190 | + for i, item in enumerate(children): |
| 191 | + text = item.property_get(linux.Dbusmenu.MENUITEM_PROP_LABEL) |
| 192 | + percentage = item.property_get_int( |
| 193 | + linux.SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE) |
| 194 | + name, size, written = expected[i] |
| 195 | + percentage_expected = written * 100 / size |
| 196 | + self.assertEqual(text, name) |
| 197 | + self.assertEqual(percentage, percentage_expected) |
| 198 | + |
| 199 | + def test_update_transfers_delay(self): |
| 200 | + """Check that the timer is being handle properly.""" |
| 201 | + self.patch(linux.status.aggregator, "Timer", FakeTimer) |
| 202 | + self.sync_menu.next_update = time.time() |
| 203 | + self.sync_menu.update_transfers() |
| 204 | + self.sync_menu.timer = None |
| 205 | + self.sync_menu.next_update = time.time() * 2 |
| 206 | + self.sync_menu.update_transfers() |
| 207 | + self.assertEqual(self.sync_menu.timer.delay, 3) |
| 208 | |
| 209 | === modified file 'tests/status/test_aggregator.py' |
| 210 | --- tests/status/test_aggregator.py 2012-08-20 13:18:43 +0000 |
| 211 | +++ tests/status/test_aggregator.py 2012-10-02 20:28:28 +0000 |
| 212 | @@ -39,6 +39,7 @@ |
| 213 | |
| 214 | from contrib.testing.testcase import BaseTwistedTestCase |
| 215 | from ubuntuone.devtools.handlers import MementoHandler |
| 216 | +from ubuntuone.devtools.testcases import skipTest |
| 217 | from ubuntuone.status import aggregator |
| 218 | from ubuntuone.status.notification import AbstractNotification |
| 219 | from ubuntuone.status.messaging import AbstractMessaging |
| 220 | @@ -576,7 +577,6 @@ |
| 221 | """Initialize this test instance.""" |
| 222 | yield super(ProgressBarTestCase, self).setUp() |
| 223 | self.patch(aggregator, "UbuntuOneLauncher", FakeLauncher) |
| 224 | - self.patch(aggregator.session, "Inhibitor", FakeInhibitor) |
| 225 | self.clock = PatchedClock() |
| 226 | self.bar = aggregator.ProgressBar(clock=self.clock) |
| 227 | self.addCleanup(self.bar.cleanup) |
| 228 | @@ -662,6 +662,7 @@ |
| 229 | self.assertFalse(self.bar.visible) |
| 230 | self.assertFalse(self.bar.launcher.progress_visible) |
| 231 | |
| 232 | + @skipTest('Inhibitor is disabled to prevent bug #737620') |
| 233 | @defer.inlineCallbacks |
| 234 | def test_progress_made_inhibits_logout_suspend(self): |
| 235 | """Suspend and logout are inhibited when the progressbar is shown.""" |
| 236 | @@ -670,6 +671,7 @@ |
| 237 | inhibitor = yield self.bar.inhibitor_defer |
| 238 | self.assertEqual(inhibitor.flags, expected) |
| 239 | |
| 240 | + @skipTest('Inhibitor is disabled to prevent bug #737620') |
| 241 | @defer.inlineCallbacks |
| 242 | def test_completed_uninhibits_logout_suspend(self): |
| 243 | """Suspend and logout are uninhibited when all has completed.""" |
| 244 | @@ -1291,7 +1293,6 @@ |
| 245 | self.patch(aggregator, "ToggleableNotification", |
| 246 | FakeNotificationSingleton()) |
| 247 | self.patch(aggregator, "UbuntuOneLauncher", FakeLauncher) |
| 248 | - self.patch(aggregator.session, "Inhibitor", FakeInhibitor) |
| 249 | clock = PatchedClock() |
| 250 | self.status_frontend = aggregator.StatusFrontend(clock=clock) |
| 251 | self.aggregator = self.status_frontend.aggregator |
| 252 | @@ -1314,6 +1315,21 @@ |
| 253 | self.assertEqual({}, self.aggregator.to_do) |
| 254 | self.assertIdentical(None, self.aggregator.queue_done_timer) |
| 255 | |
| 256 | + def test_register_progress_listener(self): |
| 257 | + """Check that register listener handles properly additions.""" |
| 258 | + |
| 259 | + def fake_callback(): |
| 260 | + """Do nothing.""" |
| 261 | + |
| 262 | + self.aggregator.register_progress_listener(fake_callback) |
| 263 | + self.assertEqual(len(self.aggregator.progress_listeners), 1) |
| 264 | + |
| 265 | + def test_register_progress_listener_fail(self): |
| 266 | + """Check that register listener fails with not Callable objects.""" |
| 267 | + self.assertRaises(TypeError, |
| 268 | + self.aggregator.register_progress_listener, []) |
| 269 | + self.assertEqual(len(self.aggregator.progress_listeners), 0) |
| 270 | + |
| 271 | def assertMiscCommandQueued(self, fc): |
| 272 | """Assert that some command was queued.""" |
| 273 | self.assertEqual(len(self.aggregator.to_do), 1) |
| 274 | |
| 275 | === modified file 'tests/syncdaemon/test_main.py' |
| 276 | --- tests/syncdaemon/test_main.py 2012-09-19 17:23:02 +0000 |
| 277 | +++ tests/syncdaemon/test_main.py 2012-10-02 20:28:28 +0000 |
| 278 | @@ -70,19 +70,6 @@ |
| 279 | return defer.succeed(None) |
| 280 | |
| 281 | |
| 282 | -class FakedSyncMenu(object): |
| 283 | - """Do nothing.""" |
| 284 | - |
| 285 | - def __init__(self, *args, **kwargs): |
| 286 | - self.arguments = (args, kwargs) |
| 287 | - |
| 288 | - def update_progress(self): |
| 289 | - """Do nothing.""" |
| 290 | - |
| 291 | - def start_timer(self): |
| 292 | - """Do nothing.""" |
| 293 | - |
| 294 | - |
| 295 | class MainTests(BaseTwistedTestCase): |
| 296 | """ Basic tests to check main.Main """ |
| 297 | |
| 298 | @@ -100,7 +87,6 @@ |
| 299 | self.patch(main_mod.event_logging, "get_listener", lambda *a: None) |
| 300 | # no status listener by default |
| 301 | self.patch(main_mod.status_listener, "get_listener", lambda *a: None) |
| 302 | - self.patch(main_mod.sync_menu, "UbuntuOneSyncMenu", FakedSyncMenu) |
| 303 | |
| 304 | self.handler = MementoHandler() |
| 305 | self.handler.setLevel(logging.DEBUG) |
| 306 | |
| 307 | === modified file 'tests/syncdaemon/test_status_listener.py' |
| 308 | --- tests/syncdaemon/test_status_listener.py 2012-04-09 20:07:05 +0000 |
| 309 | +++ tests/syncdaemon/test_status_listener.py 2012-10-02 20:28:28 +0000 |
| 310 | @@ -84,7 +84,7 @@ |
| 311 | callback(args) |
| 312 | |
| 313 | listener = Listener() |
| 314 | - setattr(listener, 'handle_'+event, listener._handle_event) |
| 315 | + setattr(listener, 'handle_' + event, listener._handle_event) |
| 316 | event_q.subscribe(listener) |
| 317 | return listener |
| 318 | |
| 319 | @@ -92,7 +92,7 @@ |
| 320 | class FakeStatusFrontend(object): |
| 321 | """A fake status frontend.""" |
| 322 | |
| 323 | - def __init__(self): |
| 324 | + def __init__(self, *args, **kwargs): |
| 325 | """Initialize this instance.""" |
| 326 | self.call_log = [] |
| 327 | |
| 328 | |
| 329 | === modified file 'ubuntuone/platform/__init__.py' |
| 330 | --- ubuntuone/platform/__init__.py 2012-08-06 19:18:57 +0000 |
| 331 | +++ ubuntuone/platform/__init__.py 2012-10-02 20:28:28 +0000 |
| 332 | @@ -33,11 +33,12 @@ |
| 333 | |
| 334 | from dirspec.utils import user_home |
| 335 | |
| 336 | -# very hackish way to avoid "import *" to satisfy pyflakes |
| 337 | -# and to avoid import ubuntuone.platform.X as source (it wont work) |
| 338 | - |
| 339 | +# define a platform string separate from sys.platform to be sent to |
| 340 | +# the server for metrics in ActionQueue.authenticate(). |
| 341 | if sys.platform == "win32": |
| 342 | platform = "win32" |
| 343 | +elif sys.platform == "darwin": |
| 344 | + platform = "darwin" |
| 345 | else: |
| 346 | platform = "linux" |
| 347 | |
| 348 | |
| 349 | === modified file 'ubuntuone/platform/filesystem_notifications/monitor/__init__.py' |
| 350 | --- ubuntuone/platform/filesystem_notifications/monitor/__init__.py 2012-08-28 07:58:50 +0000 |
| 351 | +++ ubuntuone/platform/filesystem_notifications/monitor/__init__.py 2012-10-02 20:28:28 +0000 |
| 352 | @@ -60,8 +60,8 @@ |
| 353 | ) |
| 354 | |
| 355 | FILEMONITOR_IDS = { |
| 356 | - DEFAULT_MONITOR: common.FilesystemMonitor, |
| 357 | - 'daemon': darwin.fsevents_daemon.FilesystemMonitor, |
| 358 | + DEFAULT_MONITOR: darwin.fsevents_daemon.FilesystemMonitor, |
| 359 | + 'macfsevents': common.FilesystemMonitor, |
| 360 | } |
| 361 | ACTIONS = darwin.fsevents_client.ACTIONS |
| 362 | else: |
| 363 | |
| 364 | === modified file 'ubuntuone/platform/messaging/linux.py' |
| 365 | --- ubuntuone/platform/messaging/linux.py 2012-04-30 17:33:18 +0000 |
| 366 | +++ ubuntuone/platform/messaging/linux.py 2012-10-02 20:28:28 +0000 |
| 367 | @@ -34,6 +34,7 @@ |
| 368 | # of them are available, we should fall back to silently discarding |
| 369 | # messages. |
| 370 | |
| 371 | +import logging |
| 372 | import sys |
| 373 | |
| 374 | indicate = None |
| 375 | @@ -61,6 +62,8 @@ |
| 376 | |
| 377 | from ubuntuone.status.messaging import AbstractMessaging |
| 378 | |
| 379 | +logger = logging.getLogger("ubuntuone.platform.Messaging") |
| 380 | + |
| 381 | |
| 382 | def open_volumes(): |
| 383 | """Open the control panel to the shares tab.""" |
| 384 | @@ -69,8 +72,10 @@ |
| 385 | |
| 386 | def _server_callback(the_indicator, message_time=None): |
| 387 | """Open the control panel to the shares tab.""" |
| 388 | - glib.spawn_command_line_async('ubuntuone-installer') |
| 389 | -# pylint: enable=W0613 |
| 390 | + try: |
| 391 | + glib.spawn_command_line_async('ubuntuone-control-panel-qt') |
| 392 | + except glib.GError as e: |
| 393 | + logger.warning('Failed to open the control panel: %s' % e) |
| 394 | |
| 395 | |
| 396 | class Messaging(AbstractMessaging): |
| 397 | |
| 398 | === modified file 'ubuntuone/platform/sync_menu/common.py' |
| 399 | --- ubuntuone/platform/sync_menu/common.py 2012-09-18 16:29:22 +0000 |
| 400 | +++ ubuntuone/platform/sync_menu/common.py 2012-10-02 20:28:28 +0000 |
| 401 | @@ -26,18 +26,13 @@ |
| 402 | # do not wish to do so, delete this exception statement from your |
| 403 | # version. If you delete this exception statement from all source |
| 404 | # files in the program, then also delete it here. |
| 405 | -"""Use SyncMenu lib to integrate U1 with the Systray Sync Icon.""" |
| 406 | +"""Dummy implementation of sync_menu lib for win32 and darwin.""" |
| 407 | |
| 408 | |
| 409 | class UbuntuOneSyncMenu(object): |
| 410 | """Integrate U1 with the Ubuntu Sync Menu.""" |
| 411 | - |
| 412 | - def __init__(self, *args): |
| 413 | - self.transfers = DummyTransfersMenu() |
| 414 | - |
| 415 | - |
| 416 | -class DummyTransfersMenu(object): |
| 417 | - """Dummy Transfers Menu.""" |
| 418 | - |
| 419 | - def start_timer(self): |
| 420 | - """Empty start timer, this is not needed on windows.""" |
| 421 | + def __init__(self, status, syncdaemon_service): |
| 422 | + """Match #args of linux syncmenu and do nothing.""" |
| 423 | + |
| 424 | + def update_transfers(self): |
| 425 | + """Do nothing.""" |
| 426 | |
| 427 | === modified file 'ubuntuone/platform/sync_menu/linux.py' |
| 428 | --- ubuntuone/platform/sync_menu/linux.py 2012-09-21 12:39:52 +0000 |
| 429 | +++ ubuntuone/platform/sync_menu/linux.py 2012-10-02 20:28:28 +0000 |
| 430 | @@ -29,8 +29,12 @@ |
| 431 | """Use SyncMenu lib to integrate U1 with the Systray Sync Icon.""" |
| 432 | |
| 433 | import gettext |
| 434 | +import logging |
| 435 | +import time |
| 436 | import sys |
| 437 | import webbrowser |
| 438 | +from collections import OrderedDict |
| 439 | +from operator import itemgetter |
| 440 | |
| 441 | glib = None |
| 442 | try: |
| 443 | @@ -50,10 +54,12 @@ |
| 444 | use_syncmenu = True |
| 445 | except: |
| 446 | use_syncmenu = False |
| 447 | -from twisted.internet import reactor |
| 448 | |
| 449 | from ubuntuone.clientdefs import GETTEXT_PACKAGE |
| 450 | - |
| 451 | +from ubuntuone import status |
| 452 | + |
| 453 | + |
| 454 | +logger = logging.getLogger("ubuntuone.platform.SyncMenu") |
| 455 | |
| 456 | Q_ = lambda string: gettext.dgettext(GETTEXT_PACKAGE, string) |
| 457 | |
| 458 | @@ -63,6 +69,7 @@ |
| 459 | MORE_STORAGE = Q_("Get More Space") |
| 460 | GET_HELP = Q_("Get Help on the Web") |
| 461 | |
| 462 | +DELAY_BETWEEN_UPDATES = 3 |
| 463 | UBUNTUONE_LINK = u'https://one.ubuntu.com/' |
| 464 | DASHBOARD = UBUNTUONE_LINK + u'dashboard/' |
| 465 | HELP_LINK = UBUNTUONE_LINK + u'support/' |
| 466 | @@ -72,11 +79,12 @@ |
| 467 | class UbuntuOneSyncMenuLinux(object): |
| 468 | """Integrate U1 with the Ubuntu Sync Menu.""" |
| 469 | |
| 470 | - def __init__(self, main): |
| 471 | + def __init__(self, status, syncdaemon_service): |
| 472 | """Initialize menu.""" |
| 473 | - self._main = main |
| 474 | - self.status_listener = main.status_listener |
| 475 | + self._syncdaemon_service = syncdaemon_service |
| 476 | self._paused = False |
| 477 | + self.timer = None |
| 478 | + self.next_update = time.time() |
| 479 | self.root_menu = Dbusmenu.Menuitem() |
| 480 | |
| 481 | self.open_u1 = Dbusmenu.Menuitem() |
| 482 | @@ -86,7 +94,7 @@ |
| 483 | self.go_to_web.property_set(Dbusmenu.MENUITEM_PROP_LABEL, |
| 484 | GO_TO_WEB) |
| 485 | |
| 486 | - self.transfers = TransfersMenu(self.status_listener) |
| 487 | + self.transfers = TransfersMenu(status) |
| 488 | self.transfers.property_set(Dbusmenu.MENUITEM_PROP_LABEL, |
| 489 | TRANSFERS) |
| 490 | |
| 491 | @@ -121,22 +129,21 @@ |
| 492 | self.app.set_menu(self.server) |
| 493 | self.app.connect("notify::paused", self.change_sync_status) |
| 494 | |
| 495 | - def start_timer(self): |
| 496 | - """Start the transfers timer.""" |
| 497 | - self.transfers.start_timer() |
| 498 | - |
| 499 | def change_sync_status(self, *args): |
| 500 | """Triggered when the sync status is changed fromm the menu.""" |
| 501 | if self._paused: |
| 502 | - self._main.external.connect() |
| 503 | + self._syncdaemon_service.connect() |
| 504 | self._paused = False |
| 505 | else: |
| 506 | - self._main.external.disconnect() |
| 507 | + self._syncdaemon_service.disconnect() |
| 508 | self._paused = True |
| 509 | |
| 510 | def open_control_panel(self, *args): |
| 511 | """Open the Ubuntu One Control Panel.""" |
| 512 | - glib.spawn_command_line_async('ubuntuone-installer') |
| 513 | + try: |
| 514 | + glib.spawn_command_line_async('ubuntuone-control-panel-qt') |
| 515 | + except glib.GError as e: |
| 516 | + logger.warning('Failed to open the control panel: %s.' % e) |
| 517 | |
| 518 | def open_go_to_web(self, *args): |
| 519 | """Open the Ubunto One Help Page""" |
| 520 | @@ -150,36 +157,49 @@ |
| 521 | """Open the Ubunto One Help Page""" |
| 522 | webbrowser.open(GET_STORAGE_LINK) |
| 523 | |
| 524 | + def _timeout(self, result): |
| 525 | + """The aggregating timer has expired, so update the UI.""" |
| 526 | + self.next_update = int(time.time()) + DELAY_BETWEEN_UPDATES |
| 527 | + self.transfers.update_progress() |
| 528 | + self.timer = None |
| 529 | + |
| 530 | + def update_transfers(self): |
| 531 | + """Set up a timer if there isn't one ticking and update the ui.""" |
| 532 | + if not self.timer: |
| 533 | + logger.debug("Updating Transfers.") |
| 534 | + delay = int(max(0, min(DELAY_BETWEEN_UPDATES, |
| 535 | + self.next_update - time.time()))) |
| 536 | + self.timer = status.aggregator.Timer(delay) |
| 537 | + self.timer.addCallback(self._timeout) |
| 538 | + |
| 539 | |
| 540 | class TransfersMenu(Dbusmenu.Menuitem): |
| 541 | """Menu that handles the recent and current transfers.""" |
| 542 | |
| 543 | - def __init__(self, listener): |
| 544 | + def __init__(self, status_frontend): |
| 545 | super(TransfersMenu, self).__init__() |
| 546 | - self.listener = listener |
| 547 | + self.status_frontend = status_frontend |
| 548 | self.uploading = {} |
| 549 | self.previous_transfers = [] |
| 550 | self._transfers_items = {} |
| 551 | self._uploading_items = {} |
| 552 | |
| 553 | - def start_timer(self): |
| 554 | - """Trigger an update in one second.""" |
| 555 | - self.update_progress() |
| 556 | - reactor.callLater(3, self.start_timer) |
| 557 | - |
| 558 | def update_progress(self): |
| 559 | """Update the list of recent transfers and current transfers.""" |
| 560 | - current_transfers = self.listener.status_frontend.recent_transfers() |
| 561 | - uploading_data = {} |
| 562 | - for filename, size, written in \ |
| 563 | - self.listener.status_frontend.files_uploading(): |
| 564 | + recent_transfers = self.status_frontend.recent_transfers() |
| 565 | + current_transfers = self.status_frontend.files_uploading() |
| 566 | + current_transfers.sort(key=itemgetter(2)) |
| 567 | + current_transfers.reverse() |
| 568 | + uploading_data = OrderedDict() |
| 569 | + for filename, size, written in current_transfers: |
| 570 | uploading_data[filename] = (size, written) |
| 571 | |
| 572 | temp_transfers = {} |
| 573 | - if current_transfers != self.previous_transfers: |
| 574 | + if recent_transfers != self.previous_transfers: |
| 575 | + logger.debug("Update recent transfers with: %r", recent_transfers) |
| 576 | for item_transfer in self._transfers_items: |
| 577 | self.child_delete(self._transfers_items[item_transfer]) |
| 578 | - for item in current_transfers: |
| 579 | + for item in recent_transfers: |
| 580 | recent_file = Dbusmenu.Menuitem() |
| 581 | recent_file.property_set(Dbusmenu.MENUITEM_PROP_LABEL, |
| 582 | item) |
| 583 | @@ -197,6 +217,8 @@ |
| 584 | upload_item.property_set_int( |
| 585 | SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE, |
| 586 | percentage) |
| 587 | + logger.debug("Current transfer %s progress update: %r", |
| 588 | + item, percentage) |
| 589 | items_added += 1 |
| 590 | else: |
| 591 | self.child_delete(self._uploading_items[item]) |
| 592 | @@ -216,6 +238,7 @@ |
| 593 | uploading_file.property_set_int( |
| 594 | SyncMenu.PROGRESS_MENUITEM_PROP_PERCENT_DONE, |
| 595 | percentage) |
| 596 | + logger.debug("Current transfer %s created", item) |
| 597 | self.child_append(uploading_file) |
| 598 | self._uploading_items[item] = uploading_file |
| 599 | items_added += 1 |
| 600 | @@ -227,8 +250,7 @@ |
| 601 | def __init__(self, *args, **kwargs): |
| 602 | """Initialize menu.""" |
| 603 | |
| 604 | - def start_timer(self): |
| 605 | + def update_transfers(self): |
| 606 | """Do nothing.""" |
| 607 | |
| 608 | - |
| 609 | UbuntuOneSyncMenu = UbuntuOneSyncMenuLinux if use_syncmenu else DummySyncMenu |
| 610 | |
| 611 | === modified file 'ubuntuone/status/aggregator.py' |
| 612 | --- ubuntuone/status/aggregator.py 2012-08-16 12:00:12 +0000 |
| 613 | +++ ubuntuone/status/aggregator.py 2012-10-02 20:28:28 +0000 |
| 614 | @@ -33,7 +33,7 @@ |
| 615 | import itertools |
| 616 | import operator |
| 617 | import os |
| 618 | -from collections import deque |
| 619 | +from collections import deque, Callable |
| 620 | |
| 621 | import gettext |
| 622 | |
| 623 | @@ -41,7 +41,10 @@ |
| 624 | |
| 625 | from ubuntuone.clientdefs import GETTEXT_PACKAGE |
| 626 | from ubuntuone.status.logger import logger |
| 627 | -from ubuntuone.platform import session, notification |
| 628 | +from ubuntuone.platform import ( |
| 629 | + notification, |
| 630 | + sync_menu |
| 631 | +) |
| 632 | from ubuntuone.platform.messaging import Messaging |
| 633 | from ubuntuone.platform.launcher import UbuntuOneLauncher, DummyLauncher |
| 634 | |
| 635 | @@ -529,7 +532,6 @@ |
| 636 | progress = 0.0 |
| 637 | updates_delay = 0.1 |
| 638 | timer = None |
| 639 | - inhibitor_defer = None |
| 640 | |
| 641 | def __init__(self, clock=reactor): |
| 642 | """Initialize this instance.""" |
| 643 | @@ -559,9 +561,6 @@ |
| 644 | self.visible = True |
| 645 | self.launcher.show_progressbar() |
| 646 | logger.debug("progressbar shown") |
| 647 | - if self.inhibitor_defer is None: |
| 648 | - self.inhibitor_defer = session.inhibit_logout_suspend( |
| 649 | - FILE_SYNC_IN_PROGRESS) |
| 650 | if not self.timer: |
| 651 | self.timer = Timer(self.updates_delay, clock=self.clock) |
| 652 | self.timer.addCallback(self._timeout) |
| 653 | @@ -572,14 +571,6 @@ |
| 654 | self.visible = False |
| 655 | self.launcher.hide_progressbar() |
| 656 | logger.debug("progressbar hidden") |
| 657 | - if self.inhibitor_defer is not None: |
| 658 | - |
| 659 | - def inhibitor_callback(inhibitor): |
| 660 | - """The inhibitor was found, so cancel it.""" |
| 661 | - self.inhibitor_defer = None |
| 662 | - return inhibitor.cancel() |
| 663 | - |
| 664 | - self.inhibitor_defer.addCallback(inhibitor_callback) |
| 665 | |
| 666 | |
| 667 | class FinalStatusBubble(object): |
| 668 | @@ -625,6 +616,7 @@ |
| 669 | self.progress = {} |
| 670 | self.to_do = {} |
| 671 | self.recent_transfers = deque(maxlen=5) |
| 672 | + self.progress_listeners = [] |
| 673 | |
| 674 | def get_notification(self): |
| 675 | """Create a new toggleable notification object.""" |
| 676 | @@ -655,6 +647,13 @@ |
| 677 | self.to_do = {} |
| 678 | # pylint: enable=W0201 |
| 679 | |
| 680 | + def register_progress_listener(self, listener): |
| 681 | + """Register a callable object to be notified.""" |
| 682 | + if isinstance(listener, Callable): |
| 683 | + self.progress_listeners.append(listener) |
| 684 | + else: |
| 685 | + raise TypeError("Callable object expected.") |
| 686 | + |
| 687 | def get_discovery_message(self): |
| 688 | """Get the text for the discovery bubble.""" |
| 689 | lines = [] |
| 690 | @@ -716,6 +715,8 @@ |
| 691 | progress = float( |
| 692 | sum(self.progress.values())) / sum(self.to_do.values()) |
| 693 | self.progress_bar.set_progress(progress) |
| 694 | + for listener in self.progress_listeners: |
| 695 | + listener() |
| 696 | |
| 697 | def download_started(self, command): |
| 698 | """A download just started.""" |
| 699 | @@ -801,13 +802,25 @@ |
| 700 | class StatusFrontend(object): |
| 701 | """Frontend for the status aggregator, used by the StatusListener.""" |
| 702 | |
| 703 | - def __init__(self, clock=reactor): |
| 704 | + def __init__(self, clock=reactor, service=None): |
| 705 | """Initialize this instance.""" |
| 706 | self.aggregator = StatusAggregator(clock=clock) |
| 707 | self.notification = self.aggregator.get_notification() |
| 708 | self.messaging = Messaging() |
| 709 | self.quota_timer = None |
| 710 | |
| 711 | + self.syncdaemon_service = service |
| 712 | + self.sync_menu = None |
| 713 | + self.start_sync_menu() |
| 714 | + |
| 715 | + def start_sync_menu(self): |
| 716 | + """Create the sync menu and register the progress listener.""" |
| 717 | + if self.syncdaemon_service is not None: |
| 718 | + self.sync_menu = sync_menu.UbuntuOneSyncMenu(self, |
| 719 | + self.syncdaemon_service) |
| 720 | + self.aggregator.register_progress_listener( |
| 721 | + self.sync_menu.update_transfers) |
| 722 | + |
| 723 | def recent_transfers(self): |
| 724 | """Return a tuple with the recent transfers paths.""" |
| 725 | return list(self.aggregator.recent_transfers) |
| 726 | |
| 727 | === modified file 'ubuntuone/syncdaemon/main.py' |
| 728 | --- ubuntuone/syncdaemon/main.py 2012-09-19 17:23:02 +0000 |
| 729 | +++ ubuntuone/syncdaemon/main.py 2012-10-02 20:28:28 +0000 |
| 730 | @@ -48,10 +48,7 @@ |
| 731 | volume_manager, |
| 732 | ) |
| 733 | from ubuntuone import syncdaemon, clientdefs |
| 734 | -from ubuntuone.platform import ( |
| 735 | - event_logging, |
| 736 | - sync_menu, |
| 737 | -) |
| 738 | +from ubuntuone.platform import event_logging |
| 739 | from ubuntuone.syncdaemon import status_listener |
| 740 | from ubuntuone.syncdaemon.interaction_interfaces import SyncdaemonService |
| 741 | from ubuntuone.syncdaemon.states import StateManager, QueueManager |
| 742 | @@ -159,14 +156,6 @@ |
| 743 | self.mark = task.LoopingCall(self.log_mark) |
| 744 | self.mark.start(mark_interval) |
| 745 | |
| 746 | - self.sync_menu = None |
| 747 | - self.start_sync_menu() |
| 748 | - |
| 749 | - def start_sync_menu(self): |
| 750 | - """Create the sync menu and run the loop.""" |
| 751 | - self.sync_menu = sync_menu.UbuntuOneSyncMenu(self) |
| 752 | - self.sync_menu.start_timer() |
| 753 | - |
| 754 | def start_event_logger(self): |
| 755 | """Start the event logger if it's available for this platform.""" |
| 756 | self.eventlog_listener = event_logging.get_listener(self.fs, self.vm) |
| 757 | @@ -176,7 +165,8 @@ |
| 758 | |
| 759 | def start_status_listener(self): |
| 760 | """Start the status listener if it is configured to start.""" |
| 761 | - self.status_listener = status_listener.get_listener(self.fs, self.vm) |
| 762 | + self.status_listener = status_listener.get_listener(self.fs, self.vm, |
| 763 | + self.external) |
| 764 | # subscribe to EQ, to be unsubscribed in shutdown |
| 765 | if self.status_listener: |
| 766 | self.event_q.subscribe(self.status_listener) |
| 767 | |
| 768 | === modified file 'ubuntuone/syncdaemon/status_listener.py' |
| 769 | --- ubuntuone/syncdaemon/status_listener.py 2012-08-10 12:49:46 +0000 |
| 770 | +++ ubuntuone/syncdaemon/status_listener.py 2012-10-02 20:28:28 +0000 |
| 771 | @@ -48,10 +48,10 @@ |
| 772 | return True |
| 773 | |
| 774 | |
| 775 | -def get_listener(fsm, vm): |
| 776 | +def get_listener(fsm, vm, syncdaemon_service=None): |
| 777 | """Return an instance of the status listener, or None if turned off.""" |
| 778 | if should_start_listener(): |
| 779 | - status_frontend = StatusFrontend() |
| 780 | + status_frontend = StatusFrontend(service=syncdaemon_service) |
| 781 | return StatusListener(fsm, vm, status_frontend) |
| 782 | else: |
| 783 | return None |

