Merge lp:~michihenning/storage-framework/exception-marshaling into lp:storage-framework/devel

Proposed by Michi Henning
Status: Merged
Approved by: James Henstridge
Approved revision: 87
Merged at revision: 47
Proposed branch: lp:~michihenning/storage-framework/exception-marshaling
Merge into: lp:storage-framework/devel
Prerequisite: lp:~michihenning/storage-framework/upload-download-fixes
Diff against target: 3501 lines (+1403/-899)
27 files modified
demo/provider_test/provider-test.cpp (+13/-2)
include/unity/storage/internal/dbus_error.h (+32/-0)
include/unity/storage/provider/Exceptions.h (+187/-0)
include/unity/storage/provider/internal/Handler.h (+2/-0)
include/unity/storage/qt/client/Exceptions.h (+1/-1)
include/unity/storage/qt/client/internal/ItemBase.h (+1/-1)
include/unity/storage/qt/client/internal/remote_client/Handler.h (+19/-2)
include/unity/storage/qt/client/internal/remote_client/dbusmarshal.h (+20/-2)
src/provider/CMakeLists.txt (+1/-0)
src/provider/Exceptions.cpp (+157/-0)
src/provider/internal/Handler.cpp (+61/-14)
src/qt/client/Exceptions.cpp (+1/-1)
src/qt/client/internal/local_client/FolderImpl.cpp (+1/-1)
src/qt/client/internal/local_client/RootImpl.cpp (+12/-5)
src/qt/client/internal/local_client/RuntimeImpl.cpp (+1/-1)
src/qt/client/internal/remote_client/AccountImpl.cpp (+9/-0)
src/qt/client/internal/remote_client/FileImpl.cpp (+11/-9)
src/qt/client/internal/remote_client/FolderImpl.cpp (+0/-4)
src/qt/client/internal/remote_client/ItemImpl.cpp (+12/-5)
src/qt/client/internal/remote_client/RootImpl.cpp (+38/-21)
src/qt/client/internal/remote_client/RuntimeImpl.cpp (+0/-1)
src/qt/client/internal/remote_client/dbusmarshal.cpp (+102/-7)
tests/local-client/local-client_test.cpp (+37/-92)
tests/provider-ProviderInterface/CMakeLists.txt (+2/-0)
tests/provider-ProviderInterface/ProviderInterface_test.cpp (+10/-9)
tests/provider-ProviderInterface/TestProvider.cpp (+1/-0)
tests/remote-client/remote-client_test.cpp (+672/-721)
To merge this branch: bzr merge lp:~michihenning/storage-framework/exception-marshaling
Reviewer Review Type Date Requested Status
James Henstridge Approve
unity-api-1-bot continuous-integration Approve
Review via email: mp+302116@code.launchpad.net

Commit message

Added exception hierarchy for server side, plus marshaling/unmarshaling code
for exceptions. Some coverage in the remote client to show that this works
(but more coverage is needed).

Description of the change

Added exception hierarchy for server side, plus marshaling/unmarshaling code
for exceptions. Some coverage in the remote client to show that this works
(but more coverage is needed).

To post a comment you must log in.
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :

FAILED: Continuous integration, rev:84
https://jenkins.canonical.com/unity-api-1/job/lp-storage-framework-ci/50/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build/292/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/298
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=vivid+overlay/227
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=xenial+overlay/227
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=yakkety/227
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/156/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/156/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/156/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/156/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/156/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/156/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/156/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/156/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/156/console

Click here to trigger a rebuild:
https://jenkins.canonical.com/unity-api-1/job/lp-storage-framework-ci/50/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
James Henstridge (jamesh) wrote :

This looks pretty good. I've left comments about a few minor issues inline.

review: Needs Fixing
85. By Michi Henning

Merged devel and resolved conflicts.

86. By Michi Henning

Addressed review comments from James.

Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :

FAILED: Continuous integration, rev:86
https://jenkins.canonical.com/unity-api-1/job/lp-storage-framework-ci/59/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build/343/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/349
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=vivid+overlay/271
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=xenial+overlay/271
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=yakkety/271
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/201
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/201/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/201
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/201/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/201/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/201
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/201/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/201
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/201/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/201
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/201/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/201
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/201/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/201
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/201/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/201
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/201/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/unity-api-1/job/lp-storage-framework-ci/59/rebuild

review: Needs Fixing (continuous-integration)
87. By Michi Henning

Instrumented failing test.

Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :

PASSED: Continuous integration, rev:87
https://jenkins.canonical.com/unity-api-1/job/lp-storage-framework-ci/63/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build/347
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/353
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=vivid+overlay/274
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=xenial+overlay/274
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=yakkety/274
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/204
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/204/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/204
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/204/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/204
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/204/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/204
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/204/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/204
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/204/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/204
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/204/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/204
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/204/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/204
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/204/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/204
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/204/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/unity-api-1/job/lp-storage-framework-ci/63/rebuild

review: Approve (continuous-integration)
Revision history for this message
James Henstridge (jamesh) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'demo/provider_test/provider-test.cpp'
2--- demo/provider_test/provider-test.cpp 2016-07-22 03:48:19 +0000
3+++ demo/provider_test/provider-test.cpp 2016-08-11 02:49:47 +0000
4@@ -17,6 +17,7 @@
5 */
6
7 #include <unity/storage/provider/DownloadJob.h>
8+#include <unity/storage/provider/Exceptions.h>
9 #include <unity/storage/provider/ProviderBase.h>
10 #include <unity/storage/provider/Server.h>
11 #include <unity/storage/provider/TempfileUploadJob.h>
12@@ -193,7 +194,12 @@
13 Item metadata{"child_id", "root_id", "Child", "etag", ItemType::file, {}};
14 return make_ready_future<Item>(metadata);
15 }
16- return make_exceptional_future<Item>(runtime_error("no such file"));
17+ else if (item_id == "child_folder_id")
18+ {
19+ Item metadata{"child_folder_id", "root_id", "Child_Folder", "etag", ItemType::folder, {}};
20+ return make_ready_future<Item>(metadata);
21+ }
22+ return make_exceptional_future<Item>(NotExistsException("metadata(): no such item: " + item_id, item_id));
23 }
24
25 boost::future<Item> MyProvider::create_folder(
26@@ -234,7 +240,12 @@
27
28 unique_ptr<DownloadJob> job(new MyDownloadJob(make_job_id()));
29 const char contents[] = "Hello world";
30- write(job->write_socket(), contents, sizeof(contents));
31+ if (write(job->write_socket(), contents, sizeof(contents)) != sizeof(contents))
32+ {
33+ ResourceException e("download(): write failed", errno);
34+ job->report_error(make_exception_ptr(e));
35+ return make_exceptional_future<unique_ptr<DownloadJob>>(e);
36+ }
37 job->report_complete();
38 return make_ready_future(std::move(job));
39 }
40
41=== added file 'include/unity/storage/internal/dbus_error.h'
42--- include/unity/storage/internal/dbus_error.h 1970-01-01 00:00:00 +0000
43+++ include/unity/storage/internal/dbus_error.h 2016-08-11 02:49:47 +0000
44@@ -0,0 +1,32 @@
45+/*
46+ * Copyright (C) 2016 Canonical Ltd
47+ *
48+ * This program is free software: you can redistribute it and/or modify
49+ * it under the terms of the GNU Lesser General Public License version 3 as
50+ * published by the Free Software Foundation.
51+ *
52+ * This program is distributed in the hope that it will be useful,
53+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
54+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
55+ * GNU Lesser General Public License for more details.
56+ *
57+ * You should have received a copy of the GNU Lesser General Public License
58+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
59+ *
60+ * Authors: Michi Henning <michi.henning@canonical.com>
61+ */
62+
63+#pragma once
64+
65+namespace unity
66+{
67+namespace storage
68+{
69+namespace internal
70+{
71+
72+constexpr char DBUS_ERROR_PREFIX[] = "com.canonical.StorageFramework.";
73+
74+} // namespace internal
75+} // namespace storage
76+} // namespace unity
77
78=== added file 'include/unity/storage/provider/Exceptions.h'
79--- include/unity/storage/provider/Exceptions.h 1970-01-01 00:00:00 +0000
80+++ include/unity/storage/provider/Exceptions.h 2016-08-11 02:49:47 +0000
81@@ -0,0 +1,187 @@
82+/*
83+ * Copyright (C) 2016 Canonical Ltd
84+ *
85+ * This program is free software: you can redistribute it and/or modify
86+ * it under the terms of the GNU Lesser General Public License version 3 as
87+ * published by the Free Software Foundation.
88+ *
89+ * This program is distributed in the hope that it will be useful,
90+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
91+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
92+ * GNU Lesser General Public License for more details.
93+ *
94+ * You should have received a copy of the GNU Lesser General Public License
95+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
96+ *
97+ * Authors: Michi Henning <michi.henning@canonical.com>
98+ */
99+
100+#pragma once
101+
102+#include <unity/storage/visibility.h>
103+
104+#include <string>
105+
106+namespace unity
107+{
108+namespace storage
109+{
110+namespace provider
111+{
112+
113+// Note: Adding new exception types also requires updating the marshaling and
114+// unmarshaling code for exceptions in the client and server APIs.
115+
116+/**
117+\brief Base exception class for all server-side exceptions.
118+*/
119+class UNITY_STORAGE_EXPORT StorageException : public std::exception
120+{
121+public:
122+ StorageException(std::string const& exception_type, std::string const& error_message);
123+ ~StorageException();
124+
125+ virtual char const* what() const noexcept override;
126+
127+ std::string type() const;
128+ std::string error_message() const;
129+
130+private:
131+ std::string what_string_;
132+ std::string type_;
133+ std::string error_message_;
134+};
135+
136+/**
137+\brief Indicates errors in the communication between the storage provider and the cloud service.
138+*/
139+class UNITY_STORAGE_EXPORT RemoteCommsException : public StorageException
140+{
141+public:
142+ RemoteCommsException(std::string const& error_message);
143+ ~RemoteCommsException();
144+};
145+
146+/**
147+\brief Indicates that an item does not exist or could not be found.
148+*/
149+class UNITY_STORAGE_EXPORT NotExistsException : public StorageException
150+{
151+public:
152+ NotExistsException(std::string const& error_message, std::string const& key);
153+ ~NotExistsException();
154+
155+ std::string key() const;
156+
157+private:
158+ std::string key_;
159+};
160+
161+/**
162+\brief Indicates that an item cannot be created because it exists already.
163+*/
164+class UNITY_STORAGE_EXPORT ExistsException : public StorageException
165+{
166+public:
167+ ExistsException(std::string const& error_message, std::string const& identity, std::string const& name);
168+ ~ExistsException();
169+
170+ std::string native_identity() const;
171+ std::string name() const;
172+
173+private:
174+ std::string identity_;
175+ std::string name_;
176+};
177+
178+/**
179+\brief Indicates that an upload detected a version mismatch.
180+*/
181+class UNITY_STORAGE_EXPORT ConflictException : public StorageException
182+{
183+public:
184+ ConflictException(std::string const& error_message);
185+ ~ConflictException();
186+};
187+
188+/**
189+\brief Indicates that an operation failed because of a permission problem.
190+*/
191+class UNITY_STORAGE_EXPORT PermissionException : public StorageException
192+{
193+public:
194+ PermissionException(std::string const& error_message);
195+ ~PermissionException();
196+};
197+
198+/**
199+\brief Indicates that an update failed because the provider ran out of space.
200+*/
201+class UNITY_STORAGE_EXPORT QuotaException : public StorageException
202+{
203+public:
204+ QuotaException(std::string const& error_message);
205+ ~QuotaException();
206+};
207+
208+/**
209+\brief Indicates that an upload or download was cancelled before it could complete.
210+*/
211+class UNITY_STORAGE_EXPORT CancelledException : public StorageException
212+{
213+public:
214+ CancelledException(std::string const& error_message);
215+ ~CancelledException();
216+};
217+
218+/**
219+\brief Indicates incorrect use of the API, such as calling methods in the wrong order.
220+*/
221+class UNITY_STORAGE_EXPORT LogicException : public StorageException
222+{
223+public:
224+ LogicException(std::string const& error_message);
225+ ~LogicException();
226+};
227+
228+/**
229+\brief Indicates an invalid parameter, such as a negative value when a positive one was
230+expected, or a string that does not parse correctly or is empty when it should be non-empty.
231+*/
232+class UNITY_STORAGE_EXPORT InvalidArgumentException : public StorageException
233+{
234+public:
235+ InvalidArgumentException(std::string const& error_message);
236+ ~InvalidArgumentException();
237+};
238+
239+/**
240+\brief Indicates a system error, such as failure to create a file or folder,
241+or any other (usually non-recoverable) kind of error that should not arise during normal operation.
242+*/
243+class UNITY_STORAGE_EXPORT ResourceException : public StorageException
244+{
245+public:
246+ ResourceException(std::string const& error_message, int error_code);
247+ ~ResourceException();
248+
249+ int error_code() const noexcept;
250+
251+private:
252+ int error_code_;
253+};
254+
255+/**
256+\brief Indicates that the server side caught an exception that does not derive from
257+StorageException, such as a std::exception, or caught some other unknown type (such as `int`).
258+*/
259+class UNITY_STORAGE_EXPORT UnknownException : public StorageException
260+{
261+public:
262+ UnknownException(std::string const& error_message);
263+ ~UnknownException();
264+};
265+
266+} // namespace provider
267+} // namespace storage
268+} // namespace unity
269
270=== modified file 'include/unity/storage/provider/internal/Handler.h'
271--- include/unity/storage/provider/internal/Handler.h 2016-07-19 07:08:50 +0000
272+++ include/unity/storage/provider/internal/Handler.h 2016-08-11 02:49:47 +0000
273@@ -63,6 +63,8 @@
274 void finished();
275
276 private:
277+ void marshal_exception(std::exception_ptr ep);
278+
279 std::shared_ptr<AccountData> const account_;
280 Callback const callback_;
281 QDBusConnection const bus_;
282
283=== modified file 'include/unity/storage/qt/client/Exceptions.h'
284--- include/unity/storage/qt/client/Exceptions.h 2016-08-02 04:29:06 +0000
285+++ include/unity/storage/qt/client/Exceptions.h 2016-08-11 02:49:47 +0000
286@@ -228,7 +228,7 @@
287 };
288
289 /**
290-\brief Indicates a system error, such as failure to to create a file or folder,
291+\brief Indicates a system error, such as failure to create a file or folder,
292 or any other (usually non-recoverable) kind of error that should not arise during normal operation.
293 */
294 class UNITY_STORAGE_EXPORT ResourceException : public StorageException
295
296=== modified file 'include/unity/storage/qt/client/internal/ItemBase.h'
297--- include/unity/storage/qt/client/internal/ItemBase.h 2016-08-02 04:29:06 +0000
298+++ include/unity/storage/qt/client/internal/ItemBase.h 2016-08-11 02:49:47 +0000
299@@ -20,10 +20,10 @@
300
301 #include <unity/storage/common.h>
302
303-#include <QDateTime>
304 #pragma GCC diagnostic push
305 #pragma GCC diagnostic ignored "-Wcast-align"
306 #pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
307+#include <QDateTime>
308 #include <QFuture>
309 #pragma GCC diagnostic pop
310 #include <QVariantMap>
311
312=== modified file 'include/unity/storage/qt/client/internal/remote_client/Handler.h'
313--- include/unity/storage/qt/client/internal/remote_client/Handler.h 2016-08-02 03:02:15 +0000
314+++ include/unity/storage/qt/client/internal/remote_client/Handler.h 2016-08-11 02:49:47 +0000
315@@ -20,10 +20,13 @@
316
317 #include <unity/storage/qt/client/Exceptions.h>
318 #include <unity/storage/qt/client/internal/make_future.h>
319+#include <unity/storage/qt/client/internal/remote_client/dbusmarshal.h>
320 #include <unity/storage/qt/client/internal/remote_client/HandlerBase.h>
321
322 #include <QDBusPendingReply>
323
324+#include <cassert>
325+
326 namespace unity
327 {
328 namespace storage
329@@ -72,8 +75,22 @@
330 {
331 if (call.isError())
332 {
333- int err = call.error().type();
334- make_exceptional_future<T>(ResourceException("DBus error return", err));
335+ try
336+ {
337+ auto ep = unmarshal_exception(call);
338+ std::rethrow_exception(ep);
339+ }
340+ catch (StorageException const& e)
341+ {
342+ make_exceptional_future<T>(qf_, e);
343+ return;
344+ }
345+ // LCOV_EXCL_START
346+ catch (...)
347+ {
348+ abort(); // Impossible.
349+ }
350+ // LCOV_EXCL_STOP
351 return;
352 }
353 // TODO: See HACK above. Should just be closure(call, qf_);
354
355=== modified file 'include/unity/storage/qt/client/internal/remote_client/dbusmarshal.h'
356--- include/unity/storage/qt/client/internal/remote_client/dbusmarshal.h 2016-07-12 02:22:05 +0000
357+++ include/unity/storage/qt/client/internal/remote_client/dbusmarshal.h 2016-08-11 02:49:47 +0000
358@@ -24,6 +24,8 @@
359 #include <QMetaType>
360 #include <QVariant>
361
362+class QDBusPendingCallWatcher;
363+
364 namespace unity
365 {
366 namespace storage
367@@ -39,8 +41,24 @@
368 QDBusArgument const& operator>>(QDBusArgument const& argument, QList<storage::internal::ItemMetadata>& md_list);
369
370 } // namespace internal
371-} // namespace storage
372-} // namespace unity
373+
374+namespace qt
375+{
376+namespace client
377+{
378+namespace internal
379+{
380+namespace remote_client
381+{
382+
383+std::exception_ptr unmarshal_exception(QDBusPendingCallWatcher const& call);
384+
385+} // namespace remote_client
386+} // namespace internal
387+} // client
388+} // qt
389+} // storage
390+} // unity
391
392 Q_DECLARE_METATYPE(unity::storage::internal::ItemMetadata)
393 Q_DECLARE_METATYPE(QList<unity::storage::internal::ItemMetadata>)
394
395=== modified file 'src/provider/CMakeLists.txt'
396--- src/provider/CMakeLists.txt 2016-08-03 03:49:49 +0000
397+++ src/provider/CMakeLists.txt 2016-08-11 02:49:47 +0000
398@@ -14,6 +14,7 @@
399
400 add_library(sf-provider-objects OBJECT
401 DownloadJob.cpp
402+ Exceptions.cpp
403 ProviderBase.cpp
404 Server.cpp
405 TempfileUploadJob.cpp
406
407=== added file 'src/provider/Exceptions.cpp'
408--- src/provider/Exceptions.cpp 1970-01-01 00:00:00 +0000
409+++ src/provider/Exceptions.cpp 2016-08-11 02:49:47 +0000
410@@ -0,0 +1,157 @@
411+/*
412+ * Copyright (C) 2016 Canonical Ltd
413+ *
414+ * This program is free software: you can redistribute it and/or modify
415+ * it under the terms of the GNU Lesser General Public License version 3 as
416+ * published by the Free Software Foundation.
417+ *
418+ * This program is distributed in the hope that it will be useful,
419+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
420+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
421+ * GNU Lesser General Public License for more details.
422+ *
423+ * You should have received a copy of the GNU Lesser General Public License
424+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
425+ *
426+ * Authors: Michi Henning <michi.henning@canonical.com>
427+ */
428+
429+#include <unity/storage/provider/Exceptions.h>
430+
431+using namespace std;
432+
433+namespace unity
434+{
435+namespace storage
436+{
437+namespace provider
438+{
439+
440+StorageException::StorageException(std::string const& exception_type, string const& error_message)
441+ : what_string_(string(exception_type) + ": " + error_message)
442+ , type_(exception_type)
443+ , error_message_(error_message)
444+{
445+}
446+
447+StorageException::~StorageException() = default;
448+
449+char const* StorageException::what() const noexcept
450+{
451+ return what_string_.c_str();
452+}
453+
454+string StorageException::type() const
455+{
456+ return type_;
457+}
458+
459+string StorageException::error_message() const
460+{
461+ return error_message_;
462+}
463+
464+RemoteCommsException::RemoteCommsException(string const& error_message)
465+ : StorageException("RemoteCommsException", error_message)
466+{
467+}
468+
469+RemoteCommsException::~RemoteCommsException() = default;
470+
471+NotExistsException::NotExistsException(string const& error_message, string const& key)
472+ : StorageException("NotExistsException", error_message)
473+ , key_(key)
474+{
475+}
476+
477+NotExistsException::~NotExistsException() = default;
478+
479+string NotExistsException::key() const
480+{
481+ return key_;
482+}
483+
484+ExistsException::ExistsException(string const& error_message, string const& identity, string const& name)
485+ : StorageException("ExistsException", error_message)
486+ , identity_(identity)
487+ , name_(name)
488+{
489+}
490+
491+ExistsException::~ExistsException() = default;
492+
493+string ExistsException::native_identity() const
494+{
495+ return identity_;
496+}
497+
498+string ExistsException::name() const
499+{
500+ return name_;
501+}
502+
503+ConflictException::ConflictException(string const& error_message)
504+ : StorageException("ConflictException", error_message)
505+{
506+}
507+
508+ConflictException::~ConflictException() = default;
509+
510+PermissionException::PermissionException(string const& error_message)
511+ : StorageException("PermissionException", error_message)
512+{
513+}
514+
515+PermissionException::~PermissionException() = default;
516+
517+QuotaException::QuotaException(string const& error_message)
518+ : StorageException("QuotaException", error_message)
519+{
520+}
521+
522+QuotaException::~QuotaException() = default;
523+
524+CancelledException::CancelledException(string const& error_message)
525+ : StorageException("CancelledException", error_message)
526+{
527+}
528+
529+CancelledException::~CancelledException() = default;
530+
531+LogicException::LogicException(string const& error_message)
532+ : StorageException("LogicException", error_message)
533+{
534+}
535+
536+LogicException::~LogicException() = default;
537+
538+InvalidArgumentException::InvalidArgumentException(string const& error_message)
539+ : StorageException("InvalidArgumentException", error_message)
540+{
541+}
542+
543+InvalidArgumentException::~InvalidArgumentException() = default;
544+
545+ResourceException::ResourceException(string const& error_message, int error_code)
546+ : StorageException("ResourceException", error_message)
547+ , error_code_(error_code)
548+{
549+}
550+
551+ResourceException::~ResourceException() = default;
552+
553+int ResourceException::error_code() const noexcept
554+{
555+ return error_code_;
556+}
557+
558+UnknownException::UnknownException(string const& error_message)
559+ : StorageException("UnknownException", error_message)
560+{
561+}
562+
563+UnknownException::~UnknownException() = default;
564+
565+} // namespace provider
566+} // namespace storage
567+} // namespace unity
568
569=== modified file 'src/provider/internal/Handler.cpp'
570--- src/provider/internal/Handler.cpp 2016-07-20 02:20:53 +0000
571+++ src/provider/internal/Handler.cpp 2016-08-11 02:49:47 +0000
572@@ -17,20 +17,20 @@
573 */
574
575 #include <unity/storage/provider/internal/Handler.h>
576+
577+#include <unity/storage/internal/dbus_error.h>
578 #include <unity/storage/provider/internal/AccountData.h>
579+#include <unity/storage/provider/internal/dbusmarshal.h>
580 #include <unity/storage/provider/internal/DBusPeerCache.h>
581 #include <unity/storage/provider/internal/MainLoopExecutor.h>
582 #include <unity/storage/provider/ProviderBase.h>
583+#include <unity/storage/provider/Exceptions.h>
584
585 #include <stdexcept>
586
587+using namespace unity::storage::internal;
588 using namespace std;
589
590-namespace
591-{
592-char const ERROR[] = "com.canonical.StorageFramework.Provider.Error";
593-}
594-
595 namespace unity
596 {
597 namespace storage
598@@ -53,7 +53,8 @@
599 auto peer_future = account_->dbus_peer().get(message_.service());
600 creds_future_ = peer_future.then(
601 EXEC_IN_MAIN
602- [this](decltype(peer_future) f) {
603+ [this](decltype(peer_future) f)
604+ {
605 auto info = f.get();
606 if (info.valid)
607 {
608@@ -64,8 +65,8 @@
609 }
610 else
611 {
612- reply_ = message_.createErrorReply(
613- ERROR, "Handler::begin(): could not retrieve credentials");
614+ auto ep = make_exception_ptr(PermissionException("Handler::begin(): could not retrieve credentials"));
615+ marshal_exception(ep);
616 QMetaObject::invokeMethod(this, "send_reply",
617 Qt::QueuedConnection);
618 }
619@@ -75,23 +76,27 @@
620 void Handler::credentials_received()
621 {
622 boost::future<QDBusMessage> msg_future;
623- try {
624+ try
625+ {
626 msg_future = callback_(account_, context_, message_);
627- } catch (std::exception const& e) {
628- reply_ = message_.createErrorReply(ERROR, e.what());
629+ }
630+ catch (std::exception const& e)
631+ {
632+ marshal_exception(current_exception());
633 QMetaObject::invokeMethod(this, "send_reply", Qt::QueuedConnection);
634 return;
635 }
636 reply_future_ = msg_future.then(
637 EXEC_IN_MAIN
638- [this](decltype(msg_future) f) {
639+ [this](decltype(msg_future) f)
640+ {
641 try
642 {
643 reply_ = f.get();
644 }
645- catch (std::exception const& e)
646+ catch (std::exception const&)
647 {
648- reply_ = message_.createErrorReply(ERROR, e.what());
649+ marshal_exception(current_exception());
650 }
651 QMetaObject::invokeMethod(this, "send_reply", Qt::QueuedConnection);
652 });
653@@ -103,6 +108,48 @@
654 Q_EMIT finished();
655 }
656
657+void Handler::marshal_exception(exception_ptr ep)
658+{
659+ try
660+ {
661+ rethrow_exception(ep);
662+ }
663+ catch (StorageException const& e)
664+ {
665+ reply_ = message_.createErrorReply(QString(DBUS_ERROR_PREFIX) + QString::fromStdString(e.type()),
666+ QString::fromStdString(e.error_message()));
667+ try
668+ {
669+ throw;
670+ }
671+ catch (NotExistsException const& e)
672+ {
673+ reply_ << QVariant(QString::fromStdString(e.key()));
674+ }
675+ catch (ExistsException const& e)
676+ {
677+ reply_ << QVariant(QString::fromStdString(e.native_identity()));
678+ reply_ << QVariant(QString::fromStdString(e.name()));
679+ }
680+ catch (ResourceException const& e)
681+ {
682+ reply_ << QVariant(e.error_code());
683+ }
684+ catch (StorageException const&)
685+ {
686+ // Some other sub-type of StorageException without additional data members.
687+ }
688+ }
689+ catch (std::exception const& e)
690+ {
691+ reply_ = message_.createErrorReply(QString(DBUS_ERROR_PREFIX) + "UnknownException", e.what());
692+ }
693+ catch (...)
694+ {
695+ reply_ = message_.createErrorReply(QString(DBUS_ERROR_PREFIX) + "UnknownException", "unknown exception type");
696+ }
697+}
698+
699 }
700 }
701 }
702
703=== modified file 'src/qt/client/Exceptions.cpp'
704--- src/qt/client/Exceptions.cpp 2016-08-02 04:29:06 +0000
705+++ src/qt/client/Exceptions.cpp 2016-08-11 02:49:47 +0000
706@@ -105,7 +105,7 @@
707 }
708
709 RuntimeDestroyedException::RuntimeDestroyedException(QString const& method)
710- : StorageException("RuntimeDestroyedException", method + ": Runtime was destroyed previously")
711+ : StorageException("RuntimeDestroyedException", method + ": runtime was destroyed previously")
712 {
713 }
714
715
716=== modified file 'src/qt/client/internal/local_client/FolderImpl.cpp'
717--- src/qt/client/internal/local_client/FolderImpl.cpp 2016-08-02 05:27:00 +0000
718+++ src/qt/client/internal/local_client/FolderImpl.cpp 2016-08-11 02:49:47 +0000
719@@ -67,7 +67,7 @@
720 {
721 lock_guard<decltype(mutex_)> guard(mutex_);
722
723- throw_if_destroyed("Folder::name()");
724+ throw_if_destroyed("Item::name()");
725 return name_;
726 }
727
728
729=== modified file 'src/qt/client/internal/local_client/RootImpl.cpp'
730--- src/qt/client/internal/local_client/RootImpl.cpp 2016-08-03 06:09:59 +0000
731+++ src/qt/client/internal/local_client/RootImpl.cpp 2016-08-11 02:49:47 +0000
732@@ -71,7 +71,7 @@
733 {
734 lock_guard<decltype(mutex_)> guard(mutex_);
735
736- throw_if_destroyed("Root::name()");
737+ throw_if_destroyed("Item::name()");
738 return "";
739 }
740
741@@ -79,7 +79,14 @@
742 {
743 lock_guard<decltype(mutex_)> guard(mutex_);
744
745- throw_if_destroyed("Root::parents()");
746+ try
747+ {
748+ throw_if_destroyed("Item::parents()");
749+ }
750+ catch (StorageException const& e)
751+ {
752+ return internal::make_exceptional_future<QVector<Folder::SPtr>>(e);
753+ }
754 return make_ready_future(QVector<Folder::SPtr>()); // For the root, we return an empty vector.
755 }
756
757@@ -87,7 +94,7 @@
758 {
759 lock_guard<decltype(mutex_)> guard(mutex_);
760
761- throw_if_destroyed("Root::parent_ids()");
762+ throw_if_destroyed("Item::parent_ids()");
763 return QVector<QString>(); // For the root, we return an empty vector.
764 }
765
766@@ -97,14 +104,14 @@
767
768 try
769 {
770- throw_if_destroyed("Root::delete_item()");
771+ throw_if_destroyed("Item::delete_item()");
772 }
773 catch (StorageException const& e)
774 {
775 return internal::make_exceptional_future(e);
776 }
777 // Cannot delete root.
778- return internal::make_exceptional_future(LogicException("Root::delete_item(): Cannot delete root folder"));
779+ return internal::make_exceptional_future(LogicException("Item::delete_item(): cannot delete root folder"));
780 }
781
782 QFuture<int64_t> RootImpl::free_space_bytes() const
783
784=== modified file 'src/qt/client/internal/local_client/RuntimeImpl.cpp'
785--- src/qt/client/internal/local_client/RuntimeImpl.cpp 2016-08-02 03:02:15 +0000
786+++ src/qt/client/internal/local_client/RuntimeImpl.cpp 2016-08-11 02:49:47 +0000
787@@ -80,7 +80,7 @@
788 {
789 if (destroyed_)
790 {
791- throw RuntimeDestroyedException("Runtime::accounts()");
792+ return internal::make_exceptional_future<QVector<Account::SPtr>>(RuntimeDestroyedException("Runtime::accounts()"));
793 }
794
795 char const* user = g_get_user_name();
796
797=== modified file 'src/qt/client/internal/remote_client/AccountImpl.cpp'
798--- src/qt/client/internal/remote_client/AccountImpl.cpp 2016-08-02 03:02:15 +0000
799+++ src/qt/client/internal/remote_client/AccountImpl.cpp 2016-08-11 02:49:47 +0000
800@@ -83,6 +83,15 @@
801
802 QFuture<QVector<Root::SPtr>> AccountImpl::roots()
803 {
804+ try
805+ {
806+ runtime(); // Throws if runtime was destroyed.
807+ }
808+ catch (RuntimeDestroyedException const& e)
809+ {
810+ return make_exceptional_future<QVector<Root::SPtr>>(e);
811+ }
812+
813 auto reply = provider_->Roots();
814
815 auto process_reply = [this](decltype(reply) const& reply, QFutureInterface<QVector<Root::SPtr>>& qf)
816
817=== modified file 'src/qt/client/internal/remote_client/FileImpl.cpp'
818--- src/qt/client/internal/remote_client/FileImpl.cpp 2016-08-02 04:29:06 +0000
819+++ src/qt/client/internal/remote_client/FileImpl.cpp 2016-08-11 02:49:47 +0000
820@@ -56,7 +56,7 @@
821 {
822 try
823 {
824- throw_if_destroyed("File::create_uploader()()");
825+ throw_if_destroyed("File::create_uploader()");
826 }
827 catch (StorageException const& e)
828 {
829@@ -78,7 +78,7 @@
830 auto root = get_root();
831 if (!root)
832 {
833- make_exceptional_future<shared_ptr<Uploader>>(RuntimeDestroyedException("File::create_uploader()"));
834+ make_exceptional_future(qf, RuntimeDestroyedException("File::create_uploader()"));
835 return;
836 }
837
838@@ -89,7 +89,7 @@
839 // TODO: log server error here
840 QString msg = "File::create_uploader(): impossible file descriptor returned by server: "
841 + QString::number(fd.fileDescriptor());
842- make_exceptional_future<shared_ptr<Uploader>>(qf, LocalCommsException(msg));
843+ make_exceptional_future(qf, LocalCommsException(msg));
844 return;
845 }
846 auto uploader = UploaderImpl::make_uploader(upload_id, fd, size, old_etag, root, prov);
847@@ -103,7 +103,7 @@
848 {
849 try
850 {
851- throw_if_destroyed("File::create_downloader()()");
852+ throw_if_destroyed("File::create_downloader()");
853 }
854 catch (StorageException const& e)
855 {
856@@ -116,11 +116,13 @@
857 auto process_reply = [this, prov](QDBusPendingReply<QString, QDBusUnixFileDescriptor> const& reply,
858 QFutureInterface<std::shared_ptr<Downloader>>& qf)
859 {
860- auto root = get_root();
861- if (!root)
862- {
863- make_exceptional_future<shared_ptr<Uploader>>(RuntimeDestroyedException("File::create_downloader()"));
864- return;
865+ try
866+ {
867+ throw_if_destroyed("File::create_downloader()");
868+ }
869+ catch (StorageException const& e)
870+ {
871+ make_exceptional_future(qf, e);
872 }
873
874 auto download_id = reply.argumentAt<0>();
875
876=== modified file 'src/qt/client/internal/remote_client/FolderImpl.cpp'
877--- src/qt/client/internal/remote_client/FolderImpl.cpp 2016-08-02 04:29:06 +0000
878+++ src/qt/client/internal/remote_client/FolderImpl.cpp 2016-08-11 02:49:47 +0000
879@@ -67,10 +67,6 @@
880 }
881
882 auto prov = provider();
883- if (!prov)
884- {
885- return make_exceptional_future<QVector<shared_ptr<Item>>>(RuntimeDestroyedException("Folder::list()"));
886- }
887 auto reply = prov->List(md_.item_id, "");
888
889 // Sorry for the mess, but we can't use auto for the lambda because it calls itself,
890
891=== modified file 'src/qt/client/internal/remote_client/ItemImpl.cpp'
892--- src/qt/client/internal/remote_client/ItemImpl.cpp 2016-08-02 04:29:06 +0000
893+++ src/qt/client/internal/remote_client/ItemImpl.cpp 2016-08-11 02:49:47 +0000
894@@ -82,16 +82,16 @@
895 return internal::make_exceptional_future<shared_ptr<Item>>(InvalidArgumentException(msg));
896 }
897
898+ auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);
899 try
900 {
901 throw_if_destroyed("Item::copy()");
902+ new_parent_impl->throw_if_destroyed("Item::copy()");
903 }
904 catch (StorageException const& e)
905 {
906 return make_exceptional_future<shared_ptr<Item>>(e);
907 }
908- auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);
909- new_parent_impl->throw_if_destroyed("Item::copy()");
910
911 auto prov = provider();
912 auto reply = prov->Copy(md_.item_id, new_parent->native_identity(), new_name);
913@@ -130,16 +130,16 @@
914 return internal::make_exceptional_future<shared_ptr<Item>>(InvalidArgumentException(msg));
915 }
916
917+ auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);
918 try
919 {
920 throw_if_destroyed("Item::move()");
921+ new_parent_impl->throw_if_destroyed("Item::move()");
922 }
923 catch (StorageException const& e)
924 {
925 return make_exceptional_future<shared_ptr<Item>>(e);
926 }
927- auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);
928- new_parent_impl->throw_if_destroyed("Item::move()");
929
930 auto prov = provider();
931 if (!prov)
932@@ -195,7 +195,14 @@
933
934 QFuture<void> ItemImpl::delete_item()
935 {
936- throw_if_destroyed("Item::delete_item()");
937+ try
938+ {
939+ throw_if_destroyed("Item::delete_item()");
940+ }
941+ catch (StorageException const& e)
942+ {
943+ return internal::make_exceptional_future(e);
944+ }
945
946 auto prov = provider();
947 auto reply = prov->Delete(md_.item_id);
948
949=== modified file 'src/qt/client/internal/remote_client/RootImpl.cpp'
950--- src/qt/client/internal/remote_client/RootImpl.cpp 2016-08-02 03:02:15 +0000
951+++ src/qt/client/internal/remote_client/RootImpl.cpp 2016-08-11 02:49:47 +0000
952@@ -50,37 +50,46 @@
953
954 QFuture<QVector<Folder::SPtr>> RootImpl::parents() const
955 {
956- if (!get_root())
957- {
958- return make_exceptional_future<QVector<Folder::SPtr>>(RuntimeDestroyedException("Root::parents()"));
959+ try
960+ {
961+ throw_if_destroyed("Root::parents()");
962+ }
963+ catch (StorageException const& e)
964+ {
965+ return make_exceptional_future<QVector<Folder::SPtr>>(e);
966 }
967 return make_ready_future(QVector<Folder::SPtr>()); // For the root, we return an empty vector.
968 }
969
970 QVector<QString> RootImpl::parent_ids() const
971 {
972- if (!get_root())
973- {
974- return make_exceptional_future<QVector<QString>>(RuntimeDestroyedException("Root::parent_ids()"));
975- }
976+ throw_if_destroyed("Root::parent_ids()");
977 return QVector<QString>(); // For the root, we return an empty vector.
978 }
979
980 QFuture<void> RootImpl::delete_item()
981 {
982- if (!get_root())
983- {
984- return make_exceptional_future(RuntimeDestroyedException("Root::delete_item()"));
985+ try
986+ {
987+ throw_if_destroyed("Item::delete_item()");
988+ }
989+ catch (StorageException const& e)
990+ {
991+ return make_exceptional_future<QVector<Folder::SPtr>>(e);
992 }
993 // Cannot delete root.
994- return make_exceptional_future(LogicException("Root::delete_item(): root item cannot be deleted"));
995+ return make_exceptional_future(LogicException("Item::delete_item(): cannot delete root folder"));
996 }
997
998 QFuture<int64_t> RootImpl::free_space_bytes() const
999 {
1000- if (!get_root())
1001- {
1002- return make_exceptional_future<int64_t>(RuntimeDestroyedException("Root::free_space_bytes()"));
1003+ try
1004+ {
1005+ throw_if_destroyed("Root::free_space_bytes()");
1006+ }
1007+ catch (StorageException const& e)
1008+ {
1009+ return make_exceptional_future<int64_t>(e);
1010 }
1011 // TODO, need to refresh metadata here instead.
1012 return make_ready_future(int64_t(1));
1013@@ -88,9 +97,13 @@
1014
1015 QFuture<int64_t> RootImpl::used_space_bytes() const
1016 {
1017- if (!get_root())
1018- {
1019- return make_exceptional_future<int64_t>(RuntimeDestroyedException("Root::used_space_bytes()"));
1020+ try
1021+ {
1022+ throw_if_destroyed("Root::used_space_bytes()");
1023+ }
1024+ catch (StorageException const& e)
1025+ {
1026+ return make_exceptional_future<int64_t>(e);
1027 }
1028 // TODO, need to refresh metadata here instead.
1029 return make_ready_future(int64_t(1));
1030@@ -98,9 +111,13 @@
1031
1032 QFuture<Item::SPtr> RootImpl::get(QString native_identity) const
1033 {
1034- if (!get_root())
1035- {
1036- return make_exceptional_future<Item::SPtr>(RuntimeDestroyedException("Root::get()"));
1037+ try
1038+ {
1039+ throw_if_destroyed("Root::get()");
1040+ }
1041+ catch (StorageException const& e)
1042+ {
1043+ return make_exceptional_future<Item::SPtr>(e);
1044 }
1045
1046 auto prov = provider();
1047@@ -115,7 +132,7 @@
1048 }
1049 catch (RuntimeDestroyedException const&)
1050 {
1051- make_exceptional_future<Item::SPtr>(qf, RuntimeDestroyedException("Root::get()"));
1052+ make_exceptional_future(qf, RuntimeDestroyedException("Root::get()"));
1053 return;
1054 }
1055
1056
1057=== modified file 'src/qt/client/internal/remote_client/RuntimeImpl.cpp'
1058--- src/qt/client/internal/remote_client/RuntimeImpl.cpp 2016-08-02 03:02:15 +0000
1059+++ src/qt/client/internal/remote_client/RuntimeImpl.cpp 2016-08-11 02:49:47 +0000
1060@@ -128,7 +128,6 @@
1061 QVector<Account::SPtr> accounts;
1062 for (auto const& a : manager_->availableAccounts("google-drive-scope"))
1063 {
1064- qDebug() << "got account:" << a->displayName() << a->serviceId() << a->id();
1065 auto impl = new AccountImpl(public_instance_, a->id(), "", a->serviceId(), a->displayName());
1066 Account::SPtr acc(new Account(impl));
1067 impl->set_public_instance(acc);
1068
1069=== modified file 'src/qt/client/internal/remote_client/dbusmarshal.cpp'
1070--- src/qt/client/internal/remote_client/dbusmarshal.cpp 2016-07-12 02:22:05 +0000
1071+++ src/qt/client/internal/remote_client/dbusmarshal.cpp 2016-08-11 02:49:47 +0000
1072@@ -18,8 +18,19 @@
1073
1074 #include <unity/storage/qt/client/internal/remote_client/dbusmarshal.h>
1075
1076+#include <unity/storage/internal/dbus_error.h>
1077+#include <unity/storage/qt/client/Exceptions.h>
1078+#include <unity/storage/qt/client/internal/make_future.h>
1079+
1080+#include <QDBusPendingCallWatcher>
1081+#include <QDBusPendingReply>
1082 #include <QDebug>
1083
1084+#include <cassert>
1085+#include <functional>
1086+#include <map>
1087+
1088+using namespace unity::storage::internal;
1089 using namespace std;
1090
1091 namespace unity
1092@@ -117,10 +128,94 @@
1093 {
1094 namespace remote_client
1095 {
1096-
1097-} // remote_client
1098-} // internal
1099-} // client
1100-} // qt
1101-} // storage
1102-} // unity
1103+namespace
1104+{
1105+
1106+template<typename T>
1107+exception_ptr make_exception(QDBusPendingCallWatcher const& call)
1108+{
1109+ QDBusPendingReply<QString> reply = call;
1110+ auto msg = reply.argumentAt<0>();
1111+ return make_exception_ptr(T(msg));
1112+}
1113+
1114+template<>
1115+exception_ptr make_exception<NotExistsException>(QDBusPendingCallWatcher const& call)
1116+{
1117+ QDBusPendingReply<QString, QString> reply = call;
1118+ auto msg = reply.argumentAt<0>();
1119+ auto key = reply.argumentAt<1>();
1120+ return make_exception_ptr(NotExistsException(msg, key));
1121+}
1122+
1123+template<>
1124+exception_ptr make_exception<ExistsException>(QDBusPendingCallWatcher const& call)
1125+{
1126+ QDBusPendingReply<QString, QString, QString> reply = call;
1127+ auto msg = reply.argumentAt<0>();
1128+ auto id = reply.argumentAt<1>();
1129+ auto name = reply.argumentAt<2>();
1130+ return make_exception_ptr(ExistsException(msg, id, name));
1131+}
1132+
1133+template<>
1134+exception_ptr make_exception<ResourceException>(QDBusPendingCallWatcher const& call)
1135+{
1136+ QDBusPendingReply<QString, int> reply = call;
1137+ auto msg = reply.argumentAt<0>();
1138+ auto error_code = reply.argumentAt<1>();
1139+ return make_exception_ptr(ResourceException(msg, error_code));
1140+}
1141+
1142+static const map<QString, function<exception_ptr(QDBusPendingCallWatcher const& call)>> exception_factories =
1143+{
1144+ { "NotExistsException", make_exception<NotExistsException> },
1145+ { "ExistsException", make_exception<ExistsException> },
1146+ { "ResourceException", make_exception<ResourceException> },
1147+ { "RemoteCommsException", make_exception<RemoteCommsException> },
1148+ { "ConflictException", make_exception<ConflictException> },
1149+ { "PermissionException", make_exception<PermissionException> },
1150+ { "QuotaException", make_exception<QuotaException> },
1151+ { "CancelledException", make_exception<CancelledException> },
1152+ { "LogicException", make_exception<LogicException> },
1153+ { "InvalidArgumentException", make_exception<InvalidArgumentException> },
1154+ { "UnknownException", make_exception<LocalCommsException> } // Yes, LocalCommsException is intentional
1155+};
1156+
1157+} // namespace
1158+
1159+std::exception_ptr unmarshal_exception(QDBusPendingCallWatcher const& call)
1160+{
1161+ assert(call.isError());
1162+
1163+ int err = call.error().type();
1164+ if (err != QDBusError::Other)
1165+ {
1166+ return make_exception_ptr(LocalCommsException(call.error().message()));
1167+ }
1168+
1169+ auto exception_type = call.error().name();
1170+ if (!exception_type.startsWith(DBUS_ERROR_PREFIX))
1171+ {
1172+ QString msg = "unmarshal_exception(): unknown exception type received from server: " + exception_type
1173+ + ": " + call.error().message();
1174+ return make_exception_ptr(LocalCommsException(msg));
1175+ }
1176+ exception_type = exception_type.remove(0, strlen(DBUS_ERROR_PREFIX));
1177+
1178+ auto factory_it = exception_factories.find(exception_type);
1179+ if (factory_it == exception_factories.end())
1180+ {
1181+ QString msg = "unmarshal_exception(): unknown exception type received from server: " + exception_type
1182+ + ": " + call.error().message();
1183+ return make_exception_ptr(LocalCommsException(msg));
1184+ }
1185+ return factory_it->second(call);
1186+}
1187+
1188+} // namespace remote_client
1189+} // namespace internal
1190+} // namespace client
1191+} // namespace qt
1192+} // namespace storage
1193+} // namespace unity
1194
1195=== modified file 'tests/local-client/local-client_test.cpp'
1196--- tests/local-client/local-client_test.cpp 2016-08-03 03:54:44 +0000
1197+++ tests/local-client/local-client_test.cpp 2016-08-11 02:49:47 +0000
1198@@ -43,7 +43,7 @@
1199
1200 static constexpr int SIGNAL_WAIT_TIME = 1000;
1201
1202-// Bunch of helper function to reduce the amount of noise in the tests.
1203+// Bunch of helper functions to reduce the amount of noise in the tests.
1204
1205 template<typename T>
1206 void wait(T fut)
1207@@ -218,7 +218,6 @@
1208 {
1209 auto runtime = Runtime::create();
1210
1211- auto acc = get_account(runtime);
1212 auto root = get_root(runtime);
1213 clear_folder(root);
1214
1215@@ -311,7 +310,6 @@
1216 {
1217 auto runtime = Runtime::create();
1218
1219- auto acc = get_account(runtime);
1220 auto root = get_root(runtime);
1221 clear_folder(root);
1222
1223@@ -332,7 +330,6 @@
1224 {
1225 auto runtime = Runtime::create();
1226
1227- auto acc = get_account(runtime);
1228 auto root = get_root(runtime);
1229 clear_folder(root);
1230
1231@@ -416,7 +413,6 @@
1232 {
1233 auto runtime = Runtime::create();
1234
1235- auto acc = get_account(runtime);
1236 auto root = get_root(runtime);
1237 clear_folder(root);
1238
1239@@ -452,7 +448,6 @@
1240 {
1241 auto runtime = Runtime::create();
1242
1243- auto acc = get_account(runtime);
1244 auto root = get_root(runtime);
1245 clear_folder(root);
1246
1247@@ -498,7 +493,6 @@
1248 {
1249 auto runtime = Runtime::create();
1250
1251- auto acc = get_account(runtime);
1252 auto root = get_root(runtime);
1253 clear_folder(root);
1254
1255@@ -529,7 +523,6 @@
1256 {
1257 auto runtime = Runtime::create();
1258
1259- auto acc = get_account(runtime);
1260 auto root = get_root(runtime);
1261 clear_folder(root);
1262
1263@@ -555,7 +548,6 @@
1264 {
1265 auto runtime = Runtime::create();
1266
1267- auto acc = get_account(runtime);
1268 auto root = get_root(runtime);
1269 clear_folder(root);
1270
1271@@ -618,7 +610,6 @@
1272 {
1273 auto runtime = Runtime::create();
1274
1275- auto acc = get_account(runtime);
1276 auto root = get_root(runtime);
1277 clear_folder(root);
1278
1279@@ -637,7 +628,6 @@
1280 {
1281 auto runtime = Runtime::create();
1282
1283- auto acc = get_account(runtime);
1284 auto root = get_root(runtime);
1285 clear_folder(root);
1286
1287@@ -847,7 +837,6 @@
1288 {
1289 auto runtime = Runtime::create();
1290
1291- auto acc = get_account(runtime);
1292 auto root = get_root(runtime);
1293 clear_folder(root);
1294
1295@@ -904,7 +893,6 @@
1296 {
1297 auto runtime = Runtime::create();
1298
1299- auto acc = get_account(runtime);
1300 auto root = get_root(runtime);
1301 clear_folder(root);
1302
1303@@ -951,7 +939,6 @@
1304 {
1305 auto runtime = Runtime::create();
1306
1307- auto acc = get_account(runtime);
1308 auto root = get_root(runtime);
1309 clear_folder(root);
1310
1311@@ -965,7 +952,6 @@
1312 {
1313 auto runtime = Runtime::create();
1314
1315- auto acc = get_account(runtime);
1316 auto root = get_root(runtime);
1317 clear_folder(root);
1318
1319@@ -995,7 +981,6 @@
1320 {
1321 auto runtime = Runtime::create();
1322
1323- auto acc = get_account(runtime);
1324 auto root = get_root(runtime);
1325 clear_folder(root);
1326
1327@@ -1012,7 +997,6 @@
1328 {
1329 auto runtime = Runtime::create();
1330
1331- auto acc = get_account(runtime);
1332 auto root = get_root(runtime);
1333 clear_folder(root);
1334
1335@@ -1055,7 +1039,6 @@
1336 {
1337 auto runtime = Runtime::create();
1338
1339- auto acc = get_account(runtime);
1340 auto root = get_root(runtime);
1341 clear_folder(root);
1342
1343@@ -1076,7 +1059,6 @@
1344 {
1345 auto runtime = Runtime::create();
1346
1347- auto acc = get_account(runtime);
1348 auto root = get_root(runtime);
1349 clear_folder(root);
1350
1351@@ -1109,7 +1091,6 @@
1352 {
1353 auto runtime = Runtime::create();
1354
1355- auto acc = get_account(runtime);
1356 auto root = get_root(runtime);
1357 clear_folder(root);
1358
1359@@ -1261,7 +1242,6 @@
1360 {
1361 auto runtime = Runtime::create();
1362
1363- auto acc = get_account(runtime);
1364 auto root = get_root(runtime);
1365 clear_folder(root);
1366
1367@@ -1417,7 +1397,6 @@
1368 {
1369 auto runtime = Runtime::create();
1370
1371- auto acc = get_account(runtime);
1372 auto root = get_root(runtime);
1373 clear_folder(root);
1374
1375@@ -1428,7 +1407,7 @@
1376 }
1377 catch (LogicException const& e)
1378 {
1379- EXPECT_EQ("Root::delete_item(): Cannot delete root folder", e.error_message());
1380+ EXPECT_EQ("Item::delete_item(): cannot delete root folder", e.error_message());
1381 }
1382
1383 try
1384@@ -1545,7 +1524,6 @@
1385 {
1386 auto runtime = Runtime::create();
1387
1388- auto acc = get_account(runtime);
1389 auto root = get_root(runtime);
1390 clear_folder(root);
1391
1392@@ -1693,7 +1671,6 @@
1393 {
1394 auto runtime = Runtime::create();
1395
1396- auto acc = get_account(runtime);
1397 auto root = get_root(runtime);
1398 clear_folder(root);
1399
1400@@ -1705,7 +1682,7 @@
1401 }
1402 catch (DeletedException const& e)
1403 {
1404- EXPECT_TRUE(e.error_message().startsWith("Folder::name(): "));
1405+ EXPECT_TRUE(e.error_message().startsWith("Item::name(): "));
1406 }
1407
1408 try
1409@@ -1790,7 +1767,6 @@
1410 {
1411 auto runtime = Runtime::create();
1412
1413- auto acc = get_account(runtime);
1414 auto root = get_root(runtime);
1415 clear_folder(root);
1416
1417@@ -1861,7 +1837,7 @@
1418 }
1419 catch (RuntimeDestroyedException const& e)
1420 {
1421- EXPECT_EQ("Account::runtime(): Runtime was destroyed previously", e.error_message());
1422+ EXPECT_EQ("Account::runtime(): runtime was destroyed previously", e.error_message());
1423 }
1424 }
1425
1426@@ -1877,7 +1853,7 @@
1427 }
1428 catch (RuntimeDestroyedException const& e)
1429 {
1430- EXPECT_EQ("Account::runtime(): Runtime was destroyed previously", e.error_message());
1431+ EXPECT_EQ("Account::runtime(): runtime was destroyed previously", e.error_message());
1432 }
1433 }
1434
1435@@ -1887,19 +1863,18 @@
1436 runtime->shutdown();
1437 try
1438 {
1439- runtime->accounts();
1440+ call(runtime->accounts());
1441 FAIL();
1442 }
1443 catch (RuntimeDestroyedException const& e)
1444 {
1445- EXPECT_EQ("Runtime::accounts(): Runtime was destroyed previously", e.error_message());
1446+ EXPECT_EQ("Runtime::accounts(): runtime was destroyed previously", e.error_message());
1447 }
1448 }
1449
1450 // Getting the account from a root with a destroyed runtime must fail.
1451 {
1452 auto runtime = Runtime::create();
1453- auto acc = get_account(runtime);
1454 auto root = get_root(runtime);
1455 runtime.reset();
1456 try
1457@@ -1909,7 +1884,7 @@
1458 }
1459 catch (RuntimeDestroyedException const& e)
1460 {
1461- EXPECT_EQ("Root::account(): Runtime was destroyed previously", e.error_message());
1462+ EXPECT_EQ("Root::account(): runtime was destroyed previously", e.error_message());
1463 }
1464 }
1465
1466@@ -1927,14 +1902,13 @@
1467 }
1468 catch (RuntimeDestroyedException const& e)
1469 {
1470- EXPECT_EQ("Root::account(): Runtime was destroyed previously", e.error_message());
1471+ EXPECT_EQ("Root::account(): runtime was destroyed previously", e.error_message());
1472 }
1473 }
1474
1475 // Getting the root from an item with a destroyed runtime must fail.
1476 {
1477 auto runtime = Runtime::create();
1478- auto acc = get_account(runtime);
1479 auto root = get_root(runtime);
1480 clear_folder(root);
1481
1482@@ -1947,20 +1921,18 @@
1483 }
1484 catch (RuntimeDestroyedException const& e)
1485 {
1486- EXPECT_EQ("Item::root(): Runtime was destroyed previously", e.error_message());
1487+ EXPECT_EQ("Item::root(): runtime was destroyed previously", e.error_message());
1488 }
1489 }
1490
1491 // Getting the root from an item with a destroyed root must fail.
1492 {
1493 auto runtime = Runtime::create();
1494- auto acc = get_account(runtime);
1495 auto root = get_root(runtime);
1496 clear_folder(root);
1497
1498 auto file = write_file(root, "file", "");
1499 runtime.reset();
1500- acc.reset();
1501 root.reset();
1502 try
1503 {
1504@@ -1969,14 +1941,13 @@
1505 }
1506 catch (RuntimeDestroyedException const& e)
1507 {
1508- EXPECT_EQ("Item::root(): Runtime was destroyed previously", e.error_message());
1509+ EXPECT_EQ("Item::root(): runtime was destroyed previously", e.error_message());
1510 }
1511 }
1512
1513 // etag() with destroyed runtime must fail.
1514 {
1515 auto runtime = Runtime::create();
1516- auto acc = get_account(runtime);
1517 auto root = get_root(runtime);
1518 clear_folder(root);
1519
1520@@ -1989,14 +1960,13 @@
1521 }
1522 catch (RuntimeDestroyedException const& e)
1523 {
1524- EXPECT_EQ("Item::etag(): Runtime was destroyed previously", e.error_message());
1525+ EXPECT_EQ("Item::etag(): runtime was destroyed previously", e.error_message());
1526 }
1527 }
1528
1529 // metadata() with destroyed runtime must fail.
1530 {
1531 auto runtime = Runtime::create();
1532- auto acc = get_account(runtime);
1533 auto root = get_root(runtime);
1534 clear_folder(root);
1535
1536@@ -2009,14 +1979,13 @@
1537 }
1538 catch (RuntimeDestroyedException const& e)
1539 {
1540- EXPECT_EQ("Item::metadata(): Runtime was destroyed previously", e.error_message());
1541+ EXPECT_EQ("Item::metadata(): runtime was destroyed previously", e.error_message());
1542 }
1543 }
1544
1545 // last_modified_time() with destroyed runtime must fail.
1546 {
1547 auto runtime = Runtime::create();
1548- auto acc = get_account(runtime);
1549 auto root = get_root(runtime);
1550 clear_folder(root);
1551
1552@@ -2029,14 +1998,13 @@
1553 }
1554 catch (RuntimeDestroyedException const& e)
1555 {
1556- EXPECT_EQ("Item::last_modified_time(): Runtime was destroyed previously", e.error_message());
1557+ EXPECT_EQ("Item::last_modified_time(): runtime was destroyed previously", e.error_message());
1558 }
1559 }
1560
1561 // copy() with destroyed runtime must fail.
1562 {
1563 auto runtime = Runtime::create();
1564- auto acc = get_account(runtime);
1565 auto root = get_root(runtime);
1566 clear_folder(root);
1567
1568@@ -2049,14 +2017,13 @@
1569 }
1570 catch (RuntimeDestroyedException const& e)
1571 {
1572- EXPECT_EQ("Item::copy(): Runtime was destroyed previously", e.error_message());
1573+ EXPECT_EQ("Item::copy(): runtime was destroyed previously", e.error_message());
1574 }
1575 }
1576
1577 // move() with destroyed runtime must fail.
1578 {
1579 auto runtime = Runtime::create();
1580- auto acc = get_account(runtime);
1581 auto root = get_root(runtime);
1582 clear_folder(root);
1583
1584@@ -2069,18 +2036,16 @@
1585 }
1586 catch (RuntimeDestroyedException const& e)
1587 {
1588- EXPECT_EQ("Item::move(): Runtime was destroyed previously", e.error_message());
1589+ EXPECT_EQ("Item::move(): runtime was destroyed previously", e.error_message());
1590 }
1591 }
1592
1593 // parents() on root with destroyed runtime must fail.
1594 {
1595 auto runtime = Runtime::create();
1596- auto acc = get_account(runtime);
1597 auto root = get_root(runtime);
1598 clear_folder(root);
1599
1600- auto file = write_file(root, "file", "");
1601 runtime->shutdown();
1602 try
1603 {
1604@@ -2089,14 +2054,13 @@
1605 }
1606 catch (RuntimeDestroyedException const& e)
1607 {
1608- EXPECT_EQ("Root::parents(): Runtime was destroyed previously", e.error_message());
1609+ EXPECT_EQ("Item::parents(): runtime was destroyed previously", e.error_message());
1610 }
1611 }
1612
1613 // parents() on file with destroyed runtime must fail.
1614 {
1615 auto runtime = Runtime::create();
1616- auto acc = get_account(runtime);
1617 auto root = get_root(runtime);
1618 clear_folder(root);
1619
1620@@ -2109,14 +2073,13 @@
1621 }
1622 catch (RuntimeDestroyedException const& e)
1623 {
1624- EXPECT_EQ("Item::parents(): Runtime was destroyed previously", e.error_message());
1625+ EXPECT_EQ("Item::parents(): runtime was destroyed previously", e.error_message());
1626 }
1627 }
1628
1629 // parent_ids() with destroyed runtime must fail.
1630 {
1631 auto runtime = Runtime::create();
1632- auto acc = get_account(runtime);
1633 auto root = get_root(runtime);
1634 clear_folder(root);
1635
1636@@ -2129,14 +2092,13 @@
1637 }
1638 catch (RuntimeDestroyedException const& e)
1639 {
1640- EXPECT_EQ("Item::parent_ids(): Runtime was destroyed previously", e.error_message());
1641+ EXPECT_EQ("Item::parent_ids(): runtime was destroyed previously", e.error_message());
1642 }
1643 }
1644
1645 // parent_ids() on root with destroyed runtime must fail.
1646 {
1647 auto runtime = Runtime::create();
1648- auto acc = get_account(runtime);
1649 auto root = get_root(runtime);
1650 clear_folder(root);
1651
1652@@ -2148,14 +2110,13 @@
1653 }
1654 catch (RuntimeDestroyedException const& e)
1655 {
1656- EXPECT_EQ("Root::parent_ids(): Runtime was destroyed previously", e.error_message());
1657+ EXPECT_EQ("Item::parent_ids(): runtime was destroyed previously", e.error_message());
1658 }
1659 }
1660
1661 // delete_item() with destroyed runtime must fail.
1662 {
1663 auto runtime = Runtime::create();
1664- auto acc = get_account(runtime);
1665 auto root = get_root(runtime);
1666 clear_folder(root);
1667
1668@@ -2168,14 +2129,13 @@
1669 }
1670 catch (RuntimeDestroyedException const& e)
1671 {
1672- EXPECT_EQ("Item::delete_item(): Runtime was destroyed previously", e.error_message());
1673+ EXPECT_EQ("Item::delete_item(): runtime was destroyed previously", e.error_message());
1674 }
1675 }
1676
1677 // delete_item() on root with destroyed runtime must fail.
1678 {
1679 auto runtime = Runtime::create();
1680- auto acc = get_account(runtime);
1681 auto root = get_root(runtime);
1682 clear_folder(root);
1683
1684@@ -2187,14 +2147,13 @@
1685 }
1686 catch (RuntimeDestroyedException const& e)
1687 {
1688- EXPECT_EQ("Root::delete_item(): Runtime was destroyed previously", e.error_message());
1689+ EXPECT_EQ("Item::delete_item(): runtime was destroyed previously", e.error_message());
1690 }
1691 }
1692
1693 // creation_time() with destroyed runtime must fail.
1694 {
1695 auto runtime = Runtime::create();
1696- auto acc = get_account(runtime);
1697 auto root = get_root(runtime);
1698 clear_folder(root);
1699
1700@@ -2207,14 +2166,13 @@
1701 }
1702 catch (RuntimeDestroyedException const& e)
1703 {
1704- EXPECT_EQ("Item::creation_time(): Runtime was destroyed previously", e.error_message());
1705+ EXPECT_EQ("Item::creation_time(): runtime was destroyed previously", e.error_message());
1706 }
1707 }
1708
1709 // native_metadata() with destroyed runtime must fail.
1710 {
1711 auto runtime = Runtime::create();
1712- auto acc = get_account(runtime);
1713 auto root = get_root(runtime);
1714 clear_folder(root);
1715
1716@@ -2227,14 +2185,13 @@
1717 }
1718 catch (RuntimeDestroyedException const& e)
1719 {
1720- EXPECT_EQ("Item::native_metadata(): Runtime was destroyed previously", e.error_message());
1721+ EXPECT_EQ("Item::native_metadata(): runtime was destroyed previously", e.error_message());
1722 }
1723 }
1724
1725 // name() on root with destroyed runtime must fail.
1726 {
1727 auto runtime = Runtime::create();
1728- auto acc = get_account(runtime);
1729 auto root = get_root(runtime);
1730 clear_folder(root);
1731
1732@@ -2246,14 +2203,13 @@
1733 }
1734 catch (RuntimeDestroyedException const& e)
1735 {
1736- EXPECT_EQ("Root::name(): Runtime was destroyed previously", e.error_message());
1737+ EXPECT_EQ("Item::name(): runtime was destroyed previously", e.error_message());
1738 }
1739 }
1740
1741 // name() on folder with destroyed runtime must fail.
1742 {
1743 auto runtime = Runtime::create();
1744- auto acc = get_account(runtime);
1745 auto root = get_root(runtime);
1746 clear_folder(root);
1747
1748@@ -2266,14 +2222,13 @@
1749 }
1750 catch (RuntimeDestroyedException const& e)
1751 {
1752- EXPECT_EQ("Folder::name(): Runtime was destroyed previously", e.error_message());
1753+ EXPECT_EQ("Item::name(): runtime was destroyed previously", e.error_message());
1754 }
1755 }
1756
1757 // name() on file with destroyed runtime must fail.
1758 {
1759 auto runtime = Runtime::create();
1760- auto acc = get_account(runtime);
1761 auto root = get_root(runtime);
1762 clear_folder(root);
1763
1764@@ -2286,14 +2241,13 @@
1765 }
1766 catch (RuntimeDestroyedException const& e)
1767 {
1768- EXPECT_EQ("File::name(): Runtime was destroyed previously", e.error_message());
1769+ EXPECT_EQ("File::name(): runtime was destroyed previously", e.error_message());
1770 }
1771 }
1772
1773 // list() with destroyed runtime must fail.
1774 {
1775 auto runtime = Runtime::create();
1776- auto acc = get_account(runtime);
1777 auto root = get_root(runtime);
1778 clear_folder(root);
1779
1780@@ -2305,14 +2259,13 @@
1781 }
1782 catch (RuntimeDestroyedException const& e)
1783 {
1784- EXPECT_EQ("Folder::list(): Runtime was destroyed previously", e.error_message());
1785+ EXPECT_EQ("Folder::list(): runtime was destroyed previously", e.error_message());
1786 }
1787 }
1788
1789 // lookup() with destroyed runtime must fail.
1790 {
1791 auto runtime = Runtime::create();
1792- auto acc = get_account(runtime);
1793 auto root = get_root(runtime);
1794 clear_folder(root);
1795
1796@@ -2324,14 +2277,13 @@
1797 }
1798 catch (RuntimeDestroyedException const& e)
1799 {
1800- EXPECT_EQ("Folder::lookup(): Runtime was destroyed previously", e.error_message());
1801+ EXPECT_EQ("Folder::lookup(): runtime was destroyed previously", e.error_message());
1802 }
1803 }
1804
1805 // create_folder() with destroyed runtime must fail.
1806 {
1807 auto runtime = Runtime::create();
1808- auto acc = get_account(runtime);
1809 auto root = get_root(runtime);
1810 clear_folder(root);
1811
1812@@ -2343,14 +2295,13 @@
1813 }
1814 catch (RuntimeDestroyedException const& e)
1815 {
1816- EXPECT_EQ("Folder::create_folder(): Runtime was destroyed previously", e.error_message());
1817+ EXPECT_EQ("Folder::create_folder(): runtime was destroyed previously", e.error_message());
1818 }
1819 }
1820
1821 // create_file() with destroyed runtime must fail.
1822 {
1823 auto runtime = Runtime::create();
1824- auto acc = get_account(runtime);
1825 auto root = get_root(runtime);
1826 clear_folder(root);
1827
1828@@ -2362,14 +2313,13 @@
1829 }
1830 catch (RuntimeDestroyedException const& e)
1831 {
1832- EXPECT_EQ("Folder::create_file(): Runtime was destroyed previously", e.error_message());
1833+ EXPECT_EQ("Folder::create_file(): runtime was destroyed previously", e.error_message());
1834 }
1835 }
1836
1837 // size() with destroyed runtime must fail.
1838 {
1839 auto runtime = Runtime::create();
1840- auto acc = get_account(runtime);
1841 auto root = get_root(runtime);
1842 clear_folder(root);
1843
1844@@ -2382,14 +2332,13 @@
1845 }
1846 catch (RuntimeDestroyedException const& e)
1847 {
1848- EXPECT_EQ("File::size(): Runtime was destroyed previously", e.error_message());
1849+ EXPECT_EQ("File::size(): runtime was destroyed previously", e.error_message());
1850 }
1851 }
1852
1853 // create_uploader() with destroyed runtime must fail.
1854 {
1855 auto runtime = Runtime::create();
1856- auto acc = get_account(runtime);
1857 auto root = get_root(runtime);
1858 clear_folder(root);
1859
1860@@ -2402,14 +2351,13 @@
1861 }
1862 catch (RuntimeDestroyedException const& e)
1863 {
1864- EXPECT_EQ("File::create_uploader(): Runtime was destroyed previously", e.error_message());
1865+ EXPECT_EQ("File::create_uploader(): runtime was destroyed previously", e.error_message());
1866 }
1867 }
1868
1869 // create_downloader() with destroyed runtime must fail.
1870 {
1871 auto runtime = Runtime::create();
1872- auto acc = get_account(runtime);
1873 auto root = get_root(runtime);
1874 clear_folder(root);
1875
1876@@ -2422,14 +2370,13 @@
1877 }
1878 catch (RuntimeDestroyedException const& e)
1879 {
1880- EXPECT_EQ("File::create_downloader(): Runtime was destroyed previously", e.error_message());
1881+ EXPECT_EQ("File::create_downloader(): runtime was destroyed previously", e.error_message());
1882 }
1883 }
1884
1885 // free_space_bytes() with destroyed runtime must fail.
1886 {
1887 auto runtime = Runtime::create();
1888- auto acc = get_account(runtime);
1889 auto root = get_root(runtime);
1890 clear_folder(root);
1891
1892@@ -2441,14 +2388,13 @@
1893 }
1894 catch (RuntimeDestroyedException const& e)
1895 {
1896- EXPECT_EQ("Root::free_space_bytes(): Runtime was destroyed previously", e.error_message());
1897+ EXPECT_EQ("Root::free_space_bytes(): runtime was destroyed previously", e.error_message());
1898 }
1899 }
1900
1901 // used_space_bytes() with destroyed runtime must fail.
1902 {
1903 auto runtime = Runtime::create();
1904- auto acc = get_account(runtime);
1905 auto root = get_root(runtime);
1906 clear_folder(root);
1907
1908@@ -2460,14 +2406,13 @@
1909 }
1910 catch (RuntimeDestroyedException const& e)
1911 {
1912- EXPECT_EQ("Root::used_space_bytes(): Runtime was destroyed previously", e.error_message());
1913+ EXPECT_EQ("Root::used_space_bytes(): runtime was destroyed previously", e.error_message());
1914 }
1915 }
1916
1917 // get() with destroyed runtime must fail.
1918 {
1919 auto runtime = Runtime::create();
1920- auto acc = get_account(runtime);
1921 auto root = get_root(runtime);
1922 clear_folder(root);
1923
1924@@ -2479,7 +2424,7 @@
1925 }
1926 catch (RuntimeDestroyedException const& e)
1927 {
1928- EXPECT_EQ("Root::get(): Runtime was destroyed previously", e.error_message());
1929+ EXPECT_EQ("Root::get(): runtime was destroyed previously", e.error_message());
1930 }
1931 }
1932 }
1933
1934=== modified file 'tests/provider-ProviderInterface/CMakeLists.txt'
1935--- tests/provider-ProviderInterface/CMakeLists.txt 2016-08-05 10:53:57 +0000
1936+++ tests/provider-ProviderInterface/CMakeLists.txt 2016-08-11 02:49:47 +0000
1937@@ -18,11 +18,13 @@
1938 ../../src/qt/client/internal/remote_client/dbusmarshal.cpp
1939 ${generated_files}
1940 )
1941+add_dependencies(provider-ProviderInterface_test storage-framework-provider)
1942 set_target_properties(provider-ProviderInterface_test PROPERTIES
1943 AUTOMOC TRUE
1944 )
1945 target_link_libraries(provider-ProviderInterface_test
1946 storage-framework-provider-static
1947+ storage-framework-qt-client
1948 Qt5::Test
1949 testutils
1950 gtest
1951
1952=== modified file 'tests/provider-ProviderInterface/ProviderInterface_test.cpp'
1953--- tests/provider-ProviderInterface/ProviderInterface_test.cpp 2016-08-09 06:19:48 +0000
1954+++ tests/provider-ProviderInterface/ProviderInterface_test.cpp 2016-08-11 02:49:47 +0000
1955@@ -16,6 +16,7 @@
1956 * Authors: James Henstridge <james.henstridge@canonical.com>
1957 */
1958
1959+#include <unity/storage/internal/dbus_error.h>
1960 #include <unity/storage/provider/ProviderBase.h>
1961 #include <unity/storage/provider/internal/DBusPeerCache.h>
1962 #include <unity/storage/provider/internal/AccountData.h>
1963@@ -54,7 +55,7 @@
1964 const auto SERVICE_CONNECTION_NAME = QStringLiteral("service-session-bus");
1965 const auto BUS_PATH = QStringLiteral("/provider");
1966 const auto PROVIDER_IFACE = QStringLiteral("com.canonical.StorageFramework.Provider");
1967-const char PROVIDER_ERROR[] = "com.canonical.StorageFramework.Provider.Error";
1968+const QString PROVIDER_ERROR = unity::storage::internal::DBUS_ERROR_PREFIX;
1969
1970 }
1971
1972@@ -165,13 +166,13 @@
1973 reply = client_->List("root_id", "bad_page_token");
1974 wait_for(reply);
1975 EXPECT_TRUE(reply.isError());
1976- EXPECT_EQ(PROVIDER_ERROR, reply.error().name());
1977- EXPECT_EQ("Unknown page token", reply.error().message());
1978+ EXPECT_EQ(PROVIDER_ERROR + "UnknownException", reply.error().name()) << reply.error().name().toStdString();
1979+ EXPECT_EQ("Unknown page token", reply.error().message()) << reply.error().message().toStdString();
1980
1981 reply = client_->List("no_such_folder_id", "");
1982 wait_for(reply);
1983 EXPECT_TRUE(reply.isError());
1984- EXPECT_EQ(PROVIDER_ERROR, reply.error().name());
1985+ EXPECT_EQ(PROVIDER_ERROR + "UnknownException", reply.error().name());
1986 EXPECT_EQ("Unknown folder", reply.error().message());
1987 }
1988
1989@@ -260,7 +261,7 @@
1990
1991 auto reply = client_->FinishUpload(upload_id);
1992 wait_for(reply);
1993- ASSERT_TRUE(reply.isValid());
1994+ ASSERT_TRUE(reply.isValid()) << reply.error().message().toStdString();
1995 auto item = reply.value();
1996 EXPECT_EQ("new_file_id", item.item_id);
1997 EXPECT_EQ("parent_id", item.parent_id);
1998@@ -329,7 +330,7 @@
1999 auto reply = client_->FinishUpload(upload_id);
2000 wait_for(reply);
2001 ASSERT_TRUE(reply.isError());
2002- EXPECT_EQ(PROVIDER_ERROR, reply.error().name());
2003+ EXPECT_EQ(PROVIDER_ERROR + "UnknownException", reply.error().name());
2004 EXPECT_EQ("wrong number of bytes written", reply.error().message());
2005 }
2006
2007@@ -403,7 +404,7 @@
2008 auto reply = client_->FinishUpload("no-such-upload");
2009 wait_for(reply);
2010 ASSERT_TRUE(reply.isError());
2011- EXPECT_EQ(PROVIDER_ERROR, reply.error().name());
2012+ EXPECT_EQ(PROVIDER_ERROR + "UnknownException", reply.error().name());
2013 EXPECT_EQ("map::at", reply.error().message());
2014 }
2015
2016@@ -466,7 +467,7 @@
2017 auto reply = client_->FinishDownload(download_id);
2018 wait_for(reply);
2019 ASSERT_TRUE(reply.isError());
2020- EXPECT_EQ(PROVIDER_ERROR, reply.error().name());
2021+ EXPECT_EQ(PROVIDER_ERROR + "UnknownException", reply.error().name());
2022 EXPECT_EQ("Not all data read", reply.error().message());
2023 }
2024
2025@@ -477,7 +478,7 @@
2026 auto reply = client_->FinishDownload("no-such-download");
2027 wait_for(reply);
2028 ASSERT_TRUE(reply.isError());
2029- EXPECT_EQ(PROVIDER_ERROR, reply.error().name());
2030+ EXPECT_EQ(PROVIDER_ERROR + "UnknownException", reply.error().name());
2031 EXPECT_EQ("map::at", reply.error().message());
2032 }
2033
2034
2035=== modified file 'tests/provider-ProviderInterface/TestProvider.cpp'
2036--- tests/provider-ProviderInterface/TestProvider.cpp 2016-08-09 06:47:45 +0000
2037+++ tests/provider-ProviderInterface/TestProvider.cpp 2016-08-11 02:49:47 +0000
2038@@ -18,6 +18,7 @@
2039
2040 #include "TestProvider.h"
2041 #include <unity/storage/provider/DownloadJob.h>
2042+#include <unity/storage/provider/Exceptions.h>
2043 #include <unity/storage/provider/UploadJob.h>
2044
2045 #include <QSocketNotifier>
2046
2047=== modified file 'tests/remote-client/remote-client_test.cpp'
2048--- tests/remote-client/remote-client_test.cpp 2016-08-03 03:20:39 +0000
2049+++ tests/remote-client/remote-client_test.cpp 2016-08-11 02:49:47 +0000
2050@@ -40,8 +40,6 @@
2051
2052 static constexpr int SIGNAL_WAIT_TIME = 1000;
2053
2054-// Bunch of helper function to reduce the amount of noise in the tests.
2055-
2056 class RemoteClientTest : public ::testing::Test
2057 {
2058 public:
2059@@ -73,6 +71,40 @@
2060 class FileTest : public RemoteClientTest {};
2061 class ItemTest : public RemoteClientTest {};
2062
2063+// Bunch of helper functions to reduce the amount of noise in the tests.
2064+
2065+template<typename T>
2066+void wait(T fut)
2067+{
2068+ QFutureWatcher<decltype(fut.result())> w;
2069+ QSignalSpy spy(&w, &decltype(w)::finished);
2070+ w.setFuture(fut);
2071+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2072+}
2073+
2074+template<>
2075+void wait(QFuture<void> fut)
2076+{
2077+ QFutureWatcher<void> w;
2078+ QSignalSpy spy(&w, &decltype(w)::finished);
2079+ w.setFuture(fut);
2080+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2081+}
2082+
2083+template <typename T>
2084+T call(QFuture<T> fut)
2085+{
2086+ wait(fut);
2087+ return fut.result();
2088+}
2089+
2090+template <>
2091+void call(QFuture<void> fut)
2092+{
2093+ wait(fut);
2094+ fut.waitForFinished();
2095+}
2096+
2097 Account::SPtr get_account(Runtime::SPtr const& runtime)
2098 {
2099 auto accounts_fut = runtime->accounts();
2100@@ -142,25 +174,6 @@
2101 }
2102 }
2103
2104-bool content_matches(File::SPtr const& file, QByteArray const& expected)
2105-{
2106- QFile f(file->native_identity());
2107- assert(f.open(QIODevice::ReadOnly));
2108- QByteArray buf = f.readAll();
2109- return buf == expected;
2110-}
2111-
2112-void write_file(Folder::SPtr const& folder, QString const& name, QByteArray const& contents)
2113-{
2114- QString ofile = folder->native_identity() + "/" + name;
2115- QFile f(ofile);
2116- assert(f.open(QIODevice::Truncate | QIODevice::WriteOnly));
2117- if (!contents.isEmpty())
2118- {
2119- assert(f.write(contents));
2120- }
2121-}
2122-
2123 TEST_F(RuntimeTest, lifecycle)
2124 {
2125 auto runtime = Runtime::create(connection());
2126@@ -174,9 +187,9 @@
2127
2128 auto acc = get_account(runtime);
2129 EXPECT_EQ(runtime, acc->runtime());
2130- qDebug() << "owner: " << acc->owner();
2131- qDebug() << "owner ID: " << acc->owner_id();
2132- qDebug() << "description:" << acc->description();
2133+ EXPECT_EQ("", acc->owner());
2134+ EXPECT_EQ("google-drive-scope", acc->owner_id()) << acc->owner_id().toStdString();
2135+ EXPECT_EQ("Fake google account", acc->description()) << acc->description().toStdString();
2136 }
2137
2138 TEST_F(RuntimeTest, roots)
2139@@ -253,7 +266,6 @@
2140 {
2141 auto runtime = Runtime::create(connection());
2142
2143- auto acc = get_account(runtime);
2144 auto root = get_root(runtime);
2145 clear_folder(root);
2146
2147@@ -301,105 +313,12 @@
2148 file = dynamic_pointer_cast<File>(get_fut.result());
2149 EXPECT_EQ("child_id", file->native_identity());
2150 EXPECT_EQ("Child", file->name());
2151-
2152-#if 0
2153- // Create a folder and check that it was created with correct type and name.
2154- auto create_folder_fut = root->create_folder("folder1");
2155- {
2156- QFutureWatcher<Folder::SPtr> w;
2157- QSignalSpy spy(&w, &decltype(w)::finished);
2158- w.setFuture(create_folder_fut);
2159- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2160- }
2161- EXPECT_EQ(ItemType::folder, folder->type());
2162- EXPECT_EQ("folder1", folder->name());
2163- EXPECT_EQ(root->native_identity() + "/folder1", folder->native_identity());
2164-
2165- // Check that we can find both file1 and folder1.
2166- auto item = root->lookup("file1").result();
2167- file = dynamic_pointer_cast<File>(item);
2168- ASSERT_NE(nullptr, file);
2169- EXPECT_EQ("file1", file->name());
2170- EXPECT_EQ(0, file->size());
2171-
2172- item = root->lookup("folder1").result();
2173- folder = dynamic_pointer_cast<Folder>(item);
2174- ASSERT_NE(nullptr, folder);
2175- ASSERT_EQ(nullptr, dynamic_pointer_cast<Root>(folder));
2176- EXPECT_EQ("folder1", folder->name());
2177-
2178- // Check that list() returns file1 and folder1.
2179- items = root->list();
2180- ASSERT_EQ(2, items.size());
2181- auto left = items[0];
2182- auto right = items[1];
2183- ASSERT_TRUE((dynamic_pointer_cast<File>(left) && dynamic_pointer_cast<Folder>(right))
2184- ||
2185- (dynamic_pointer_cast<File>(right) && dynamic_pointer_cast<Folder>(left)));
2186- if (dynamic_pointer_cast<File>(left))
2187- {
2188- file = dynamic_pointer_cast<File>(left);
2189- folder = dynamic_pointer_cast<Folder>(right);
2190- }
2191- else
2192- {
2193- file = dynamic_pointer_cast<File>(right);
2194- folder = dynamic_pointer_cast<Folder>(left);
2195- }
2196- EXPECT_EQ("file1", file->name());
2197- EXPECT_EQ("folder1", folder->name());
2198- EXPECT_TRUE(file->root()->equal_to(root));
2199- EXPECT_TRUE(folder->root()->equal_to(root));
2200-
2201- // Parent of both file and folder must be the root.
2202- EXPECT_TRUE(root->equal_to(get_parent(file)));
2203- EXPECT_TRUE(root->equal_to(get_parent(folder)));
2204- EXPECT_EQ(root->native_identity(), file->parent_ids()[0]);
2205- EXPECT_EQ(root->native_identity(), folder->parent_ids()[0]);
2206-
2207- // Destroy the file and check that only the directory is left.
2208- file->delete_item().waitForFinished();
2209- items = root->list().result();
2210- ASSERT_EQ(1, items.size());
2211- folder = dynamic_pointer_cast<Folder>(items[0]);
2212- ASSERT_NE(nullptr, folder);
2213- EXPECT_EQ("folder1", folder->name());;
2214-
2215- // Destroy the folder and check that the root is empty.
2216- folder->delete_item().waitForFinished();
2217- items = root->list().result();
2218- ASSERT_EQ(0, items.size());
2219-#endif
2220-}
2221-
2222-#if 0
2223-TEST_F(FolderTest, nested)
2224-{
2225- auto runtime = Runtime::create(connection());
2226-
2227- auto acc = get_account(runtime);
2228- auto root = get_root(runtime);
2229- clear_folder(root);
2230-
2231- auto d1 = root->create_folder("d1").result();
2232- auto d2 = d1->create_folder("d2").result();
2233-
2234- // Parent of d2 must be d1.
2235- EXPECT_TRUE(get_parent(d2)->equal_to(d1));
2236- EXPECT_TRUE(d2->parent_ids()[0] == d1->native_identity());
2237-
2238- // Destroy is recursive
2239- d1->delete_item().waitForFinished();
2240- auto items = root->list().result();
2241- ASSERT_EQ(0, items.size());
2242-}
2243-#endif
2244+}
2245
2246 TEST_F(FileTest, upload)
2247 {
2248 auto runtime = Runtime::create(connection());
2249
2250- auto acc = get_account(runtime);
2251 auto root = get_root(runtime);
2252 clear_folder(root);
2253
2254@@ -437,613 +356,645 @@
2255 auto uploaded_file = finish_upload_fut.result();
2256 EXPECT_EQ("some_id", uploaded_file->native_identity());
2257 EXPECT_EQ("some_upload", uploaded_file->name());
2258-
2259-#if 0
2260- QByteArray const contents = "Hello\n";
2261- auto written = uploader->socket()->write(contents);
2262- ASSERT_EQ(contents.size(), written);
2263-
2264- auto finish_upload_fut = uploader->finish_upload();
2265- {
2266- QFutureWatcher<File::SPtr> w;
2267- QSignalSpy spy(&w, &decltype(w)::finished);
2268- w.setFuture(finish_upload_fut);
2269- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2270- }
2271- auto file = finish_upload_fut.result();
2272- }
2273-
2274- {
2275- // Don't upload anything.
2276- auto uploader = root->create_file("new_file").result();
2277- auto file = uploader->file();
2278- uploader->socket()->disconnectFromServer();
2279-
2280- // We never write anything, so there is no disconnected signal from the socket.
2281-
2282- auto state = uploader->finish_upload().result();
2283- EXPECT_EQ(TransferState::ok, state);
2284- ASSERT_EQ(0, uploader->file()->size());
2285-
2286- file->delete_item().waitForFinished();
2287- }
2288-
2289- {
2290- // Let the uploader go out of scope and check
2291- // that the file was created regardless.
2292- auto file = root->create_file("new_file").result()->file();
2293- ASSERT_EQ(0, file->size());
2294-
2295- file->delete_item().waitForFinished();
2296- }
2297-#endif
2298-}
2299-#if 0
2300-
2301-TEST_F(FileTest, create_uploader)
2302-{
2303- auto runtime = Runtime::create(connection());
2304-
2305- auto acc = get_account(runtime);
2306- auto root = get_root(runtime);
2307- clear_folder(root);
2308-
2309- auto file = root->create_file("new_file").result()->file();
2310-
2311- {
2312- auto uploader = file->create_uploader(ConflictPolicy::overwrite).result();
2313-
2314- auto finish_fut = uploader->finish_upload();
2315- {
2316- QFutureWatcher<TransferState> w;
2317- QSignalSpy spy(&w, &decltype(w)::finished);
2318- w.setFuture(finish_fut);
2319- // We never disconnected from the socket, so the transfer is still in progress.
2320- ASSERT_FALSE(spy.wait(SIGNAL_WAIT_TIME));
2321- }
2322- uploader->socket()->disconnectFromServer();
2323- {
2324- QFutureWatcher<TransferState> w;
2325- QSignalSpy spy(&w, &decltype(w)::finished);
2326- w.setFuture(finish_fut);
2327- // Now that we have disconnected, the future must become ready.
2328- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2329- }
2330- EXPECT_EQ(TransferState::ok, finish_fut.result());
2331- }
2332-
2333- // Same test again, but this time we write a bunch of data and don't disconnect.
2334- {
2335- auto uploader = file->create_uploader(ConflictPolicy::overwrite).result();
2336-
2337- std::string s(1000000, 'a');
2338- uploader->socket()->write(&s[0], s.size());
2339-
2340- auto finish_fut = uploader->finish_upload();
2341- {
2342- QFutureWatcher<TransferState> w;
2343- QSignalSpy spy(&w, &decltype(w)::finished);
2344- w.setFuture(finish_fut);
2345- // We never disconnected from the socket, so the transfer is still in progress.
2346- ASSERT_FALSE(spy.wait(SIGNAL_WAIT_TIME));
2347- }
2348- uploader->socket()->disconnectFromServer();
2349- {
2350- QFutureWatcher<TransferState> w;
2351- QSignalSpy spy(&w, &decltype(w)::finished);
2352- w.setFuture(finish_fut);
2353- // Now that we have disconnected, the future must become ready.
2354- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2355- }
2356- EXPECT_EQ(TransferState::ok, finish_fut.result());
2357- }
2358-
2359- file->delete_item().waitForFinished();
2360-}
2361-
2362-TEST_F(FileTest, cancel_upload)
2363-{
2364- auto runtime = Runtime::create(connection());
2365-
2366- auto acc = get_account(runtime);
2367- auto root = get_root(runtime);
2368- clear_folder(root);
2369-
2370- {
2371- // Upload a few bytes.
2372- auto uploader = root->create_file("new_file").result();
2373-
2374- // We haven't written anything and haven't pumped the event loop,
2375- // so the cancel is guaranteed to catch the uploader in the in_progress state.
2376- uploader->cancel();
2377- EXPECT_EQ(TransferState::cancelled, uploader->finish_upload().result());
2378-
2379- auto file = uploader->file();
2380- EXPECT_EQ(0, file->size());
2381-
2382- file->delete_item().waitForFinished();
2383- }
2384-
2385- {
2386- // Create a file with a few bytes.
2387- QByteArray original_contents = "Hello World!\n";
2388- write_file(root, "new_file", original_contents);
2389- auto file = dynamic_pointer_cast<File>(root->lookup("new_file").result());
2390- ASSERT_NE(nullptr, file);
2391-
2392- // Create an uploader for the file and write a bunch of bytes.
2393- auto uploader = file->create_uploader(ConflictPolicy::overwrite).result();
2394- QByteArray const contents(1024, 'a');
2395- auto written = uploader->socket()->write(contents);
2396- ASSERT_EQ(contents.size(), written);
2397-
2398- QSignalSpy spy(uploader->socket().get(), &QLocalSocket::bytesWritten);
2399- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2400-
2401- // No disconnect here, so the transfer is still in progress. Now cancel.
2402- uploader->cancel().waitForFinished();
2403-
2404- // finish_upload() must indicate that the upload was cancelled.
2405- auto state = uploader->finish_upload().result();
2406- EXPECT_EQ(TransferState::cancelled, state);
2407-
2408- // The original file contents must still be intact.
2409- EXPECT_EQ(original_contents.size(), uploader->file()->size());
2410- ASSERT_TRUE(content_matches(uploader->file(), original_contents));
2411-
2412- file->delete_item().waitForFinished();
2413- }
2414-
2415- {
2416- // Upload a few bytes.
2417- auto uploader = root->create_file("new_file").result();
2418- auto file = uploader->file();
2419- QByteArray const contents = "Hello\n";
2420-
2421- // Finish the upload.
2422- auto written = uploader->socket()->write(contents);
2423- ASSERT_EQ(contents.size(), written);
2424- uploader->socket()->disconnectFromServer();
2425-
2426- // Pump the event loop for a bit, so the socket can finish doing its thing.
2427- QTimer timer;
2428- QSignalSpy spy(&timer, &QTimer::timeout);
2429- timer.start(SIGNAL_WAIT_TIME);
2430- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2431-
2432- // Now send the cancel. The upload is finished already, and the cancel
2433- // is too late, so finish_upload() must report that the upload
2434- // worked OK.
2435- uploader->cancel();
2436- EXPECT_EQ(TransferState::ok, uploader->finish_upload().result());
2437-
2438- file->delete_item().waitForFinished();
2439- }
2440-}
2441-
2442-TEST_F(FileTest, upload_conflict)
2443-{
2444- auto runtime = Runtime::create(connection());
2445-
2446- auto acc = get_account(runtime);
2447- auto root = get_root(runtime);
2448- clear_folder(root);
2449-
2450- // Make a new file.
2451- auto uploader = root->create_file("new_file").result();
2452- auto file = uploader->file();
2453-
2454- // Write a few bytes.
2455- QByteArray const contents = "Hello\n";
2456-
2457- // Pump the event loop for a bit, so the socket can finish doing its thing.
2458- QTimer timer;
2459- QSignalSpy spy(&timer, &QTimer::timeout);
2460- timer.start(SIGNAL_WAIT_TIME);
2461- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2462-
2463- // Touch the file on disk to give it a new time stamp.
2464- ASSERT_EQ(0, system((string("touch ") + file->native_identity().toStdString()).c_str()));
2465-
2466- // Finish the upload.
2467- uploader->socket()->disconnectFromServer();
2468+}
2469+
2470+TEST_F(RootTest, root_exceptions)
2471+{
2472+ auto runtime = Runtime::create(connection());
2473+
2474+ auto root = get_root(runtime);
2475+ clear_folder(root);
2476
2477 try
2478 {
2479- // Must get an exception because the time stamps no longer match.
2480- uploader->finish_upload().result();
2481+ call(root->delete_item());
2482 FAIL();
2483 }
2484- catch (ConflictException const&)
2485- {
2486- // TODO: check exception details.
2487- }
2488-
2489- file->delete_item().waitForFinished();
2490-}
2491-
2492-TEST_F(FileTest, download)
2493-{
2494- auto runtime = Runtime::create(connection());
2495-
2496- auto acc = get_account(runtime);
2497- auto root = get_root(runtime);
2498- clear_folder(root);
2499-
2500- {
2501- // Download a few bytes.
2502- QByteArray const contents = "Hello\n";
2503- write_file(root, "file", contents);
2504-
2505- auto item = root->lookup("file").result();
2506- File::SPtr file = dynamic_pointer_cast<File>(item);
2507- ASSERT_FALSE(file == nullptr);
2508-
2509- auto downloader = file->create_downloader().result();
2510- EXPECT_TRUE(file->equal_to(downloader->file()));
2511-
2512- auto socket = downloader->socket();
2513- QByteArray buf;
2514- do
2515- {
2516- // Need to pump the event loop while the socket does its thing.
2517- QSignalSpy spy(downloader->socket().get(), &QIODevice::readyRead);
2518- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2519- auto bytes_to_read = socket->bytesAvailable();
2520- buf.append(socket->read(bytes_to_read));
2521- } while (buf.size() < contents.size());
2522-
2523- // Wait for disconnected signal.
2524- QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);
2525- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2526-
2527- auto state = downloader->finish_download().result();
2528- EXPECT_EQ(TransferState::ok, state);
2529-
2530- // Contents must match.
2531- EXPECT_EQ(contents, buf);
2532- }
2533-
2534- {
2535- // Download exactly 64 KB.
2536- QByteArray const contents(64 * 1024, 'a');
2537- write_file(root, "file", contents);
2538-
2539- auto item = root->lookup("file").result();
2540- File::SPtr file = dynamic_pointer_cast<File>(item);
2541- ASSERT_FALSE(file == nullptr);
2542-
2543- auto downloader = file->create_downloader().result();
2544- EXPECT_TRUE(file->equal_to(downloader->file()));
2545-
2546- auto socket = downloader->socket();
2547- QByteArray buf;
2548- do
2549- {
2550- // Need to pump the event loop while the socket does its thing.
2551- QSignalSpy spy(downloader->socket().get(), &QIODevice::readyRead);
2552- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2553- auto bytes_to_read = socket->bytesAvailable();
2554- buf.append(socket->read(bytes_to_read));
2555- } while (buf.size() < contents.size());
2556-
2557- // Wait for disconnected signal.
2558- QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);
2559- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2560-
2561- auto state = downloader->finish_download().result();
2562- EXPECT_EQ(TransferState::ok, state);
2563-
2564- // Contents must match
2565- EXPECT_EQ(contents, buf);
2566- }
2567-
2568- {
2569- // Download 1 MB + 1 bytes.
2570- QByteArray const contents(1024 * 1024 + 1, 'a');
2571- write_file(root, "file", contents);
2572-
2573- auto item = root->lookup("file").result();
2574- File::SPtr file = dynamic_pointer_cast<File>(item);
2575- ASSERT_FALSE(file == nullptr);
2576-
2577- auto downloader = file->create_downloader().result();
2578- EXPECT_TRUE(file->equal_to(downloader->file()));
2579-
2580- auto socket = downloader->socket();
2581- QByteArray buf;
2582- do
2583- {
2584- // Need to pump the event loop while the socket does its thing.
2585- QSignalSpy spy(downloader->socket().get(), &QIODevice::readyRead);
2586- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2587- auto bytes_to_read = socket->bytesAvailable();
2588- buf.append(socket->read(bytes_to_read));
2589- } while (buf.size() < contents.size());
2590-
2591- // Wait for disconnected signal.
2592- QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);
2593- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2594-
2595- auto state = downloader->finish_download().result();
2596- EXPECT_EQ(TransferState::ok, state);
2597-
2598- // Contents must match
2599- EXPECT_EQ(contents, buf);
2600- }
2601-
2602- {
2603- // Download file containing zero bytes
2604- QByteArray const contents;
2605- write_file(root, "file", contents);
2606-
2607- auto item = root->lookup("file").result();
2608- File::SPtr file = dynamic_pointer_cast<File>(item);
2609- ASSERT_FALSE(file == nullptr);
2610-
2611- auto downloader = file->create_downloader().result();
2612- EXPECT_TRUE(file->equal_to(downloader->file()));
2613-
2614- auto socket = downloader->socket();
2615-
2616- // No readyRead every arrives in this case, just wait for disconnected.
2617- QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);
2618- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2619-
2620- auto state = downloader->finish_download().result();
2621- EXPECT_EQ(TransferState::ok, state);
2622- }
2623-
2624- {
2625- // Don't ever call read on empty file.
2626- QByteArray const contents;
2627- write_file(root, "file", contents);
2628-
2629- auto item = root->lookup("file").result();
2630- File::SPtr file = dynamic_pointer_cast<File>(item);
2631- ASSERT_FALSE(file == nullptr);
2632-
2633- auto downloader = file->create_downloader().result();
2634- EXPECT_TRUE(file->equal_to(downloader->file()));
2635-
2636- // This succeeds because the provider disconnects as soon
2637- // as it realizes that there is nothing to write.
2638- downloader->finish_download().result();
2639- }
2640-
2641- {
2642- // Don't ever call read on non-empty file.
2643- QByteArray const contents("some contents");
2644- write_file(root, "file", contents);
2645-
2646- auto item = root->lookup("file").result();
2647- File::SPtr file = dynamic_pointer_cast<File>(item);
2648- ASSERT_FALSE(file == nullptr);
2649-
2650- auto downloader = file->create_downloader().result();
2651- EXPECT_TRUE(file->equal_to(downloader->file()));
2652-
2653- try
2654- {
2655- downloader->finish_download().result();
2656- FAIL();
2657- }
2658- catch (StorageException const&)
2659- {
2660- // TODO: check exception details
2661- }
2662- }
2663-
2664- {
2665- // Let downloader go out of scope.
2666- QByteArray const contents("some contents");
2667- write_file(root, "file", contents);
2668-
2669- auto item = root->lookup("file").result();
2670- File::SPtr file = dynamic_pointer_cast<File>(item);
2671- ASSERT_FALSE(file == nullptr);
2672-
2673- auto downloader = file->create_downloader().result();
2674- }
2675-
2676- {
2677- // Let downloader future go out of scope.
2678- QByteArray const contents("some contents");
2679- write_file(root, "file", contents);
2680-
2681- auto item = root->lookup("file").result();
2682- File::SPtr file = dynamic_pointer_cast<File>(item);
2683- ASSERT_FALSE(file == nullptr);
2684-
2685- auto downloader_fut = file->create_downloader();
2686- }
2687-}
2688-
2689-TEST_F(FileTest, cancel_download)
2690-{
2691- auto runtime = Runtime::create(connection());
2692-
2693- auto acc = get_account(runtime);
2694- auto root = get_root(runtime);
2695- clear_folder(root);
2696-
2697- {
2698- // Download enough bytes to prevent a single read in the provider from completing the download.
2699- QByteArray const contents(1024 * 1024, 'a');
2700- write_file(root, "file", contents);
2701-
2702- auto item = root->lookup("file").result();
2703- File::SPtr file = dynamic_pointer_cast<File>(item);
2704- ASSERT_FALSE(file == nullptr);
2705-
2706- auto downloader = file->create_downloader().result();
2707- // We haven't read anything and haven't pumped the event loop,
2708- // so the cancel is guaranteed to catch the downloader in the in_progress state.
2709- downloader->cancel();
2710- EXPECT_EQ(TransferState::cancelled, downloader->finish_download().result());
2711- }
2712-
2713- {
2714- // Download a few bytes.
2715- QByteArray const contents = "Hello\n";
2716- write_file(root, "file", contents);
2717-
2718- auto item = root->lookup("file").result();
2719- File::SPtr file = dynamic_pointer_cast<File>(item);
2720- ASSERT_FALSE(file == nullptr);
2721-
2722- // Finish the download.
2723- auto downloader = file->create_downloader().result();
2724- auto socket = downloader->socket();
2725- QByteArray buf;
2726- do
2727- {
2728- // Need to pump the event loop while the socket does its thing.
2729- QSignalSpy spy(downloader->socket().get(), &QIODevice::readyRead);
2730- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2731- auto bytes_to_read = socket->bytesAvailable();
2732- buf.append(socket->read(bytes_to_read));
2733- } while (buf.size() < contents.size());
2734-
2735- // Wait for disconnected signal.
2736- QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);
2737- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2738-
2739- // Now send the cancel. The download is finished already, and the cancel
2740- // is too late, so finish_download() must report that the download
2741- // worked OK.
2742- downloader->cancel();
2743- EXPECT_EQ(TransferState::ok, downloader->finish_download().result());
2744- }
2745-}
2746-
2747-Test_F(ItemTest, move)
2748-{
2749- auto runtime = Runtime::create(connection());
2750-
2751- auto acc = get_account(runtime);
2752- auto root = get_root(runtime);
2753- clear_folder(root);
2754-
2755- // Check that rename works within the same folder.
2756- auto f1 = root->create_file("f1").result()->file();
2757- auto f2 = f1->move(root, "f2").result();
2758- EXPECT_EQ("f2", f2->name());
2759- EXPECT_THROW(f1->name(), DestroyedException); // TODO: check exception details.
2760-
2761- // File must be found under new name.
2762- auto items = root->list().result();
2763- ASSERT_EQ(1, items.size());
2764- f2 = dynamic_pointer_cast<File>(items[0]);
2765- ASSERT_FALSE(f2 == nullptr);
2766-
2767- // Make a folder and move f2 into it.
2768- auto folder = root->create_folder("folder").result();
2769- f2 = f2->move(folder, "f2").result();
2770- EXPECT_TRUE(get_parent(f2)->equal_to(folder));
2771-
2772- // Move the folder
2773- auto item = folder->move(root, "folder2").result();
2774- folder = dynamic_pointer_cast<Folder>(item);
2775- EXPECT_EQ("folder2", folder->name());
2776-}
2777-
2778-Test_F(ItemTest, copy)
2779-{
2780- auto runtime = Runtime::create(connection());
2781-
2782- auto acc = get_account(runtime);
2783- auto root = get_root(runtime);
2784- clear_folder(root);
2785-
2786- QByteArray const contents = "hello\n";
2787- write_file(root, "file", contents);
2788-
2789- auto item = root->lookup("file").result();
2790- auto copied_item = item->copy(root, "copy_of_file").result();
2791- EXPECT_EQ("copy_of_file", copied_item->name());
2792- File::SPtr copied_file = dynamic_pointer_cast<File>(item);
2793- ASSERT_NE(nullptr, copied_file);
2794- EXPECT_TRUE(content_matches(copied_file, contents));
2795-}
2796-
2797-Test_F(ItemTest, recursive_copy)
2798-{
2799- auto runtime = Runtime::create(connection());
2800-
2801- auto acc = get_account(runtime);
2802- auto root = get_root(runtime);
2803- clear_folder(root);
2804-
2805- // Create the following structure:
2806- // folder
2807- // folder/empty_folder
2808- // folder/non_empty_folder
2809- // folder/non_empty_folder/nested_file
2810- // folder/file
2811-
2812- string root_path = root->native_identity().toStdString();
2813- ASSERT_EQ(0, mkdir((root_path + "/folder").c_str(), 0700));
2814- ASSERT_EQ(0, mkdir((root_path + "/folder/empty_folder").c_str(), 0700));
2815- ASSERT_EQ(0, mkdir((root_path + "/folder/non_empty_folder").c_str(), 0700));
2816- ofstream(root_path + "/folder/non_empty_folder/nested_file");
2817- ofstream(root_path + "/folder/file");
2818-
2819- // Copy folder to folder2
2820- auto folder = dynamic_pointer_cast<Folder>(root->lookup("folder").result());
2821- ASSERT_NE(nullptr, folder);
2822- auto item = folder->copy(root, "folder2").result();
2823-
2824- // Verify that folder2 now contains the same structure as folder.
2825- auto folder2 = dynamic_pointer_cast<Folder>(item);
2826- ASSERT_NE(nullptr, folder2);
2827- EXPECT_NO_THROW(folder2->lookup("empty_folder").result());
2828- item = folder2->lookup("non_empty_folder").result();
2829- auto non_empty_folder = dynamic_pointer_cast<Folder>(item);
2830- ASSERT_NE(nullptr, non_empty_folder);
2831- EXPECT_NO_THROW(non_empty_folder->lookup("nested_file").result());
2832- EXPECT_NO_THROW(folder2->lookup("file").result());
2833-}
2834-
2835-Test_F(ItemTest, modified_time)
2836-{
2837- auto runtime = Runtime::create(connection());
2838-
2839- auto acc = get_account(runtime);
2840- auto root = get_root(runtime);
2841- clear_folder(root);
2842-
2843- auto now = QDateTime::currentDateTimeUtc();
2844- // Need to sleep because time_t provides only 1-second resolution.
2845- sleep(1);
2846- auto file = root->create_file("file").result()->file();
2847- auto t = file->last_modified_time();
2848- // Rough check that the time is sane.
2849- EXPECT_LE(now, t);
2850- EXPECT_LE(t, now.addSecs(5));
2851-}
2852-
2853-Test_F(ItemTest, comparison)
2854-{
2855- auto runtime = Runtime::create(connection());
2856-
2857- auto acc = get_account(runtime);
2858- auto root = get_root(runtime);
2859- clear_folder(root);
2860-
2861- // Create two files.
2862- auto file1 = root->create_file("file1").result()->file();
2863- auto file2 = root->create_file("file2").result()->file();
2864-
2865- EXPECT_FALSE(file1->equal_to(file2));
2866-
2867- // Retrieve file1 via lookup, so we get a different proxy.
2868- auto item = root->lookup("file1").result();
2869- auto other_file1 = dynamic_pointer_cast<File>(item);
2870- EXPECT_NE(file1, other_file1); // Compares shared_ptr values
2871- EXPECT_TRUE(file1->equal_to(other_file1)); // Deep comparison
2872-}
2873-#endif
2874+ catch (LogicException const& e)
2875+ {
2876+ EXPECT_EQ("Item::delete_item(): cannot delete root folder", e.error_message()) << e.what();
2877+ }
2878+
2879+ {
2880+ try
2881+ {
2882+ call(root->get("no_such_file_id"));
2883+ FAIL();
2884+ }
2885+ catch (NotExistsException const& e)
2886+ {
2887+ EXPECT_EQ("no_such_file_id", e.key());
2888+ }
2889+ }
2890+}
2891+
2892+TEST_F(RuntimeTest, runtime_destroyed_exceptions)
2893+{
2894+ // Gettting an account after shutting down the runtime must fail.
2895+ {
2896+ auto runtime = Runtime::create(connection());
2897+ auto acc = get_account(runtime);
2898+ runtime->shutdown();
2899+ try
2900+ {
2901+ acc->runtime();
2902+ FAIL();
2903+ }
2904+ catch (RuntimeDestroyedException const& e)
2905+ {
2906+ EXPECT_EQ("Account::runtime(): runtime was destroyed previously", e.error_message());
2907+ }
2908+ }
2909+
2910+ // Getting an account after destroying the runtime must fail.
2911+ {
2912+ auto runtime = Runtime::create(connection());
2913+ auto acc = get_account(runtime);
2914+ runtime.reset();
2915+ try
2916+ {
2917+ acc->runtime();
2918+ FAIL();
2919+ }
2920+ catch (RuntimeDestroyedException const& e)
2921+ {
2922+ EXPECT_EQ("Account::runtime(): runtime was destroyed previously", e.error_message());
2923+ }
2924+ }
2925+
2926+ // Getting accounts after shutting down the runtime must fail.
2927+ {
2928+ auto runtime = Runtime::create(connection());
2929+ runtime->shutdown();
2930+ try
2931+ {
2932+ call(runtime->accounts());
2933+ FAIL();
2934+ }
2935+ catch (RuntimeDestroyedException const& e)
2936+ {
2937+ EXPECT_EQ("Runtime::accounts(): runtime was destroyed previously", e.error_message());
2938+ }
2939+ }
2940+
2941+ // Getting the account from a root with a destroyed runtime must fail.
2942+ {
2943+ auto runtime = Runtime::create(connection());
2944+ auto root = get_root(runtime);
2945+ runtime.reset();
2946+ try
2947+ {
2948+ root->account();
2949+ FAIL();
2950+ }
2951+ catch (RuntimeDestroyedException const& e)
2952+ {
2953+ EXPECT_EQ("Root::account(): runtime was destroyed previously", e.error_message());
2954+ }
2955+ }
2956+
2957+ // Getting the account from a root with a destroyed account must fail.
2958+ {
2959+ auto runtime = Runtime::create(connection());
2960+ auto acc = get_account(runtime);
2961+ auto root = get_root(runtime);
2962+ runtime.reset();
2963+ acc.reset();
2964+ try
2965+ {
2966+ root->account();
2967+ FAIL();
2968+ }
2969+ catch (RuntimeDestroyedException const& e)
2970+ {
2971+ EXPECT_EQ("Root::account(): runtime was destroyed previously", e.error_message());
2972+ }
2973+ }
2974+
2975+ // Getting the root from an item with a destroyed runtime must fail.
2976+ {
2977+ auto runtime = Runtime::create(connection());
2978+ auto root = get_root(runtime);
2979+ clear_folder(root);
2980+
2981+ auto file = dynamic_pointer_cast<File>(call(root->get("child_id")));
2982+ runtime.reset();
2983+ try
2984+ {
2985+ file->root();
2986+ FAIL();
2987+ }
2988+ catch (RuntimeDestroyedException const& e)
2989+ {
2990+ EXPECT_EQ("Item::root(): runtime was destroyed previously", e.error_message());
2991+ }
2992+ }
2993+
2994+ // Getting the root from an item with a destroyed root must fail.
2995+ {
2996+ auto runtime = Runtime::create(connection());
2997+ auto acc = get_account(runtime);
2998+ auto root = get_root(runtime);
2999+ clear_folder(root);
3000+
3001+ auto file = dynamic_pointer_cast<File>(call(root->get("child_id")));
3002+ runtime.reset();
3003+ acc.reset();
3004+ root.reset();
3005+ try
3006+ {
3007+ file->root();
3008+ FAIL();
3009+ }
3010+ catch (RuntimeDestroyedException const& e)
3011+ {
3012+ EXPECT_EQ("Item::root(): runtime was destroyed previously", e.error_message());
3013+ }
3014+ }
3015+
3016+ // etag() with destroyed runtime must fail.
3017+ {
3018+ auto runtime = Runtime::create(connection());
3019+ auto root = get_root(runtime);
3020+ clear_folder(root);
3021+
3022+ auto file = dynamic_pointer_cast<File>(call(root->get("child_id")));
3023+ runtime->shutdown();
3024+ try
3025+ {
3026+ file->etag();
3027+ FAIL();
3028+ }
3029+ catch (RuntimeDestroyedException const& e)
3030+ {
3031+ EXPECT_EQ("Item::etag(): runtime was destroyed previously", e.error_message());
3032+ }
3033+ }
3034+
3035+ // metadata() with destroyed runtime must fail.
3036+ {
3037+ auto runtime = Runtime::create(connection());
3038+ auto root = get_root(runtime);
3039+ clear_folder(root);
3040+
3041+ auto file = dynamic_pointer_cast<File>(call(root->get("child_id")));
3042+ runtime->shutdown();
3043+ try
3044+ {
3045+ file->metadata();
3046+ FAIL();
3047+ }
3048+ catch (RuntimeDestroyedException const& e)
3049+ {
3050+ EXPECT_EQ("Item::metadata(): runtime was destroyed previously", e.error_message());
3051+ }
3052+ }
3053+
3054+ // last_modified_time() with destroyed runtime must fail.
3055+ {
3056+ auto runtime = Runtime::create(connection());
3057+ auto root = get_root(runtime);
3058+ clear_folder(root);
3059+
3060+ auto file = dynamic_pointer_cast<File>(call(root->get("child_id")));
3061+ runtime->shutdown();
3062+ try
3063+ {
3064+ file->last_modified_time();
3065+ FAIL();
3066+ }
3067+ catch (RuntimeDestroyedException const& e)
3068+ {
3069+ EXPECT_EQ("Item::last_modified_time(): runtime was destroyed previously", e.error_message());
3070+ }
3071+ }
3072+
3073+ // copy() with destroyed runtime must fail.
3074+ {
3075+ auto runtime = Runtime::create(connection());
3076+ auto root = get_root(runtime);
3077+ clear_folder(root);
3078+
3079+ auto file = dynamic_pointer_cast<File>(call(root->get("child_id")));
3080+ runtime->shutdown();
3081+ try
3082+ {
3083+ call(file->copy(root, "file2"));
3084+ FAIL();
3085+ }
3086+ catch (RuntimeDestroyedException const& e)
3087+ {
3088+ EXPECT_EQ("Item::copy(): runtime was destroyed previously", e.error_message());
3089+ }
3090+ }
3091+
3092+ // move() with destroyed runtime must fail.
3093+ {
3094+ auto runtime = Runtime::create(connection());
3095+ auto root = get_root(runtime);
3096+ clear_folder(root);
3097+
3098+ auto file = dynamic_pointer_cast<File>(call(root->get("child_id")));
3099+ runtime->shutdown();
3100+ try
3101+ {
3102+ call(file->move(root, "file2"));
3103+ FAIL();
3104+ }
3105+ catch (RuntimeDestroyedException const& e)
3106+ {
3107+ EXPECT_EQ("Item::move(): runtime was destroyed previously", e.error_message());
3108+ }
3109+ }
3110+
3111+ // parents() on root with destroyed runtime must fail.
3112+ {
3113+ auto runtime = Runtime::create(connection());
3114+ auto root = get_root(runtime);
3115+ clear_folder(root);
3116+
3117+ runtime->shutdown();
3118+ try
3119+ {
3120+ call(root->parents());
3121+ FAIL();
3122+ }
3123+ catch (RuntimeDestroyedException const& e)
3124+ {
3125+ EXPECT_EQ("Root::parents(): runtime was destroyed previously", e.error_message());
3126+ }
3127+ }
3128+
3129+ // parents() on file with destroyed runtime must fail.
3130+ {
3131+ auto runtime = Runtime::create(connection());
3132+ auto root = get_root(runtime);
3133+ clear_folder(root);
3134+
3135+ auto file = dynamic_pointer_cast<File>(call(root->get("child_id")));
3136+ runtime->shutdown();
3137+ try
3138+ {
3139+ call(file->parents());
3140+ FAIL();
3141+ }
3142+ catch (RuntimeDestroyedException const& e)
3143+ {
3144+ EXPECT_EQ("Item::parents(): runtime was destroyed previously", e.error_message());
3145+ }
3146+ }
3147+
3148+ // parent_ids() with destroyed runtime must fail.
3149+ {
3150+ auto runtime = Runtime::create(connection());
3151+ auto root = get_root(runtime);
3152+ clear_folder(root);
3153+
3154+ auto file = dynamic_pointer_cast<File>(call(root->get("child_id")));
3155+ runtime->shutdown();
3156+ try
3157+ {
3158+ file->parent_ids();
3159+ FAIL();
3160+ }
3161+ catch (RuntimeDestroyedException const& e)
3162+ {
3163+ EXPECT_EQ("Item::parent_ids(): runtime was destroyed previously", e.error_message());
3164+ }
3165+ }
3166+
3167+ // parent_ids() on root with destroyed runtime must fail.
3168+ {
3169+ auto runtime = Runtime::create(connection());
3170+ auto root = get_root(runtime);
3171+ clear_folder(root);
3172+
3173+ runtime->shutdown();
3174+ try
3175+ {
3176+ root->parent_ids();
3177+ FAIL();
3178+ }
3179+ catch (RuntimeDestroyedException const& e)
3180+ {
3181+ EXPECT_EQ("Root::parent_ids(): runtime was destroyed previously", e.error_message());
3182+ }
3183+ }
3184+
3185+ // delete_item() with destroyed runtime must fail.
3186+ {
3187+ auto runtime = Runtime::create(connection());
3188+ auto root = get_root(runtime);
3189+ clear_folder(root);
3190+
3191+ auto file = dynamic_pointer_cast<File>(call(root->get("child_id")));
3192+ runtime->shutdown();
3193+ try
3194+ {
3195+ call(file->delete_item());
3196+ FAIL();
3197+ }
3198+ catch (RuntimeDestroyedException const& e)
3199+ {
3200+ EXPECT_EQ("Item::delete_item(): runtime was destroyed previously", e.error_message());
3201+ }
3202+ }
3203+
3204+ // delete_item() on root with destroyed runtime must fail.
3205+ {
3206+ auto runtime = Runtime::create(connection());
3207+ auto root = get_root(runtime);
3208+ clear_folder(root);
3209+
3210+ runtime->shutdown();
3211+ try
3212+ {
3213+ call(root->delete_item());
3214+ FAIL();
3215+ }
3216+ catch (RuntimeDestroyedException const& e)
3217+ {
3218+ EXPECT_EQ("Item::delete_item(): runtime was destroyed previously", e.error_message());
3219+ }
3220+ }
3221+
3222+ // creation_time() with destroyed runtime must fail.
3223+ {
3224+ auto runtime = Runtime::create(connection());
3225+ auto root = get_root(runtime);
3226+ clear_folder(root);
3227+
3228+ auto file = dynamic_pointer_cast<File>(call(root->get("child_id")));
3229+ runtime->shutdown();
3230+ try
3231+ {
3232+ file->creation_time();
3233+ FAIL();
3234+ }
3235+ catch (RuntimeDestroyedException const& e)
3236+ {
3237+ EXPECT_EQ("Item::creation_time(): runtime was destroyed previously", e.error_message());
3238+ }
3239+ }
3240+
3241+ // native_metadata() with destroyed runtime must fail.
3242+ {
3243+ auto runtime = Runtime::create(connection());
3244+ auto root = get_root(runtime);
3245+ clear_folder(root);
3246+
3247+ auto file = dynamic_pointer_cast<File>(call(root->get("child_id")));
3248+ runtime->shutdown();
3249+ try
3250+ {
3251+ file->native_metadata();
3252+ FAIL();
3253+ }
3254+ catch (RuntimeDestroyedException const& e)
3255+ {
3256+ EXPECT_EQ("Item::native_metadata(): runtime was destroyed previously", e.error_message());
3257+ }
3258+ }
3259+
3260+ // name() on root with destroyed runtime must fail.
3261+ {
3262+ auto runtime = Runtime::create(connection());
3263+ auto root = get_root(runtime);
3264+ clear_folder(root);
3265+
3266+ runtime->shutdown();
3267+ try
3268+ {
3269+ root->name();
3270+ FAIL();
3271+ }
3272+ catch (RuntimeDestroyedException const& e)
3273+ {
3274+ EXPECT_EQ("Item::name(): runtime was destroyed previously", e.error_message());
3275+ }
3276+ }
3277+
3278+ // name() on folder with destroyed runtime must fail.
3279+ {
3280+ auto runtime = Runtime::create(connection());
3281+ auto root = get_root(runtime);
3282+ clear_folder(root);
3283+
3284+ auto folder = dynamic_pointer_cast<Folder>(call(root->get("child_folder_id")));
3285+ runtime->shutdown();
3286+ try
3287+ {
3288+ folder->name();
3289+ FAIL();
3290+ }
3291+ catch (RuntimeDestroyedException const& e)
3292+ {
3293+ EXPECT_EQ("Item::name(): runtime was destroyed previously", e.error_message());
3294+ }
3295+ }
3296+
3297+ // name() on file with destroyed runtime must fail.
3298+ {
3299+ auto runtime = Runtime::create(connection());
3300+ auto root = get_root(runtime);
3301+ clear_folder(root);
3302+
3303+ auto file = dynamic_pointer_cast<File>(call(root->get("child_id")));
3304+ runtime->shutdown();
3305+ try
3306+ {
3307+ file->name();
3308+ FAIL();
3309+ }
3310+ catch (RuntimeDestroyedException const& e)
3311+ {
3312+ EXPECT_EQ("Item::name(): runtime was destroyed previously", e.error_message());
3313+ }
3314+ }
3315+
3316+ // list() with destroyed runtime must fail.
3317+ {
3318+ auto runtime = Runtime::create(connection());
3319+ auto root = get_root(runtime);
3320+ clear_folder(root);
3321+
3322+ runtime->shutdown();
3323+ try
3324+ {
3325+ call(root->list());
3326+ FAIL();
3327+ }
3328+ catch (RuntimeDestroyedException const& e)
3329+ {
3330+ EXPECT_EQ("Folder::list(): runtime was destroyed previously", e.error_message());
3331+ }
3332+ }
3333+
3334+ // lookup() with destroyed runtime must fail.
3335+ {
3336+ auto runtime = Runtime::create(connection());
3337+ auto root = get_root(runtime);
3338+ clear_folder(root);
3339+
3340+ runtime->shutdown();
3341+ try
3342+ {
3343+ call(root->lookup("file"));
3344+ FAIL();
3345+ }
3346+ catch (RuntimeDestroyedException const& e)
3347+ {
3348+ EXPECT_EQ("Folder::lookup(): runtime was destroyed previously", e.error_message());
3349+ }
3350+ }
3351+
3352+ // create_folder() with destroyed runtime must fail.
3353+ {
3354+ auto runtime = Runtime::create(connection());
3355+ auto root = get_root(runtime);
3356+ clear_folder(root);
3357+
3358+ runtime->shutdown();
3359+ try
3360+ {
3361+ call(root->create_folder("folder"));
3362+ FAIL();
3363+ }
3364+ catch (RuntimeDestroyedException const& e)
3365+ {
3366+ EXPECT_EQ("Folder::create_folder(): runtime was destroyed previously", e.error_message());
3367+ }
3368+ }
3369+
3370+ // create_file() with destroyed runtime must fail.
3371+ {
3372+ auto runtime = Runtime::create(connection());
3373+ auto root = get_root(runtime);
3374+ clear_folder(root);
3375+
3376+ runtime->shutdown();
3377+ try
3378+ {
3379+ call(root->create_file("file", 0));
3380+ FAIL();
3381+ }
3382+ catch (RuntimeDestroyedException const& e)
3383+ {
3384+ EXPECT_EQ("Folder::create_file(): runtime was destroyed previously", e.error_message());
3385+ }
3386+ }
3387+
3388+ // size() with destroyed runtime must fail.
3389+ {
3390+ auto runtime = Runtime::create(connection());
3391+ auto root = get_root(runtime);
3392+ clear_folder(root);
3393+
3394+ auto file = dynamic_pointer_cast<File>(call(root->get("child_id")));
3395+ runtime->shutdown();
3396+ try
3397+ {
3398+ file->size();
3399+ FAIL();
3400+ }
3401+ catch (RuntimeDestroyedException const& e)
3402+ {
3403+ EXPECT_EQ("File::size(): runtime was destroyed previously", e.error_message());
3404+ }
3405+ }
3406+
3407+ // create_uploader() with destroyed runtime must fail.
3408+ {
3409+ auto runtime = Runtime::create(connection());
3410+ auto root = get_root(runtime);
3411+ clear_folder(root);
3412+
3413+ auto file = dynamic_pointer_cast<File>(call(root->get("child_id")));
3414+ runtime->shutdown();
3415+ try
3416+ {
3417+ call(file->create_uploader(ConflictPolicy::overwrite, 0));
3418+ FAIL();
3419+ }
3420+ catch (RuntimeDestroyedException const& e)
3421+ {
3422+ EXPECT_EQ("File::create_uploader(): runtime was destroyed previously", e.error_message()) << e.what();
3423+ }
3424+ }
3425+
3426+ // create_downloader() with destroyed runtime must fail.
3427+ {
3428+ auto runtime = Runtime::create(connection());
3429+ auto root = get_root(runtime);
3430+ clear_folder(root);
3431+
3432+ auto file = dynamic_pointer_cast<File>(call(root->get("child_id")));
3433+ runtime->shutdown();
3434+ try
3435+ {
3436+ call(file->create_downloader());
3437+ FAIL();
3438+ }
3439+ catch (RuntimeDestroyedException const& e)
3440+ {
3441+ EXPECT_EQ("File::create_downloader(): runtime was destroyed previously", e.error_message());
3442+ }
3443+ }
3444+
3445+ // free_space_bytes() with destroyed runtime must fail.
3446+ {
3447+ auto runtime = Runtime::create(connection());
3448+ auto root = get_root(runtime);
3449+ clear_folder(root);
3450+
3451+ runtime->shutdown();
3452+ try
3453+ {
3454+ call(root->free_space_bytes());
3455+ FAIL();
3456+ }
3457+ catch (RuntimeDestroyedException const& e)
3458+ {
3459+ EXPECT_EQ("Root::free_space_bytes(): runtime was destroyed previously", e.error_message());
3460+ }
3461+ }
3462+
3463+ // used_space_bytes() with destroyed runtime must fail.
3464+ {
3465+ auto runtime = Runtime::create(connection());
3466+ auto root = get_root(runtime);
3467+ clear_folder(root);
3468+
3469+ runtime->shutdown();
3470+ try
3471+ {
3472+ call(root->used_space_bytes());
3473+ FAIL();
3474+ }
3475+ catch (RuntimeDestroyedException const& e)
3476+ {
3477+ EXPECT_EQ("Root::used_space_bytes(): runtime was destroyed previously", e.error_message());
3478+ }
3479+ }
3480+
3481+ // get() with destroyed runtime must fail.
3482+ {
3483+ auto runtime = Runtime::create(connection());
3484+ auto root = get_root(runtime);
3485+ clear_folder(root);
3486+
3487+ runtime->shutdown();
3488+ try
3489+ {
3490+ call(root->get("some_id"));
3491+ FAIL();
3492+ }
3493+ catch (RuntimeDestroyedException const& e)
3494+ {
3495+ EXPECT_EQ("Root::get(): runtime was destroyed previously", e.error_message());
3496+ }
3497+ }
3498+}
3499
3500 int main(int argc, char** argv)
3501 {

Subscribers

People subscribed via source and target branches

to all changes: