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

Proposed by Michi Henning
Status: Superseded
Proposed branch: lp:~michihenning/storage-framework/more-coverage
Merge into: lp:storage-framework/devel
Diff against target: 5240 lines (+2562/-575) (has conflicts)
41 files modified
include/unity/storage/qt/client/Account.h (+1/-1)
include/unity/storage/qt/client/Exceptions.h (+8/-1)
include/unity/storage/qt/client/Item.h (+1/-1)
include/unity/storage/qt/client/Root.h (+1/-1)
include/unity/storage/qt/client/Runtime.h (+2/-0)
include/unity/storage/qt/client/internal/AccountBase.h (+1/-1)
include/unity/storage/qt/client/internal/ItemBase.h (+3/-1)
include/unity/storage/qt/client/internal/RootBase.h (+1/-1)
include/unity/storage/qt/client/internal/RuntimeBase.h (+7/-4)
include/unity/storage/qt/client/internal/local_client/DownloaderImpl.h (+2/-1)
include/unity/storage/qt/client/internal/local_client/FolderImpl.h (+1/-0)
include/unity/storage/qt/client/internal/local_client/ItemImpl.h (+4/-4)
include/unity/storage/qt/client/internal/local_client/UploaderImpl.h (+2/-2)
include/unity/storage/qt/client/internal/make_future.h (+59/-0)
include/unity/storage/qt/client/internal/remote_client/Handler.h (+2/-3)
include/unity/storage/qt/client/internal/remote_client/UploaderImpl.h (+1/-1)
src/qt/client/Account.cpp (+1/-1)
src/qt/client/Exceptions.cpp (+12/-1)
src/qt/client/Item.cpp (+1/-1)
src/qt/client/Root.cpp (+2/-1)
src/qt/client/internal/AccountBase.cpp (+10/-3)
src/qt/client/internal/ItemBase.cpp (+24/-4)
src/qt/client/internal/RootBase.cpp (+11/-2)
src/qt/client/internal/RuntimeBase.cpp (+0/-5)
src/qt/client/internal/local_client/AccountImpl.cpp (+16/-3)
src/qt/client/internal/local_client/DownloaderImpl.cpp (+18/-8)
src/qt/client/internal/local_client/FileImpl.cpp (+28/-5)
src/qt/client/internal/local_client/FolderImpl.cpp (+123/-25)
src/qt/client/internal/local_client/ItemImpl.cpp (+174/-96)
src/qt/client/internal/local_client/RootImpl.cpp (+87/-15)
src/qt/client/internal/local_client/RuntimeImpl.cpp (+6/-1)
src/qt/client/internal/local_client/UploaderImpl.cpp (+19/-22)
src/qt/client/internal/remote_client/AccountImpl.cpp (+13/-0)
src/qt/client/internal/remote_client/FileImpl.cpp (+22/-10)
src/qt/client/internal/remote_client/FolderImpl.cpp (+17/-13)
src/qt/client/internal/remote_client/ItemImpl.cpp (+39/-11)
src/qt/client/internal/remote_client/RootImpl.cpp (+33/-8)
src/qt/client/internal/remote_client/RuntimeImpl.cpp (+14/-3)
src/qt/client/internal/remote_client/UploaderImpl.cpp (+3/-10)
tests/local-client/local-client_test.cpp (+1782/-294)
tests/remote-client/remote-client_test.cpp (+11/-11)
Text conflict in tests/local-client/local-client_test.cpp
To merge this branch: bzr merge lp:~michihenning/storage-framework/more-coverage
Reviewer Review Type Date Requested Status
unity-api-1-bot continuous-integration Needs Fixing
Unity API Team Pending
Review via email: mp+301448@code.launchpad.net

This proposal has been superseded by 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.

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

Removed dependency on boost from remote client for exception handling.
Refactored exception handling logic and fixed tests to look for the correct exception.

73. By Michi Henning

More refactoring for error handling.

74. By Michi Henning

More refactoring to get rid of repeated exception handlers.

75. By Michi Henning

Removed stale code, fixed spy usage in tests.

76. By Michi Henning

A few coverage suppressions.

77. By Michi Henning

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

78. By Michi Henning

Added a few missing checks for destroyed runtime or item.

79. By Michi Henning

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

80. By Michi Henning

Fix typos.

Unmerged revisions

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

Subscribers

People subscribed via source and target branches

to all changes: