Merge lp:~michihenning/storage-framework/more-coverage into lp:storage-framework/devel

Proposed by Michi Henning
Status: Merged
Approved by: James Henstridge
Approved revision: 80
Merged at revision: 41
Proposed branch: lp:~michihenning/storage-framework/more-coverage
Merge into: lp:storage-framework/devel
Prerequisite: lp:~michihenning/storage-framework/increase-coverage
Diff against target: 4904 lines (+1998/-1062)
25 files modified
include/unity/storage/qt/client/Exceptions.h (+1/-3)
include/unity/storage/qt/client/internal/ItemBase.h (+2/-0)
include/unity/storage/qt/client/internal/local_client/ItemImpl.h (+4/-4)
include/unity/storage/qt/client/internal/local_client/UploaderImpl.h (+1/-5)
include/unity/storage/qt/client/internal/local_client/storage_exception.h (+94/-0)
include/unity/storage/qt/client/internal/make_future.h (+14/-74)
include/unity/storage/qt/client/internal/remote_client/Handler.h (+1/-0)
include/unity/storage/qt/client/internal/remote_client/ItemImpl.h (+0/-3)
src/qt/client/Exceptions.cpp (+1/-7)
src/qt/client/internal/ItemBase.cpp (+15/-0)
src/qt/client/internal/local_client/CMakeLists.txt (+1/-0)
src/qt/client/internal/local_client/DownloaderImpl.cpp (+9/-0)
src/qt/client/internal/local_client/FileImpl.cpp (+23/-43)
src/qt/client/internal/local_client/FolderImpl.cpp (+72/-106)
src/qt/client/internal/local_client/ItemImpl.cpp (+131/-165)
src/qt/client/internal/local_client/RootImpl.cpp (+67/-62)
src/qt/client/internal/local_client/RuntimeImpl.cpp (+1/-0)
src/qt/client/internal/local_client/UploaderImpl.cpp (+16/-39)
src/qt/client/internal/local_client/storage_exception.cpp (+99/-0)
src/qt/client/internal/remote_client/FileImpl.cpp (+12/-19)
src/qt/client/internal/remote_client/FolderImpl.cpp (+19/-19)
src/qt/client/internal/remote_client/ItemImpl.cpp (+44/-82)
src/qt/client/internal/remote_client/RuntimeImpl.cpp (+1/-0)
tests/local-client/local-client_test.cpp (+1361/-422)
tests/remote-client/remote-client_test.cpp (+9/-9)
To merge this branch: bzr merge lp:~michihenning/storage-framework/more-coverage
Reviewer Review Type Date Requested Status
James Henstridge Approve
unity-api-1-bot continuous-integration Needs Fixing
Review via email: mp+301450@code.launchpad.net

This proposal supersedes a proposal from 2016-07-29.

Commit message

Improved coverage. Better error reporting/handling.

Description of the change

Very boring branch. Lots of extra tests for proper coverage, particularly for error handling/reporting. Refactored a bunch of code to get rid of repeated exception checks.

To post a comment you must log in.
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :
review: Needs Fixing (continuous-integration)
76. By Michi Henning

A few coverage suppressions.

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

FAILED: Continuous integration, rev:76
https://jenkins.canonical.com/unity-api-1/job/lp-storage-framework-ci/43/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build/266/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/272
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=vivid+overlay/206
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=xenial+overlay/206
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=yakkety/206
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/135/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/135/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/135/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/135/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/135/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/135/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/135/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/135/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/135/console

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

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

Fixed hang in test. Fixed sigpipe problem when cancelling upload.

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

FAILED: Continuous integration, rev:77
https://jenkins.canonical.com/unity-api-1/job/lp-storage-framework-ci/44/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build/271/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/277
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=vivid+overlay/211
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=xenial+overlay/211
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=yakkety/211
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/140/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/140/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/140/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/140/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/140/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/140/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/140/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/140/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/140/console

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

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

Added a few missing checks for destroyed runtime or item.

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

FAILED: Continuous integration, rev:78
https://jenkins.canonical.com/unity-api-1/job/lp-storage-framework-ci/47/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build/274/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/280
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=vivid+overlay/214
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=xenial+overlay/214
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=yakkety/214
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/143/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/143/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/143/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/143/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/143/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/143/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/143/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/143/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/143/console

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

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

Fixed incorrect check for parent destruction in copy() and move().

80. By Michi Henning

Fix typos.

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

FAILED: Continuous integration, rev:80
https://jenkins.canonical.com/unity-api-1/job/lp-storage-framework-ci/48/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build/275/console
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/281
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=vivid+overlay/215
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=xenial+overlay/215
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-1-sourcepkg/release=yakkety/215
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=vivid+overlay/144/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/144/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=yakkety/144/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=vivid+overlay/144/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/144/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=yakkety/144/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=vivid+overlay/144/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/144/console
    FAILURE: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=yakkety/144/console

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

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

Let's merge this.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/unity/storage/qt/client/Exceptions.h'
2--- include/unity/storage/qt/client/Exceptions.h 2016-08-03 06:10:39 +0000
3+++ include/unity/storage/qt/client/Exceptions.h 2016-08-03 06:10:39 +0000
4@@ -85,18 +85,16 @@
5 class UNITY_STORAGE_EXPORT DeletedException : public StorageException
6 {
7 public:
8- DeletedException(QString const& error_message, QString const& identity_, QString const& name_);
9+ DeletedException(QString const& error_message, QString const& identity_);
10 ~DeletedException();
11
12 virtual DeletedException* clone() const override;
13 virtual void raise() const override;
14
15 QString native_identity() const;
16- QString name() const;
17
18 private:
19 QString identity_;
20- QString name_;
21 };
22
23 /**
24
25=== modified file 'include/unity/storage/qt/client/internal/ItemBase.h'
26--- include/unity/storage/qt/client/internal/ItemBase.h 2016-08-03 06:10:39 +0000
27+++ include/unity/storage/qt/client/internal/ItemBase.h 2016-08-03 06:10:39 +0000
28@@ -83,11 +83,13 @@
29
30 protected:
31 std::shared_ptr<Root> get_root() const noexcept;
32+ void throw_if_destroyed(QString const& method) const;
33
34 const QString identity_;
35 const ItemType type_;
36 std::weak_ptr<Root> root_;
37 std::weak_ptr<Item> public_instance_;
38+ bool deleted_ = false;
39
40 friend class ItemImpl;
41 };
42
43=== modified file 'include/unity/storage/qt/client/internal/local_client/ItemImpl.h'
44--- include/unity/storage/qt/client/internal/local_client/ItemImpl.h 2016-08-03 06:10:39 +0000
45+++ include/unity/storage/qt/client/internal/local_client/ItemImpl.h 2016-08-03 06:10:39 +0000
46@@ -68,14 +68,14 @@
47 static boost::filesystem::path sanitize(QString const& name, QString const& method);
48 static bool is_reserved_path(boost::filesystem::path const& path) noexcept;
49
50- DeletedException deleted_ex(QString const& method) const noexcept;
51-
52- bool deleted_;
53 QString name_;
54 QString etag_;
55 QDateTime modified_time_;
56 QVariantMap metadata_;
57- std::mutex mutable mutex_;
58+ std::recursive_mutex mutable mutex_;
59+
60+private:
61+ static void copy_recursively(boost::filesystem::path const& source, boost::filesystem::path const& target);
62 };
63
64 } // namespace local_client
65
66=== modified file 'include/unity/storage/qt/client/internal/local_client/UploaderImpl.h'
67--- include/unity/storage/qt/client/internal/local_client/UploaderImpl.h 2016-08-03 06:10:39 +0000
68+++ include/unity/storage/qt/client/internal/local_client/UploaderImpl.h 2016-08-03 06:10:39 +0000
69@@ -68,7 +68,6 @@
70 private Q_SLOTS:
71 void on_bytes_ready();
72 void on_read_channel_finished();
73- void on_error();
74
75 private:
76 void read_and_write_chunk();
77@@ -91,11 +90,8 @@
78 QFutureInterface<std::shared_ptr<File>>& qf_;
79 QFutureInterface<void>& worker_initialized_;
80 QString error_msg_;
81-<<<<<<< TREE
82+ int error_code_ = 0;
83 bool use_linkat_ = true;
84-=======
85- int error_code_ = 0;
86->>>>>>> MERGE-SOURCE
87 };
88
89 class UploadThread : public QThread
90
91=== added file 'include/unity/storage/qt/client/internal/local_client/storage_exception.h'
92--- include/unity/storage/qt/client/internal/local_client/storage_exception.h 1970-01-01 00:00:00 +0000
93+++ include/unity/storage/qt/client/internal/local_client/storage_exception.h 2016-08-03 06:10:39 +0000
94@@ -0,0 +1,94 @@
95+/*
96+ * Copyright (C) 2016 Canonical Ltd
97+ *
98+ * This program is free software: you can redistribute it and/or modify
99+ * it under the terms of the GNU Lesser General Public License version 3 as
100+ * published by the Free Software Foundation.
101+ *
102+ * This program is distributed in the hope that it will be useful,
103+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
104+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
105+ * GNU Lesser General Public License for more details.
106+ *
107+ * You should have received a copy of the GNU Lesser General Public License
108+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
109+ *
110+ * Authors: Michi Henning <michi.henning@canonical.com>
111+ */
112+
113+#pragma once
114+
115+#include <unity/storage/qt/client/Exceptions.h>
116+#include <unity/storage/qt/client/internal/local_client/boost_filesystem.h>
117+
118+#pragma GCC diagnostic push
119+#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
120+#pragma GCC diagnostic ignored "-Wcast-align"
121+#include <QFuture>
122+#pragma GCC diagnostic pop
123+#include <QFutureInterface>
124+
125+class QString;
126+
127+namespace unity
128+{
129+namespace storage
130+{
131+namespace qt
132+{
133+namespace client
134+{
135+namespace internal
136+{
137+namespace local_client
138+{
139+
140+void throw_storage_exception(QString const& method,
141+ std::exception_ptr ep) __attribute__ ((noreturn));
142+
143+void throw_storage_exception(QString const& method,
144+ std::exception_ptr ep,
145+ QString const& key) __attribute__ ((noreturn));
146+
147+template<typename T>
148+QFuture<T> make_exceptional_future(QString const& method, std::exception_ptr ep)
149+{
150+ try
151+ {
152+ throw_storage_exception(method, ep);
153+ }
154+ catch (StorageException const& e)
155+ {
156+ QFutureInterface<T> qf;
157+ qf.reportException(e);
158+ qf.reportFinished();
159+ return qf.future();
160+ }
161+ abort(); // Impossible. // LCOV_EXCL_LINE
162+}
163+
164+template<typename T>
165+QFuture<T> make_exceptional_future(QString const& method,
166+ std::exception_ptr ep,
167+ QString const& key)
168+{
169+ try
170+ {
171+ throw_storage_exception(method, ep, key);
172+ }
173+ catch (StorageException const& e)
174+ {
175+ QFutureInterface<T> qf;
176+ qf.reportException(e);
177+ qf.reportFinished();
178+ return qf.future();
179+ }
180+ abort(); // Impossible. // LCOV_EXCL_LINE
181+}
182+
183+} // namespace local_client
184+} // namespace internal
185+} // namespace client
186+} // namespace qt
187+} // namespace storage
188+} // namespace unity
189
190=== renamed file 'include/unity/storage/qt/client/internal/local_client/tmpfile-prefix.h' => 'include/unity/storage/qt/client/internal/local_client/tmpfile_prefix.h'
191=== modified file 'include/unity/storage/qt/client/internal/make_future.h'
192--- include/unity/storage/qt/client/internal/make_future.h 2016-08-03 06:10:39 +0000
193+++ include/unity/storage/qt/client/internal/make_future.h 2016-08-03 06:10:39 +0000
194@@ -18,9 +18,6 @@
195
196 #pragma once
197
198-#include <unity/storage/qt/client/Exceptions.h>
199-#include <unity/storage/qt/client/internal/boost_filesystem.h>
200-
201 #pragma GCC diagnostic push
202 #pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
203 #pragma GCC diagnostic ignored "-Wcast-align"
204@@ -39,21 +36,6 @@
205 namespace internal
206 {
207
208-template<typename T = void>
209-QFuture<T> make_ready_future()
210-{
211- QFutureInterface<void> qf;
212- qf.reportFinished();
213- return qf.future();
214-}
215-
216-template<typename T = void>
217-QFuture<T> make_ready_future(QFutureInterface<T> qf)
218-{
219- qf.reportFinished();
220- return qf.future();
221-}
222-
223 template<typename T>
224 QFuture<T> make_ready_future(T const& val)
225 {
226@@ -71,6 +53,20 @@
227 return qf.future();
228 }
229
230+template<typename T = void>
231+QFuture<T> make_ready_future(QFutureInterface<T> qf)
232+{
233+ qf.reportFinished();
234+ return qf.future();
235+}
236+
237+template<typename T = void>
238+QFuture<T> make_ready_future()
239+{
240+ QFutureInterface<void> qf;
241+ return make_ready_future(qf);
242+}
243+
244 template<typename E>
245 QFuture<void> make_exceptional_future(E const& ex)
246 {
247@@ -97,62 +93,6 @@
248 return qf.future();
249 }
250
251-template<typename T>
252-QFuture<T> make_exceptional_future(QString const& msg, boost::filesystem::filesystem_error const& e)
253-{
254- QFutureInterface<T> qf;
255- switch (e.code().value())
256- {
257- case EACCES:
258- case EPERM:
259- {
260- qf.reportException(PermissionException(msg));
261- break;
262- }
263- case EDQUOT:
264- case ENOSPC:
265- {
266- qf.reportException(QuotaException(msg));
267- break;
268- }
269- case ENOENT:
270- {
271- //qf.reportException(NotExistsException(msg));
272- qDebug() << "ENOENT";
273- qf.reportException(ResourceException(msg, e.code().value()));
274- break;
275- }
276- default:
277- {
278- qf.reportException(ResourceException(msg, e.code().value()));
279- break;
280- }
281- }
282- qf.reportFinished();
283- return qf.future();
284-}
285-
286-template<typename T>
287-QFuture<T> make_exceptional_future(QString const& msg, boost::filesystem::filesystem_error const& e, QString const& key)
288-{
289- QFutureInterface<T> qf;
290- switch (e.code().value())
291- {
292- case ENOENT:
293- {
294- qf.reportException(NotExistsException(msg, key));
295- break;
296- }
297- default:
298- {
299- return make_exceptional_future<T>(msg, e);
300- break;
301- }
302- }
303- qf.reportFinished();
304- return qf.future();
305-}
306-
307 } // namespace internal
308 } // namespace client
309 } // namespace qt
310
311=== modified file 'include/unity/storage/qt/client/internal/remote_client/Handler.h'
312--- include/unity/storage/qt/client/internal/remote_client/Handler.h 2016-08-03 06:10:39 +0000
313+++ include/unity/storage/qt/client/internal/remote_client/Handler.h 2016-08-03 06:10:39 +0000
314@@ -18,6 +18,7 @@
315
316 #pragma once
317
318+#include <unity/storage/qt/client/Exceptions.h>
319 #include <unity/storage/qt/client/internal/make_future.h>
320 #include <unity/storage/qt/client/internal/remote_client/HandlerBase.h>
321
322
323=== modified file 'include/unity/storage/qt/client/internal/remote_client/ItemImpl.h'
324--- include/unity/storage/qt/client/internal/remote_client/ItemImpl.h 2016-07-22 00:17:24 +0000
325+++ include/unity/storage/qt/client/internal/remote_client/ItemImpl.h 2016-08-03 06:10:39 +0000
326@@ -66,9 +66,6 @@
327 static std::shared_ptr<Item> make_item(storage::internal::ItemMetadata const& md, std::weak_ptr<Root> root);
328
329 protected:
330- DeletedException deleted_ex(QString const& method) const noexcept;
331-
332- bool deleted_ = false;
333 storage::internal::ItemMetadata md_;
334
335 friend class DeleteHandler;
336
337=== modified file 'src/qt/client/Exceptions.cpp'
338--- src/qt/client/Exceptions.cpp 2016-08-03 06:10:39 +0000
339+++ src/qt/client/Exceptions.cpp 2016-08-03 06:10:39 +0000
340@@ -81,10 +81,9 @@
341 throw *this;
342 }
343
344-DeletedException::DeletedException(QString const& error_message, QString const& identity, QString const& name)
345+DeletedException::DeletedException(QString const& error_message, QString const& identity)
346 : StorageException("DeletedException", error_message)
347 , identity_(identity)
348- , name_(name)
349 {
350 }
351
352@@ -105,11 +104,6 @@
353 return identity_;
354 }
355
356-QString DeletedException::name() const
357-{
358- return name_;
359-}
360-
361 RuntimeDestroyedException::RuntimeDestroyedException(QString const& method)
362 : StorageException("RuntimeDestroyedException", method + ": Runtime was destroyed previously")
363 {
364
365=== modified file 'src/qt/client/internal/ItemBase.cpp'
366--- src/qt/client/internal/ItemBase.cpp 2016-08-03 06:10:39 +0000
367+++ src/qt/client/internal/ItemBase.cpp 2016-08-03 06:10:39 +0000
368@@ -48,11 +48,13 @@
369
370 QString ItemBase::native_identity() const
371 {
372+ throw_if_destroyed("Item::native_identity()");
373 return identity_;
374 }
375
376 ItemType ItemBase::type() const
377 {
378+ throw_if_destroyed("Item::type()");
379 return type_;
380 }
381
382@@ -95,6 +97,19 @@
383 return nullptr;
384 }
385
386+void ItemBase::throw_if_destroyed(QString const& method) const
387+{
388+ if (deleted_)
389+ {
390+ QString msg = method + ": \"" + identity_ + "\" was deleted previously";
391+ throw DeletedException(msg, identity_);
392+ }
393+ if (!get_root())
394+ {
395+ throw RuntimeDestroyedException(method);
396+ }
397+}
398+
399 } // namespace internal
400 } // namespace client
401 } // namespace qt
402
403=== modified file 'src/qt/client/internal/local_client/CMakeLists.txt'
404--- src/qt/client/internal/local_client/CMakeLists.txt 2016-07-22 00:17:24 +0000
405+++ src/qt/client/internal/local_client/CMakeLists.txt 2016-08-03 06:10:39 +0000
406@@ -7,6 +7,7 @@
407 ${CMAKE_CURRENT_SOURCE_DIR}/RootImpl.cpp
408 ${CMAKE_CURRENT_SOURCE_DIR}/Runtime_create.cpp
409 ${CMAKE_CURRENT_SOURCE_DIR}/RuntimeImpl.cpp
410+ ${CMAKE_CURRENT_SOURCE_DIR}/storage_exception.cpp
411 ${CMAKE_CURRENT_SOURCE_DIR}/UploaderImpl.cpp
412 ${CMAKE_SOURCE_DIR}/include/unity/storage/qt/client/internal/local_client/DownloaderImpl.h
413 ${CMAKE_SOURCE_DIR}/include/unity/storage/qt/client/internal/local_client/UploaderImpl.h
414
415=== modified file 'src/qt/client/internal/local_client/DownloaderImpl.cpp'
416--- src/qt/client/internal/local_client/DownloaderImpl.cpp 2016-08-03 06:10:39 +0000
417+++ src/qt/client/internal/local_client/DownloaderImpl.cpp 2016-08-03 06:10:39 +0000
418@@ -79,8 +79,10 @@
419 input_file_.reset(new QFile(filename_));
420 if (!input_file_->open(QIODevice::ReadOnly))
421 {
422+ // LCOV_EXCL_START
423 handle_error("cannot open " + filename_ + ": " + input_file_->errorString(), input_file_->error());
424 return;
425+ // LCOV_EXCL_STOP
426 }
427 bytes_to_write_ = input_file_->size();
428
429@@ -189,6 +191,7 @@
430
431 void DownloadWorker::on_error()
432 {
433+ disconnect(write_socket_.get(), nullptr, this, nullptr);
434 handle_error(write_socket_->errorString(), write_socket_->error());
435 }
436
437@@ -203,21 +206,27 @@
438 auto bytes_read = input_file_->read(buf.data(), buf.size());
439 if (bytes_read == -1)
440 {
441+ // LCOV_EXCL_START
442 handle_error(filename_ + ": read error: " + input_file_->errorString(), input_file_->error());
443 return;
444+ // LCOV_EXCL_STOP
445 }
446 buf.resize(bytes_read);
447
448 auto bytes_written = write_socket_->write(buf);
449 if (bytes_written == -1)
450 {
451+ // LCOV_EXCL_START
452 handle_error(filename_ + ": socket error: " + write_socket_->errorString(), write_socket_->error());
453+ // LCOV_EXCL_STOP
454 }
455 else if (bytes_written != bytes_read)
456 {
457+ // LCOV_EXCL_START
458 QString msg = filename_ + ": write error, requested " + bytes_read + " B, but wrote only "
459 + bytes_written + " B.";
460 handle_error(msg, 0);
461+ // LCOV_EXCL_STOP
462 }
463 }
464
465
466=== modified file 'src/qt/client/internal/local_client/FileImpl.cpp'
467--- src/qt/client/internal/local_client/FileImpl.cpp 2016-08-03 06:10:39 +0000
468+++ src/qt/client/internal/local_client/FileImpl.cpp 2016-08-03 06:10:39 +0000
469@@ -22,6 +22,7 @@
470 #include <unity/storage/qt/client/Exceptions.h>
471 #include <unity/storage/qt/client/File.h>
472 #include <unity/storage/qt/client/internal/local_client/DownloaderImpl.h>
473+#include <unity/storage/qt/client/internal/local_client/storage_exception.h>
474 #include <unity/storage/qt/client/internal/local_client/UploaderImpl.h>
475 #include <unity/storage/qt/client/internal/make_future.h>
476 #include <unity/storage/qt/client/Uploader.h>
477@@ -50,65 +51,44 @@
478
479 QString FileImpl::name() const
480 {
481- lock_guard<mutex> guard(mutex_);
482+ lock_guard<decltype(mutex_)> guard(mutex_);
483
484- if (deleted_)
485- {
486- throw deleted_ex("File::name()");
487- }
488- if (!get_root())
489- {
490- throw RuntimeDestroyedException("File::name()");
491- }
492+ throw_if_destroyed("File::name()");
493 return name_;
494 }
495
496 int64_t FileImpl::size() const
497 {
498- lock_guard<mutex> guard(mutex_);
499-
500- if (deleted_)
501- {
502- throw deleted_ex("File::size()");
503- }
504- if (!get_root())
505- {
506- throw RuntimeDestroyedException("File::size()");
507- }
508-
509+ lock_guard<decltype(mutex_)> guard(mutex_);
510+
511+ throw_if_destroyed("File::size()");
512 try
513 {
514 boost::filesystem::path p = identity_.toStdString();
515 return file_size(p);
516 }
517- catch (boost::filesystem::filesystem_error const& e)
518- {
519- throw ResourceException(e.what(), e.code().value());
520- }
521- catch (std::exception const& e)
522- {
523- throw ResourceException(e.what(), 0);
524+ catch (std::exception const&)
525+ {
526+ throw_storage_exception(QString("File::size()"), current_exception());
527 }
528 }
529
530 QFuture<Uploader::SPtr> FileImpl::create_uploader(ConflictPolicy policy, int64_t size)
531 {
532- lock_guard<mutex> guard(mutex_);
533+ lock_guard<decltype(mutex_)> guard(mutex_);
534
535- if (deleted_)
536- {
537- return make_exceptional_future<Uploader::SPtr>(deleted_ex("File::create_uploader()"));
538+ try
539+ {
540+ throw_if_destroyed("File::create_uploader()");
541+ }
542+ catch (StorageException const& e)
543+ {
544+ return internal::make_exceptional_future<Uploader::SPtr>(e);
545 }
546 if (size < 0)
547 {
548 QString msg = "File::create_uploader(): size must be >= 0";
549- return make_exceptional_future<shared_ptr<Uploader>>(InvalidArgumentException(msg));
550- }
551-
552- auto root = get_root();
553- if (!root)
554- {
555- return make_exceptional_future<Uploader::SPtr>(RuntimeDestroyedException("File::create_uploader()"));
556+ return internal::make_exceptional_future<shared_ptr<Uploader>>(InvalidArgumentException(msg));
557 }
558
559 auto file = dynamic_pointer_cast<File>(public_instance_.lock());
560@@ -120,15 +100,15 @@
561
562 QFuture<Downloader::SPtr> FileImpl::create_downloader()
563 {
564- lock_guard<mutex> guard(mutex_);
565+ lock_guard<decltype(mutex_)> guard(mutex_);
566
567- if (deleted_)
568+ try
569 {
570- return make_exceptional_future<Downloader::SPtr>(deleted_ex("File::create_downloader()"));
571+ throw_if_destroyed("File::create_downloader()");
572 }
573- if (!get_root())
574+ catch (StorageException const& e)
575 {
576- throw RuntimeDestroyedException("File::create_downloader()");
577+ return internal::make_exceptional_future<Downloader::SPtr>(e);
578 }
579
580 auto pi = public_instance_.lock();
581
582=== modified file 'src/qt/client/internal/local_client/FolderImpl.cpp'
583--- src/qt/client/internal/local_client/FolderImpl.cpp 2016-08-03 06:10:39 +0000
584+++ src/qt/client/internal/local_client/FolderImpl.cpp 2016-08-03 06:10:39 +0000
585@@ -24,7 +24,8 @@
586 #include <unity/storage/qt/client/Uploader.h>
587 #include <unity/storage/qt/client/internal/make_future.h>
588 #include <unity/storage/qt/client/internal/local_client/FileImpl.h>
589-#include <unity/storage/qt/client/internal/local_client/tmpfile-prefix.h>
590+#include <unity/storage/qt/client/internal/local_client/storage_exception.h>
591+#include <unity/storage/qt/client/internal/local_client/tmpfile_prefix.h>
592 #include <unity/storage/qt/client/internal/local_client/UploaderImpl.h>
593
594 #include <boost/algorithm/string/predicate.hpp>
595@@ -64,36 +65,34 @@
596
597 QString FolderImpl::name() const
598 {
599- lock_guard<mutex> guard(mutex_);
600+ lock_guard<decltype(mutex_)> guard(mutex_);
601
602- if (deleted_)
603- {
604- throw deleted_ex("Folder::name()");
605- }
606+ throw_if_destroyed("Folder::name()");
607 return name_;
608 }
609
610 QFuture<QVector<Item::SPtr>> FolderImpl::list() const
611 {
612+ try
613+ {
614+ throw_if_destroyed("Folder::list()");
615+ }
616+ catch (StorageException const& e)
617+ {
618+ return internal::make_exceptional_future<QVector<Item::SPtr>>(e);
619+ }
620+
621 auto This = dynamic_pointer_cast<FolderImpl const>(shared_from_this()); // Keep this folder alive while the lambda is alive.
622 auto list = [This]()
623 {
624- lock_guard<mutex> guard(This->mutex_);
625-
626- if (This->deleted_)
627- {
628- throw This->deleted_ex("Folder::list()");
629- }
630- auto root = This->get_root();
631- if (!root)
632- {
633- throw RuntimeDestroyedException("Folder::list()");
634- }
635-
636+ lock_guard<decltype(mutex_)> guard(This->mutex_);
637+
638+ This->throw_if_destroyed("Folder::list()");
639 try
640 {
641 using namespace boost::filesystem;
642
643+ auto root = This->root_.lock();
644 QVector<Item::SPtr> results;
645 for (directory_iterator it(This->native_identity().toStdString()); it != directory_iterator(); ++it)
646 {
647@@ -119,13 +118,9 @@
648 }
649 return results;
650 }
651- catch (boost::filesystem::filesystem_error const& e)
652- {
653- throw ResourceException(QString("Folder::list(): ") + e.what(), e.code().value());
654- }
655- catch (std::exception const& e)
656- {
657- throw ResourceException(QString("Folder::list(): ") + e.what(), errno);
658+ catch (std::exception const&)
659+ {
660+ throw_storage_exception("Folder::list()", current_exception());
661 }
662 };
663 return QtConcurrent::run(list);
664@@ -133,30 +128,31 @@
665
666 QFuture<QVector<Item::SPtr>> FolderImpl::lookup(QString const& name) const
667 {
668+ try
669+ {
670+ throw_if_destroyed("Folder::lookup()");
671+ }
672+ catch (StorageException const& e)
673+ {
674+ return internal::make_exceptional_future<QVector<Item::SPtr>>(e);
675+ }
676+
677 auto This = dynamic_pointer_cast<FolderImpl const>(shared_from_this()); // Keep this folder alive while the lambda is alive.
678 auto lookup = [This, name]() -> QVector<Item::SPtr>
679 {
680- lock_guard<mutex> guard(This->mutex_);
681-
682- if (This->deleted_)
683- {
684- throw This->deleted_ex("Folder::lookup()");
685- }
686- auto root = This->get_root();
687- if (!root)
688- {
689- throw RuntimeDestroyedException("Folder::lookup()");
690- }
691-
692+ lock_guard<decltype(mutex_)> guard(This->mutex_);
693+
694+ This->throw_if_destroyed("Folder::lookup()"); // LCOV_EXCL_LINE
695 try
696 {
697 using namespace boost::filesystem;
698
699+ auto root = This->root_.lock();
700 path p = This->native_identity().toStdString();
701 auto sanitized_name = sanitize(name, "Folder::lookup()");
702 if (is_reserved_path(sanitized_name))
703 {
704- throw NotExistsException("Folder::lookup(): no such item: " + name, name);
705+ throw NotExistsException("Folder::lookup(): no such item: \"" + name + "\"", name);
706 }
707 p /= sanitized_name;
708 file_status s = status(p);
709@@ -172,23 +168,11 @@
710 v.append(FileImpl::make_file(QString::fromStdString(p.native()), root));
711 return v;
712 }
713- throw NotExistsException("Folder::lookup(): no such item: " + name, name);
714- }
715- catch (StorageException const&)
716- {
717- throw;
718- }
719- catch (boost::filesystem::filesystem_error const& e)
720- {
721- if (e.code().value() == ENOENT)
722- {
723- throw NotExistsException("Folder::lookup(): no such item: " + name, name);
724- }
725- throw ResourceException(QString("Folder::lookup(): ") + e.what(), e.code().value());
726- }
727- catch (std::exception const& e)
728- {
729- throw ResourceException(QString("Folder::lookup(): ") + e.what(), errno);
730+ throw NotExistsException("Folder::lookup(): no such item: \"" + name + "\"", name);
731+ }
732+ catch (std::exception const&)
733+ {
734+ throw_storage_exception("Folder::lookup()", current_exception());
735 }
736 };
737 return QtConcurrent::run(lookup);
738@@ -196,17 +180,15 @@
739
740 QFuture<Folder::SPtr> FolderImpl::create_folder(QString const& name)
741 {
742- lock_guard<mutex> guard(mutex_);
743+ lock_guard<decltype(mutex_)> guard(mutex_);
744
745- QFutureInterface<Folder::SPtr> qf;
746- if (deleted_)
747+ try
748 {
749- return make_exceptional_future<Folder::SPtr>(deleted_ex("Folder::create_folder()"));
750+ throw_if_destroyed("Folder::create_folder()");
751 }
752- auto root = get_root();
753- if (!root)
754+ catch (StorageException const& e)
755 {
756- return make_exceptional_future<Folder::SPtr>(RuntimeDestroyedException("Folder::create_folder()"));
757+ return internal::make_exceptional_future<Folder::SPtr>(e);
758 }
759
760 try
761@@ -217,46 +199,40 @@
762 auto sanitized_name = sanitize(name, "Folder::create_folder()");
763 if (is_reserved_path(sanitized_name))
764 {
765- QString msg = "Folder::create_folder(): names beginning with " + QString(TMPFILE_PREFIX) + " are reserved";
766- return make_exceptional_future<Folder::SPtr>(InvalidArgumentException(msg));
767+ QString msg = "Folder::create_folder(): names beginning with \"" + QString(TMPFILE_PREFIX) + "\" are reserved";
768+ throw InvalidArgumentException(msg);
769 }
770 p /= sanitized_name;
771+ if (exists(p))
772+ {
773+ QString msg = "Folder::create_folder(): item with name \"" + name + "\" exists already";
774+ throw ExistsException(msg, native_identity() + "/" + name, name);
775+ }
776 create_directory(p);
777- return make_ready_future(make_folder(QString::fromStdString(p.native()), root));
778- }
779- catch (StorageException const& e)
780- {
781- return make_exceptional_future<Folder::SPtr>(e);
782- }
783- catch (boost::filesystem::filesystem_error const& e)
784- {
785- return make_exceptional_future<Folder::SPtr>(ResourceException(QString("Folder::create_folder: ") + e.what(),
786- e.code().value()));
787- }
788- catch (std::exception const& e)
789- {
790- return make_exceptional_future<Folder::SPtr>(ResourceException(QString("Folder::create_folder: ") + e.what(),
791- errno));
792+ return make_ready_future(make_folder(QString::fromStdString(p.native()), root_));
793+ }
794+ catch (std::exception const&)
795+ {
796+ return make_exceptional_future<Folder::SPtr>("Folder::create_folder()", current_exception());
797 }
798 }
799
800 QFuture<shared_ptr<Uploader>> FolderImpl::create_file(QString const& name, int64_t size)
801 {
802- unique_lock<mutex> guard(mutex_);
803+ lock_guard<decltype(mutex_)> guard(mutex_);
804
805- if (deleted_)
806- {
807- return make_exceptional_future<Uploader::SPtr>(deleted_ex("Folder::create_file()"));
808+ try
809+ {
810+ throw_if_destroyed("Folder::create_file()");
811+ }
812+ catch (StorageException const& e)
813+ {
814+ return internal::make_exceptional_future<shared_ptr<Uploader>>(e);
815 }
816 if (size < 0)
817 {
818 QString msg = "Folder::create_file(): size must be >= 0";
819- return make_exceptional_future<shared_ptr<Uploader>>(InvalidArgumentException(msg));
820- }
821- auto root = get_root();
822- if (!root)
823- {
824- return make_exceptional_future<Uploader::SPtr>(RuntimeDestroyedException("Folder::create_file()"));
825+ return internal::make_exceptional_future<shared_ptr<Uploader>>(InvalidArgumentException(msg));
826 }
827
828 try
829@@ -267,36 +243,26 @@
830 auto sanitized_name = sanitize(name, "Folder::create_file()");
831 if (is_reserved_path(sanitized_name))
832 {
833- QString msg = "Folder::create_file(): names beginning with " + QString(TMPFILE_PREFIX) + " are reserved";
834- return make_exceptional_future<Uploader::SPtr>(InvalidArgumentException(msg));
835+ QString msg = "Folder::create_file(): names beginning with \"" + QString(TMPFILE_PREFIX) + "\" are reserved";
836+ throw InvalidArgumentException(msg);
837 }
838 p /= sanitized_name;
839 if (exists(p))
840 {
841 QString msg = "Folder::create_file(): item with name \"" + name + "\" exists already";
842- return make_exceptional_future<Uploader::SPtr>(ExistsException(msg, native_identity(), name));
843+ throw ExistsException(msg, native_identity() + "/" + name, name);
844 }
845 auto impl = new UploaderImpl(shared_ptr<File>(),
846 size,
847 QString::fromStdString(p.native()),
848 ConflictPolicy::error_if_conflict,
849- root);
850+ root_);
851 Uploader::SPtr uploader(new Uploader(impl));
852 return make_ready_future(uploader);
853 }
854- catch (StorageException const& e)
855- {
856- return make_exceptional_future<Uploader::SPtr>(e);
857- }
858- catch (boost::filesystem::filesystem_error const& e)
859- {
860- return make_exceptional_future<Uploader::SPtr>(ResourceException(QString("Folder::create_file: ") + e.what(),
861- e.code().value()));
862- }
863- catch (std::exception const& e)
864- {
865- return make_exceptional_future<Uploader::SPtr>(ResourceException(QString("Folder::create_file: ") + e.what(),
866- errno));
867+ catch (std::exception const&)
868+ {
869+ return make_exceptional_future<Uploader::SPtr>("Folder::create_file()", current_exception());
870 }
871 }
872
873
874=== modified file 'src/qt/client/internal/local_client/ItemImpl.cpp'
875--- src/qt/client/internal/local_client/ItemImpl.cpp 2016-08-03 06:10:39 +0000
876+++ src/qt/client/internal/local_client/ItemImpl.cpp 2016-08-03 06:10:39 +0000
877@@ -21,11 +21,12 @@
878 #include <unity/storage/internal/safe_strerror.h>
879 #include <unity/storage/qt/client/Account.h>
880 #include <unity/storage/qt/client/Exceptions.h>
881-#include <unity/storage/qt/client/internal/make_future.h>
882 #include <unity/storage/qt/client/internal/local_client/AccountImpl.h>
883 #include <unity/storage/qt/client/internal/local_client/FileImpl.h>
884 #include <unity/storage/qt/client/internal/local_client/RootImpl.h>
885-#include <unity/storage/qt/client/internal/local_client/tmpfile-prefix.h>
886+#include <unity/storage/qt/client/internal/local_client/storage_exception.h>
887+#include <unity/storage/qt/client/internal/local_client/tmpfile_prefix.h>
888+#include <unity/storage/qt/client/internal/make_future.h>
889
890 #include <boost/algorithm/string/predicate.hpp>
891 #pragma GCC diagnostic push
892@@ -54,7 +55,6 @@
893
894 ItemImpl::ItemImpl(QString const& identity, ItemType type)
895 : ItemBase(identity, type)
896- , deleted_(false)
897 {
898 assert(!identity.isEmpty());
899 auto path = boost::filesystem::canonical(identity.toStdString());
900@@ -66,87 +66,49 @@
901
902 QString ItemImpl::etag() const
903 {
904- lock_guard<mutex> guard(mutex_);
905+ lock_guard<decltype(mutex_)> guard(mutex_);
906
907- if (deleted_)
908- {
909- throw deleted_ex("Item::etag()");
910- }
911- if (!get_root())
912- {
913- throw RuntimeDestroyedException("Item::etag()");
914- }
915+ throw_if_destroyed("Item::etag()");
916 return etag_;
917 }
918
919 QVariantMap ItemImpl::metadata() const
920 {
921- lock_guard<mutex> guard(mutex_);
922+ lock_guard<decltype(mutex_)> guard(mutex_);
923
924- if (deleted_)
925- {
926- throw deleted_ex("Item::metadata()");
927- }
928- if (!get_root())
929- {
930- throw RuntimeDestroyedException("Item::metadata()");
931- }
932+ throw_if_destroyed("Item::metadata()");
933 return metadata_;
934 }
935
936 QDateTime ItemImpl::last_modified_time() const
937 {
938- lock_guard<mutex> guard(mutex_);
939+ lock_guard<decltype(mutex_)> guard(mutex_);
940
941- if (deleted_)
942- {
943- throw deleted_ex("Item::last_modified_time()");
944- }
945- if (!get_root())
946- {
947- throw RuntimeDestroyedException("Item::last_modified_time()");
948- }
949+ throw_if_destroyed("Item::last_modified_time()");
950 return modified_time_;
951 }
952
953-namespace
954-{
955-
956-using namespace boost::filesystem;
957-
958-void copy_recursively(path const& source, path const& target)
959-{
960- auto s = status(source);
961- if (is_regular_file(s))
962- {
963- copy_file(source, target);
964- return;
965- }
966- else if (is_directory(s))
967- {
968- copy_directory(source, target); // Poorly named in boost; this creates the target dir without recursion
969- for (directory_iterator it(source); it != directory_iterator(); ++it)
970- {
971- path source_entry = it->path();
972- path target_entry = target;
973- target_entry /= source_entry.filename();
974- copy_recursively(source_entry, target_entry);
975- }
976- }
977- else
978- {
979- // Ignore everything that's not a directory or file.
980- }
981-}
982-
983-} // namespace
984-
985 QFuture<shared_ptr<Item>> ItemImpl::copy(shared_ptr<Folder> const& new_parent, QString const& new_name)
986 {
987 if (!new_parent)
988 {
989 QString msg = "Item::copy(): new_parent cannot be nullptr";
990- return make_exceptional_future<shared_ptr<Item>>(InvalidArgumentException(msg));
991+ return internal::make_exceptional_future<shared_ptr<Item>>(InvalidArgumentException(msg));
992+ }
993+ auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);
994+
995+ lock(mutex_, new_parent_impl->mutex_);
996+ lock_guard<decltype(mutex_)> this_guard(mutex_, std::adopt_lock);
997+ lock_guard<decltype(mutex_)> other_guard(new_parent_impl->mutex_, adopt_lock);
998+
999+ try
1000+ {
1001+ throw_if_destroyed("Item::copy()");
1002+ new_parent_impl->throw_if_destroyed("Item::copy()");
1003+ }
1004+ catch (StorageException const& e)
1005+ {
1006+ return internal::make_exceptional_future<shared_ptr<Item>>(e);
1007 }
1008
1009 auto This = dynamic_pointer_cast<ItemImpl>(shared_from_this()); // Keep this item alive while the lambda is alive.
1010@@ -155,23 +117,17 @@
1011 auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);
1012
1013 lock(This->mutex_, new_parent_impl->mutex_);
1014- lock_guard<mutex> this_guard(This->mutex_, std::adopt_lock);
1015- lock_guard<mutex> other_guard(new_parent_impl->mutex_, adopt_lock);
1016+ lock_guard<decltype(mutex_)> this_guard(This->mutex_, std::adopt_lock);
1017+ lock_guard<decltype(mutex_)> other_guard(new_parent_impl->mutex_, adopt_lock);
1018
1019- if (This->deleted_)
1020- {
1021- throw This->deleted_ex("Item::copy()");
1022- }
1023- if (new_parent_impl->deleted_)
1024- {
1025- throw new_parent_impl->deleted_ex("Item::copy()");
1026- }
1027+ This->throw_if_destroyed("Item::copy()");
1028+ new_parent_impl->throw_if_destroyed("Item::copy()");
1029
1030 // TODO: This needs to deeply compare account identity because the client may have refreshed the accounts list.
1031 if (This->root()->account() != new_parent->root()->account()) // Throws if account or runtime were destroyed.
1032 {
1033 // Can't do cross-account copy.
1034- QString msg = QString("Item::copy(): Source (") + This->name_ + ") and target ("
1035+ QString msg = QString("Item::copy(): source (") + This->name_ + ") and target ("
1036 + new_name + ") must belong to the same account";
1037 throw LogicException(msg);
1038 }
1039@@ -187,22 +143,22 @@
1040 target_path /= sanitized_name;
1041 if (is_reserved_path(target_path))
1042 {
1043- QString msg = "Item::copy(): names beginning with " + QString(TMPFILE_PREFIX) + " are reserved";
1044+ QString msg = "Item::copy(): names beginning with \"" + QString(TMPFILE_PREFIX) + "\" are reserved";
1045 throw InvalidArgumentException(msg);
1046 }
1047
1048+ if (exists(target_path))
1049+ {
1050+ QString msg = "Item::copy(): item with name \"" + new_name + "\" exists already";
1051+ throw ExistsException(msg, This->identity_, This->name_);
1052+ }
1053+
1054 if (This->type_ == ItemType::file)
1055 {
1056 copy_file(source_path, target_path);
1057 return FileImpl::make_file(QString::fromStdString(target_path.native()), new_parent_impl->root_);
1058 }
1059
1060- if (exists(target_path))
1061- {
1062- QString msg = "Item::copy(): item with name \"" + new_name + "\" exists already";
1063- throw ExistsException(msg, This->identity_, This->name_);
1064- }
1065-
1066 // For recursive copy, we create a temporary directory in lieu of target_path and recursively copy
1067 // everything into the temporary directory. This ensures that we don't invalidate directory iterators
1068 // by creating things while we are iterating, potentially getting trapped in an infinite loop.
1069@@ -211,7 +167,7 @@
1070 create_directories(tmp_path);
1071 for (directory_iterator it(source_path); it != directory_iterator(); ++it)
1072 {
1073- if (tmp_path.compare(canonical(it->path())) == 0)
1074+ if (is_reserved_path(it->path()))
1075 {
1076 continue; // Don't recurse into the temporary directory
1077 }
1078@@ -221,23 +177,15 @@
1079 path source_entry = it->path();
1080 path target_entry = tmp_path;
1081 target_entry /= source_entry.filename();
1082- copy_recursively(source_entry, target_entry);
1083+ ItemImpl::copy_recursively(source_entry, target_entry);
1084 }
1085 }
1086 rename(tmp_path, target_path);
1087 return FolderImpl::make_folder(QString::fromStdString(target_path.native()), new_parent_impl->root_);
1088 }
1089- catch (StorageException const&)
1090- {
1091- throw;
1092- }
1093- catch (boost::filesystem::filesystem_error const& e)
1094- {
1095- throw ResourceException(QString("Item::copy(): ") + e.what(), e.code().value());
1096- }
1097- catch (std::exception const& e)
1098- {
1099- throw ResourceException(QString("Item::copy(): ") + e.what(), errno);
1100+ catch (std::exception const&)
1101+ {
1102+ throw_storage_exception("Item::copy()", current_exception());
1103 }
1104 };
1105 return QtConcurrent::run(copy);
1106@@ -248,7 +196,22 @@
1107 if (!new_parent)
1108 {
1109 QString msg = "Item::move(): new_parent cannot be nullptr";
1110- return make_exceptional_future<shared_ptr<Item>>(InvalidArgumentException(msg));
1111+ return internal::make_exceptional_future<shared_ptr<Item>>(InvalidArgumentException(msg));
1112+ }
1113+ auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);
1114+
1115+ lock(mutex_, new_parent_impl->mutex_);
1116+ lock_guard<decltype(mutex_)> this_guard(mutex_, std::adopt_lock);
1117+ lock_guard<decltype(mutex_)> other_guard(new_parent_impl->mutex_, adopt_lock);
1118+
1119+ try
1120+ {
1121+ throw_if_destroyed("Item::move()");
1122+ new_parent_impl->throw_if_destroyed("Item::move()");
1123+ }
1124+ catch (StorageException const& e)
1125+ {
1126+ return internal::make_exceptional_future<shared_ptr<Item>>(e);
1127 }
1128
1129 auto This = dynamic_pointer_cast<ItemImpl>(shared_from_this()); // Keep this item alive while the lambda is alive.
1130@@ -257,30 +220,24 @@
1131 auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);
1132
1133 lock(This->mutex_, new_parent_impl->mutex_);
1134- lock_guard<mutex> this_guard(This->mutex_, std::adopt_lock);
1135- lock_guard<mutex> other_guard(new_parent_impl->mutex_, adopt_lock);
1136+ lock_guard<decltype(mutex_)> this_guard(This->mutex_, std::adopt_lock);
1137+ lock_guard<decltype(mutex_)> other_guard(new_parent_impl->mutex_, adopt_lock);
1138
1139- if (This->deleted_)
1140- {
1141- throw This->deleted_ex("Item::move()");
1142- }
1143- if (new_parent_impl->deleted_)
1144- {
1145- throw new_parent_impl->deleted_ex("Item::move()");
1146- }
1147+ This->throw_if_destroyed("Item::move()");
1148+ new_parent_impl->throw_if_destroyed("Item::move()");
1149
1150 // TODO: This needs to deeply compare account identity because the client may have refreshed the accounts list.
1151 if (This->root()->account() != new_parent->root()->account()) // Throws if account or runtime were destroyed.
1152 {
1153 // Can't do cross-account move.
1154- QString msg = QString("Item::move(): Source (") + This->name_ + ") and target ("
1155+ QString msg = QString("Item::move(): source (") + This->name_ + ") and target ("
1156 + new_name + ") must belong to the same account";
1157 throw LogicException(msg);
1158 }
1159 if (This->type_ == ItemType::root)
1160 {
1161 // Can't move a root.
1162- throw LogicException("Item::move(): Cannot move root folder");
1163+ throw LogicException("Item::move(): cannot move root folder");
1164 }
1165
1166 try
1167@@ -296,7 +253,7 @@
1168 }
1169 if (is_reserved_path(target_path))
1170 {
1171- QString msg = "Item::move(): names beginning with " + QString(TMPFILE_PREFIX) + " are reserved";
1172+ QString msg = "Item::move(): names beginning with \"" + QString(TMPFILE_PREFIX) + "\" are reserved";
1173 throw InvalidArgumentException(msg);
1174 }
1175 rename(This->native_identity().toStdString(), target_path);
1176@@ -307,17 +264,9 @@
1177 }
1178 return FileImpl::make_file(QString::fromStdString(target_path.native()), new_parent_impl->root_);
1179 }
1180- catch (StorageException const&)
1181- {
1182- throw;
1183- }
1184- catch (boost::filesystem::filesystem_error const& e)
1185- {
1186- throw ResourceException(QString("Item::move(): ") + e.what(), e.code().value());
1187- }
1188- catch (std::exception const& e)
1189- {
1190- throw ResourceException(QString("Item::move(): ") + e.what(), errno);
1191+ catch (std::exception const&)
1192+ {
1193+ throw_storage_exception(QString("Item::move(): "), current_exception());
1194 }
1195 };
1196 return QtConcurrent::run(move);
1197@@ -325,18 +274,15 @@
1198
1199 QFuture<QVector<Folder::SPtr>> ItemImpl::parents() const
1200 {
1201- lock_guard<mutex> guard(mutex_);
1202+ lock_guard<decltype(mutex_)> guard(mutex_);
1203
1204- QFutureInterface<QVector<Folder::SPtr>> qf;
1205- if (deleted_)
1206+ try
1207 {
1208- return make_exceptional_future<QVector<Folder::SPtr>>(deleted_ex("Item::parents()"));
1209+ throw_if_destroyed("Item::parents()");
1210 }
1211-
1212- auto root = get_root();
1213- if (!root)
1214+ catch (StorageException const& e)
1215 {
1216- return make_exceptional_future<QVector<Folder::SPtr>>(RuntimeDestroyedException("Item::parents()"));
1217+ return internal::make_exceptional_future<QVector<Folder::SPtr>>(e);
1218 }
1219
1220 using namespace boost::filesystem;
1221@@ -345,6 +291,7 @@
1222 path p = native_identity().toStdString();
1223 QString parent_path = QString::fromStdString(p.parent_path().native());
1224
1225+ auto root = root_.lock();
1226 QVector<Folder::SPtr> results;
1227 if (parent_path != root->native_identity())
1228 {
1229@@ -359,16 +306,9 @@
1230
1231 QVector<QString> ItemImpl::parent_ids() const
1232 {
1233- lock_guard<mutex> guard(mutex_);
1234+ lock_guard<decltype(mutex_)> guard(mutex_);
1235
1236- if (deleted_)
1237- {
1238- throw deleted_ex("Item::parent_ids()");
1239- }
1240- if (!get_root())
1241- {
1242- throw RuntimeDestroyedException("Item::parent_ids()");
1243- }
1244+ throw_if_destroyed("Item::parent_ids()");
1245
1246 using namespace boost::filesystem;
1247
1248@@ -383,36 +323,31 @@
1249
1250 QFuture<void> ItemImpl::delete_item()
1251 {
1252+ lock_guard<decltype(mutex_)> guard(mutex_);
1253+
1254+ try
1255+ {
1256+ throw_if_destroyed("Item::delete_item()");
1257+ }
1258+ catch (StorageException const& e)
1259+ {
1260+ return internal::make_exceptional_future(e);
1261+ }
1262+
1263 auto This = dynamic_pointer_cast<ItemImpl>(shared_from_this()); // Keep this item alive while the lambda is alive.
1264 auto destroy = [This]()
1265 {
1266- lock_guard<mutex> guard(This->mutex_);
1267-
1268- if (This->deleted_)
1269- {
1270- throw This->deleted_ex("Item::delete_item()");
1271- }
1272- if (!This->get_root())
1273- {
1274- throw RuntimeDestroyedException("Item::delete_item()");
1275- }
1276-
1277+ lock_guard<decltype(mutex_)> guard(This->mutex_);
1278+
1279+ This->throw_if_destroyed("Item::delete_item()");
1280 try
1281 {
1282 boost::filesystem::remove_all(This->native_identity().toStdString());
1283 This->deleted_ = true;
1284 }
1285- catch (StorageException const&)
1286- {
1287- throw;
1288- }
1289- catch (boost::filesystem::filesystem_error const& e)
1290- {
1291- throw ResourceException(QString("Item::delete_item(): ") + e.what(), e.code().value());
1292- }
1293- catch (std::exception const& e)
1294- {
1295- throw ResourceException(QString("Item::delete_item(): ") + e.what(), errno);
1296+ catch (std::exception const&)
1297+ {
1298+ throw_storage_exception(QString("Item::delete_item()"), current_exception());
1299 }
1300 };
1301 return QtConcurrent::run(destroy);
1302@@ -420,11 +355,17 @@
1303
1304 QDateTime ItemImpl::creation_time() const
1305 {
1306+ lock_guard<decltype(mutex_)> guard(mutex_);
1307+
1308+ throw_if_destroyed("Item::creation_time()");
1309 return QDateTime();
1310 }
1311
1312 MetadataMap ItemImpl::native_metadata() const
1313 {
1314+ lock_guard<decltype(mutex_)> guard(mutex_);
1315+
1316+ throw_if_destroyed("Item::native_metadata()");
1317 return MetadataMap();
1318 }
1319
1320@@ -438,8 +379,8 @@
1321 }
1322
1323 lock(mutex_, other_impl->mutex_);
1324- lock_guard<mutex> this_guard(mutex_, std::adopt_lock);
1325- lock_guard<mutex> other_guard(other_impl->mutex_, adopt_lock);
1326+ lock_guard<decltype(mutex_)> this_guard(mutex_, std::adopt_lock);
1327+ lock_guard<decltype(mutex_)> other_guard(other_impl->mutex_, adopt_lock);
1328
1329 if (deleted_ || other_impl->deleted_)
1330 {
1331@@ -450,7 +391,7 @@
1332
1333 void ItemImpl::set_timestamps() noexcept
1334 {
1335- lock_guard<mutex> guard(mutex_);
1336+ lock_guard<decltype(mutex_)> guard(mutex_);
1337
1338 string id = identity_.toStdString();
1339 // Use nano-second resolution for the ETag, if the file system supports it.
1340@@ -467,7 +408,7 @@
1341
1342 bool ItemImpl::has_conflict() const noexcept
1343 {
1344- lock_guard<mutex> guard(mutex_);
1345+ lock_guard<decltype(mutex_)> guard(mutex_);
1346
1347 string id = identity_.toStdString();
1348 struct stat st;
1349@@ -515,10 +456,35 @@
1350 return boost::starts_with(filename, TMPFILE_PREFIX);
1351 }
1352
1353-DeletedException ItemImpl::deleted_ex(QString const& method) const noexcept
1354+void ItemImpl::copy_recursively(boost::filesystem::path const& source, boost::filesystem::path const& target)
1355 {
1356- QString msg = method + ": \"" + identity_ + "\" was deleted previously";
1357- return DeletedException(msg, identity_, name_);
1358+ using namespace boost::filesystem;
1359+
1360+ if (is_reserved_path(source))
1361+ {
1362+ return; // Don't copy temporary directories.
1363+ }
1364+
1365+ auto s = status(source);
1366+ if (is_regular_file(s))
1367+ {
1368+ copy_file(source, target);
1369+ }
1370+ else if (is_directory(s))
1371+ {
1372+ copy_directory(source, target); // Poorly named in boost; this creates the target dir without recursion
1373+ for (directory_iterator it(source); it != directory_iterator(); ++it)
1374+ {
1375+ path source_entry = it->path();
1376+ path target_entry = target;
1377+ target_entry /= source_entry.filename();
1378+ copy_recursively(source_entry, target_entry);
1379+ }
1380+ }
1381+ else
1382+ {
1383+ // Ignore everything that's not a directory or file.
1384+ }
1385 }
1386
1387 } // namespace local_client
1388
1389=== modified file 'src/qt/client/internal/local_client/RootImpl.cpp'
1390--- src/qt/client/internal/local_client/RootImpl.cpp 2016-08-03 06:10:39 +0000
1391+++ src/qt/client/internal/local_client/RootImpl.cpp 2016-08-03 06:10:39 +0000
1392@@ -21,6 +21,7 @@
1393 #include <unity/storage/qt/client/Exceptions.h>
1394 #include <unity/storage/qt/client/internal/make_future.h>
1395 #include <unity/storage/qt/client/internal/local_client/FileImpl.h>
1396+#include <unity/storage/qt/client/internal/local_client/storage_exception.h>
1397 #include <unity/storage/qt/client/Root.h>
1398
1399 using namespace std;
1400@@ -68,46 +69,55 @@
1401
1402 QString RootImpl::name() const
1403 {
1404- if (!get_root())
1405- {
1406- throw RuntimeDestroyedException("Root::name()");
1407- }
1408+ lock_guard<decltype(mutex_)> guard(mutex_);
1409+
1410+ throw_if_destroyed("Root::name()");
1411 return "";
1412 }
1413
1414 QFuture<QVector<Folder::SPtr>> RootImpl::parents() const
1415 {
1416- if (!get_root())
1417- {
1418- throw RuntimeDestroyedException("Root::parents()");
1419- }
1420+ lock_guard<decltype(mutex_)> guard(mutex_);
1421+
1422+ throw_if_destroyed("Root::parents()");
1423 return make_ready_future(QVector<Folder::SPtr>()); // For the root, we return an empty vector.
1424 }
1425
1426 QVector<QString> RootImpl::parent_ids() const
1427 {
1428- if (!get_root())
1429- {
1430- throw RuntimeDestroyedException("Root::parent_ids()");
1431- }
1432+ lock_guard<decltype(mutex_)> guard(mutex_);
1433+
1434+ throw_if_destroyed("Root::parent_ids()");
1435 return QVector<QString>(); // For the root, we return an empty vector.
1436 }
1437
1438 QFuture<void> RootImpl::delete_item()
1439 {
1440- if (!get_root())
1441- {
1442- throw RuntimeDestroyedException("Root::delete_item()");
1443+ lock_guard<decltype(mutex_)> guard(mutex_);
1444+
1445+ try
1446+ {
1447+ throw_if_destroyed("Root::delete_item()");
1448+ }
1449+ catch (StorageException const& e)
1450+ {
1451+ return internal::make_exceptional_future(e);
1452 }
1453 // Cannot delete root.
1454- return make_exceptional_future(LogicException("Root::delete_item(): Cannot delete root folder"));
1455+ return internal::make_exceptional_future(LogicException("Root::delete_item(): Cannot delete root folder"));
1456 }
1457
1458 QFuture<int64_t> RootImpl::free_space_bytes() const
1459 {
1460- if (!get_root())
1461- {
1462- throw RuntimeDestroyedException("Root::free_space_bytes()");
1463+ lock_guard<decltype(mutex_)> guard(mutex_);
1464+
1465+ try
1466+ {
1467+ throw_if_destroyed("Root::free_space_bytes()");
1468+ }
1469+ catch (StorageException const& e)
1470+ {
1471+ return internal::make_exceptional_future<int64_t>(e);
1472 }
1473
1474 using namespace boost::filesystem;
1475@@ -118,24 +128,24 @@
1476 return make_ready_future<int64_t>(si.available);
1477 }
1478 // LCOV_EXCL_START
1479- catch (boost::filesystem::filesystem_error const& e)
1480- {
1481- return make_exceptional_future<int64_t>(ResourceException(QString("Root::free_space_bytes(): ") + e.what(),
1482- e.code().value()));
1483- }
1484- catch (std::exception const& e)
1485- {
1486- return make_exceptional_future<int64_t>(ResourceException(QString("Root::free_space_bytes(): ") + e.what(),
1487- errno));
1488+ catch (std::exception const&)
1489+ {
1490+ return make_exceptional_future<int64_t>(QString("Root::free_space_bytes()"), current_exception());
1491 }
1492 // LCOV_EXCL_STOP
1493 }
1494
1495 QFuture<int64_t> RootImpl::used_space_bytes() const
1496 {
1497- if (!get_root())
1498- {
1499- throw RuntimeDestroyedException("Root::used_space_bytes()");
1500+ lock_guard<decltype(mutex_)> guard(mutex_);
1501+
1502+ try
1503+ {
1504+ throw_if_destroyed("Root::used_space_bytes()");
1505+ }
1506+ catch (StorageException const& e)
1507+ {
1508+ return internal::make_exceptional_future<int64_t>(e);
1509 }
1510
1511 using namespace boost::filesystem;
1512@@ -146,25 +156,30 @@
1513 return make_ready_future<int64_t>(si.capacity - si.available);
1514 }
1515 // LCOV_EXCL_START
1516- catch (boost::filesystem::filesystem_error const& e)
1517- {
1518- return make_exceptional_future<int64_t>(ResourceException(QString("Root::used_space_bytes(): ") + e.what(),
1519- e.code().value()));
1520- }
1521- catch (std::exception const& e)
1522- {
1523- return make_exceptional_future<int64_t>(ResourceException(QString("Root::used_space_bytes(): ") + e.what(),
1524- errno));
1525+ catch (std::exception const&)
1526+ {
1527+ return make_exceptional_future<int64_t>(QString("Root::used_space_bytes()"), current_exception());
1528 }
1529 // LCOV_EXCL_STOP
1530 }
1531
1532 QFuture<Item::SPtr> RootImpl::get(QString native_identity) const
1533 {
1534+ lock_guard<decltype(mutex_)> guard(mutex_);
1535+
1536+ try
1537+ {
1538+ throw_if_destroyed("Root::get()");
1539+ }
1540+ catch (StorageException const& e)
1541+ {
1542+ return internal::make_exceptional_future<Item::SPtr>(e);
1543+ }
1544+
1545 auto root = get_root();
1546 if (!root)
1547 {
1548- throw RuntimeDestroyedException("Root::native_identity()");
1549+ return internal::make_exceptional_future<Item::SPtr>(RuntimeDestroyedException("Root::get()"));
1550 }
1551
1552 using namespace boost::filesystem;
1553@@ -176,7 +191,7 @@
1554 if (!id_path.is_absolute())
1555 {
1556 QString msg = "Root::get(): identity \"" + native_identity + "\" must be an absolute path";
1557- return make_exceptional_future<Item::SPtr>(InvalidArgumentException(msg));
1558+ throw InvalidArgumentException(msg);
1559 }
1560
1561 // Make sure that native_identity is contained in or equal to the root path.
1562@@ -189,14 +204,14 @@
1563 // Too few components, or wrong path prefix. Therefore, native_identity can't
1564 // possibly point at something below the root.
1565 QString msg = QString("Root::get(): identity \"") + native_identity + "\" points outside the root folder";
1566- return make_exceptional_future<Item::SPtr>(InvalidArgumentException(msg));
1567+ throw InvalidArgumentException(msg);
1568 }
1569
1570 // Don't allow reserved files to be found.
1571 if (is_reserved_path(id_path))
1572 {
1573- QString msg = "Root::get(): no such item: " + native_identity;
1574- return make_exceptional_future<Item::SPtr>(NotExistsException(msg, native_identity));
1575+ QString msg = "Root::get(): no such item: \"" + native_identity + "\"";
1576+ throw NotExistsException(msg, native_identity);
1577 }
1578
1579 file_status s = status(id_path);
1580@@ -213,23 +228,13 @@
1581 {
1582 return make_ready_future<Item::SPtr>(FileImpl::make_file(path, root));
1583 }
1584- QString msg = "Root::get(): no such item: " + native_identity;
1585- return make_exceptional_future<Item::SPtr>(NotExistsException(msg, native_identity));
1586- }
1587- catch (StorageException const& e)
1588- {
1589- return make_exceptional_future<Item::SPtr>(e);
1590- }
1591- catch (boost::filesystem::filesystem_error const& e)
1592- {
1593- return make_exceptional_future<Item::SPtr>(QString("Root::get(): ") + e.what(), e, native_identity);
1594- }
1595- // LCOV_EXCL_START
1596- catch (std::exception const& e)
1597- {
1598- return make_exceptional_future<Item::SPtr>(ResourceException(QString("Root::get(): ") + e.what(), errno));
1599- }
1600- // LCOV_EXCL_STOP
1601+ QString msg = "Root::get(): no such item: \"" + native_identity + "\"";
1602+ throw NotExistsException(msg, native_identity);
1603+ }
1604+ catch (std::exception const&)
1605+ {
1606+ return make_exceptional_future<Item::SPtr>(QString("Root::get()"), current_exception(), native_identity);
1607+ }
1608 }
1609
1610 Root::SPtr RootImpl::make_root(QString const& identity, std::weak_ptr<Account> const& account)
1611
1612=== modified file 'src/qt/client/internal/local_client/RuntimeImpl.cpp'
1613--- src/qt/client/internal/local_client/RuntimeImpl.cpp 2016-08-03 06:10:39 +0000
1614+++ src/qt/client/internal/local_client/RuntimeImpl.cpp 2016-08-03 06:10:39 +0000
1615@@ -19,6 +19,7 @@
1616 #include <unity/storage/qt/client/internal/local_client/RuntimeImpl.h>
1617
1618 #include <unity/storage/qt/client/Account.h>
1619+#include <unity/storage/qt/client/Exceptions.h>
1620 #include <unity/storage/qt/client/internal/make_future.h>
1621 #include <unity/storage/qt/client/internal/local_client/AccountImpl.h>
1622
1623
1624=== modified file 'src/qt/client/internal/local_client/UploaderImpl.cpp'
1625--- src/qt/client/internal/local_client/UploaderImpl.cpp 2016-08-03 06:10:39 +0000
1626+++ src/qt/client/internal/local_client/UploaderImpl.cpp 2016-08-03 06:10:39 +0000
1627@@ -22,7 +22,7 @@
1628 #include <unity/storage/qt/client/Exceptions.h>
1629 #include <unity/storage/qt/client/File.h>
1630 #include <unity/storage/qt/client/internal/local_client/FileImpl.h>
1631-#include <unity/storage/qt/client/internal/local_client/tmpfile-prefix.h>
1632+#include <unity/storage/qt/client/internal/local_client/tmpfile_prefix.h>
1633 #include <unity/storage/qt/client/internal/make_future.h>
1634
1635 #include <QLocalSocket>
1636@@ -93,8 +93,6 @@
1637 // Monitor read socket for ready-to-read, disconnected, and error events.
1638 connect(read_socket_.get(), &QLocalSocket::readyRead, this, &UploadWorker::on_bytes_ready);
1639 connect(read_socket_.get(), &QIODevice::readChannelFinished, this, &UploadWorker::on_read_channel_finished);
1640- connect(read_socket_.get(), static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error),
1641- this, &UploadWorker::on_error);
1642
1643 using namespace boost::filesystem;
1644
1645@@ -114,10 +112,11 @@
1646 tmp_fd_.reset(mkstemp(const_cast<char*>(tmpfile.data())));
1647 if (tmp_fd_.get() == -1)
1648 {
1649+ int error_code = errno;
1650 worker_initialized_.reportFinished();
1651 QString msg = "cannot create temp file \"" + QString::fromStdString(tmpfile)
1652 + "\": " + QString::fromStdString(storage::internal::safe_strerror(errno));
1653- handle_error(msg);
1654+ handle_error(msg, error_code);
1655 return;
1656 }
1657 output_file_.reset(new QFile(QString::fromStdString(tmpfile)));
1658@@ -140,11 +139,6 @@
1659
1660 void UploadWorker::do_finish()
1661 {
1662- if (qf_.future().isFinished())
1663- {
1664- return; // Future was set previously, no point in continuing.
1665- }
1666-
1667 switch (state_)
1668 {
1669 case in_progress:
1670@@ -165,8 +159,10 @@
1671 }
1672 case error:
1673 {
1674+ // LCOV_EXCL_START
1675 make_exceptional_future(qf_, ResourceException(error_msg_, error_code_));
1676 break;
1677+ // LCOV_EXCL_STOP
1678 }
1679 default:
1680 {
1681@@ -201,23 +197,15 @@
1682 auto bytes_written = output_file_->write(buf);
1683 if (bytes_written == -1)
1684 {
1685-<<<<<<< TREE
1686- handle_error("socket error: " + output_file_->errorString()); // LCOV_EXCL_LINE
1687-=======
1688- handle_error("socket error: " + output_file_->errorString(), output_file_->error());
1689->>>>>>> MERGE-SOURCE
1690+ handle_error("socket error: " + output_file_->errorString(), output_file_->error()); // LCOV_EXCL_LINE
1691 }
1692 else if (bytes_written != buf.size())
1693 {
1694 // LCOV_EXCL_START
1695 QString msg = "write error, requested " + QString::number(buf.size()) + " B, but wrote only "
1696 + bytes_written + " B.";
1697-<<<<<<< TREE
1698- handle_error(msg);
1699- // LCOV_EXCL_STOP
1700-=======
1701 handle_error(msg, 0);
1702->>>>>>> MERGE-SOURCE
1703+ // LCOV_EXCL_STOP
1704 }
1705 }
1706 }
1707@@ -228,11 +216,6 @@
1708 do_finish();
1709 }
1710
1711-void UploadWorker::on_error()
1712-{
1713- handle_error(read_socket_->errorString(), read_socket_->error());
1714-}
1715-
1716 void UploadWorker::finalize()
1717 {
1718 auto file = file_.lock();
1719@@ -287,7 +270,6 @@
1720 }
1721
1722 // Link the anonymous tmp file into the file system.
1723-<<<<<<< TREE
1724 auto new_path = file->native_identity().toStdString();
1725 if (use_linkat_)
1726 {
1727@@ -296,11 +278,12 @@
1728 if (linkat(-1, old_path.c_str(), tmp_fd_.get(), new_path.c_str(), AT_SYMLINK_FOLLOW) == -1)
1729 {
1730 // LCOV_EXCL_START
1731+ int error_code = errno;
1732 state_ = error;
1733 QString msg = "Uploader::finish_upload(): linkat \"" + QString::fromStdString(old_path)
1734 + "\" to \"" + file->native_identity() + "\" failed: "
1735 + QString::fromStdString(storage::internal::safe_strerror(errno));
1736- make_exceptional_future(qf_, ResourceException(msg));
1737+ make_exceptional_future(qf_, ResourceException(msg, error_code));
1738 return;
1739 // LCOV_EXCL_STOP
1740 }
1741@@ -311,26 +294,15 @@
1742 auto old_path = output_file_->fileName().toStdString();
1743 if (rename(old_path.c_str(), new_path.c_str()) == -1)
1744 {
1745+ int error_code = errno;
1746 state_ = error;
1747 QString msg = "Uploader::finish_upload(): rename \"" + QString::fromStdString(old_path)
1748 + "\" to \"" + file->native_identity() + "\" failed: "
1749 + QString::fromStdString(storage::internal::safe_strerror(errno));
1750- make_exceptional_future(qf_, ResourceException(msg));
1751+ make_exceptional_future(qf_, ResourceException(msg, error_code));
1752 return;
1753 }
1754 // LCOV_EXCL_STOP
1755-=======
1756- string oldpath = string("/proc/self/fd/") + std::to_string(tmp_fd_.get());
1757- string newpath = file->native_identity().toStdString();
1758- ::unlink(newpath.c_str()); // linkat() will not remove existing file: http://lwn.net/Articles/559969/
1759- if (linkat(-1, oldpath.c_str(), tmp_fd_.get(), newpath.c_str(), AT_SYMLINK_FOLLOW) == -1)
1760- {
1761- state_ = error;
1762- QString msg = "Uploader::finish_upload(): linkat \"" + file->native_identity() + "\" failed: "
1763- + QString::fromStdString(storage::internal::safe_strerror(errno));
1764- make_exceptional_future(qf_, ResourceException(msg, errno));
1765- return;
1766->>>>>>> MERGE-SOURCE
1767 }
1768
1769 state_ = finalized;
1770@@ -339,6 +311,7 @@
1771 make_ready_future(qf_, file);
1772 }
1773
1774+// LCOV_EXCL_START
1775 void UploadWorker::handle_error(QString const& msg, int error_code)
1776 {
1777 if (state_ == in_progress)
1778@@ -351,6 +324,7 @@
1779 error_code_ = error_code;
1780 do_finish();
1781 }
1782+// LCOV_EXCL_STOP
1783
1784 UploadThread::UploadThread(UploadWorker* worker)
1785 : worker_(worker)
1786@@ -427,6 +401,7 @@
1787 if (write_socket_->state() == QLocalSocket::ConnectedState)
1788 {
1789 write_socket_->disconnectFromServer();
1790+ write_socket_->close();
1791 }
1792 return qf_.future();
1793 }
1794@@ -434,6 +409,8 @@
1795 QFuture<void> UploaderImpl::cancel() noexcept
1796 {
1797 Q_EMIT do_cancel();
1798+ upload_thread_->wait();
1799+ write_socket_->abort();
1800 return qf_.future();
1801 }
1802
1803
1804=== added file 'src/qt/client/internal/local_client/storage_exception.cpp'
1805--- src/qt/client/internal/local_client/storage_exception.cpp 1970-01-01 00:00:00 +0000
1806+++ src/qt/client/internal/local_client/storage_exception.cpp 2016-08-03 06:10:39 +0000
1807@@ -0,0 +1,99 @@
1808+/*
1809+ * Copyright (C) 2016 Canonical Ltd
1810+ *
1811+ * This program is free software: you can redistribute it and/or modify
1812+ * it under the terms of the GNU Lesser General Public License version 3 as
1813+ * published by the Free Software Foundation.
1814+ *
1815+ * This program is distributed in the hope that it will be useful,
1816+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1817+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1818+ * GNU Lesser General Public License for more details.
1819+ *
1820+ * You should have received a copy of the GNU Lesser General Public License
1821+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1822+ *
1823+ * Authors: Michi Henning <michi.henning@canonical.com>
1824+ */
1825+
1826+#include <unity/storage/qt/client/Exceptions.h>
1827+#include <unity/storage/qt/client/internal/local_client/storage_exception.h>
1828+
1829+namespace unity
1830+{
1831+namespace storage
1832+{
1833+namespace qt
1834+{
1835+namespace client
1836+{
1837+namespace internal
1838+{
1839+namespace local_client
1840+{
1841+
1842+using namespace boost::filesystem;
1843+
1844+void throw_storage_exception(QString const& method, std::exception_ptr ep)
1845+{
1846+ int error_code = errno;
1847+ try
1848+ {
1849+ std::rethrow_exception(ep);
1850+ }
1851+ catch (StorageException const&)
1852+ {
1853+ throw;
1854+ }
1855+ catch (filesystem_error const& e)
1856+ {
1857+ QString msg = method + ": " + e.what();
1858+ switch (e.code().value())
1859+ {
1860+ case EACCES:
1861+ case EPERM:
1862+ {
1863+ throw PermissionException(msg);
1864+ }
1865+ case EDQUOT:
1866+ case ENOSPC:
1867+ {
1868+ throw QuotaException(msg); // Too messy to cover with a test case. // LCOV_EXCL_LINE
1869+ }
1870+ default:
1871+ {
1872+ throw ResourceException(msg, e.code().value());
1873+ }
1874+ }
1875+ }
1876+ // LCOV_EXCL_START
1877+ catch (std::exception const& e)
1878+ {
1879+ QString msg = method + ": " + e.what();
1880+ throw ResourceException(msg, error_code);
1881+ }
1882+ // LCOV_EXCL_STOP
1883+}
1884+
1885+void throw_storage_exception(QString const& method, std::exception_ptr ep, QString const& key)
1886+{
1887+ try
1888+ {
1889+ std::rethrow_exception(ep);
1890+ }
1891+ catch (filesystem_error const& e)
1892+ {
1893+ if (e.code().value() == ENOENT)
1894+ {
1895+ throw NotExistsException(method + ": " + e.what(), key);
1896+ }
1897+ }
1898+ throw_storage_exception(method, ep);
1899+}
1900+
1901+} // namespace local_client
1902+} // namespace internal
1903+} // namespace client
1904+} // namespace qt
1905+} // namespace storage
1906+} // namespace unity
1907
1908=== modified file 'src/qt/client/internal/remote_client/FileImpl.cpp'
1909--- src/qt/client/internal/remote_client/FileImpl.cpp 2016-08-03 06:10:39 +0000
1910+++ src/qt/client/internal/remote_client/FileImpl.cpp 2016-08-03 06:10:39 +0000
1911@@ -48,32 +48,25 @@
1912
1913 int64_t FileImpl::size() const
1914 {
1915- if (deleted_)
1916- {
1917- throw deleted_ex("File::size()");
1918- }
1919- if (!get_root())
1920- {
1921- throw RuntimeDestroyedException("File::size()");
1922- }
1923+ throw_if_destroyed("File::size()");
1924 return 0; // TODO
1925 }
1926
1927 QFuture<shared_ptr<Uploader>> FileImpl::create_uploader(ConflictPolicy policy, int64_t size)
1928 {
1929- if (deleted_)
1930- {
1931- return make_exceptional_future<shared_ptr<Uploader>>(deleted_ex("File::create_uploader()"));
1932+ try
1933+ {
1934+ throw_if_destroyed("File::create_uploader()()");
1935+ }
1936+ catch (StorageException const& e)
1937+ {
1938+ return make_exceptional_future<shared_ptr<Uploader>>(e);
1939 }
1940 if (size < 0)
1941 {
1942 QString msg = "File::create_uploader(): size must be >= 0";
1943 return make_exceptional_future<shared_ptr<Uploader>>(InvalidArgumentException(msg));
1944 }
1945- if (!get_root())
1946- {
1947- return make_exceptional_future<shared_ptr<Uploader>>(RuntimeDestroyedException("File::create_uploader()"));
1948- }
1949
1950 QString old_etag = policy == ConflictPolicy::overwrite ? "" : md_.etag;
1951 auto prov = provider();
1952@@ -108,13 +101,13 @@
1953
1954 QFuture<shared_ptr<Downloader>> FileImpl::create_downloader()
1955 {
1956- if (deleted_)
1957+ try
1958 {
1959- return make_exceptional_future<shared_ptr<Downloader>>(deleted_ex("File::create_downloader()"));
1960+ throw_if_destroyed("File::create_downloader()()");
1961 }
1962- if (!get_root())
1963+ catch (StorageException const& e)
1964 {
1965- return make_exceptional_future<shared_ptr<Downloader>>(RuntimeDestroyedException("File::create_downloader()"));
1966+ return make_exceptional_future<shared_ptr<Downloader>>(e);
1967 }
1968
1969 auto prov = provider();
1970
1971=== modified file 'src/qt/client/internal/remote_client/FolderImpl.cpp'
1972--- src/qt/client/internal/remote_client/FolderImpl.cpp 2016-08-03 06:10:39 +0000
1973+++ src/qt/client/internal/remote_client/FolderImpl.cpp 2016-08-03 06:10:39 +0000
1974@@ -57,13 +57,13 @@
1975
1976 QFuture<QVector<shared_ptr<Item>>> FolderImpl::list() const
1977 {
1978- if (deleted_)
1979+ try
1980 {
1981- return make_exceptional_future<QVector<shared_ptr<Item>>>(deleted_ex("Folder::list()"));
1982+ throw_if_destroyed("Folder::list()");
1983 }
1984- if (!get_root())
1985+ catch (StorageException const& e)
1986 {
1987- return make_exceptional_future<QVector<shared_ptr<Item>>>(RuntimeDestroyedException("Folder::list()"));
1988+ return make_exceptional_future<QVector<shared_ptr<Item>>>(e);
1989 }
1990
1991 auto prov = provider();
1992@@ -117,13 +117,13 @@
1993
1994 QFuture<QVector<shared_ptr<Item>>> FolderImpl::lookup(QString const& name) const
1995 {
1996- if (deleted_)
1997+ try
1998 {
1999- return make_exceptional_future<QVector<shared_ptr<Item>>>(deleted_ex("Folder::lookup()"));
2000+ throw_if_destroyed("Folder::lookup()");
2001 }
2002- if (!get_root())
2003+ catch (StorageException const& e)
2004 {
2005- return make_exceptional_future<QVector<shared_ptr<Item>>>(RuntimeDestroyedException("Folder::lookup()"));
2006+ return make_exceptional_future<QVector<shared_ptr<Item>>>(e);
2007 }
2008
2009 auto prov = provider();
2010@@ -163,13 +163,13 @@
2011
2012 QFuture<shared_ptr<Folder>> FolderImpl::create_folder(QString const& name)
2013 {
2014- if (deleted_)
2015+ try
2016 {
2017- return make_exceptional_future<shared_ptr<Folder>>(deleted_ex("Folder::create_folder()"));
2018+ throw_if_destroyed("Folder::create_folder()");
2019 }
2020- if (!get_root())
2021+ catch (StorageException const& e)
2022 {
2023- return make_exceptional_future<shared_ptr<Folder>>(RuntimeDestroyedException("Folder::create_folder()"));
2024+ return make_exceptional_future<shared_ptr<Folder>>(e);
2025 }
2026
2027 auto prov = provider();
2028@@ -203,19 +203,19 @@
2029
2030 QFuture<shared_ptr<Uploader>> FolderImpl::create_file(QString const& name, int64_t size)
2031 {
2032- if (deleted_)
2033- {
2034- return make_exceptional_future<shared_ptr<Uploader>>(deleted_ex("Folder::create_file()"));
2035+ try
2036+ {
2037+ throw_if_destroyed("Folder::create_file()");
2038+ }
2039+ catch (StorageException const& e)
2040+ {
2041+ return make_exceptional_future<shared_ptr<Uploader>>(e);
2042 }
2043 if (size < 0)
2044 {
2045 QString msg = "Folder::create_file(): size must be >= 0";
2046 return make_exceptional_future<shared_ptr<Uploader>>(InvalidArgumentException(msg));
2047 }
2048- if (!get_root())
2049- {
2050- return make_exceptional_future<shared_ptr<Uploader>>(RuntimeDestroyedException("Folder::create_file()"));
2051- }
2052
2053 auto prov = provider();
2054 auto reply = prov->CreateFile(md_.item_id, name, size, "application/octet-stream", false);
2055
2056=== modified file 'src/qt/client/internal/remote_client/ItemImpl.cpp'
2057--- src/qt/client/internal/remote_client/ItemImpl.cpp 2016-08-03 06:10:39 +0000
2058+++ src/qt/client/internal/remote_client/ItemImpl.cpp 2016-08-03 06:10:39 +0000
2059@@ -50,68 +50,48 @@
2060
2061 QString ItemImpl::name() const
2062 {
2063- if (deleted_)
2064- {
2065- throw deleted_ex("Item::name()");
2066- }
2067- if (!get_root())
2068- {
2069- throw RuntimeDestroyedException("Item::name()");
2070- }
2071+ throw_if_destroyed("Item::name()");
2072 return md_.name;
2073 }
2074
2075 QString ItemImpl::etag() const
2076 {
2077- if (deleted_)
2078- {
2079- throw deleted_ex("Item::etag()");
2080- }
2081- if (!get_root())
2082- {
2083- throw RuntimeDestroyedException("Item::etag()");
2084- }
2085+ throw_if_destroyed("Item::etag()");
2086 return md_.etag;
2087 }
2088
2089 QVariantMap ItemImpl::metadata() const
2090 {
2091- if (deleted_)
2092- {
2093- throw deleted_ex("Item::metadata()");
2094- }
2095- if (!get_root())
2096- {
2097- throw RuntimeDestroyedException("Item::metadata()");
2098- }
2099+ throw_if_destroyed("Item::metadata()");
2100 // TODO: need to agree on metadata representation
2101 return QVariantMap();
2102 }
2103
2104 QDateTime ItemImpl::last_modified_time() const
2105 {
2106- if (deleted_)
2107- {
2108- throw deleted_ex("Item::last_modified_time()");
2109- }
2110- if (!get_root())
2111- {
2112- throw RuntimeDestroyedException("Item::last_modified_time()");
2113- }
2114+ throw_if_destroyed("Item::last_modified_time()");
2115 // TODO: need to agree on metadata representation
2116 return QDateTime();
2117 }
2118
2119 QFuture<shared_ptr<Item>> ItemImpl::copy(shared_ptr<Folder> const& new_parent, QString const& new_name)
2120 {
2121- if (deleted_)
2122- {
2123- return make_exceptional_future<shared_ptr<Item>>(deleted_ex("Item::copy()"));
2124- }
2125- if (!get_root())
2126- {
2127- return make_exceptional_future<shared_ptr<Item>>(RuntimeDestroyedException("Item::copy()"));
2128- }
2129+ if (!new_parent)
2130+ {
2131+ QString msg = "Item::copy(): new_parent cannot be nullptr";
2132+ return internal::make_exceptional_future<shared_ptr<Item>>(InvalidArgumentException(msg));
2133+ }
2134+
2135+ try
2136+ {
2137+ throw_if_destroyed("Item::copy()");
2138+ }
2139+ catch (StorageException const& e)
2140+ {
2141+ return make_exceptional_future<shared_ptr<Item>>(e);
2142+ }
2143+ auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);
2144+ new_parent_impl->throw_if_destroyed("Item::copy()");
2145
2146 auto prov = provider();
2147 auto reply = prov->Copy(md_.item_id, new_parent->native_identity(), new_name);
2148@@ -144,14 +124,22 @@
2149
2150 QFuture<shared_ptr<Item>> ItemImpl::move(shared_ptr<Folder> const& new_parent, QString const& new_name)
2151 {
2152- if (deleted_)
2153- {
2154- return make_exceptional_future<shared_ptr<Item>>(deleted_ex("Item::move()"));
2155- }
2156- if (!get_root())
2157- {
2158- return make_exceptional_future<shared_ptr<Item>>(RuntimeDestroyedException("Item::copy()"));
2159- }
2160+ if (!new_parent)
2161+ {
2162+ QString msg = "Item::move(): new_parent cannot be nullptr";
2163+ return internal::make_exceptional_future<shared_ptr<Item>>(InvalidArgumentException(msg));
2164+ }
2165+
2166+ try
2167+ {
2168+ throw_if_destroyed("Item::move()");
2169+ }
2170+ catch (StorageException const& e)
2171+ {
2172+ return make_exceptional_future<shared_ptr<Item>>(e);
2173+ }
2174+ auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);
2175+ new_parent_impl->throw_if_destroyed("Item::move()");
2176
2177 auto prov = provider();
2178 if (!prov)
2179@@ -186,13 +174,13 @@
2180
2181 QFuture<QVector<Folder::SPtr>> ItemImpl::parents() const
2182 {
2183- if (deleted_)
2184+ try
2185 {
2186- return make_exceptional_future<QVector<Folder::SPtr>>(deleted_ex("Item::parents()"));
2187+ throw_if_destroyed("Item::parents()");
2188 }
2189- if (!get_root())
2190+ catch (StorageException const& e)
2191 {
2192- return make_exceptional_future<QVector<Folder::SPtr>>(RuntimeDestroyedException("Item::parents()"));
2193+ return make_exceptional_future<QVector<Folder::SPtr>>(e);
2194 }
2195 // TODO, need different metadata representation, affects xml
2196 return QFuture<QVector<Folder::SPtr>>();
2197@@ -200,28 +188,14 @@
2198
2199 QVector<QString> ItemImpl::parent_ids() const
2200 {
2201- if (deleted_)
2202- {
2203- throw deleted_ex("Item::parent_ids()");
2204- }
2205- if (!get_root())
2206- {
2207- RuntimeDestroyedException("Item::parent_ids()");
2208- }
2209+ throw_if_destroyed("Item::parent_ids()");
2210 // TODO, need different metadata representation, affects xml
2211 return QVector<QString>();
2212 }
2213
2214 QFuture<void> ItemImpl::delete_item()
2215 {
2216- if (deleted_)
2217- {
2218- return make_exceptional_future(deleted_ex("Item::delete_item()"));
2219- }
2220- if (!get_root())
2221- {
2222- return make_exceptional_future(RuntimeDestroyedException("Item::parents()"));
2223- }
2224+ throw_if_destroyed("Item::delete_item()");
2225
2226 auto prov = provider();
2227 auto reply = prov->Delete(md_.item_id);
2228@@ -238,20 +212,14 @@
2229
2230 QDateTime ItemImpl::creation_time() const
2231 {
2232- if (deleted_)
2233- {
2234- throw deleted_ex("Item::creation_time()");
2235- }
2236+ throw_if_destroyed("Item::creation_time()");
2237 // TODO: need to agree on metadata representation
2238 return QDateTime();
2239 }
2240
2241 MetadataMap ItemImpl::native_metadata() const
2242 {
2243- if (deleted_)
2244- {
2245- throw deleted_ex("Item::native_metadata()");
2246- }
2247+ throw_if_destroyed("Item::native_metadata()");
2248 // TODO: need to agree on metadata representation
2249 return MetadataMap();
2250 }
2251@@ -316,12 +284,6 @@
2252 return item;
2253 }
2254
2255-DeletedException ItemImpl::deleted_ex(QString const& method) const noexcept
2256-{
2257- QString msg = method + ": " + identity_ + " was deleted previously";
2258- return DeletedException(msg, identity_, md_.name);
2259-}
2260-
2261 } // namespace remote_client
2262 } // namespace internal
2263 } // namespace client
2264
2265=== modified file 'src/qt/client/internal/remote_client/RuntimeImpl.cpp'
2266--- src/qt/client/internal/remote_client/RuntimeImpl.cpp 2016-08-03 06:10:39 +0000
2267+++ src/qt/client/internal/remote_client/RuntimeImpl.cpp 2016-08-03 06:10:39 +0000
2268@@ -19,6 +19,7 @@
2269 #include <unity/storage/qt/client/internal/remote_client/RuntimeImpl.h>
2270
2271 #include <unity/storage/qt/client/Account.h>
2272+#include <unity/storage/qt/client/Exceptions.h>
2273 #include <unity/storage/qt/client/internal/make_future.h>
2274 #include <unity/storage/qt/client/internal/remote_client/AccountImpl.h>
2275 #include <unity/storage/qt/client/internal/remote_client/dbusmarshal.h>
2276
2277=== modified file 'tests/local-client/local-client_test.cpp'
2278--- tests/local-client/local-client_test.cpp 2016-08-03 06:10:39 +0000
2279+++ tests/local-client/local-client_test.cpp 2016-08-03 06:10:39 +0000
2280@@ -18,12 +18,8 @@
2281
2282 #include <unity/storage/qt/client/client-api.h>
2283
2284-<<<<<<< TREE
2285 #include <unity/storage/qt/client/internal/local_client/boost_filesystem.h>
2286-=======
2287-#include <unity/storage/qt/client/internal/boost_filesystem.h>
2288-#include <unity/storage/qt/client/internal/local_client/tmpfile-prefix.h>
2289->>>>>>> MERGE-SOURCE
2290+#include <unity/storage/qt/client/internal/local_client/tmpfile_prefix.h>
2291
2292 #include <gtest/gtest.h>
2293 #include <QCoreApplication>
2294@@ -39,6 +35,8 @@
2295
2296 #include <fstream>
2297
2298+Q_DECLARE_METATYPE(QLocalSocket::LocalSocketState)
2299+
2300 using namespace unity::storage;
2301 using namespace unity::storage::qt::client;
2302 using namespace std;
2303@@ -47,31 +45,61 @@
2304
2305 // Bunch of helper function to reduce the amount of noise in the tests.
2306
2307+template<typename T>
2308+void wait(T fut)
2309+{
2310+ QFutureWatcher<decltype(fut.result())> w;
2311+ QSignalSpy spy(&w, &decltype(w)::finished);
2312+ w.setFuture(fut);
2313+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2314+}
2315+
2316+template<>
2317+void wait(QFuture<void> fut)
2318+{
2319+ QFutureWatcher<void> w;
2320+ QSignalSpy spy(&w, &decltype(w)::finished);
2321+ w.setFuture(fut);
2322+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2323+}
2324+
2325+template <typename T>
2326+T call(QFuture<T> fut)
2327+{
2328+ wait(fut);
2329+ return fut.result();
2330+}
2331+
2332+template <>
2333+void call(QFuture<void> fut)
2334+{
2335+ wait(fut);
2336+ fut.waitForFinished();
2337+}
2338+
2339 Account::SPtr get_account(Runtime::SPtr const& runtime)
2340 {
2341- auto accounts = runtime->accounts().result();
2342- assert(accounts.size() == 1);
2343+ auto accounts = call(runtime->accounts());
2344 return accounts[0];
2345 }
2346
2347 Root::SPtr get_root(Runtime::SPtr const& runtime)
2348 {
2349 auto acc = get_account(runtime);
2350- auto roots = acc->roots().result();
2351- assert(roots.size() == 1);
2352+ auto roots = call(acc->roots());
2353 return roots[0];
2354 }
2355
2356 Folder::SPtr get_parent(Item::SPtr const& item)
2357 {
2358 assert(item->type() != ItemType::root);
2359- auto parents = item->parents().result();
2360+ auto parents = call(item->parents());
2361 return parents[0];
2362 }
2363
2364 void clear_folder(Folder::SPtr folder)
2365 {
2366- auto items = folder->list().result();
2367+ auto items = call(folder->list());
2368 for (auto i : items)
2369 {
2370 i->delete_item().waitForFinished();
2371@@ -86,7 +114,7 @@
2372 return buf == expected;
2373 }
2374
2375-void write_file(Folder::SPtr const& folder, QString const& name, QByteArray const& contents)
2376+File::SPtr write_file(Folder::SPtr const& folder, QString const& name, QByteArray const& contents)
2377 {
2378 QString ofile = folder->native_identity() + "/" + name;
2379 QFile f(ofile);
2380@@ -95,6 +123,23 @@
2381 {
2382 assert(f.write(contents));
2383 }
2384+ f.close();
2385+ auto items = call(folder->lookup(name));
2386+ return dynamic_pointer_cast<File>(items[0]);
2387+}
2388+
2389+File::SPtr make_deleted_file(Folder::SPtr parent, QString const& name)
2390+{
2391+ auto file = write_file(parent, name, "bytes");
2392+ call(file->delete_item());
2393+ return file;
2394+}
2395+
2396+Folder::SPtr make_deleted_folder(Folder::SPtr parent, QString const& name)
2397+{
2398+ auto folder = call(parent->create_folder(name));
2399+ call(folder->delete_item());
2400+ return folder;
2401 }
2402
2403 TEST(Runtime, lifecycle)
2404@@ -123,11 +168,11 @@
2405 auto runtime = Runtime::create();
2406
2407 auto acc = get_account(runtime);
2408- auto roots = acc->roots().result();
2409+ auto roots = call(acc->roots());
2410 EXPECT_EQ(1, roots.size());
2411
2412 // Get roots again, to get coverage for lazy initialization.
2413- roots = acc->roots().result();
2414+ roots = call(acc->roots());
2415 ASSERT_EQ(1, roots.size());
2416 }
2417
2418@@ -142,23 +187,31 @@
2419 EXPECT_EQ("", root->name());
2420 EXPECT_NE("", root->etag());
2421
2422- auto parents = root->parents().result();
2423- EXPECT_TRUE(parents.isEmpty());
2424- EXPECT_TRUE(root->parent_ids().isEmpty());
2425+ {
2426+ auto parents = call(root->parents());
2427+ EXPECT_TRUE(parents.isEmpty());
2428+ EXPECT_TRUE(root->parent_ids().isEmpty());
2429+ }
2430
2431- // get(<root-path>) must return the root.
2432- auto item = root->get(root->native_identity()).result();
2433- EXPECT_NE(nullptr, dynamic_pointer_cast<Root>(item));
2434- EXPECT_TRUE(root->equal_to(item));
2435+ {
2436+ // get(<root-path>) must return the root.
2437+ auto item = call(root->get(root->native_identity()));
2438+ EXPECT_NE(nullptr, dynamic_pointer_cast<Root>(item));
2439+ EXPECT_TRUE(root->equal_to(item));
2440+ }
2441
2442 // Free and used space can be anything, but must be > 0.
2443- auto free_space = root->free_space_bytes().result();
2444- cerr << "bytes free: " << free_space << endl;
2445- EXPECT_GT(free_space, 0);
2446+ {
2447+ auto free_space = call(root->free_space_bytes());
2448+ cerr << "bytes free: " << free_space << endl;
2449+ EXPECT_GT(free_space, 0);
2450+ }
2451
2452- auto used_space = root->used_space_bytes().result();
2453- cerr << "bytes used: " << used_space << endl;
2454- EXPECT_GT(used_space, 0);
2455+ {
2456+ auto used_space = call(root->used_space_bytes());
2457+ cerr << "bytes used: " << used_space << endl;
2458+ EXPECT_GT(used_space, 0);
2459+ }
2460 }
2461
2462 TEST(Folder, basic)
2463@@ -169,43 +222,43 @@
2464 auto root = get_root(runtime);
2465 clear_folder(root);
2466
2467- auto items = root->list().result();
2468+ auto items = call(root->list());
2469 EXPECT_TRUE(items.isEmpty());
2470
2471 // Create a file and check that it was created with correct type, name, and size 0.
2472- auto uploader = root->create_file("file1", 0).result();
2473- auto file = uploader->finish_upload().result();
2474+ auto uploader = call(root->create_file("file1", 0));
2475+ auto file = call(uploader->finish_upload());
2476 EXPECT_EQ(ItemType::file, file->type());
2477 EXPECT_EQ("file1", file->name());
2478 EXPECT_EQ(0, file->size());
2479 EXPECT_EQ(root->native_identity() + "/file1", file->native_identity());
2480
2481 // Create a folder and check that it was created with correct type and name.
2482- auto folder = root->create_folder("folder1").result();
2483+ auto folder = call(root->create_folder("folder1"));
2484 EXPECT_EQ(ItemType::folder, folder->type());
2485 EXPECT_EQ("folder1", folder->name());
2486 EXPECT_EQ(root->native_identity() + "/folder1", folder->native_identity());
2487
2488 // Check that we can find both file1 and folder1.
2489- auto item = root->lookup("file1").result()[0];
2490+ auto item = call(root->lookup("file1"))[0];
2491 file = dynamic_pointer_cast<File>(item);
2492 ASSERT_NE(nullptr, file);
2493 EXPECT_EQ("file1", file->name());
2494 EXPECT_EQ(0, file->size());
2495
2496- item = root->lookup("folder1").result()[0];
2497+ item = call(root->lookup("folder1"))[0];
2498 folder = dynamic_pointer_cast<Folder>(item);
2499 ASSERT_NE(nullptr, folder);
2500 ASSERT_EQ(nullptr, dynamic_pointer_cast<Root>(folder));
2501 EXPECT_EQ("folder1", folder->name());
2502
2503- item = root->get(file->native_identity()).result();
2504+ item = call(root->get(file->native_identity()));
2505 file = dynamic_pointer_cast<File>(item);
2506 ASSERT_NE(nullptr, file);
2507 EXPECT_EQ("file1", file->name());
2508 EXPECT_EQ(0, file->size());
2509
2510- item = root->get(folder->native_identity()).result();
2511+ item = call(root->get(folder->native_identity()));
2512 folder = dynamic_pointer_cast<Folder>(item);
2513 ASSERT_NE(nullptr, folder);
2514 ASSERT_EQ(nullptr, dynamic_pointer_cast<Root>(folder));
2515@@ -241,8 +294,8 @@
2516 EXPECT_EQ(root->native_identity(), folder->parent_ids()[0]);
2517
2518 // Delete the file and check that only the directory is left.
2519- file->delete_item().waitForFinished();
2520- items = root->list().result();
2521+ call(file->delete_item());
2522+ items = call(root->list());
2523 ASSERT_EQ(1, items.size());
2524 folder = dynamic_pointer_cast<Folder>(items[0]);
2525 ASSERT_NE(nullptr, folder);
2526@@ -250,7 +303,7 @@
2527
2528 // Delete the folder and check that the root is empty.
2529 folder->delete_item().waitForFinished();
2530- items = root->list().result();
2531+ items = call(root->list());
2532 ASSERT_EQ(0, items.size());
2533 }
2534
2535@@ -262,8 +315,8 @@
2536 auto root = get_root(runtime);
2537 clear_folder(root);
2538
2539- auto d1 = root->create_folder("d1").result();
2540- auto d2 = d1->create_folder("d2").result();
2541+ auto d1 = call(root->create_folder("d1"));
2542+ auto d2 = call(d1->create_folder("d2"));
2543
2544 // Parent of d2 must be d1.
2545 EXPECT_TRUE(get_parent(d2)->equal_to(d1));
2546@@ -271,28 +324,10 @@
2547
2548 // Delete is recursive
2549 d1->delete_item().waitForFinished();
2550- auto items = root->list().result();
2551+ auto items = call(root->list());
2552 ASSERT_EQ(0, items.size());
2553 }
2554
2555-template<typename T>
2556-bool wait(T fut)
2557-{
2558- QFutureWatcher<decltype(fut.result())> w;
2559- QSignalSpy spy(&w, &decltype(w)::finished);
2560- w.setFuture(fut);
2561- return spy.wait(SIGNAL_WAIT_TIME);
2562-}
2563-
2564-template<>
2565-bool wait(QFuture<void> fut)
2566-{
2567- QFutureWatcher<void> w;
2568- QSignalSpy spy(&w, &decltype(w)::finished);
2569- w.setFuture(fut);
2570- return spy.wait(SIGNAL_WAIT_TIME);
2571-}
2572-
2573 TEST(File, upload)
2574 {
2575 auto runtime = Runtime::create();
2576@@ -304,79 +339,73 @@
2577 {
2578 // Upload a few bytes.
2579 QByteArray const contents = "Hello\n";
2580- auto uploader = root->create_file("new_file", contents.size()).result();
2581+ auto uploader = call(root->create_file("new_file", contents.size()));
2582 auto written = uploader->socket()->write(contents);
2583 ASSERT_EQ(contents.size(), written);
2584
2585- auto file_fut = uploader->finish_upload();
2586- ASSERT_TRUE(wait(file_fut));
2587- auto file = file_fut.result();
2588+ auto file = call(uploader->finish_upload());
2589 EXPECT_EQ(contents.size(), file->size());
2590 ASSERT_TRUE(content_matches(file, contents));
2591
2592 // Calling finish_upload() more than once must return the original future.
2593- auto file2 = uploader->finish_upload().result();
2594+ auto file2 = call(uploader->finish_upload());
2595 EXPECT_TRUE(file2->equal_to(file));
2596
2597 // Calling cancel() after finish_upload must do nothing.
2598 uploader->cancel();
2599- file2 = uploader->finish_upload().result();
2600+ file2 = call(uploader->finish_upload());
2601 EXPECT_TRUE(file2->equal_to(file));
2602
2603- file->delete_item().waitForFinished();
2604+ call(file->delete_item());
2605 }
2606
2607 {
2608 // Upload exactly 64 KB.
2609 QByteArray const contents(64 * 1024, 'a');
2610- auto uploader = root->create_file("new_file", contents.size()).result();
2611+ auto uploader = call(root->create_file("new_file", contents.size()));
2612 auto written = uploader->socket()->write(contents);
2613 ASSERT_EQ(contents.size(), written);
2614
2615- auto file_fut = uploader->finish_upload();
2616- ASSERT_TRUE(wait(file_fut));
2617- auto file = file_fut.result();
2618+ auto file = call(uploader->finish_upload());
2619 EXPECT_EQ(contents.size(), file->size());
2620 ASSERT_TRUE(content_matches(file, contents));
2621
2622- file->delete_item().waitForFinished();
2623+ call(file->delete_item());
2624 }
2625
2626 {
2627 // Upload 1000 KBj
2628 QByteArray const contents(1000 * 1024, 'a');
2629- auto uploader = root->create_file("new_file", contents.size()).result();
2630+ auto uploader = call(root->create_file("new_file", contents.size()));
2631 auto written = uploader->socket()->write(contents);
2632 ASSERT_EQ(contents.size(), written);
2633
2634- auto file_fut = uploader->finish_upload();
2635- ASSERT_TRUE(wait(file_fut));
2636- auto file = file_fut.result();
2637+ auto file = call(uploader->finish_upload());
2638 EXPECT_EQ(contents.size(), file->size());
2639 ASSERT_TRUE(content_matches(file, contents));
2640
2641- file->delete_item().waitForFinished();
2642+ call(file->delete_item());
2643 }
2644
2645 {
2646 // Upload empty file.
2647- auto uploader = root->create_file("new_file", 0).result();
2648- auto file = uploader->finish_upload().result();
2649+ auto uploader = call(root->create_file("new_file", 0));
2650+ auto file = call(uploader->finish_upload());
2651 ASSERT_EQ(0, file->size());
2652
2653 // Again, and check that the ETag is different.
2654 auto old_etag = file->etag();
2655 sleep(1);
2656- uploader = file->create_uploader(ConflictPolicy::overwrite, 0).result();
2657- file = uploader->finish_upload().result();
2658+ uploader = call(file->create_uploader(ConflictPolicy::overwrite, 0));
2659+ file = call(uploader->finish_upload());
2660 EXPECT_NE(old_etag, file->etag());
2661
2662- file->delete_item().waitForFinished();
2663+ call(file->delete_item());
2664 }
2665
2666 {
2667 // Let the uploader go out of scope and check that the file was not created.
2668- root->create_file("new_file", 0).result();
2669+ call(root->create_file("new_file", 0));
2670 boost::filesystem::path path(TEST_DIR "/storage-framework/new_file");
2671 auto status = boost::filesystem::status(path);
2672 ASSERT_FALSE(boost::filesystem::exists(status));
2673@@ -392,31 +421,25 @@
2674 clear_folder(root);
2675
2676 // Make a new file first.
2677- auto uploader = root->create_file("new_file", 0).result();
2678- auto file_fut = uploader->finish_upload();
2679- ASSERT_TRUE(wait(file_fut));
2680- auto file = file_fut.result();
2681+ auto uploader = call(root->create_file("new_file", 0));
2682+ auto file = call(uploader->finish_upload());
2683 EXPECT_EQ(0, file->size());
2684 auto old_etag = file->etag();
2685
2686 // Create uploader for the file and write nothing.
2687- uploader = file->create_uploader(ConflictPolicy::overwrite, 0).result();
2688- file_fut = uploader->finish_upload();
2689- ASSERT_TRUE(wait(file_fut));
2690- file = file_fut.result();
2691+ uploader = call(file->create_uploader(ConflictPolicy::overwrite, 0));
2692+ file = call(uploader->finish_upload());
2693 EXPECT_EQ(0, file->size());
2694
2695 // Same test again, but this time, we write a bunch of data.
2696 std::string s(1000000, 'a');
2697- uploader = file->create_uploader(ConflictPolicy::overwrite, s.size()).result();
2698+ uploader = call(file->create_uploader(ConflictPolicy::overwrite, s.size()));
2699 uploader->socket()->write(&s[0], s.size());
2700
2701 // Need to sleep here, otherwise it is possible for the
2702 // upload to finish within the granularity of the file system time stamps.
2703 sleep(1);
2704- file_fut = uploader->finish_upload();
2705- ASSERT_TRUE(wait(file_fut));
2706- file = file_fut.result();
2707+ file = call(uploader->finish_upload());
2708 EXPECT_EQ(1000000, file->size());
2709 EXPECT_NE(old_etag, file->etag());
2710
2711@@ -432,12 +455,12 @@
2712 clear_folder(root);
2713
2714 {
2715- auto uploader = root->create_file("new_file", 20).result();
2716+ auto uploader = call(root->create_file("new_file", 20));
2717
2718 // We haven't called finish_upload(), so the cancel is guaranteed
2719 // to catch the uploader in the in_progress state.
2720 uploader->cancel();
2721- EXPECT_THROW(uploader->finish_upload().result(), CancelledException);
2722+ EXPECT_THROW(call(uploader->finish_upload()), CancelledException);
2723
2724 boost::filesystem::path path(TEST_DIR "/storage-framework/new_file");
2725 auto status = boost::filesystem::status(path);
2726@@ -447,28 +470,25 @@
2727 {
2728 // Create a file with a few bytes.
2729 QByteArray original_contents = "Hello World!\n";
2730- write_file(root, "new_file", original_contents);
2731- auto file = dynamic_pointer_cast<File>(root->lookup("new_file").result()[0]);
2732- ASSERT_NE(nullptr, file);
2733+ auto file = write_file(root, "new_file", original_contents);
2734
2735 // Create an uploader for the file and write a bunch of bytes.
2736- auto uploader = file->create_uploader(ConflictPolicy::overwrite, original_contents.size()).result();
2737+ auto uploader = call(file->create_uploader(ConflictPolicy::overwrite, original_contents.size()));
2738 QByteArray const contents(1024 * 1024, 'a');
2739 auto written = uploader->socket()->write(contents);
2740 ASSERT_EQ(contents.size(), written);
2741
2742 // No finish_upload() here, so the transfer is still in progress. Now cancel.
2743- auto cancel_fut = uploader->cancel();
2744- ASSERT_TRUE(wait(cancel_fut));
2745+ uploader->cancel();
2746
2747 // finish_upload() must indicate that the upload was cancelled.
2748- EXPECT_THROW(uploader->finish_upload().result(), CancelledException);
2749+ EXPECT_THROW(call(uploader->finish_upload()), CancelledException);
2750
2751 // The original file contents must still be intact.
2752 EXPECT_EQ(original_contents.size(), file->size());
2753 ASSERT_TRUE(content_matches(file, original_contents));
2754
2755- file->delete_item().waitForFinished();
2756+ call(file->delete_item());
2757 }
2758 }
2759
2760@@ -482,10 +502,8 @@
2761
2762 // Make a new file on disk.
2763 QByteArray const contents = "";
2764- write_file(root, "new_file", contents);
2765- auto file = dynamic_pointer_cast<File>(root->lookup("new_file").result()[0]);
2766- ASSERT_NE(nullptr, file);
2767- auto uploader = file->create_uploader(ConflictPolicy::error_if_conflict, contents.size()).result();
2768+ auto file = write_file(root, "new_file", contents);
2769+ auto uploader = call(file->create_uploader(ConflictPolicy::error_if_conflict, contents.size()));
2770
2771 // Touch the file on disk to give it a new time stamp.
2772 sleep(1);
2773@@ -494,7 +512,7 @@
2774 try
2775 {
2776 // Must get an exception because the time stamps no longer match.
2777- uploader->finish_upload().result();
2778+ call(uploader->finish_upload());
2779 FAIL();
2780 }
2781 catch (ConflictException const&)
2782@@ -502,7 +520,52 @@
2783 // TODO: check exception details.
2784 }
2785
2786- file->delete_item().waitForFinished();
2787+ call(file->delete_item());
2788+}
2789+
2790+TEST(File, upload_error)
2791+{
2792+ auto runtime = Runtime::create();
2793+
2794+ auto acc = get_account(runtime);
2795+ auto root = get_root(runtime);
2796+ clear_folder(root);
2797+
2798+ auto uploader = call(root->create_file("new_file", 0));
2799+ // Make new_file, so it gets in the way during finish_upload().
2800+ write_file(root, "new_file", "");
2801+
2802+ try
2803+ {
2804+ call(uploader->finish_upload());
2805+ FAIL();
2806+ }
2807+ catch (ExistsException const& e)
2808+ {
2809+ EXPECT_TRUE(e.error_message().startsWith("Uploader::finish_upload(): item with name \""));
2810+ EXPECT_TRUE(e.error_message().endsWith("\" exists already"));
2811+ EXPECT_EQ(TEST_DIR "/storage-framework/new_file", e.native_identity()) << e.native_identity().toStdString();
2812+ EXPECT_EQ("new_file", e.name());
2813+ }
2814+}
2815+
2816+TEST(File, create_uploader_bad_arg)
2817+{
2818+ auto runtime = Runtime::create();
2819+
2820+ auto acc = get_account(runtime);
2821+ auto root = get_root(runtime);
2822+ clear_folder(root);
2823+
2824+ auto file = write_file(root, "new_file", 0);
2825+ try
2826+ {
2827+ call(file->create_uploader(ConflictPolicy::overwrite, -1));
2828+ }
2829+ catch (InvalidArgumentException const& e)
2830+ {
2831+ EXPECT_EQ("File::create_uploader(): size must be >= 0", e.error_message());
2832+ }
2833 }
2834
2835 TEST(File, download)
2836@@ -516,13 +579,9 @@
2837 {
2838 // Download a few bytes.
2839 QByteArray const contents = "Hello\n";
2840- write_file(root, "file", contents);
2841-
2842- auto item = root->lookup("file").result()[0];
2843- File::SPtr file = dynamic_pointer_cast<File>(item);
2844- ASSERT_FALSE(file == nullptr);
2845-
2846- auto downloader = file->create_downloader().result();
2847+ auto file = write_file(root, "file", contents);
2848+
2849+ auto downloader = call(file->create_downloader());
2850 EXPECT_TRUE(file->equal_to(downloader->file()));
2851
2852 auto socket = downloader->socket();
2853@@ -530,17 +589,20 @@
2854 do
2855 {
2856 // Need to pump the event loop while the socket does its thing.
2857- QSignalSpy spy(downloader->socket().get(), &QIODevice::readyRead);
2858- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2859+ QSignalSpy spy(socket.get(), &QIODevice::readyRead);
2860 auto bytes_to_read = socket->bytesAvailable();
2861+ if (bytes_to_read == 0)
2862+ {
2863+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2864+ }
2865 buf.append(socket->read(bytes_to_read));
2866 } while (buf.size() < contents.size());
2867
2868 // Wait for disconnected signal.
2869- QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);
2870+ QSignalSpy spy(socket.get(), &QLocalSocket::disconnected);
2871 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2872
2873- ASSERT_NO_THROW(downloader->finish_download().waitForFinished());
2874+ ASSERT_NO_THROW(call(downloader->finish_download()));
2875
2876 // Contents must match.
2877 EXPECT_EQ(contents, buf);
2878@@ -549,13 +611,9 @@
2879 {
2880 // Download exactly 64 KB.
2881 QByteArray const contents(64 * 1024, 'a');
2882- write_file(root, "file", contents);
2883-
2884- auto item = root->lookup("file").result()[0];
2885- File::SPtr file = dynamic_pointer_cast<File>(item);
2886- ASSERT_FALSE(file == nullptr);
2887-
2888- auto downloader = file->create_downloader().result();
2889+ auto file = write_file(root, "file", contents);
2890+
2891+ auto downloader = call(file->create_downloader());
2892 EXPECT_TRUE(file->equal_to(downloader->file()));
2893
2894 auto socket = downloader->socket();
2895@@ -563,17 +621,20 @@
2896 do
2897 {
2898 // Need to pump the event loop while the socket does its thing.
2899- QSignalSpy spy(downloader->socket().get(), &QIODevice::readyRead);
2900- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2901+ QSignalSpy spy(socket.get(), &QIODevice::readyRead);
2902 auto bytes_to_read = socket->bytesAvailable();
2903+ if (bytes_to_read == 0)
2904+ {
2905+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2906+ }
2907 buf.append(socket->read(bytes_to_read));
2908 } while (buf.size() < contents.size());
2909
2910 // Wait for disconnected signal.
2911- QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);
2912+ QSignalSpy spy(socket.get(), &QLocalSocket::disconnected);
2913 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2914
2915- ASSERT_NO_THROW(downloader->finish_download().waitForFinished());
2916+ ASSERT_NO_THROW(call(downloader->finish_download()));
2917
2918 // Contents must match
2919 EXPECT_EQ(contents, buf);
2920@@ -582,13 +643,9 @@
2921 {
2922 // Download 1 MB + 1 bytes.
2923 QByteArray const contents(1024 * 1024 + 1, 'a');
2924- write_file(root, "file", contents);
2925-
2926- auto item = root->lookup("file").result()[0];
2927- File::SPtr file = dynamic_pointer_cast<File>(item);
2928- ASSERT_FALSE(file == nullptr);
2929-
2930- auto downloader = file->create_downloader().result();
2931+ auto file = write_file(root, "file", contents);
2932+
2933+ auto downloader = call(file->create_downloader());
2934 EXPECT_TRUE(file->equal_to(downloader->file()));
2935
2936 auto socket = downloader->socket();
2937@@ -596,17 +653,20 @@
2938 do
2939 {
2940 // Need to pump the event loop while the socket does its thing.
2941- QSignalSpy spy(downloader->socket().get(), &QIODevice::readyRead);
2942- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2943+ QSignalSpy spy(socket.get(), &QIODevice::readyRead);
2944 auto bytes_to_read = socket->bytesAvailable();
2945+ if (bytes_to_read == 0)
2946+ {
2947+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2948+ }
2949 buf.append(socket->read(bytes_to_read));
2950 } while (buf.size() < contents.size());
2951
2952 // Wait for disconnected signal.
2953- QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);
2954+ QSignalSpy spy(socket.get(), &QLocalSocket::disconnected);
2955 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2956
2957- ASSERT_NO_THROW(downloader->finish_download().waitForFinished());
2958+ ASSERT_NO_THROW(call(downloader->finish_download()));
2959
2960 // Contents must match
2961 EXPECT_EQ(contents, buf);
2962@@ -615,85 +675,84 @@
2963 {
2964 // Download file containing zero bytes
2965 QByteArray const contents;
2966- write_file(root, "file", contents);
2967-
2968- auto item = root->lookup("file").result()[0];
2969- File::SPtr file = dynamic_pointer_cast<File>(item);
2970- ASSERT_FALSE(file == nullptr);
2971-
2972- auto downloader = file->create_downloader().result();
2973+ auto file = write_file(root, "file", contents);
2974+
2975+ auto downloader = call(file->create_downloader());
2976 EXPECT_TRUE(file->equal_to(downloader->file()));
2977
2978 auto socket = downloader->socket();
2979
2980- // No readyRead every arrives in this case, just wait for disconnected.
2981- QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);
2982- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2983+ // No readyRead ever arrives in this case, just wait for disconnected.
2984+ QSignalSpy spy(socket.get(), &QLocalSocket::disconnected);
2985+ if (socket->state() != QLocalSocket::UnconnectedState)
2986+ {
2987+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
2988+ }
2989
2990- ASSERT_NO_THROW(downloader->finish_download().waitForFinished());
2991+ ASSERT_NO_THROW(call(downloader->finish_download()));
2992 }
2993
2994 {
2995 // Don't ever call read on empty file.
2996 QByteArray const contents;
2997- write_file(root, "file", contents);
2998-
2999- auto item = root->lookup("file").result()[0];
3000- File::SPtr file = dynamic_pointer_cast<File>(item);
3001- ASSERT_FALSE(file == nullptr);
3002-
3003- auto downloader = file->create_downloader().result();
3004+ auto file = write_file(root, "file", contents);
3005+
3006+ auto downloader = call(file->create_downloader());
3007 EXPECT_TRUE(file->equal_to(downloader->file()));
3008
3009+ auto socket = downloader->socket();
3010+
3011 // No readyRead ever arrives in this case, just wait for disconnected.
3012- QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);
3013- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
3014+ QSignalSpy spy(socket.get(), &QLocalSocket::disconnected);
3015+ if (socket->state() != QLocalSocket::UnconnectedState)
3016+ {
3017+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
3018+ }
3019
3020 // This succeeds because the provider disconnects as soon
3021 // as it realizes that there is nothing to write.
3022- ASSERT_NO_THROW(downloader->finish_download().waitForFinished());
3023+ ASSERT_NO_THROW(call(downloader->finish_download()));
3024 }
3025
3026 {
3027 // Don't ever call read on small file.
3028 QByteArray const contents("some contents");
3029- write_file(root, "file", contents);
3030-
3031- auto item = root->lookup("file").result()[0];
3032- File::SPtr file = dynamic_pointer_cast<File>(item);
3033- ASSERT_FALSE(file == nullptr);
3034-
3035- auto downloader = file->create_downloader().result();
3036+ auto file = write_file(root, "file", contents);
3037+
3038+ auto downloader = call(file->create_downloader());
3039 EXPECT_TRUE(file->equal_to(downloader->file()));
3040
3041+ auto socket = downloader->socket();
3042+
3043 // Wait for disconnected.
3044- QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);
3045- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
3046+ if (socket->state() != QLocalSocket::UnconnectedState)
3047+ {
3048+ QSignalSpy spy(socket.get(), &QLocalSocket::disconnected);
3049+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
3050+ }
3051
3052 // This succeeds because the provider has written everything and disconnected.
3053- ASSERT_NO_THROW(downloader->finish_download().waitForFinished());
3054+ ASSERT_NO_THROW(call(downloader->finish_download()));
3055 }
3056
3057 {
3058 // Don't ever call read on large file.
3059 QByteArray const contents(1024 * 1024, 'a');
3060- write_file(root, "file", contents);
3061-
3062- auto item = root->lookup("file").result()[0];
3063- File::SPtr file = dynamic_pointer_cast<File>(item);
3064- ASSERT_FALSE(file == nullptr);
3065-
3066- auto downloader = file->create_downloader().result();
3067+ auto file = write_file(root, "file", contents);
3068+
3069+ auto downloader = call(file->create_downloader());
3070 EXPECT_TRUE(file->equal_to(downloader->file()));
3071
3072+ auto socket = downloader->socket();
3073+
3074 // Wait for first readyRead. Not all data fits into the socket buffer.
3075- QSignalSpy spy(downloader->socket().get(), &QLocalSocket::readyRead);
3076+ QSignalSpy spy(socket.get(), &QLocalSocket::readyRead);
3077 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
3078
3079 // This fails because the provider still has data left to write.
3080 try
3081 {
3082- downloader->finish_download().waitForFinished();
3083+ call(downloader->finish_download());
3084 FAIL();
3085 }
3086 catch (StorageException const& e)
3087@@ -705,25 +764,17 @@
3088 {
3089 // Let downloader go out of scope.
3090 QByteArray const contents(1024 * 1024, 'a');
3091- write_file(root, "file", contents);
3092-
3093- auto item = root->lookup("file").result()[0];
3094- File::SPtr file = dynamic_pointer_cast<File>(item);
3095- ASSERT_FALSE(file == nullptr);
3096-
3097- auto downloader = file->create_downloader().result();
3098+ auto file = write_file(root, "file", contents);
3099+
3100+ auto downloader = call(file->create_downloader());
3101 }
3102
3103 {
3104 // Let downloader future go out of scope.
3105 QByteArray const contents(1024 * 1024, 'a');
3106- write_file(root, "file", contents);
3107-
3108- auto item = root->lookup("file").result()[0];
3109- File::SPtr file = dynamic_pointer_cast<File>(item);
3110- ASSERT_FALSE(file == nullptr);
3111-
3112- auto downloader_fut = file->create_downloader();
3113+ auto file = write_file(root, "file", contents);
3114+
3115+ file->create_downloader();
3116 }
3117 }
3118
3119@@ -738,55 +789,111 @@
3120 {
3121 // Download enough bytes to prevent a single write in the provider from completing the download.
3122 QByteArray const contents(1024 * 1024, 'a');
3123- write_file(root, "file", contents);
3124-
3125- auto item = root->lookup("file").result()[0];
3126- File::SPtr file = dynamic_pointer_cast<File>(item);
3127- ASSERT_FALSE(file == nullptr);
3128-
3129- auto download_fut = file->create_downloader();
3130- ASSERT_TRUE(wait(download_fut));
3131- auto downloader = download_fut.result();
3132+ auto file = write_file(root, "file", contents);
3133+
3134+ auto downloader = call(file->create_downloader());
3135
3136 // We haven't read anything, so the cancel is guaranteed to catch the
3137 // downloader in the in_progress state.
3138- auto cancel_fut = downloader->cancel();
3139- ASSERT_TRUE(wait(cancel_fut));
3140- ASSERT_THROW(downloader->finish_download().waitForFinished(), CancelledException);
3141+ downloader->cancel();
3142+ ASSERT_THROW(call(downloader->finish_download()), CancelledException);
3143 }
3144
3145 {
3146 // Download a few bytes.
3147 QByteArray const contents = "Hello\n";
3148- write_file(root, "file", contents);
3149-
3150- auto item = root->lookup("file").result()[0];
3151- File::SPtr file = dynamic_pointer_cast<File>(item);
3152- ASSERT_FALSE(file == nullptr);
3153+ auto file = write_file(root, "file", contents);
3154
3155 // Finish the download.
3156- auto downloader = file->create_downloader().result();
3157+ auto downloader = call(file->create_downloader());
3158 auto socket = downloader->socket();
3159 QByteArray buf;
3160 do
3161 {
3162 // Need to pump the event loop while the socket does its thing.
3163- QSignalSpy spy(downloader->socket().get(), &QIODevice::readyRead);
3164- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
3165+ QSignalSpy spy(socket.get(), &QIODevice::readyRead);
3166 auto bytes_to_read = socket->bytesAvailable();
3167+ if (bytes_to_read == 0)
3168+ {
3169+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
3170+ }
3171 buf.append(socket->read(bytes_to_read));
3172 } while (buf.size() < contents.size());
3173
3174 // Wait for disconnected signal.
3175- QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);
3176- ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
3177+ if (socket->state() != QLocalSocket::UnconnectedState)
3178+ {
3179+ QSignalSpy spy(socket.get(), &QLocalSocket::disconnected);
3180+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
3181+ }
3182
3183 // Now send the cancel. The download is finished already, and the cancel
3184 // is too late, so finish_download() must report that the download
3185 // worked OK.
3186 downloader->cancel();
3187- ASSERT_NO_THROW(downloader->finish_download().waitForFinished());
3188- }
3189+ ASSERT_NO_THROW(call(downloader->finish_download()));
3190+ }
3191+}
3192+
3193+TEST(File, download_error)
3194+{
3195+ auto runtime = Runtime::create();
3196+
3197+ auto acc = get_account(runtime);
3198+ auto root = get_root(runtime);
3199+ clear_folder(root);
3200+
3201+ QByteArray const contents(1024 * 1024, 'a');
3202+ auto file = write_file(root, "file", contents);
3203+
3204+ auto downloader = call(file->create_downloader());
3205+ EXPECT_TRUE(file->equal_to(downloader->file()));
3206+
3207+ auto socket = downloader->socket();
3208+
3209+ {
3210+ // Wait for first readyRead. Not all data fits into the socket buffer.
3211+ QSignalSpy spy(socket.get(), &QLocalSocket::readyRead);
3212+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
3213+ }
3214+
3215+ {
3216+ // Now close the socket, to force an error at the writing end.
3217+ // This gives us coverage of the error handling logic in the download worker.
3218+ socket->abort();
3219+ // Wait a little, to give the worker a chance to notice the problem.
3220+ // We don't wait for a signal here because all attempts to disable the
3221+ // socket (via close(), abort(), or disconnectFromServer() also
3222+ // stop the stateChanged signal from arriving.
3223+ QTimer timer;
3224+ QSignalSpy spy(&timer, &QTimer::timeout);
3225+ timer.start(1000);
3226+ spy.wait();
3227+ }
3228+
3229+ try
3230+ {
3231+ call(downloader->finish_download());
3232+ FAIL();
3233+ }
3234+ catch (ResourceException const& e)
3235+ {
3236+ EXPECT_TRUE(e.error_message().startsWith("Downloader: QLocalSocket: "));
3237+ }
3238+}
3239+
3240+TEST(File, size_error)
3241+{
3242+ auto runtime = Runtime::create();
3243+
3244+ auto acc = get_account(runtime);
3245+ auto root = get_root(runtime);
3246+ clear_folder(root);
3247+
3248+ auto file = write_file(root, "file", "");
3249+
3250+ ASSERT_EQ(0, system("rm " TEST_DIR "/storage-framework/file"));
3251+ EXPECT_THROW(file->size(), ResourceException);
3252 }
3253
3254 TEST(Item, move)
3255@@ -799,25 +906,22 @@
3256
3257 // Check that rename works within the same folder.
3258 QByteArray const contents = "Hello\n";
3259- write_file(root, "f1", contents);
3260- auto f1 = root->lookup("f1").result()[0];
3261- auto f2 = f1->move(root, "f2").result();
3262- EXPECT_EQ("f2", f2->name());
3263- EXPECT_THROW(f1->name(), DeletedException); // TODO: check exception details.
3264+ auto f1 = write_file(root, "f1", contents);
3265+ auto f2 = call(f1->move(root, "f2"));
3266
3267 // File must be found under new name.
3268- auto items = root->list().result();
3269+ auto items = call(root->list());
3270 ASSERT_EQ(1, items.size());
3271 f2 = dynamic_pointer_cast<File>(items[0]);
3272 ASSERT_FALSE(f2 == nullptr);
3273
3274 // Make a folder and move f2 into it.
3275- auto folder = root->create_folder("folder").result();
3276- f2 = f2->move(folder, "f2").result();
3277+ auto folder = call(root->create_folder("folder"));
3278+ f2 = call(f2->move(folder, "f2"));
3279 EXPECT_TRUE(get_parent(f2)->equal_to(folder));
3280
3281 // Move the folder
3282- auto item = folder->move(root, "folder2").result();
3283+ auto item = call(folder->move(root, "folder2"));
3284 folder = dynamic_pointer_cast<Folder>(item);
3285 EXPECT_EQ("folder2", folder->name());
3286 }
3287@@ -831,10 +935,8 @@
3288 clear_folder(root);
3289
3290 QByteArray const contents = "hello\n";
3291- write_file(root, "file", contents);
3292-
3293- auto item = root->lookup("file").result()[0];
3294- auto copied_item = item->copy(root, "copy_of_file").result();
3295+ auto item = write_file(root, "file", contents);
3296+ auto copied_item = call(item->copy(root, "copy_of_file"));
3297 EXPECT_EQ("copy_of_file", copied_item->name());
3298 File::SPtr copied_file = dynamic_pointer_cast<File>(item);
3299 ASSERT_NE(nullptr, copied_file);
3300@@ -854,7 +956,9 @@
3301 // folder/empty_folder
3302 // folder/non_empty_folder
3303 // folder/non_empty_folder/nested_file
3304+ // folder/non_empty_folder/<TMPFILE_PREFIX>1234-1234-1234-1234
3305 // folder/file
3306+ // folder/<TMPFILE_PREFIX>1234-1234-1234-1234
3307
3308 string root_path = root->native_identity().toStdString();
3309 ASSERT_EQ(0, mkdir((root_path + "/folder").c_str(), 0700));
3310@@ -863,23 +967,26 @@
3311 ofstream(root_path + "/folder/non_empty_folder/nested_file");
3312 ofstream(root_path + "/folder/file");
3313
3314+ // Add dirs that look like a tmp dirs, to get coverage on skipping those.
3315+ ASSERT_EQ(0, mkdir((root_path + "/folder/" + TMPFILE_PREFIX "1234-1234-1234-1234").c_str(), 0700));
3316+ ASSERT_EQ(0, mkdir((root_path + "/folder/non_empty_folder/" + TMPFILE_PREFIX "1234-1234-1234-1234").c_str(), 0700));
3317 // Copy folder to folder2
3318- auto folder = dynamic_pointer_cast<Folder>(root->lookup("folder").result()[0]);
3319+ auto folder = dynamic_pointer_cast<Folder>(call(root->lookup("folder"))[0]);
3320 ASSERT_NE(nullptr, folder);
3321- auto item = folder->copy(root, "folder2").result();
3322+ auto item = call(folder->copy(root, "folder2"));
3323
3324 // Verify that folder2 now contains the same structure as folder.
3325 auto folder2 = dynamic_pointer_cast<Folder>(item);
3326 ASSERT_NE(nullptr, folder2);
3327- EXPECT_NO_THROW(folder2->lookup("empty_folder").result()[0]);
3328- item = folder2->lookup("non_empty_folder").result()[0];
3329+ EXPECT_NO_THROW(call(folder2->lookup("empty_folder"))[0]);
3330+ item = call(folder2->lookup("non_empty_folder"))[0];
3331 auto non_empty_folder = dynamic_pointer_cast<Folder>(item);
3332 ASSERT_NE(nullptr, non_empty_folder);
3333- EXPECT_NO_THROW(non_empty_folder->lookup("nested_file").result()[0]);
3334- EXPECT_NO_THROW(folder2->lookup("file").result()[0]);
3335+ EXPECT_NO_THROW(call(non_empty_folder->lookup("nested_file"))[0]);
3336+ EXPECT_NO_THROW(call(folder2->lookup("file"))[0]);
3337 }
3338
3339-TEST(Item, modified_time)
3340+TEST(Item, time)
3341 {
3342 auto runtime = Runtime::create();
3343
3344@@ -889,14 +996,15 @@
3345
3346 auto now = QDateTime::currentDateTimeUtc();
3347 sleep(1);
3348- auto uploader = root->create_file("file", 0).result();
3349- auto file_fut = uploader->finish_upload();
3350- ASSERT_TRUE(wait(file_fut));
3351- auto file = file_fut.result();
3352+ auto uploader = call(root->create_file("file", 0));
3353+ auto file = call(uploader->finish_upload());
3354 auto t = file->last_modified_time();
3355 // Rough check that the time is sane.
3356 EXPECT_LE(now, t);
3357 EXPECT_LE(t, now.addSecs(5));
3358+
3359+ auto creation_time = file->creation_time();
3360+ EXPECT_FALSE(creation_time.isValid());
3361 }
3362
3363 TEST(Item, comparison)
3364@@ -908,36 +1016,338 @@
3365 clear_folder(root);
3366
3367 // Create two files.
3368- auto uploader = root->create_file("file1", 0).result();
3369- auto file_fut = uploader->finish_upload();
3370- ASSERT_TRUE(wait(file_fut));
3371- auto file1 = file_fut.result();
3372+ auto uploader = call(root->create_file("file1", 0));
3373+ auto file1 = call(uploader->finish_upload());
3374
3375- uploader = root->create_file("file2", 0).result();
3376- file_fut = uploader->finish_upload();
3377- ASSERT_TRUE(wait(file_fut));
3378- auto file2 = file_fut.result();
3379+ uploader = call(root->create_file("file2", 0));
3380+ auto file2 = call(uploader->finish_upload());
3381
3382 EXPECT_FALSE(file1->equal_to(file2));
3383
3384 // Retrieve file1 via lookup, so we get a different proxy.
3385- auto item = root->lookup("file1").result()[0];
3386+ auto item = call(root->lookup("file1"))[0];
3387 auto other_file1 = dynamic_pointer_cast<File>(item);
3388 EXPECT_NE(file1, other_file1); // Compares shared_ptr values
3389 EXPECT_TRUE(file1->equal_to(other_file1)); // Deep comparison
3390
3391 // Comparing against a deleted file must return false.
3392- auto delete_fut = file1->delete_item();
3393- ASSERT_TRUE(wait(delete_fut));
3394+ call(file1->delete_item());
3395 EXPECT_FALSE(file1->equal_to(file2));
3396 EXPECT_FALSE(file2->equal_to(file1));
3397
3398 // Delete file2 as well and compare again.
3399- delete_fut = file2->delete_item();
3400- ASSERT_TRUE(wait(delete_fut));
3401+ call(file2->delete_item());
3402 EXPECT_FALSE(file1->equal_to(file2));
3403 }
3404
3405+TEST(Item, exceptions)
3406+{
3407+ auto runtime = Runtime::create();
3408+
3409+ auto acc = get_account(runtime);
3410+ auto root = get_root(runtime);
3411+ clear_folder(root);
3412+
3413+ try
3414+ {
3415+ call(root->copy(nullptr, "new name"));
3416+ FAIL();
3417+ }
3418+ catch (InvalidArgumentException const& e)
3419+ {
3420+ EXPECT_EQ("Item::copy(): new_parent cannot be nullptr", e.error_message());
3421+ }
3422+
3423+ auto file = write_file(root, "file", 0);
3424+
3425+ try
3426+ {
3427+ call(file->copy(root, TMPFILE_PREFIX "copy_of_file"));
3428+ FAIL();
3429+ }
3430+ catch (InvalidArgumentException const& e)
3431+ {
3432+ EXPECT_EQ("Item::copy(): names beginning with \".storage-framework-\" are reserved", e.error_message());
3433+ }
3434+
3435+ try
3436+ {
3437+ call(file->copy(root, file->name()));
3438+ FAIL();
3439+ }
3440+ catch (ExistsException const& e)
3441+ {
3442+ EXPECT_EQ("Item::copy(): item with name \"file\" exists already", e.error_message());
3443+ EXPECT_EQ("file", e.name());
3444+ EXPECT_EQ(TEST_DIR "/storage-framework/file", e.native_identity());
3445+ }
3446+
3447+ try
3448+ {
3449+ auto file = write_file(root, "file", "");
3450+ ASSERT_EQ(0, unlink(file->native_identity().toStdString().c_str()));
3451+
3452+ call(file->copy(root, file->name()));
3453+ FAIL();
3454+ }
3455+ catch (ResourceException const& e)
3456+ {
3457+ EXPECT_TRUE(e.error_message().startsWith("Item::copy(): "));
3458+ }
3459+
3460+ try
3461+ {
3462+ auto file = write_file(root, "file", "");
3463+ ASSERT_EQ(0, unlink(file->native_identity().toStdString().c_str()));
3464+
3465+ call(file->move(root, "new_name"));
3466+ FAIL();
3467+ }
3468+ catch (ResourceException const& e)
3469+ {
3470+ EXPECT_TRUE(e.error_message().startsWith("Item::move(): "));
3471+ }
3472+
3473+ try
3474+ {
3475+ auto file = write_file(root, "file", "");
3476+ ASSERT_EQ(0, unlink(file->native_identity().toStdString().c_str()));
3477+ ASSERT_EQ(0, system("chmod -x " TEST_DIR "/storage-framework"));
3478+
3479+ call(file->delete_item());
3480+ FAIL();
3481+ }
3482+ catch (PermissionException const& e)
3483+ {
3484+ ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
3485+ EXPECT_TRUE(e.error_message().startsWith("Item::delete_item(): "));
3486+ }
3487+ catch (std::exception const&)
3488+ {
3489+ ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
3490+ FAIL();
3491+ }
3492+ ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
3493+
3494+ try
3495+ {
3496+ call(root->move(nullptr, "new name"));
3497+ FAIL();
3498+ }
3499+ catch (InvalidArgumentException const& e)
3500+ {
3501+ EXPECT_EQ("Item::move(): new_parent cannot be nullptr", e.error_message());
3502+ }
3503+
3504+ try
3505+ {
3506+ call(root->move(root, "new name"));
3507+ FAIL();
3508+ }
3509+ catch (LogicException const& e)
3510+ {
3511+ EXPECT_EQ("Item::move(): cannot move root folder", e.error_message());
3512+ }
3513+
3514+ try
3515+ {
3516+ auto file = write_file(root, "file", "");
3517+ call(file->move(root, file->name()));
3518+ FAIL();
3519+ }
3520+ catch (ExistsException const& e)
3521+ {
3522+ EXPECT_EQ("Item::move(): item with name \"file\" exists already", e.error_message());
3523+ EXPECT_EQ("file", e.name());
3524+ EXPECT_EQ(TEST_DIR "/storage-framework/file", e.native_identity());
3525+ }
3526+
3527+ try
3528+ {
3529+ call(file->move(root, TMPFILE_PREFIX "copy_of_file"));
3530+ FAIL();
3531+ }
3532+ catch (InvalidArgumentException const& e)
3533+ {
3534+ EXPECT_EQ("Item::move(): names beginning with \".storage-framework-\" are reserved", e.error_message());
3535+ }
3536+
3537+ try
3538+ {
3539+ call(root->lookup("abc/def"));
3540+ }
3541+ catch (InvalidArgumentException const& e)
3542+ {
3543+ EXPECT_EQ("Folder::lookup(): name \"abc/def\" contains more than one path component",
3544+ e.error_message()) << e.what();
3545+ }
3546+
3547+ try
3548+ {
3549+ call(root->create_folder(".."));
3550+ }
3551+ catch (InvalidArgumentException const& e)
3552+ {
3553+ EXPECT_EQ("Folder::create_folder(): invalid name: \"..\"", e.error_message()) << e.what();
3554+ }
3555+}
3556+
3557+TEST(Folder, exceptions)
3558+{
3559+ auto runtime = Runtime::create();
3560+
3561+ auto acc = get_account(runtime);
3562+ auto root = get_root(runtime);
3563+ clear_folder(root);
3564+
3565+ try
3566+ {
3567+ write_file(root, TMPFILE_PREFIX "file", "");
3568+ call(root->lookup(TMPFILE_PREFIX "file"));
3569+ FAIL();
3570+ }
3571+ catch (NotExistsException const& e)
3572+ {
3573+ string cmd = "rm ";
3574+ cmd += string(TEST_DIR) + "/storage-framework/" + TMPFILE_PREFIX "file";
3575+ ASSERT_EQ(0, system(cmd.c_str()));
3576+ EXPECT_EQ("Folder::lookup(): no such item: \".storage-framework-file\"", e.error_message());
3577+ EXPECT_EQ(".storage-framework-file", e.key());
3578+ }
3579+
3580+ {
3581+ auto fifo_id = root->native_identity() + "/fifo";
3582+ string cmd = "mkfifo " + fifo_id.toStdString();
3583+ ASSERT_EQ(0, system(cmd.c_str()));
3584+
3585+ try
3586+ {
3587+ call(root->lookup("fifo"));
3588+ FAIL();
3589+ }
3590+ catch (NotExistsException const& e)
3591+ {
3592+ EXPECT_EQ("Folder::lookup(): no such item: \"fifo\"", e.error_message()) << e.what();
3593+ EXPECT_EQ("fifo", e.key());
3594+ }
3595+
3596+ cmd = "rm " + fifo_id.toStdString();
3597+ ASSERT_EQ(0, system(cmd.c_str()));
3598+ }
3599+
3600+ try
3601+ {
3602+ call(root->lookup("no_such_file"));
3603+ FAIL();
3604+ }
3605+ catch (NotExistsException const& e)
3606+ {
3607+ EXPECT_EQ("Folder::lookup(): no such item: \"no_such_file\"", e.error_message());
3608+ EXPECT_EQ("no_such_file", e.key());
3609+ }
3610+
3611+ try
3612+ {
3613+ call(root->create_folder(TMPFILE_PREFIX "folder"));
3614+ FAIL();
3615+ }
3616+ catch (InvalidArgumentException const& e)
3617+ {
3618+ EXPECT_EQ("Folder::create_folder(): names beginning with \".storage-framework-\" are reserved", e.error_message());
3619+ }
3620+
3621+ try
3622+ {
3623+ EXPECT_NO_THROW(call(root->create_folder("folder")));
3624+ call(root->create_folder("folder"));
3625+ FAIL();
3626+ }
3627+ catch (ExistsException const& e)
3628+ {
3629+ EXPECT_EQ("Folder::create_folder(): item with name \"folder\" exists already", e.error_message());
3630+ EXPECT_EQ("folder", e.name());
3631+ EXPECT_EQ(TEST_DIR "/storage-framework/folder", e.native_identity());
3632+ }
3633+
3634+ try
3635+ {
3636+ call(root->create_file("new_file", -1));
3637+ FAIL();
3638+ }
3639+ catch (InvalidArgumentException const& e)
3640+ {
3641+ EXPECT_EQ("Folder::create_file(): size must be >= 0", e.error_message());
3642+ }
3643+
3644+ try
3645+ {
3646+ call(root->create_file(TMPFILE_PREFIX "new_file", 0));
3647+ FAIL();
3648+ }
3649+ catch (InvalidArgumentException const& e)
3650+ {
3651+ EXPECT_EQ("Folder::create_file(): names beginning with \".storage-framework-\" are reserved", e.error_message());
3652+ }
3653+
3654+ try
3655+ {
3656+ write_file(root, "file", "");
3657+ call(root->create_file("file", 0));
3658+ FAIL();
3659+ }
3660+ catch (ExistsException const& e)
3661+ {
3662+ EXPECT_EQ("Folder::create_file(): item with name \"file\" exists already", e.error_message());
3663+ EXPECT_EQ("file", e.name());
3664+ EXPECT_EQ(TEST_DIR "/storage-framework/file", e.native_identity());
3665+ }
3666+
3667+ ASSERT_EQ(0, system("chmod -x " TEST_DIR "/storage-framework"));
3668+ try
3669+ {
3670+ call(root->create_file("new_file", 0));
3671+ FAIL();
3672+ }
3673+ catch (PermissionException const& e)
3674+ {
3675+ EXPECT_TRUE(e.error_message().startsWith("Folder::create_file(): "));
3676+ ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
3677+ }
3678+ catch (std::exception const& e)
3679+ {
3680+ ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
3681+ FAIL();
3682+ }
3683+ ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
3684+
3685+ ASSERT_EQ(0, system("chmod -x " TEST_DIR "/storage-framework"));
3686+ try
3687+ {
3688+ call(root->create_folder("new_folder"));
3689+ FAIL();
3690+ }
3691+ catch (PermissionException const& e)
3692+ {
3693+ EXPECT_TRUE(e.error_message().startsWith("Folder::create_folder(): "));
3694+ ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
3695+ }
3696+ catch (std::exception const& e)
3697+ {
3698+ ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
3699+ FAIL();
3700+ }
3701+ ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
3702+
3703+ try
3704+ {
3705+ call(root->create_file("new_file", -1));
3706+ }
3707+ catch (InvalidArgumentException const& e)
3708+ {
3709+ EXPECT_EQ("Folder::create_file(): size must be >= 0", e.error_message());
3710+ }
3711+}
3712+
3713 TEST(Root, root_exceptions)
3714 {
3715 auto runtime = Runtime::create();
3716@@ -946,51 +1356,39 @@
3717 auto root = get_root(runtime);
3718 clear_folder(root);
3719
3720- {
3721- auto fut = root->delete_item();
3722- try
3723- {
3724- fut.waitForFinished();
3725- FAIL();
3726- }
3727- catch (LogicException const& e)
3728- {
3729- EXPECT_EQ("Root::delete_item(): Cannot delete root folder", e.error_message());
3730- }
3731- }
3732-
3733- {
3734- auto fut = root->get("abc");
3735- try
3736- {
3737- fut.waitForFinished();
3738- FAIL();
3739- }
3740- catch (InvalidArgumentException const& e)
3741- {
3742- EXPECT_EQ("Root::get(): identity \"abc\" must be an absolute path", e.error_message());
3743- }
3744- }
3745-
3746- {
3747- auto fut = root->get("/etc");
3748- try
3749- {
3750- fut.waitForFinished();
3751- FAIL();
3752- }
3753- catch (InvalidArgumentException const& e)
3754- {
3755- EXPECT_EQ("Root::get(): identity \"/etc\" points outside the root folder", e.error_message());
3756- }
3757- }
3758-
3759- {
3760- auto folder = root->create_folder("folder").result();
3761- write_file(root, "folder/testfile", "hello");
3762-
3763- auto file = dynamic_pointer_cast<File>(folder->lookup("testfile").result()[0]);
3764- ASSERT_NE(nullptr, file);
3765+ try
3766+ {
3767+ call(root->delete_item());
3768+ FAIL();
3769+ }
3770+ catch (LogicException const& e)
3771+ {
3772+ EXPECT_EQ("Root::delete_item(): Cannot delete root folder", e.error_message());
3773+ }
3774+
3775+ try
3776+ {
3777+ call(root->get("abc"));
3778+ FAIL();
3779+ }
3780+ catch (InvalidArgumentException const& e)
3781+ {
3782+ EXPECT_EQ("Root::get(): identity \"abc\" must be an absolute path", e.error_message());
3783+ }
3784+
3785+ try
3786+ {
3787+ call(root->get("/etc"));
3788+ FAIL();
3789+ }
3790+ catch (InvalidArgumentException const& e)
3791+ {
3792+ EXPECT_EQ("Root::get(): identity \"/etc\" points outside the root folder", e.error_message());
3793+ }
3794+
3795+ {
3796+ auto folder = call(root->create_folder("folder"));
3797+ auto file = write_file(folder, "testfile", "hello");
3798
3799 // Remove permission from folder.
3800 string cmd = "chmod -x " + folder->native_identity().toStdString();
3801@@ -998,7 +1396,7 @@
3802
3803 try
3804 {
3805- file = dynamic_pointer_cast<File>(root->get(file->native_identity()).result());
3806+ file = dynamic_pointer_cast<File>(call(root->get(file->native_identity())));
3807 FAIL();
3808 }
3809 catch (PermissionException const& e)
3810@@ -1006,6 +1404,11 @@
3811 EXPECT_TRUE(e.error_message().startsWith("Root::get(): "));
3812 EXPECT_TRUE(e.error_message().contains("Permission denied"));
3813 }
3814+ catch (...)
3815+ {
3816+ cmd = "chmod +x " + folder->native_identity().toStdString();
3817+ ASSERT_EQ(0, system(cmd.c_str()));
3818+ }
3819
3820 cmd = "chmod +x " + folder->native_identity().toStdString();
3821 ASSERT_EQ(0, system(cmd.c_str()));
3822@@ -1014,17 +1417,14 @@
3823 }
3824
3825 {
3826- write_file(root, "testfile", "hello");
3827-
3828- auto file = dynamic_pointer_cast<File>(root->lookup("testfile").result()[0]);
3829- ASSERT_NE(nullptr, file);
3830+ auto file = write_file(root, "testfile", "hello");
3831
3832 QString id = file->native_identity();
3833 id.append("_doesnt_exist");
3834
3835 try
3836 {
3837- file = dynamic_pointer_cast<File>(root->get(id).result());
3838+ file = dynamic_pointer_cast<File>(call(root->get(id)));
3839 FAIL();
3840 }
3841 catch (NotExistsException const& e)
3842@@ -1042,7 +1442,7 @@
3843
3844 try
3845 {
3846- root->get(fifo_id).result();
3847+ call(root->get(fifo_id));
3848 FAIL();
3849 }
3850 catch (NotExistsException const& e)
3851@@ -1051,17 +1451,20 @@
3852 }
3853
3854 cmd = "rm " + fifo_id.toStdString();
3855- system(cmd.c_str());
3856+ ASSERT_EQ(0, system(cmd.c_str()));
3857 }
3858
3859 {
3860- QString reserved_name = TMPFILE_PREFIX "somefile";
3861- write_file(root, reserved_name, "some bytes");
3862+ string reserved_name = TMPFILE_PREFIX "somefile";
3863+ string full_path = string(TEST_DIR) + "/storage-framework/" + reserved_name;
3864+ string cmd = "touch ";
3865+ cmd += full_path;
3866+ ASSERT_EQ(0, system(cmd.c_str()));
3867
3868- auto reserved_id = QString(TEST_DIR) + "/storage-framework/" + reserved_name;
3869+ auto reserved_id = QString::fromStdString(full_path);
3870 try
3871 {
3872- root->get(reserved_id).result();
3873+ call(root->get(reserved_id));
3874 FAIL();
3875 }
3876 catch (NotExistsException const& e)
3877@@ -1073,27 +1476,6 @@
3878 }
3879 }
3880
3881-File::SPtr make_deleted_file(Folder::SPtr parent, QString const& name)
3882-{
3883- write_file(parent, name, "bytes");
3884- auto file_fut = parent->lookup("file");
3885- assert(wait(file_fut));
3886- auto file = dynamic_pointer_cast<File>(file_fut.result()[0]);
3887- auto void_fut = file->delete_item();
3888- assert(wait(void_fut));
3889- return file;
3890-}
3891-
3892-Folder::SPtr make_deleted_folder(Folder::SPtr parent, QString const& name)
3893-{
3894- auto fut = parent->create_folder(name);
3895- assert(wait(fut));
3896- auto folder = fut.result();
3897- auto void_fut = folder->delete_item();
3898- assert(wait(void_fut));
3899- return folder;
3900-}
3901-
3902 TEST(Item, deleted_exceptions)
3903 {
3904 auto runtime = Runtime::create();
3905@@ -1110,7 +1492,6 @@
3906 }
3907 catch (DeletedException const& e)
3908 {
3909- EXPECT_EQ("file", e.name());
3910 EXPECT_TRUE(e.error_message().startsWith("Item::etag(): "));
3911 EXPECT_TRUE(e.error_message().endsWith(" was deleted previously"));
3912 EXPECT_EQ(TEST_DIR "/storage-framework/file", e.native_identity());
3913@@ -1124,7 +1505,6 @@
3914 }
3915 catch (DeletedException const& e)
3916 {
3917- EXPECT_EQ("file", e.name());
3918 EXPECT_TRUE(e.error_message().startsWith("Item::metadata(): "));
3919 }
3920
3921@@ -1136,7 +1516,6 @@
3922 }
3923 catch (DeletedException const& e)
3924 {
3925- EXPECT_EQ("file", e.name());
3926 EXPECT_TRUE(e.error_message().startsWith("Item::last_modified_time(): "));
3927 }
3928
3929@@ -1144,14 +1523,11 @@
3930 {
3931 // Copying deleted file must fail.
3932 auto file = make_deleted_file(root, "file");
3933- auto fut = file->copy(root, "copy_of_file");
3934- ASSERT_TRUE(wait(fut));
3935- fut.result();
3936+ call(file->copy(root, "copy_of_file"));
3937 FAIL();
3938 }
3939 catch (DeletedException const& e)
3940 {
3941- EXPECT_EQ("file", e.name());
3942 EXPECT_TRUE(e.error_message().startsWith("Item::copy(): "));
3943 }
3944
3945@@ -1160,30 +1536,20 @@
3946 // Copying file into deleted folder must fail.
3947
3948 // Make target folder.
3949- auto fut = root->create_folder("folder");
3950- ASSERT_TRUE(wait(fut));
3951- auto folder = fut.result();
3952+ auto folder = call(root->create_folder("folder"));
3953
3954 // Make a file in the root.
3955- auto uploader_fut = root->create_file("file", 0);
3956- ASSERT_TRUE(wait(uploader_fut));
3957- auto uploader = uploader_fut.result();
3958- auto finish_fut = uploader->finish_upload();
3959- ASSERT_TRUE(wait(finish_fut));
3960- auto file = finish_fut.result();
3961+ auto uploader = call(root->create_file("file", 0));
3962+ auto file = call(uploader->finish_upload());
3963
3964 // Delete folder.
3965- auto void_fut = folder->delete_item();
3966- ASSERT_TRUE(wait(void_fut));
3967+ call(folder->delete_item());
3968
3969- auto copy_fut = file->copy(folder, "file");
3970- ASSERT_TRUE(wait(copy_fut));
3971- copy_fut.result();
3972+ call(file->copy(folder, "file"));
3973 FAIL();
3974 }
3975 catch (DeletedException const& e)
3976 {
3977- EXPECT_EQ("folder", e.name());
3978 EXPECT_TRUE(e.error_message().startsWith("Item::copy(): "));
3979 }
3980 clear_folder(root);
3981@@ -1192,14 +1558,11 @@
3982 {
3983 // Moving deleted file must fail.
3984 auto file = make_deleted_file(root, "file");
3985- auto fut = file->move(root, "moved_file");
3986- ASSERT_TRUE(wait(fut));
3987- fut.result();
3988+ call(file->move(root, "moved_file"));
3989 FAIL();
3990 }
3991 catch (DeletedException const& e)
3992 {
3993- EXPECT_EQ("file", e.name());
3994 EXPECT_TRUE(e.error_message().startsWith("Item::move(): "));
3995 }
3996
3997@@ -1208,30 +1571,20 @@
3998 // Moving file into deleted folder must fail.
3999
4000 // Make target folder.
4001- auto fut = root->create_folder("folder");
4002- ASSERT_TRUE(wait(fut));
4003- auto folder = fut.result();
4004+ auto folder = call(root->create_folder("folder"));
4005
4006 // Make a file in the root.
4007- auto uploader_fut = root->create_file("file", 0);
4008- ASSERT_TRUE(wait(uploader_fut));
4009- auto uploader = uploader_fut.result();
4010- auto finish_fut = uploader->finish_upload();
4011- ASSERT_TRUE(wait(finish_fut));
4012- auto file = finish_fut.result();
4013+ auto uploader = call(root->create_file("file", 0));
4014+ auto file = call(uploader->finish_upload());
4015
4016 // Delete folder.
4017- auto void_fut = folder->delete_item();
4018- ASSERT_TRUE(wait(void_fut));
4019+ call(folder->delete_item());
4020
4021- auto move_fut = file->move(folder, "file");
4022- ASSERT_TRUE(wait(move_fut));
4023- move_fut.result();
4024+ call(file->move(folder, "file"));
4025 FAIL();
4026 }
4027 catch (DeletedException const& e)
4028 {
4029- EXPECT_EQ("folder", e.name());
4030 EXPECT_TRUE(e.error_message().startsWith("Item::move(): "));
4031 }
4032 clear_folder(root);
4033@@ -1239,12 +1592,11 @@
4034 try
4035 {
4036 auto file = make_deleted_file(root, "file");
4037- file->parents().result();
4038+ call(file->parents());
4039 FAIL();
4040 }
4041 catch (DeletedException const& e)
4042 {
4043- EXPECT_EQ("file", e.name());
4044 EXPECT_TRUE(e.error_message().startsWith("Item::parents(): "));
4045 }
4046
4047@@ -1256,7 +1608,6 @@
4048 }
4049 catch (DeletedException const& e)
4050 {
4051- EXPECT_EQ("file", e.name());
4052 EXPECT_TRUE(e.error_message().startsWith("Item::parent_ids(): "));
4053 }
4054
4055@@ -1264,14 +1615,11 @@
4056 {
4057 // Deleting a deleted item must fail.
4058 auto file = make_deleted_file(root, "file");
4059- auto delete_fut = file->delete_item();
4060- ASSERT_TRUE(wait(delete_fut));
4061- delete_fut.waitForFinished();
4062+ call(file->delete_item());
4063 FAIL();
4064 }
4065 catch (DeletedException const& e)
4066 {
4067- EXPECT_EQ("file", e.name());
4068 EXPECT_TRUE(e.error_message().startsWith("Item::delete_item(): "));
4069 }
4070 }
4071@@ -1292,65 +1640,146 @@
4072 }
4073 catch (DeletedException const& e)
4074 {
4075- EXPECT_EQ("folder", e.name());
4076 EXPECT_TRUE(e.error_message().startsWith("Folder::name(): "));
4077 }
4078
4079 try
4080 {
4081 auto folder = make_deleted_folder(root, "folder");
4082- auto fut = folder->list();
4083- ASSERT_TRUE(wait(fut));
4084- fut.result();
4085+ call(folder->list());
4086 FAIL();
4087 }
4088 catch (DeletedException const& e)
4089 {
4090- EXPECT_EQ("folder", e.name());
4091 EXPECT_TRUE(e.error_message().startsWith("Folder::list(): "));
4092 }
4093
4094 try
4095 {
4096 auto folder = make_deleted_folder(root, "folder");
4097- auto fut = folder->lookup("something");
4098- ASSERT_TRUE(wait(fut));
4099- fut.result();
4100+ call(folder->lookup("something"));
4101 FAIL();
4102 }
4103 catch (DeletedException const& e)
4104 {
4105- EXPECT_EQ("folder", e.name());
4106 EXPECT_TRUE(e.error_message().startsWith("Folder::lookup(): "));
4107 }
4108
4109 try
4110 {
4111 auto folder = make_deleted_folder(root, "folder");
4112- auto fut = folder->create_folder("nested_folder");
4113- ASSERT_TRUE(wait(fut));
4114- fut.result();
4115- FAIL();
4116- }
4117- catch (DeletedException const& e)
4118- {
4119- EXPECT_EQ("folder", e.name());
4120+ call(folder->list());
4121+ FAIL();
4122+ }
4123+ catch (DeletedException const& e)
4124+ {
4125+ EXPECT_TRUE(e.error_message().startsWith("Folder::list(): "));
4126+ }
4127+
4128+ try
4129+ {
4130+ auto folder = make_deleted_folder(root, "folder");
4131+ call(folder->create_folder("nested_folder"));
4132+ FAIL();
4133+ }
4134+ catch (DeletedException const& e)
4135+ {
4136 EXPECT_TRUE(e.error_message().startsWith("Folder::create_folder(): "));
4137 }
4138
4139 try
4140 {
4141 auto folder = make_deleted_folder(root, "folder");
4142- auto fut = folder->create_file("nested_file", 0);
4143- ASSERT_TRUE(wait(fut));
4144- fut.result();
4145+ call(folder->create_file("nested_file", 0));
4146 FAIL();
4147 }
4148 catch (DeletedException const& e)
4149 {
4150- EXPECT_EQ("folder", e.name());
4151 EXPECT_TRUE(e.error_message().startsWith("Folder::create_file(): "));
4152 }
4153+
4154+ try
4155+ {
4156+ auto folder = make_deleted_folder(root, "folder");
4157+ folder->creation_time();
4158+ FAIL();
4159+ }
4160+ catch (DeletedException const& e)
4161+ {
4162+ EXPECT_TRUE(e.error_message().startsWith("Item::creation_time(): ")) << e.what();
4163+ }
4164+
4165+ try
4166+ {
4167+ auto folder = make_deleted_folder(root, "folder");
4168+ folder->native_metadata();
4169+ FAIL();
4170+ }
4171+ catch (DeletedException const& e)
4172+ {
4173+ EXPECT_TRUE(e.error_message().startsWith("Item::native_metadata(): ")) << e.what();
4174+ }
4175+}
4176+
4177+TEST(File, deleted_exceptions)
4178+{
4179+ auto runtime = Runtime::create();
4180+
4181+ auto acc = get_account(runtime);
4182+ auto root = get_root(runtime);
4183+ clear_folder(root);
4184+
4185+ try
4186+ {
4187+ auto file = make_deleted_file(root, "file");
4188+ file->name();
4189+ FAIL();
4190+ }
4191+ catch (DeletedException const& e)
4192+ {
4193+ EXPECT_TRUE(e.error_message().startsWith("File::name(): "));
4194+ EXPECT_TRUE(e.error_message().endsWith(" was deleted previously"));
4195+ EXPECT_EQ(TEST_DIR "/storage-framework/file", e.native_identity());
4196+ }
4197+
4198+ try
4199+ {
4200+ auto file = make_deleted_file(root, "file");
4201+ file->size();
4202+ FAIL();
4203+ }
4204+ catch (DeletedException const& e)
4205+ {
4206+ EXPECT_TRUE(e.error_message().startsWith("File::size(): "));
4207+ EXPECT_TRUE(e.error_message().endsWith(" was deleted previously"));
4208+ EXPECT_EQ(TEST_DIR "/storage-framework/file", e.native_identity());
4209+ }
4210+
4211+ try
4212+ {
4213+ auto file = make_deleted_file(root, "file");
4214+ call(file->create_uploader(ConflictPolicy::overwrite, 0));
4215+ FAIL();
4216+ }
4217+ catch (DeletedException const& e)
4218+ {
4219+ EXPECT_TRUE(e.error_message().startsWith("File::create_uploader(): "));
4220+ EXPECT_TRUE(e.error_message().endsWith(" was deleted previously"));
4221+ EXPECT_EQ(TEST_DIR "/storage-framework/file", e.native_identity());
4222+ }
4223+
4224+ try
4225+ {
4226+ auto file = make_deleted_file(root, "file");
4227+ call(file->create_downloader());
4228+ FAIL();
4229+ }
4230+ catch (DeletedException const& e)
4231+ {
4232+ EXPECT_TRUE(e.error_message().startsWith("File::create_downloader(): "));
4233+ EXPECT_TRUE(e.error_message().endsWith(" was deleted previously"));
4234+ EXPECT_EQ(TEST_DIR "/storage-framework/file", e.native_identity());
4235+ }
4236 }
4237
4238 TEST(Runtime, runtime_destroyed_exceptions)
4239@@ -1387,6 +1816,21 @@
4240 }
4241 }
4242
4243+ // Getting accounts after shutting down the runtime must fail.
4244+ {
4245+ auto runtime = Runtime::create();
4246+ runtime->shutdown();
4247+ try
4248+ {
4249+ runtime->accounts();
4250+ FAIL();
4251+ }
4252+ catch (RuntimeDestroyedException const& e)
4253+ {
4254+ EXPECT_EQ("Runtime::accounts(): Runtime was destroyed previously", e.error_message());
4255+ }
4256+ }
4257+
4258 // Getting the account from a root with a destroyed runtime must fail.
4259 {
4260 auto runtime = Runtime::create();
4261@@ -1429,13 +1873,7 @@
4262 auto root = get_root(runtime);
4263 clear_folder(root);
4264
4265- auto create_fut = root->create_file("file1", 0);
4266- ASSERT_TRUE(wait(create_fut));
4267- auto uploader = create_fut.result();
4268- auto finish_fut = uploader->finish_upload();
4269- ASSERT_TRUE(wait(finish_fut));
4270- auto file = finish_fut.result();
4271-
4272+ auto file = write_file(root, "file", "");
4273 runtime.reset();
4274 try
4275 {
4276@@ -1455,17 +1893,9 @@
4277 auto root = get_root(runtime);
4278 clear_folder(root);
4279
4280- auto create_fut = root->create_file("file1", 0);
4281- ASSERT_TRUE(wait(create_fut));
4282- auto uploader = create_fut.result();
4283- auto finish_fut = uploader->finish_upload();
4284- ASSERT_TRUE(wait(finish_fut));
4285- auto file = finish_fut.result();
4286-
4287+ auto file = write_file(root, "file", "");
4288 runtime.reset();
4289- qDebug() << "shared count:" << acc.use_count();
4290 acc.reset();
4291- qDebug() << "shared count:" << acc.use_count();
4292 root.reset();
4293 try
4294 {
4295@@ -1478,7 +1908,515 @@
4296 }
4297 }
4298
4299- // TODO: Lots more places to cover here.
4300+ // etag() with destroyed runtime must fail.
4301+ {
4302+ auto runtime = Runtime::create();
4303+ auto acc = get_account(runtime);
4304+ auto root = get_root(runtime);
4305+ clear_folder(root);
4306+
4307+ auto file = write_file(root, "file", "");
4308+ runtime->shutdown();
4309+ try
4310+ {
4311+ file->etag();
4312+ FAIL();
4313+ }
4314+ catch (RuntimeDestroyedException const& e)
4315+ {
4316+ EXPECT_EQ("Item::etag(): Runtime was destroyed previously", e.error_message());
4317+ }
4318+ }
4319+
4320+ // metadata() with destroyed runtime must fail.
4321+ {
4322+ auto runtime = Runtime::create();
4323+ auto acc = get_account(runtime);
4324+ auto root = get_root(runtime);
4325+ clear_folder(root);
4326+
4327+ auto file = write_file(root, "file", "");
4328+ runtime->shutdown();
4329+ try
4330+ {
4331+ file->metadata();
4332+ FAIL();
4333+ }
4334+ catch (RuntimeDestroyedException const& e)
4335+ {
4336+ EXPECT_EQ("Item::metadata(): Runtime was destroyed previously", e.error_message());
4337+ }
4338+ }
4339+
4340+ // last_modified_time() with destroyed runtime must fail.
4341+ {
4342+ auto runtime = Runtime::create();
4343+ auto acc = get_account(runtime);
4344+ auto root = get_root(runtime);
4345+ clear_folder(root);
4346+
4347+ auto file = write_file(root, "file", "");
4348+ runtime->shutdown();
4349+ try
4350+ {
4351+ file->last_modified_time();
4352+ FAIL();
4353+ }
4354+ catch (RuntimeDestroyedException const& e)
4355+ {
4356+ EXPECT_EQ("Item::last_modified_time(): Runtime was destroyed previously", e.error_message());
4357+ }
4358+ }
4359+
4360+ // copy() with destroyed runtime must fail.
4361+ {
4362+ auto runtime = Runtime::create();
4363+ auto acc = get_account(runtime);
4364+ auto root = get_root(runtime);
4365+ clear_folder(root);
4366+
4367+ auto file = write_file(root, "file", "");
4368+ runtime->shutdown();
4369+ try
4370+ {
4371+ call(file->copy(root, "file2"));
4372+ FAIL();
4373+ }
4374+ catch (RuntimeDestroyedException const& e)
4375+ {
4376+ EXPECT_EQ("Item::copy(): Runtime was destroyed previously", e.error_message());
4377+ }
4378+ }
4379+
4380+ // move() with destroyed runtime must fail.
4381+ {
4382+ auto runtime = Runtime::create();
4383+ auto acc = get_account(runtime);
4384+ auto root = get_root(runtime);
4385+ clear_folder(root);
4386+
4387+ auto file = write_file(root, "file", "");
4388+ runtime->shutdown();
4389+ try
4390+ {
4391+ call(file->move(root, "file2"));
4392+ FAIL();
4393+ }
4394+ catch (RuntimeDestroyedException const& e)
4395+ {
4396+ EXPECT_EQ("Item::move(): Runtime was destroyed previously", e.error_message());
4397+ }
4398+ }
4399+
4400+ // parents() on root with destroyed runtime must fail.
4401+ {
4402+ auto runtime = Runtime::create();
4403+ auto acc = get_account(runtime);
4404+ auto root = get_root(runtime);
4405+ clear_folder(root);
4406+
4407+ auto file = write_file(root, "file", "");
4408+ runtime->shutdown();
4409+ try
4410+ {
4411+ call(root->parents());
4412+ FAIL();
4413+ }
4414+ catch (RuntimeDestroyedException const& e)
4415+ {
4416+ EXPECT_EQ("Root::parents(): Runtime was destroyed previously", e.error_message());
4417+ }
4418+ }
4419+
4420+ // parents() on file with destroyed runtime must fail.
4421+ {
4422+ auto runtime = Runtime::create();
4423+ auto acc = get_account(runtime);
4424+ auto root = get_root(runtime);
4425+ clear_folder(root);
4426+
4427+ auto file = write_file(root, "file", "");
4428+ runtime->shutdown();
4429+ try
4430+ {
4431+ call(file->parents());
4432+ FAIL();
4433+ }
4434+ catch (RuntimeDestroyedException const& e)
4435+ {
4436+ EXPECT_EQ("Item::parents(): Runtime was destroyed previously", e.error_message());
4437+ }
4438+ }
4439+
4440+ // parent_ids() with destroyed runtime must fail.
4441+ {
4442+ auto runtime = Runtime::create();
4443+ auto acc = get_account(runtime);
4444+ auto root = get_root(runtime);
4445+ clear_folder(root);
4446+
4447+ auto file = write_file(root, "file", "");
4448+ runtime->shutdown();
4449+ try
4450+ {
4451+ file->parent_ids();
4452+ FAIL();
4453+ }
4454+ catch (RuntimeDestroyedException const& e)
4455+ {
4456+ EXPECT_EQ("Item::parent_ids(): Runtime was destroyed previously", e.error_message());
4457+ }
4458+ }
4459+
4460+ // parent_ids() on root with destroyed runtime must fail.
4461+ {
4462+ auto runtime = Runtime::create();
4463+ auto acc = get_account(runtime);
4464+ auto root = get_root(runtime);
4465+ clear_folder(root);
4466+
4467+ runtime->shutdown();
4468+ try
4469+ {
4470+ root->parent_ids();
4471+ FAIL();
4472+ }
4473+ catch (RuntimeDestroyedException const& e)
4474+ {
4475+ EXPECT_EQ("Root::parent_ids(): Runtime was destroyed previously", e.error_message());
4476+ }
4477+ }
4478+
4479+ // delete_item() with destroyed runtime must fail.
4480+ {
4481+ auto runtime = Runtime::create();
4482+ auto acc = get_account(runtime);
4483+ auto root = get_root(runtime);
4484+ clear_folder(root);
4485+
4486+ auto file = write_file(root, "file", "");
4487+ runtime->shutdown();
4488+ try
4489+ {
4490+ call(file->delete_item());
4491+ FAIL();
4492+ }
4493+ catch (RuntimeDestroyedException const& e)
4494+ {
4495+ EXPECT_EQ("Item::delete_item(): Runtime was destroyed previously", e.error_message());
4496+ }
4497+ }
4498+
4499+ // delete_item() on root with destroyed runtime must fail.
4500+ {
4501+ auto runtime = Runtime::create();
4502+ auto acc = get_account(runtime);
4503+ auto root = get_root(runtime);
4504+ clear_folder(root);
4505+
4506+ runtime->shutdown();
4507+ try
4508+ {
4509+ call(root->delete_item());
4510+ FAIL();
4511+ }
4512+ catch (RuntimeDestroyedException const& e)
4513+ {
4514+ EXPECT_EQ("Root::delete_item(): Runtime was destroyed previously", e.error_message());
4515+ }
4516+ }
4517+
4518+ // creation_time() with destroyed runtime must fail.
4519+ {
4520+ auto runtime = Runtime::create();
4521+ auto acc = get_account(runtime);
4522+ auto root = get_root(runtime);
4523+ clear_folder(root);
4524+
4525+ auto file = write_file(root, "file", "");
4526+ runtime->shutdown();
4527+ try
4528+ {
4529+ file->creation_time();
4530+ FAIL();
4531+ }
4532+ catch (RuntimeDestroyedException const& e)
4533+ {
4534+ EXPECT_EQ("Item::creation_time(): Runtime was destroyed previously", e.error_message());
4535+ }
4536+ }
4537+
4538+ // native_metadata() with destroyed runtime must fail.
4539+ {
4540+ auto runtime = Runtime::create();
4541+ auto acc = get_account(runtime);
4542+ auto root = get_root(runtime);
4543+ clear_folder(root);
4544+
4545+ auto file = write_file(root, "file", "");
4546+ runtime->shutdown();
4547+ try
4548+ {
4549+ file->native_metadata();
4550+ FAIL();
4551+ }
4552+ catch (RuntimeDestroyedException const& e)
4553+ {
4554+ EXPECT_EQ("Item::native_metadata(): Runtime was destroyed previously", e.error_message());
4555+ }
4556+ }
4557+
4558+ // name() on root with destroyed runtime must fail.
4559+ {
4560+ auto runtime = Runtime::create();
4561+ auto acc = get_account(runtime);
4562+ auto root = get_root(runtime);
4563+ clear_folder(root);
4564+
4565+ runtime->shutdown();
4566+ try
4567+ {
4568+ root->name();
4569+ FAIL();
4570+ }
4571+ catch (RuntimeDestroyedException const& e)
4572+ {
4573+ EXPECT_EQ("Root::name(): Runtime was destroyed previously", e.error_message());
4574+ }
4575+ }
4576+
4577+ // name() on folder with destroyed runtime must fail.
4578+ {
4579+ auto runtime = Runtime::create();
4580+ auto acc = get_account(runtime);
4581+ auto root = get_root(runtime);
4582+ clear_folder(root);
4583+
4584+ auto folder = call(root->create_folder("folder"));
4585+ runtime->shutdown();
4586+ try
4587+ {
4588+ folder->name();
4589+ FAIL();
4590+ }
4591+ catch (RuntimeDestroyedException const& e)
4592+ {
4593+ EXPECT_EQ("Folder::name(): Runtime was destroyed previously", e.error_message());
4594+ }
4595+ }
4596+
4597+ // name() on file with destroyed runtime must fail.
4598+ {
4599+ auto runtime = Runtime::create();
4600+ auto acc = get_account(runtime);
4601+ auto root = get_root(runtime);
4602+ clear_folder(root);
4603+
4604+ auto file = write_file(root, "file", "");
4605+ runtime->shutdown();
4606+ try
4607+ {
4608+ file->name();
4609+ FAIL();
4610+ }
4611+ catch (RuntimeDestroyedException const& e)
4612+ {
4613+ EXPECT_EQ("File::name(): Runtime was destroyed previously", e.error_message());
4614+ }
4615+ }
4616+
4617+ // list() with destroyed runtime must fail.
4618+ {
4619+ auto runtime = Runtime::create();
4620+ auto acc = get_account(runtime);
4621+ auto root = get_root(runtime);
4622+ clear_folder(root);
4623+
4624+ runtime->shutdown();
4625+ try
4626+ {
4627+ call(root->list());
4628+ FAIL();
4629+ }
4630+ catch (RuntimeDestroyedException const& e)
4631+ {
4632+ EXPECT_EQ("Folder::list(): Runtime was destroyed previously", e.error_message());
4633+ }
4634+ }
4635+
4636+ // lookup() with destroyed runtime must fail.
4637+ {
4638+ auto runtime = Runtime::create();
4639+ auto acc = get_account(runtime);
4640+ auto root = get_root(runtime);
4641+ clear_folder(root);
4642+
4643+ runtime->shutdown();
4644+ try
4645+ {
4646+ call(root->lookup("file"));
4647+ FAIL();
4648+ }
4649+ catch (RuntimeDestroyedException const& e)
4650+ {
4651+ EXPECT_EQ("Folder::lookup(): Runtime was destroyed previously", e.error_message());
4652+ }
4653+ }
4654+
4655+ // create_folder() with destroyed runtime must fail.
4656+ {
4657+ auto runtime = Runtime::create();
4658+ auto acc = get_account(runtime);
4659+ auto root = get_root(runtime);
4660+ clear_folder(root);
4661+
4662+ runtime->shutdown();
4663+ try
4664+ {
4665+ call(root->create_folder("folder"));
4666+ FAIL();
4667+ }
4668+ catch (RuntimeDestroyedException const& e)
4669+ {
4670+ EXPECT_EQ("Folder::create_folder(): Runtime was destroyed previously", e.error_message());
4671+ }
4672+ }
4673+
4674+ // create_file() with destroyed runtime must fail.
4675+ {
4676+ auto runtime = Runtime::create();
4677+ auto acc = get_account(runtime);
4678+ auto root = get_root(runtime);
4679+ clear_folder(root);
4680+
4681+ runtime->shutdown();
4682+ try
4683+ {
4684+ call(root->create_file("file", 0));
4685+ FAIL();
4686+ }
4687+ catch (RuntimeDestroyedException const& e)
4688+ {
4689+ EXPECT_EQ("Folder::create_file(): Runtime was destroyed previously", e.error_message());
4690+ }
4691+ }
4692+
4693+ // size() with destroyed runtime must fail.
4694+ {
4695+ auto runtime = Runtime::create();
4696+ auto acc = get_account(runtime);
4697+ auto root = get_root(runtime);
4698+ clear_folder(root);
4699+
4700+ auto file = write_file(root, "file", "");
4701+ runtime->shutdown();
4702+ try
4703+ {
4704+ file->size();
4705+ FAIL();
4706+ }
4707+ catch (RuntimeDestroyedException const& e)
4708+ {
4709+ EXPECT_EQ("File::size(): Runtime was destroyed previously", e.error_message());
4710+ }
4711+ }
4712+
4713+ // create_uploader() with destroyed runtime must fail.
4714+ {
4715+ auto runtime = Runtime::create();
4716+ auto acc = get_account(runtime);
4717+ auto root = get_root(runtime);
4718+ clear_folder(root);
4719+
4720+ auto file = write_file(root, "file", "");
4721+ runtime->shutdown();
4722+ try
4723+ {
4724+ call(file->create_uploader(ConflictPolicy::overwrite, 0));
4725+ FAIL();
4726+ }
4727+ catch (RuntimeDestroyedException const& e)
4728+ {
4729+ EXPECT_EQ("File::create_uploader(): Runtime was destroyed previously", e.error_message());
4730+ }
4731+ }
4732+
4733+ // create_downloader() with destroyed runtime must fail.
4734+ {
4735+ auto runtime = Runtime::create();
4736+ auto acc = get_account(runtime);
4737+ auto root = get_root(runtime);
4738+ clear_folder(root);
4739+
4740+ auto file = write_file(root, "file", "");
4741+ runtime->shutdown();
4742+ try
4743+ {
4744+ call(file->create_downloader());
4745+ FAIL();
4746+ }
4747+ catch (RuntimeDestroyedException const& e)
4748+ {
4749+ EXPECT_EQ("File::create_downloader(): Runtime was destroyed previously", e.error_message());
4750+ }
4751+ }
4752+
4753+ // free_space_bytes() with destroyed runtime must fail.
4754+ {
4755+ auto runtime = Runtime::create();
4756+ auto acc = get_account(runtime);
4757+ auto root = get_root(runtime);
4758+ clear_folder(root);
4759+
4760+ runtime->shutdown();
4761+ try
4762+ {
4763+ call(root->free_space_bytes());
4764+ FAIL();
4765+ }
4766+ catch (RuntimeDestroyedException const& e)
4767+ {
4768+ EXPECT_EQ("Root::free_space_bytes(): Runtime was destroyed previously", e.error_message());
4769+ }
4770+ }
4771+
4772+ // used_space_bytes() with destroyed runtime must fail.
4773+ {
4774+ auto runtime = Runtime::create();
4775+ auto acc = get_account(runtime);
4776+ auto root = get_root(runtime);
4777+ clear_folder(root);
4778+
4779+ runtime->shutdown();
4780+ try
4781+ {
4782+ call(root->used_space_bytes());
4783+ FAIL();
4784+ }
4785+ catch (RuntimeDestroyedException const& e)
4786+ {
4787+ EXPECT_EQ("Root::used_space_bytes(): Runtime was destroyed previously", e.error_message());
4788+ }
4789+ }
4790+
4791+ // get() with destroyed runtime must fail.
4792+ {
4793+ auto runtime = Runtime::create();
4794+ auto acc = get_account(runtime);
4795+ auto root = get_root(runtime);
4796+ clear_folder(root);
4797+
4798+ runtime->shutdown();
4799+ try
4800+ {
4801+ call(root->get("some_id"));
4802+ FAIL();
4803+ }
4804+ catch (RuntimeDestroyedException const& e)
4805+ {
4806+ EXPECT_EQ("Root::get(): Runtime was destroyed previously", e.error_message());
4807+ }
4808+ }
4809 }
4810
4811 int main(int argc, char** argv)
4812@@ -1488,6 +2426,7 @@
4813 setenv("STORAGE_FRAMEWORK_ROOT", TEST_DIR, true);
4814
4815 QCoreApplication app(argc, argv);
4816+ qRegisterMetaType<QLocalSocket::LocalSocketState>();
4817
4818 ::testing::InitGoogleTest(&argc, argv);
4819 return RUN_ALL_TESTS();
4820
4821=== modified file 'tests/remote-client/remote-client_test.cpp'
4822--- tests/remote-client/remote-client_test.cpp 2016-08-03 06:10:39 +0000
4823+++ tests/remote-client/remote-client_test.cpp 2016-08-03 06:10:39 +0000
4824@@ -262,7 +262,7 @@
4825 QFutureWatcher<QVector<Item::SPtr>> w;
4826 QSignalSpy spy(&w, &decltype(w)::finished);
4827 w.setFuture(list_fut);
4828- assert(spy.wait(SIGNAL_WAIT_TIME));
4829+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
4830 }
4831 auto items = list_fut.result();
4832 ASSERT_EQ(1, items.size());
4833@@ -273,7 +273,7 @@
4834 QFutureWatcher<Uploader::SPtr> w;
4835 QSignalSpy spy(&w, &decltype(w)::finished);
4836 w.setFuture(create_file_fut);
4837- assert(spy.wait(SIGNAL_WAIT_TIME));
4838+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
4839 }
4840 auto uploader = create_file_fut.result();
4841 auto finish_upload_fut = uploader->finish_upload();
4842@@ -281,7 +281,7 @@
4843 QFutureWatcher<File::SPtr> w;
4844 QSignalSpy spy(&w, &decltype(w)::finished);
4845 w.setFuture(finish_upload_fut);
4846- assert(spy.wait(SIGNAL_WAIT_TIME));
4847+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
4848 }
4849 auto file = finish_upload_fut.result();
4850 EXPECT_EQ(ItemType::file, file->type());
4851@@ -295,7 +295,7 @@
4852 QFutureWatcher<Item::SPtr> w;
4853 QSignalSpy spy(&w, &decltype(w)::finished);
4854 w.setFuture(get_fut);
4855- assert(spy.wait(SIGNAL_WAIT_TIME));
4856+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
4857 }
4858 file = dynamic_pointer_cast<File>(get_fut.result());
4859 EXPECT_EQ("child_id", file->native_identity());
4860@@ -308,7 +308,7 @@
4861 QFutureWatcher<Folder::SPtr> w;
4862 QSignalSpy spy(&w, &decltype(w)::finished);
4863 w.setFuture(create_folder_fut);
4864- assert(spy.wait(SIGNAL_WAIT_TIME));
4865+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
4866 }
4867 EXPECT_EQ(ItemType::folder, folder->type());
4868 EXPECT_EQ("folder1", folder->name());
4869@@ -408,7 +408,7 @@
4870 QFutureWatcher<QVector<Item::SPtr>> w;
4871 QSignalSpy spy(&w, &decltype(w)::finished);
4872 w.setFuture(lookup_fut);
4873- assert(spy.wait(SIGNAL_WAIT_TIME));
4874+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
4875 }
4876 auto children = lookup_fut.result();
4877 ASSERT_EQ(1, children.size());
4878@@ -421,7 +421,7 @@
4879 QFutureWatcher<Uploader::SPtr> w;
4880 QSignalSpy spy(&w, &decltype(w)::finished);
4881 w.setFuture(create_uploader_fut);
4882- assert(spy.wait(SIGNAL_WAIT_TIME));
4883+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
4884 }
4885 auto uploader = create_uploader_fut.result();
4886
4887@@ -430,7 +430,7 @@
4888 QFutureWatcher<File::SPtr> w;
4889 QSignalSpy spy(&w, &decltype(w)::finished);
4890 w.setFuture(finish_upload_fut);
4891- assert(spy.wait(SIGNAL_WAIT_TIME));
4892+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
4893 }
4894 auto uploaded_file = finish_upload_fut.result();
4895 EXPECT_EQ("some_id", uploaded_file->native_identity());
4896@@ -446,7 +446,7 @@
4897 QFutureWatcher<File::SPtr> w;
4898 QSignalSpy spy(&w, &decltype(w)::finished);
4899 w.setFuture(finish_upload_fut);
4900- assert(spy.wait(SIGNAL_WAIT_TIME));
4901+ ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
4902 }
4903 auto file = finish_upload_fut.result();
4904 }

Subscribers

People subscribed via source and target branches

to all changes: