Merge lp:~charlesk/keeper/upate-status-properties-pt-1 into lp:keeper

Proposed by Charles Kerr
Status: Merged
Merge reported by: Charles Kerr
Merged at revision: not available
Proposed branch: lp:~charlesk/keeper/upate-status-properties-pt-1
Merge into: lp:keeper
Diff against target: 373 lines (+71/-77)
7 files modified
src/qdbus-stubs/com.canonical.keeper.User.xml (+13/-15)
src/service/backup-choices.cpp (+8/-18)
tests/com_canonical_keeper.py (+42/-29)
tests/dbusmock/keeper-template-test.cpp (+0/-3)
tests/unit/metadata-providers/user-dirs-test.cpp (+1/-3)
tests/unit/tar/keeper-tar-create-test.cpp (+0/-1)
tests/utils/keeper-dbusmock-fixture.h (+7/-8)
To merge this branch: bzr merge lp:~charlesk/keeper/upate-status-properties-pt-1
Reviewer Review Type Date Requested Status
Xavi Garcia (community) Approve
Unity API Team Pending
Review via email: mp+301424@code.launchpad.net

Commit message

Update what properties are provided by the com.canonical.keeper.User methods.

Description of the change

Update what properties are provided by the com.canonical.keeper.User methods.

The low-hanging fruit from https://trello.com/c/dCHzIxCG/58-com-canonical-keeper-user-should-support-design-s-specs.

We don't have anything implementing the state property maps in the service itself, so the card can't be completed yet. But now I can at least test the dbusmock template for an error state when the helper errors out :)

To post a comment you must log in.
Revision history for this message
Xavi Garcia (xavi-garcia-mena) wrote :

I'm not sure if after landing this I will have conflicts with my branch:
https://code.launchpad.net/~xavi-garcia-mena/keeper/using-cwd-and-user-startbackup/+merge/301496

It looks good and your branch was first, so... let's merge it. :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/qdbus-stubs/com.canonical.keeper.User.xml'
--- src/qdbus-stubs/com.canonical.keeper.User.xml 2016-07-07 21:42:19 +0000
+++ src/qdbus-stubs/com.canonical.keeper.User.xml 2016-07-28 19:50:41 +0000
@@ -14,8 +14,7 @@
14 as a map from opaque backup keys to key/value pairs14 as a map from opaque backup keys to key/value pairs
15 of the choice’s properties,15 of the choice’s properties,
16 including a ‘display-name’ string,16 including a ‘display-name’ string,
17 ‘type’ string (‘application’, ‘system-data’, or ‘folder’)17 ‘type’ string (‘application’, ‘system-data’, or ‘folder’).</doc:para>
18 and, when possible, an ‘icon’ symbolic icon string.</doc:para>
19 <doc:para>The choices should be presented to the user,18 <doc:para>The choices should be presented to the user,
20 and the keys of those selected should be passed19 and the keys of those selected should be passed
21 to StartBackup().</doc:para>20 to StartBackup().</doc:para>
@@ -71,19 +70,18 @@
71 <doc:description>70 <doc:description>
72 <doc:para>Provides state information so the user interface can show71 <doc:para>Provides state information so the user interface can show
73 the progress of backup or restore tasks.</doc:para>72 the progress of backup or restore tasks.</doc:para>
74 <doc:para>State is a map of opaque backup keys to property maps,73 <doc:para>State is a map of opaque backup keys to property maps.
75 which will contain a 'display-name' string and 'action' int3274 The property maps include:
76 whose possible values are queued(0), saving(1),75 * 'action' (string): tells what the task is doing right now.
77 restoring(2), complete(3), stopped(4)</doc:para>76 Possible values are 'queued', 'saving', 'restoring',
78 <doc:para>Some property maps may also have an 'item' string77 'cancelled', 'failed', and 'complete'
79 and a 'percent-done' double [0..1.0].78 * 'display-name' (string): human-readable task name, e.g. "Pictures"
80 For example if these are set to (1, “Photos”, 0.36),79 * 'percent-done' (double): how much of this task is complete
81 the user interface could show “Backing up Photos (36%)”.80 * 'speed' (int32): bytes per second
82 Clients should gracefully handle missing properties;81 </doc:para>
83 eg a missing percent-done could instead show82 <doc:para>If a task's 'action' state is 'failed' the property map also includes:
84 “Backing up Photos”.</doc:para>83 * 'error' (string): a human-readable error message
85 <doc:para>A failed task's property map may also contain an 'error'84 </doc:para>
86 string if set by the backup helpers.</doc:para>
87 </doc:description>85 </doc:description>
88 </doc:doc>86 </doc:doc>
89 </property>87 </property>
9088
=== modified file 'src/service/backup-choices.cpp'
--- src/service/backup-choices.cpp 2016-07-07 14:04:38 +0000
+++ src/service/backup-choices.cpp 2016-07-28 19:50:41 +0000
@@ -59,12 +59,10 @@
59 //59 //
6060
61 const auto type_key = QStringLiteral("type");61 const auto type_key = QStringLiteral("type");
62 const auto icon_key = QStringLiteral("icon");
63 const auto system_data_str = QStringLiteral("system-data");62 const auto system_data_str = QStringLiteral("system-data");
64 {63 {
65 Metadata m(generate_new_uuid(), "System Data"); // FIXME: how to i18n in a Qt DBus service?64 Metadata m(generate_new_uuid(), "System Data"); // FIXME: how to i18n in a Qt DBus service?
66 m.set_property(type_key, system_data_str);65 m.set_property(type_key, system_data_str);
67 m.set_property(icon_key, QStringLiteral("folder-system"));
68 ret.push_back(m);66 ret.push_back(m);
69 }67 }
7068
@@ -129,10 +127,6 @@
129 if (version != QJsonValue::Undefined)127 if (version != QJsonValue::Undefined)
130 m.set_property(version_key, version.toString());128 m.set_property(version_key, version.toString());
131129
132 const auto icon = o[icon_key];
133 if (icon != QJsonValue::Undefined)
134 m.set_property(icon_key, icon.toString());
135
136 ret.push_back(m);130 ret.push_back(m);
137 }131 }
138 }132 }
@@ -142,22 +136,19 @@
142 // XDG User Directories136 // XDG User Directories
143 //137 //
144138
145 const struct {139 const std::array<QStandardPaths::StandardLocation,4> standard_locations = {
146 QStandardPaths::StandardLocation location;140 QStandardPaths::DocumentsLocation,
147 QString icon;141 QStandardPaths::MoviesLocation,
148 } standard_locations[] = {142 QStandardPaths::PicturesLocation,
149 { QStandardPaths::DocumentsLocation, QStringLiteral("folder-documents") },143 QStandardPaths::MusicLocation
150 { QStandardPaths::MoviesLocation, QStringLiteral("folder-movies") },
151 { QStandardPaths::PicturesLocation, QStringLiteral("folder-pictures") },
152 { QStandardPaths::MusicLocation, QStringLiteral("folder-music") }
153 };144 };
154145
155 const auto path_key = QStringLiteral("path");146 const auto path_key = QStringLiteral("path");
156 const auto user_folder_str = QStringLiteral("folder");147 const auto user_folder_str = QStringLiteral("folder");
157 for (const auto& sl : standard_locations)148 for (const auto& location : standard_locations)
158 {149 {
159 const auto name = QStandardPaths::displayName(sl.location);150 const auto name = QStandardPaths::displayName(location);
160 const auto locations = QStandardPaths::standardLocations(sl.location);151 const auto locations = QStandardPaths::standardLocations(location);
161 if (locations.empty())152 if (locations.empty())
162 {153 {
163 qWarning() << "unable to find path for" << name;154 qWarning() << "unable to find path for" << name;
@@ -168,7 +159,6 @@
168 Metadata m(keystr, name);159 Metadata m(keystr, name);
169 m.set_property(path_key, locations.front());160 m.set_property(path_key, locations.front());
170 m.set_property(type_key, user_folder_str);161 m.set_property(type_key, user_folder_str);
171 m.set_property(icon_key, sl.icon);
172 ret.push_back(m);162 ret.push_back(m);
173 }163 }
174 }164 }
175165
=== modified file 'tests/com_canonical_keeper.py'
--- tests/com_canonical_keeper.py 2016-07-21 04:21:47 +0000
+++ tests/com_canonical_keeper.py 2016-07-28 19:50:41 +0000
@@ -5,6 +5,7 @@
5import socket5import socket
6import subprocess6import subprocess
7import sys7import sys
8import time
8from dbusmock import OBJECT_MANAGER_IFACE, mockobject9from dbusmock import OBJECT_MANAGER_IFACE, mockobject
9from gi.repository import GLib10from gi.repository import GLib
1011
@@ -36,17 +37,19 @@
36MAIN_OBJ = SERVICE_PATH37MAIN_OBJ = SERVICE_PATH
37SYSTEM_BUS = False38SYSTEM_BUS = False
3839
39ACTION_QUEUED = 040ACTION_QUEUED = 'queued'
40ACTION_SAVING = 141ACTION_SAVING = 'saving'
41ACTION_RESTORING = 242ACTION_RESTORING = 'restoring'
42ACTION_COMPLETE = 343ACTION_CANCELLED = 'cancelled'
43ACTION_STOPPED = 444ACTION_FAILED = 'failed'
45ACTION_COMPLETE = 'complete'
4446
47KEY_ACTION = 'action'
45KEY_CTIME = 'ctime'48KEY_CTIME = 'ctime'
46KEY_BLOB = 'blob-data'49KEY_BLOB = 'blob-data'
47KEY_HELPER = 'helper-exec'50KEY_HELPER = 'helper-exec'
48KEY_ICON = 'icon'
49KEY_NAME = 'display-name'51KEY_NAME = 'display-name'
52KEY_SPEED = 'speed'
50KEY_SIZE = 'size'53KEY_SIZE = 'size'
51KEY_SUBTYPE = 'subtype'54KEY_SUBTYPE = 'subtype'
52KEY_TYPE = 'type'55KEY_TYPE = 'type'
@@ -179,21 +182,9 @@
179def user_build_state(user):182def user_build_state(user):
180 """Returns a generated state dictionary.183 """Returns a generated state dictionary.
181184
182 State is a map of opaque backup keys to property maps,185 State is a map of opaque backup keys to property maps.
183 which will contain a 'display-name' string and 'action' int186 See the documentation for com.canonical.keeper.User's
184 whose possible values are queued(0), saving(1),187 State() method for more information.
185 restoring(2), complete(3), and stopped(4).
186
187 Some property maps may also have an 'item' string
188 and a 'percent-done' double [0..1.0].
189 For example if these are set to (1, "Photos", 0.36),
190 the user interface could show "Backing up Photos (36%)".
191 Clients should gracefully handle missing properties;
192 eg a missing percent-done could instead show
193 "Backing up Photos".
194
195 A failed task's property map may also contain an 'error'
196 string if set by the backup helpers.
197 """188 """
198189
199 tasks_states = {}190 tasks_states = {}
@@ -208,9 +199,9 @@
208 action = ACTION_RESTORING199 action = ACTION_RESTORING
209 elif uuid in user.remaining_tasks:200 elif uuid in user.remaining_tasks:
210 action = ACTION_QUEUED201 action = ACTION_QUEUED
211 else: # FIXME: 'ACTION_STOPPED' not handled yet202 else: # fixme: handle ACTION_CANCELLED, ACTION_FAILED
212 action = ACTION_COMPLETE203 action = ACTION_COMPLETE
213 task_state['action'] = dbus.Int32(action)204 task_state[KEY_ACTION] = dbus.String(action)
214205
215 # get the task's display-name206 # get the task's display-name
216 choice = user.backup_choices.get(uuid, None)207 choice = user.backup_choices.get(uuid, None)
@@ -221,7 +212,22 @@
221 display_name = choice.get(KEY_NAME, None)212 display_name = choice.get(KEY_NAME, None)
222 task_state[KEY_NAME] = dbus.String(display_name)213 task_state[KEY_NAME] = dbus.String(display_name)
223214
215 # speed
216 helper = mockobject.objects[HELPER_PATH]
217 if (uuid == user.current_task) and helper.work:
218 n_secs = 2
219 n_bytes = 0
220 too_old = time.time() - n_secs
221 for key in helper.work.bytes_per_second:
222 if key > too_old:
223 n_bytes += helper.work.bytes_per_second[key]
224 bytes_per_second = n_bytes / n_secs
225 else:
226 bytes_per_second = 0
227 task_state[KEY_SPEED] = dbus.Int32(bytes_per_second)
228
224 # FIXME: use a real percentage here229 # FIXME: use a real percentage here
230 # FIXME: handle ACTION_CANCELLED, ACTION_FAILED
225 if action == ACTION_COMPLETE:231 if action == ACTION_COMPLETE:
226 percent_done = dbus.Double(1.0)232 percent_done = dbus.Double(1.0)
227 elif action == ACTION_SAVING or action == ACTION_RESTORING:233 elif action == ACTION_SAVING or action == ACTION_RESTORING:
@@ -256,6 +262,7 @@
256 n_left = None262 n_left = None
257 sock = None263 sock = None
258 uuid = None264 uuid = None
265 bytes_per_second = None
259266
260267
261def helper_periodic_func(helper):268def helper_periodic_func(helper):
@@ -265,10 +272,15 @@
265272
266 # try to read a bit273 # try to read a bit
267 chunk = helper.work.sock.recv(4096*2)274 chunk = helper.work.sock.recv(4096*2)
268 if len(chunk):275 chunk_len = len(chunk)
276 if chunk_len:
269 helper.work.chunks.append(chunk)277 helper.work.chunks.append(chunk)
270 helper.work.n_left -= len(chunk)278 helper.work.n_left -= chunk_len
271 helper.log('got %s bytes; %s left' % (len(chunk), helper.work.n_left))279 key = int(time.time())
280 old_n_bytes = helper.work.bytes_per_second.get(key, 0)
281 new_n_bytes = old_n_bytes + chunk_len
282 helper.work.bytes_per_second[key] = new_n_bytes
283 helper.log('got %s bytes; %s left' % (chunk_len, helper.work.n_left))
272284
273 # cleanup if done285 # cleanup if done
274 done = helper.work.n_left <= 0286 done = helper.work.n_left <= 0
@@ -284,7 +296,7 @@
284 user.start_next_task(user)296 user.start_next_task(user)
285 helper.work = None297 helper.work = None
286298
287 if len(chunk) or done:299 if chunk_len or done:
288 user = mockobject.objects[USER_PATH]300 user = mockobject.objects[USER_PATH]
289 user.update_state_property(user)301 user.update_state_property(user)
290302
@@ -306,6 +318,7 @@
306 work.n_left = n_bytes318 work.n_left = n_bytes
307 work.sock = parent319 work.sock = parent
308 work.uuid = mockobject.objects[USER_PATH].current_task320 work.uuid = mockobject.objects[USER_PATH].current_task
321 work.bytes_per_second = {}
309 helper.work = work322 helper.work = work
310323
311 # start checking periodically324 # start checking periodically
@@ -325,7 +338,7 @@
325338
326def mock_add_backup_choice(mock, uuid, props):339def mock_add_backup_choice(mock, uuid, props):
327340
328 keys = [KEY_NAME, KEY_TYPE, KEY_SUBTYPE, KEY_ICON, KEY_HELPER]341 keys = [KEY_NAME, KEY_TYPE, KEY_SUBTYPE, KEY_HELPER]
329 if set(keys) != set(props.keys()):342 if set(keys) != set(props.keys()):
330 badarg('need props: %s got %s' % (keys, props.keys()))343 badarg('need props: %s got %s' % (keys, props.keys()))
331344
@@ -339,7 +352,7 @@
339352
340def mock_add_restore_choice(mock, uuid, props):353def mock_add_restore_choice(mock, uuid, props):
341354
342 keys = [KEY_NAME, KEY_TYPE, KEY_SUBTYPE, KEY_ICON, KEY_HELPER,355 keys = [KEY_NAME, KEY_TYPE, KEY_SUBTYPE, KEY_HELPER,
343 KEY_SIZE, KEY_CTIME, KEY_BLOB]356 KEY_SIZE, KEY_CTIME, KEY_BLOB]
344 if set(keys) != set(props.keys()):357 if set(keys) != set(props.keys()):
345 badarg('need props: %s got %s' % (keys, props.keys()))358 badarg('need props: %s got %s' % (keys, props.keys()))
346359
=== modified file 'tests/dbusmock/keeper-template-test.cpp'
--- tests/dbusmock/keeper-template-test.cpp 2016-07-27 10:13:02 +0000
+++ tests/dbusmock/keeper-template-test.cpp 2016-07-28 19:50:41 +0000
@@ -51,7 +51,6 @@
51 { KEY_NAME, QStringLiteral("some-name") },51 { KEY_NAME, QStringLiteral("some-name") },
52 { KEY_TYPE, QStringLiteral("some-type") },52 { KEY_TYPE, QStringLiteral("some-type") },
53 { KEY_SUBTYPE, QStringLiteral("some-subtype") },53 { KEY_SUBTYPE, QStringLiteral("some-subtype") },
54 { KEY_ICON, QStringLiteral("some-icon") },
55 { KEY_HELPER, QString::fromUtf8(FAKE_BACKUP_HELPER_EXEC) }54 { KEY_HELPER, QString::fromUtf8(FAKE_BACKUP_HELPER_EXEC) }
56 };55 };
5756
@@ -90,7 +89,6 @@
90 { KEY_NAME, QStringLiteral("some-name") },89 { KEY_NAME, QStringLiteral("some-name") },
91 { KEY_TYPE, QStringLiteral("some-type") },90 { KEY_TYPE, QStringLiteral("some-type") },
92 { KEY_SUBTYPE, QStringLiteral("some-subtype") },91 { KEY_SUBTYPE, QStringLiteral("some-subtype") },
93 { KEY_ICON, QStringLiteral("some-icon") },
94 { KEY_HELPER, QString::fromUtf8("/dev/null") },92 { KEY_HELPER, QString::fromUtf8("/dev/null") },
95 { KEY_SIZE, quint64(blob.size()) },93 { KEY_SIZE, quint64(blob.size()) },
96 { KEY_CTIME, quint64(time(nullptr)) },94 { KEY_CTIME, quint64(time(nullptr)) },
@@ -143,7 +141,6 @@
143 { KEY_NAME, QStringLiteral("Music") },141 { KEY_NAME, QStringLiteral("Music") },
144 { KEY_TYPE, QStringLiteral("folder") },142 { KEY_TYPE, QStringLiteral("folder") },
145 { KEY_SUBTYPE, sandbox.path() },143 { KEY_SUBTYPE, sandbox.path() },
146 { KEY_ICON, QStringLiteral("music-icon") },
147 { KEY_HELPER, QString::fromUtf8(FAKE_BACKUP_HELPER_EXEC) }144 { KEY_HELPER, QString::fromUtf8(FAKE_BACKUP_HELPER_EXEC) }
148 };145 };
149146
150147
=== modified file 'tests/unit/metadata-providers/user-dirs-test.cpp'
--- tests/unit/metadata-providers/user-dirs-test.cpp 2016-07-01 15:17:06 +0000
+++ tests/unit/metadata-providers/user-dirs-test.cpp 2016-07-28 19:50:41 +0000
@@ -70,13 +70,11 @@
7070
71 // confirm that choices has the advertised public properties71 // confirm that choices has the advertised public properties
72 const auto type_str = QStringLiteral("type");72 const auto type_str = QStringLiteral("type");
73 const auto icon_str = QStringLiteral("icon");
74 for(const auto& choice : choices)73 for(const auto& choice : choices)
75 {74 {
76 ASSERT_FALSE(choice.key().isEmpty());75 ASSERT_FALSE(choice.key().isEmpty());
77 ASSERT_FALSE(choice.display_name().isEmpty());76 ASSERT_FALSE(choice.display_name().isEmpty());
78 ASSERT_TRUE(choice.has_property(type_str));77 ASSERT_TRUE(choice.has_property(type_str));
79 EXPECT_TRUE(choice.has_property(icon_str));
80 }78 }
8179
82 // confirm that we have a system-data choice80 // confirm that we have a system-data choice
@@ -86,7 +84,7 @@
86 break;84 break;
87 ASSERT_TRUE(i != n);85 ASSERT_TRUE(i != n);
88 auto system_data = choices[i];86 auto system_data = choices[i];
89 EXPECT_TRUE(system_data.has_property(icon_str));87 EXPECT_TRUE(system_data.has_property(type_str));
9088
91 // confirm that we have user-dir choices89 // confirm that we have user-dir choices
92 std::set<QString> expected_user_dir_display_names = {90 std::set<QString> expected_user_dir_display_names = {
9391
=== modified file 'tests/unit/tar/keeper-tar-create-test.cpp'
--- tests/unit/tar/keeper-tar-create-test.cpp 2016-07-22 13:49:34 +0000
+++ tests/unit/tar/keeper-tar-create-test.cpp 2016-07-28 19:50:41 +0000
@@ -43,7 +43,6 @@
43 // tell keeper that's a backup choice43 // tell keeper that's a backup choice
44 const auto uuid = add_backup_choice(QMap<QString,QVariant>{44 const auto uuid = add_backup_choice(QMap<QString,QVariant>{
45 { KEY_NAME, QDir(in.path()).dirName() },45 { KEY_NAME, QDir(in.path()).dirName() },
46 { KEY_ICON, QStringLiteral("folder") },
47 { KEY_TYPE, QStringLiteral("folder") },46 { KEY_TYPE, QStringLiteral("folder") },
48 { KEY_SUBTYPE, in.path() },47 { KEY_SUBTYPE, in.path() },
49 { KEY_HELPER, QString::fromUtf8(KEEPER_TAR_CREATE_INVOKE) }48 { KEY_HELPER, QString::fromUtf8(KEEPER_TAR_CREATE_INVOKE) }
5049
=== modified file 'tests/utils/keeper-dbusmock-fixture.h'
--- tests/utils/keeper-dbusmock-fixture.h 2016-07-22 13:47:58 +0000
+++ tests/utils/keeper-dbusmock-fixture.h 2016-07-28 19:50:41 +0000
@@ -37,22 +37,21 @@
37#define KEY_CTIME "ctime"37#define KEY_CTIME "ctime"
38#define KEY_BLOB "blob-data"38#define KEY_BLOB "blob-data"
39#define KEY_HELPER "helper-exec"39#define KEY_HELPER "helper-exec"
40#define KEY_ICON "icon"
41#define KEY_NAME "display-name"40#define KEY_NAME "display-name"
42#define KEY_PERCENT "percent-done"41#define KEY_PERCENT "percent-done"
43#define KEY_SIZE "size"42#define KEY_SIZE "size"
43#define KEY_SPEED "speed"
44#define KEY_SUBTYPE "subtype"44#define KEY_SUBTYPE "subtype"
45#define KEY_TYPE "type"45#define KEY_TYPE "type"
46#define KEY_UUID "uuid"46#define KEY_UUID "uuid"
4747
48// FIXME: this should go in a shared header in include/48// FIXME: this should go in a shared header in include/
49enum49static constexpr char const * ACTION_QUEUED = {"queued"};
50{50static constexpr char const * ACTION_SAVING = {"saving"};
51 ACTION_QUEUED = 0,51static constexpr char const * ACTION_RESTORING = {"restoring"};
52 ACTION_SAVING = 1,52static constexpr char const * ACTION_CANCELLED = {"cancelled"};
53 ACTION_RESTORING = 2,53static constexpr char const * ACTION_FAILED = {"failed"};
54 ACTION_IDLE = 354static constexpr char const * ACTION_COMPLETE = {"complete"};
55};
5655
57using namespace QtDBusMock;56using namespace QtDBusMock;
58using namespace QtDBusTest;57using namespace QtDBusTest;

Subscribers

People subscribed via source and target branches

to all changes: