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
=== modified file 'include/unity/storage/qt/client/Account.h'
--- include/unity/storage/qt/client/Account.h 2016-07-22 02:35:12 +0000
+++ include/unity/storage/qt/client/Account.h 2016-07-29 05:58:49 +0000
@@ -77,7 +77,7 @@
7777
78 typedef std::shared_ptr<Account> SPtr;78 typedef std::shared_ptr<Account> SPtr;
7979
80 Runtime* runtime() const;80 std::shared_ptr<Runtime> runtime() const;
8181
82 QString owner() const;82 QString owner() const;
83 QString owner_id() const;83 QString owner_id() const;
8484
=== modified file 'include/unity/storage/qt/client/Exceptions.h'
--- include/unity/storage/qt/client/Exceptions.h 2016-07-22 02:35:12 +0000
+++ include/unity/storage/qt/client/Exceptions.h 2016-07-29 05:58:49 +0000
@@ -46,6 +46,8 @@
4646
47 virtual char const* what() const noexcept override;47 virtual char const* what() const noexcept override;
4848
49 QString error_message() const;
50
49private:51private:
50 std::string what_string_;52 std::string what_string_;
51 QString error_message_;53 QString error_message_;
@@ -234,11 +236,16 @@
234class UNITY_STORAGE_EXPORT ResourceException : public StorageException236class UNITY_STORAGE_EXPORT ResourceException : public StorageException
235{237{
236public:238public:
237 ResourceException(QString const& error_message);239 ResourceException(QString const& error_message, int error_code);
238 ~ResourceException();240 ~ResourceException();
239241
240 virtual ResourceException* clone() const override;242 virtual ResourceException* clone() const override;
241 virtual void raise() const override;243 virtual void raise() const override;
244
245 int error_code() const noexcept;
246
247private:
248 int error_code_;
242};249};
243250
244} // namespace client251} // namespace client
245252
=== modified file 'include/unity/storage/qt/client/Item.h'
--- include/unity/storage/qt/client/Item.h 2016-07-22 02:45:24 +0000
+++ include/unity/storage/qt/client/Item.h 2016-07-29 05:58:49 +0000
@@ -103,7 +103,7 @@
103103
104 If this item is a root, the returned pointer points at this item.104 If this item is a root, the returned pointer points at this item.
105 */105 */
106 Root* root() const;106 std::shared_ptr<Root> root() const;
107107
108 /**108 /**
109 \brief Returns the type of the item.109 \brief Returns the type of the item.
110110
=== modified file 'include/unity/storage/qt/client/Root.h'
--- include/unity/storage/qt/client/Root.h 2016-07-12 02:22:05 +0000
+++ include/unity/storage/qt/client/Root.h 2016-07-29 05:58:49 +0000
@@ -70,7 +70,7 @@
70 /**70 /**
71 \brief Returns the account for this root.71 \brief Returns the account for this root.
72 */72 */
73 Account* account() const;73 std::shared_ptr<Account> account() const;
7474
75 QFuture<int64_t> free_space_bytes() const;75 QFuture<int64_t> free_space_bytes() const;
76 QFuture<int64_t> used_space_bytes() const;76 QFuture<int64_t> used_space_bytes() const;
7777
=== modified file 'include/unity/storage/qt/client/Runtime.h'
--- include/unity/storage/qt/client/Runtime.h 2016-07-22 02:35:12 +0000
+++ include/unity/storage/qt/client/Runtime.h 2016-07-29 05:58:49 +0000
@@ -44,6 +44,7 @@
44namespace internal44namespace internal
45{45{
4646
47class AccountBase;
47class RuntimeBase;48class RuntimeBase;
4849
49namespace remote_client50namespace remote_client
@@ -100,6 +101,7 @@
100101
101 std::shared_ptr<internal::RuntimeBase> p_;102 std::shared_ptr<internal::RuntimeBase> p_;
102103
104 friend class internal::AccountBase;
103 friend class internal::remote_client::AccountImpl;105 friend class internal::remote_client::AccountImpl;
104};106};
105107
106108
=== modified file 'include/unity/storage/qt/client/internal/AccountBase.h'
--- include/unity/storage/qt/client/internal/AccountBase.h 2016-07-22 02:35:12 +0000
+++ include/unity/storage/qt/client/internal/AccountBase.h 2016-07-29 05:58:49 +0000
@@ -52,7 +52,7 @@
52 AccountBase(AccountBase const&) = delete;52 AccountBase(AccountBase const&) = delete;
53 AccountBase& operator=(AccountBase const&) = delete;53 AccountBase& operator=(AccountBase const&) = delete;
5454
55 Runtime* runtime() const;55 std::shared_ptr<Runtime> runtime() const;
56 virtual QString owner() const = 0;56 virtual QString owner() const = 0;
57 virtual QString owner_id() const = 0;57 virtual QString owner_id() const = 0;
58 virtual QString description() const = 0;58 virtual QString description() const = 0;
5959
=== modified file 'include/unity/storage/qt/client/internal/ItemBase.h'
--- include/unity/storage/qt/client/internal/ItemBase.h 2016-07-27 02:19:17 +0000
+++ include/unity/storage/qt/client/internal/ItemBase.h 2016-07-29 05:58:49 +0000
@@ -60,7 +60,7 @@
6060
61 QString native_identity() const;61 QString native_identity() const;
62 ItemType type() const;62 ItemType type() const;
63 Root* root() const;63 std::shared_ptr<Root> root() const;
6464
65 virtual QString name() const = 0;65 virtual QString name() const = 0;
66 virtual QString etag() const = 0;66 virtual QString etag() const = 0;
@@ -82,6 +82,8 @@
82 void set_public_instance(std::weak_ptr<Item> p);82 void set_public_instance(std::weak_ptr<Item> p);
8383
84protected:84protected:
85 std::shared_ptr<Root> get_root() const noexcept;
86
85 const QString identity_;87 const QString identity_;
86 const ItemType type_;88 const ItemType type_;
87 std::weak_ptr<Root> root_;89 std::weak_ptr<Root> root_;
8890
=== modified file 'include/unity/storage/qt/client/internal/RootBase.h'
--- include/unity/storage/qt/client/internal/RootBase.h 2016-07-12 02:22:05 +0000
+++ include/unity/storage/qt/client/internal/RootBase.h 2016-07-29 05:58:49 +0000
@@ -46,7 +46,7 @@
46public:46public:
47 RootBase(QString const& identity, std::weak_ptr<Account> const& account);47 RootBase(QString const& identity, std::weak_ptr<Account> const& account);
4848
49 Account* account() const;49 std::shared_ptr<Account> account() const;
50 virtual QFuture<int64_t> free_space_bytes() const = 0;50 virtual QFuture<int64_t> free_space_bytes() const = 0;
51 virtual QFuture<int64_t> used_space_bytes() const = 0;51 virtual QFuture<int64_t> used_space_bytes() const = 0;
52 virtual QFuture<Item::SPtr> get(QString native_identity) const = 0;52 virtual QFuture<Item::SPtr> get(QString native_identity) const = 0;
5353
=== modified file 'include/unity/storage/qt/client/internal/RuntimeBase.h'
--- include/unity/storage/qt/client/internal/RuntimeBase.h 2016-07-22 02:35:12 +0000
+++ include/unity/storage/qt/client/internal/RuntimeBase.h 2016-07-29 05:58:49 +0000
@@ -25,7 +25,6 @@
25#pragma GCC diagnostic pop25#pragma GCC diagnostic pop
26#include <QVector>26#include <QVector>
2727
28#include <atomic>
29#include <memory>28#include <memory>
3029
31namespace unity30namespace unity
@@ -43,10 +42,12 @@
43namespace internal42namespace internal
44{43{
4544
45class AccountBase;
46
46class RuntimeBase : public QObject47class RuntimeBase : public QObject
47{48{
48public:49public:
49 RuntimeBase();50 RuntimeBase() = default;
50 virtual ~RuntimeBase() = default;51 virtual ~RuntimeBase() = default;
51 RuntimeBase(RuntimeBase const&) = delete;52 RuntimeBase(RuntimeBase const&) = delete;
52 RuntimeBase& operator=(RuntimeBase const&) = delete;53 RuntimeBase& operator=(RuntimeBase const&) = delete;
@@ -57,9 +58,11 @@
57 void set_public_instance(std::weak_ptr<Runtime> p);58 void set_public_instance(std::weak_ptr<Runtime> p);
5859
59protected:60protected:
60 std::atomic_bool destroyed_;61 bool destroyed_ = false;
61 QVector<std::shared_ptr<Account>> accounts_; // Immutable once set62 QVector<std::shared_ptr<Account>> accounts_;
62 std::weak_ptr<Runtime> public_instance_; // Immutable once set63 std::weak_ptr<Runtime> public_instance_; // Immutable once set
64
65 friend class unity::storage::qt::client::internal::AccountBase;
63};66};
6467
65} // namespace internal68} // namespace internal
6669
=== modified file 'include/unity/storage/qt/client/internal/local_client/DownloaderImpl.h'
--- include/unity/storage/qt/client/internal/local_client/DownloaderImpl.h 2016-07-14 04:50:36 +0000
+++ include/unity/storage/qt/client/internal/local_client/DownloaderImpl.h 2016-07-29 05:58:49 +0000
@@ -60,7 +60,7 @@
6060
61private:61private:
62 void read_and_write_chunk();62 void read_and_write_chunk();
63 void handle_error(QString const& msg);63 void handle_error(QString const& msg, int error_code);
6464
65 enum State { in_progress, finalized, cancelled, error };65 enum State { in_progress, finalized, cancelled, error };
6666
@@ -73,6 +73,7 @@
73 QFutureInterface<void>& worker_initialized_;73 QFutureInterface<void>& worker_initialized_;
74 qint64 bytes_to_write_;74 qint64 bytes_to_write_;
75 QString error_msg_;75 QString error_msg_;
76 int error_code_ = 0;
76};77};
7778
78class DownloadThread : public QThread79class DownloadThread : public QThread
7980
=== modified file 'include/unity/storage/qt/client/internal/local_client/FolderImpl.h'
--- include/unity/storage/qt/client/internal/local_client/FolderImpl.h 2016-07-14 00:17:14 +0000
+++ include/unity/storage/qt/client/internal/local_client/FolderImpl.h 2016-07-29 05:58:49 +0000
@@ -40,6 +40,7 @@
40 FolderImpl(QString const& identity);40 FolderImpl(QString const& identity);
41 FolderImpl(QString const& identity, ItemType type);41 FolderImpl(QString const& identity, ItemType type);
4242
43 virtual QString name() const override;
43 QFuture<QVector<std::shared_ptr<Item>>> list() const override;44 QFuture<QVector<std::shared_ptr<Item>>> list() const override;
44 QFuture<QVector<std::shared_ptr<Item>>> lookup(QString const& name) const override;45 QFuture<QVector<std::shared_ptr<Item>>> lookup(QString const& name) const override;
45 QFuture<std::shared_ptr<Folder>> create_folder(QString const& name) override;46 QFuture<std::shared_ptr<Folder>> create_folder(QString const& name) override;
4647
=== modified file 'include/unity/storage/qt/client/internal/local_client/ItemImpl.h'
--- include/unity/storage/qt/client/internal/local_client/ItemImpl.h 2016-07-27 02:19:17 +0000
+++ include/unity/storage/qt/client/internal/local_client/ItemImpl.h 2016-07-29 05:58:49 +0000
@@ -47,7 +47,6 @@
47 ItemImpl(QString const& identity, ItemType type);47 ItemImpl(QString const& identity, ItemType type);
48 virtual ~ItemImpl();48 virtual ~ItemImpl();
4949
50 virtual QString name() const override;
51 virtual QString etag() const override;50 virtual QString etag() const override;
52 virtual QVariantMap metadata() const override;51 virtual QVariantMap metadata() const override;
53 virtual QDateTime last_modified_time() const override;52 virtual QDateTime last_modified_time() const override;
@@ -65,8 +64,6 @@
65 void set_timestamps() noexcept;64 void set_timestamps() noexcept;
66 bool has_conflict() const noexcept;65 bool has_conflict() const noexcept;
6766
68 std::unique_lock<std::mutex> get_lock();
69
70protected:67protected:
71 static boost::filesystem::path sanitize(QString const& name, QString const& method);68 static boost::filesystem::path sanitize(QString const& name, QString const& method);
72 static bool is_reserved_path(boost::filesystem::path const& path) noexcept;69 static bool is_reserved_path(boost::filesystem::path const& path) noexcept;
@@ -78,7 +75,10 @@
78 QString etag_;75 QString etag_;
79 QDateTime modified_time_;76 QDateTime modified_time_;
80 QVariantMap metadata_;77 QVariantMap metadata_;
81 std::mutex mutable mutex_;78 std::recursive_mutex mutable mutex_;
79
80private:
81 static void copy_recursively(boost::filesystem::path const& source, boost::filesystem::path const& target);
82};82};
8383
84} // namespace local_client84} // namespace local_client
8585
=== modified file 'include/unity/storage/qt/client/internal/local_client/UploaderImpl.h'
--- include/unity/storage/qt/client/internal/local_client/UploaderImpl.h 2016-07-26 01:51:26 +0000
+++ include/unity/storage/qt/client/internal/local_client/UploaderImpl.h 2016-07-29 05:58:49 +0000
@@ -68,12 +68,11 @@
68private Q_SLOTS:68private Q_SLOTS:
69 void on_bytes_ready();69 void on_bytes_ready();
70 void on_read_channel_finished();70 void on_read_channel_finished();
71 void on_error();
7271
73private:72private:
74 void read_and_write_chunk();73 void read_and_write_chunk();
75 void finalize();74 void finalize();
76 void handle_error(QString const& msg);75 void handle_error(QString const& msg, int error_code);
7776
78 enum State { in_progress, finalized, cancelled, error };77 enum State { in_progress, finalized, cancelled, error };
7978
@@ -91,6 +90,7 @@
91 QFutureInterface<std::shared_ptr<File>>& qf_;90 QFutureInterface<std::shared_ptr<File>>& qf_;
92 QFutureInterface<void>& worker_initialized_;91 QFutureInterface<void>& worker_initialized_;
93 QString error_msg_;92 QString error_msg_;
93 int error_code_ = 0;
94 bool use_linkat_ = true;94 bool use_linkat_ = true;
95};95};
9696
9797
=== modified file 'include/unity/storage/qt/client/internal/make_future.h'
--- include/unity/storage/qt/client/internal/make_future.h 2016-07-22 02:35:12 +0000
+++ include/unity/storage/qt/client/internal/make_future.h 2016-07-29 05:58:49 +0000
@@ -18,6 +18,9 @@
1818
19#pragma once19#pragma once
2020
21#include <unity/storage/qt/client/Exceptions.h>
22#include <unity/storage/qt/client/internal/boost_filesystem.h>
23
21#pragma GCC diagnostic push24#pragma GCC diagnostic push
22#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"25#pragma GCC diagnostic ignored "-Wctor-dtor-privacy"
23#pragma GCC diagnostic ignored "-Wcast-align"26#pragma GCC diagnostic ignored "-Wcast-align"
@@ -94,6 +97,62 @@
94 return qf.future();97 return qf.future();
95}98}
9699
100template<typename T>
101QFuture<T> make_exceptional_future(QString const& msg, boost::filesystem::filesystem_error const& e)
102{
103 QFutureInterface<T> qf;
104 switch (e.code().value())
105 {
106 case EACCES:
107 case EPERM:
108 {
109 qf.reportException(PermissionException(msg));
110 break;
111 }
112 case EDQUOT:
113 case ENOSPC:
114 {
115 qf.reportException(QuotaException(msg));
116 break;
117 }
118 case ENOENT:
119 {
120 //qf.reportException(NotExistsException(msg));
121 qDebug() << "ENOENT";
122 qf.reportException(ResourceException(msg, e.code().value()));
123 break;
124 }
125 default:
126 {
127 qf.reportException(ResourceException(msg, e.code().value()));
128 break;
129 }
130 }
131 qf.reportFinished();
132 return qf.future();
133}
134
135template<typename T>
136QFuture<T> make_exceptional_future(QString const& msg, boost::filesystem::filesystem_error const& e, QString const& key)
137{
138 QFutureInterface<T> qf;
139 switch (e.code().value())
140 {
141 case ENOENT:
142 {
143 qf.reportException(NotExistsException(msg, key));
144 break;
145 }
146 default:
147 {
148 return make_exceptional_future<T>(msg, e);
149 break;
150 }
151 }
152 qf.reportFinished();
153 return qf.future();
154}
155
97} // namespace internal156} // namespace internal
98} // namespace client157} // namespace client
99} // namespace qt158} // namespace qt
100159
=== modified file 'include/unity/storage/qt/client/internal/remote_client/Handler.h'
--- include/unity/storage/qt/client/internal/remote_client/Handler.h 2016-07-15 03:58:35 +0000
+++ include/unity/storage/qt/client/internal/remote_client/Handler.h 2016-07-29 05:58:49 +0000
@@ -18,7 +18,6 @@
1818
19#pragma once19#pragma once
2020
21#include <unity/storage/qt/client/Exceptions.h>
22#include <unity/storage/qt/client/internal/make_future.h>21#include <unity/storage/qt/client/internal/make_future.h>
23#include <unity/storage/qt/client/internal/remote_client/HandlerBase.h>22#include <unity/storage/qt/client/internal/remote_client/HandlerBase.h>
2423
@@ -72,8 +71,8 @@
72 {71 {
73 if (call.isError())72 if (call.isError())
74 {73 {
75 qDebug() << call.error().message(); // TODO, remove this74 int err = call.error().type();
76 make_exceptional_future<T>(ResourceException("DBus error return"));75 make_exceptional_future<T>(ResourceException("DBus error return", err));
77 return;76 return;
78 }77 }
79 // TODO: See HACK above. Should just be closure(call, qf_);78 // TODO: See HACK above. Should just be closure(call, qf_);
8079
=== modified file 'include/unity/storage/qt/client/internal/remote_client/UploaderImpl.h'
--- include/unity/storage/qt/client/internal/remote_client/UploaderImpl.h 2016-07-22 00:17:24 +0000
+++ include/unity/storage/qt/client/internal/remote_client/UploaderImpl.h 2016-07-29 05:58:49 +0000
@@ -69,7 +69,7 @@
69 QDBusUnixFileDescriptor fd_;69 QDBusUnixFileDescriptor fd_;
70 int64_t size_;70 int64_t size_;
71 QString old_etag_;71 QString old_etag_;
72 std::weak_ptr<Root> root_;72 std::shared_ptr<Root> root_;
73 std::shared_ptr<ProviderInterface> provider_;73 std::shared_ptr<ProviderInterface> provider_;
74 std::shared_ptr<QLocalSocket> write_socket_;74 std::shared_ptr<QLocalSocket> write_socket_;
75};75};
7676
=== modified file 'src/qt/client/Account.cpp'
--- src/qt/client/Account.cpp 2016-07-12 02:22:05 +0000
+++ src/qt/client/Account.cpp 2016-07-29 05:58:49 +0000
@@ -41,7 +41,7 @@
4141
42Account::~Account() = default;42Account::~Account() = default;
4343
44Runtime* Account::runtime() const44shared_ptr<Runtime> Account::runtime() const
45{45{
46 return p_->runtime();46 return p_->runtime();
47}47}
4848
=== modified file 'src/qt/client/Exceptions.cpp'
--- src/qt/client/Exceptions.cpp 2016-07-22 01:45:39 +0000
+++ src/qt/client/Exceptions.cpp 2016-07-29 05:58:49 +0000
@@ -42,6 +42,11 @@
42 return what_string_.c_str();42 return what_string_.c_str();
43}43}
4444
45QString StorageException::error_message() const
46{
47 return error_message_;
48}
49
45LocalCommsException::LocalCommsException(QString const& error_message)50LocalCommsException::LocalCommsException(QString const& error_message)
46 : StorageException("LocalCommsException", error_message)51 : StorageException("LocalCommsException", error_message)
47{52{
@@ -276,8 +281,9 @@
276 throw *this;281 throw *this;
277}282}
278283
279ResourceException::ResourceException(QString const& error_message)284ResourceException::ResourceException(QString const& error_message, int error_code)
280 : StorageException("ResourceException", error_message)285 : StorageException("ResourceException", error_message)
286 , error_code_(error_code)
281{287{
282}288}
283289
@@ -293,6 +299,11 @@
293 throw *this;299 throw *this;
294}300}
295301
302int ResourceException::error_code() const noexcept
303{
304 return error_code_;
305}
306
296} // namespace client307} // namespace client
297} // namespace qt308} // namespace qt
298} // namespace storage309} // namespace storage
299310
=== modified file 'src/qt/client/Item.cpp'
--- src/qt/client/Item.cpp 2016-07-14 00:25:40 +0000
+++ src/qt/client/Item.cpp 2016-07-29 05:58:49 +0000
@@ -51,7 +51,7 @@
51 return p_->name();51 return p_->name();
52}52}
5353
54Root* Item::root() const54shared_ptr<Root> Item::root() const
55{55{
56 return p_->root();56 return p_->root();
57}57}
5858
=== modified file 'src/qt/client/Root.cpp'
--- src/qt/client/Root.cpp 2016-07-12 02:22:05 +0000
+++ src/qt/client/Root.cpp 2016-07-29 05:58:49 +0000
@@ -30,6 +30,7 @@
30{30{
3131
32using namespace internal;32using namespace internal;
33using namespace std;
3334
34Root::Root(RootBase* p)35Root::Root(RootBase* p)
35 : Folder(p)36 : Folder(p)
@@ -38,7 +39,7 @@
3839
39Root::~Root() = default;40Root::~Root() = default;
4041
41Account* Root::account() const42shared_ptr<Account> Root::account() const
42{43{
43 return dynamic_cast<RootBase*>(p_.get())->account();44 return dynamic_cast<RootBase*>(p_.get())->account();
44}45}
4546
=== modified file 'src/qt/client/internal/AccountBase.cpp'
--- src/qt/client/internal/AccountBase.cpp 2016-07-14 04:50:36 +0000
+++ src/qt/client/internal/AccountBase.cpp 2016-07-29 05:58:49 +0000
@@ -19,6 +19,8 @@
19#include <unity/storage/qt/client/internal/AccountBase.h>19#include <unity/storage/qt/client/internal/AccountBase.h>
2020
21#include <unity/storage/qt/client/Exceptions.h>21#include <unity/storage/qt/client/Exceptions.h>
22#include <unity/storage/qt/client/internal/RuntimeBase.h>
23#include <unity/storage/qt/client/Runtime.h>
2224
23#include <cassert>25#include <cassert>
2426
@@ -42,13 +44,18 @@
42 assert(runtime.lock());44 assert(runtime.lock());
43}45}
4446
45Runtime* AccountBase::runtime() const47shared_ptr<Runtime> AccountBase::runtime() const
46{48{
47 if (auto runtime = runtime_.lock())49 if (auto runtime = runtime_.lock())
48 {50 {
49 return runtime.get();51 auto runtime_base = runtime->p_;
52 if (runtime_base->destroyed_)
53 {
54 throw RuntimeDestroyedException("Account::runtime()");
55 }
56 return runtime;
50 }57 }
51 throw RuntimeDestroyedException("AccountBase::runtime()");58 throw RuntimeDestroyedException("Account::runtime()");
52}59}
5360
54void AccountBase::set_public_instance(weak_ptr<Account> const& p)61void AccountBase::set_public_instance(weak_ptr<Account> const& p)
5562
=== modified file 'src/qt/client/internal/ItemBase.cpp'
--- src/qt/client/internal/ItemBase.cpp 2016-07-14 04:50:36 +0000
+++ src/qt/client/internal/ItemBase.cpp 2016-07-29 05:58:49 +0000
@@ -18,7 +18,9 @@
1818
19#include <unity/storage/qt/client/internal/ItemBase.h>19#include <unity/storage/qt/client/internal/ItemBase.h>
2020
21#include <unity/storage/qt/client/Account.h>
21#include <unity/storage/qt/client/Exceptions.h>22#include <unity/storage/qt/client/Exceptions.h>
23#include <unity/storage/qt/client/Root.h>
2224
23#include <cassert>25#include <cassert>
2426
@@ -54,13 +56,14 @@
54 return type_;56 return type_;
55}57}
5658
57Root* ItemBase::root() const59shared_ptr<Root> ItemBase::root() const
58{60{
59 if (auto r = root_.lock())61 auto root = get_root();
62 if (!root)
60 {63 {
61 return r.get();64 throw RuntimeDestroyedException("Item::root()");
62 }65 }
63 throw RuntimeDestroyedException("Item::root()");66 return root;
64}67}
6568
66void ItemBase::set_root(std::weak_ptr<Root> root)69void ItemBase::set_root(std::weak_ptr<Root> root)
@@ -75,6 +78,23 @@
75 public_instance_ = p;78 public_instance_ = p;
76}79}
7780
81shared_ptr<Root> ItemBase::get_root() const noexcept
82{
83 try
84 {
85 auto root = root_.lock();
86 if (root)
87 {
88 root->account(); // Throws if either account or runtime has been destroyed.
89 return root;
90 }
91 }
92 catch (RuntimeDestroyedException const&)
93 {
94 }
95 return nullptr;
96}
97
78} // namespace internal98} // namespace internal
79} // namespace client99} // namespace client
80} // namespace qt100} // namespace qt
81101
=== modified file 'src/qt/client/internal/RootBase.cpp'
--- src/qt/client/internal/RootBase.cpp 2016-07-27 02:19:17 +0000
+++ src/qt/client/internal/RootBase.cpp 2016-07-29 05:58:49 +0000
@@ -18,6 +18,7 @@
1818
19#include <unity/storage/qt/client/internal/RootBase.h>19#include <unity/storage/qt/client/internal/RootBase.h>
2020
21#include <unity/storage/qt/client/Account.h>
21#include <unity/storage/qt/client/Exceptions.h>22#include <unity/storage/qt/client/Exceptions.h>
2223
23#include <cassert>24#include <cassert>
@@ -43,11 +44,19 @@
43 assert(account.lock());44 assert(account.lock());
44}45}
4546
46Account* RootBase::account() const47shared_ptr<Account> RootBase::account() const
47{48{
48 if (auto acc = account_.lock())49 if (auto acc = account_.lock())
49 {50 {
50 return acc.get();51 try
52 {
53 acc->runtime();
54 }
55 catch (RuntimeDestroyedException const&)
56 {
57 throw RuntimeDestroyedException("Root::account()");
58 }
59 return acc;
51 }60 }
52 throw RuntimeDestroyedException("Root::account()");61 throw RuntimeDestroyedException("Root::account()");
53}62}
5463
=== modified file 'src/qt/client/internal/RuntimeBase.cpp'
--- src/qt/client/internal/RuntimeBase.cpp 2016-07-12 02:22:05 +0000
+++ src/qt/client/internal/RuntimeBase.cpp 2016-07-29 05:58:49 +0000
@@ -34,11 +34,6 @@
34namespace internal34namespace internal
35{35{
3636
37RuntimeBase::RuntimeBase()
38 : destroyed_(false)
39{
40}
41
42void RuntimeBase::set_public_instance(weak_ptr<Runtime> p)37void RuntimeBase::set_public_instance(weak_ptr<Runtime> p)
43{38{
44 assert(p.lock());39 assert(p.lock());
4540
=== modified file 'src/qt/client/internal/local_client/AccountImpl.cpp'
--- src/qt/client/internal/local_client/AccountImpl.cpp 2016-07-14 04:50:36 +0000
+++ src/qt/client/internal/local_client/AccountImpl.cpp 2016-07-29 05:58:49 +0000
@@ -67,7 +67,7 @@
67 if (ec)67 if (ec)
68 {68 {
69 QString msg = "Account::roots(): Cannot stat " + QString(dir) + ": " + QString::fromStdString(ec.message());69 QString msg = "Account::roots(): Cannot stat " + QString(dir) + ": " + QString::fromStdString(ec.message());
70 throw ResourceException(msg);70 throw ResourceException(msg, errno);
71 }71 }
72 if (!is_dir)72 if (!is_dir)
73 {73 {
@@ -85,7 +85,7 @@
85 {85 {
86 QString msg = "Account::roots(): Cannot create " + QString(dir) + ": "86 QString msg = "Account::roots(): Cannot create " + QString(dir) + ": "
87 + QString::fromStdString(ec.message());87 + QString::fromStdString(ec.message());
88 throw ResourceException(msg);88 throw ResourceException(msg, ec.value());
89 }89 }
90 }90 }
91 return data_dir;91 return data_dir;
@@ -109,22 +109,32 @@
109109
110QString AccountImpl::owner() const110QString AccountImpl::owner() const
111{111{
112 runtime(); // Throws RuntimeDestroyedException if runtime was destroyed.
112 return owner_;113 return owner_;
113}114}
114115
115QString AccountImpl::owner_id() const116QString AccountImpl::owner_id() const
116{117{
118 runtime(); // Throws RuntimeDestroyedException if runtime was destroyed.
117 return owner_id_;119 return owner_id_;
118}120}
119121
120QString AccountImpl::description() const122QString AccountImpl::description() const
121{123{
124 runtime(); // Throws RuntimeDestroyedException if runtime was destroyed.
122 return description_;125 return description_;
123}126}
124127
125QFuture<QVector<Root::SPtr>> AccountImpl::roots()128QFuture<QVector<Root::SPtr>> AccountImpl::roots()
126{129{
127 using namespace boost::filesystem;130 try
131 {
132 runtime(); // Throws RuntimeDestroyedException if runtime was destroyed.
133 }
134 catch (RuntimeDestroyedException const& e)
135 {
136 return make_exceptional_future<QVector<Root::SPtr>>(e);
137 }
128138
129 if (!roots_.isEmpty())139 if (!roots_.isEmpty())
130 {140 {
@@ -132,6 +142,9 @@
132 }142 }
133143
134 // Create the root on first access.144 // Create the root on first access.
145
146 using namespace boost::filesystem;
147
135 auto rpath = canonical(get_data_dir()).native();148 auto rpath = canonical(get_data_dir()).native();
136 auto root = RootImpl::make_root(QString::fromStdString(rpath), public_instance_);149 auto root = RootImpl::make_root(QString::fromStdString(rpath), public_instance_);
137 roots_.append(root);150 roots_.append(root);
138151
=== modified file 'src/qt/client/internal/local_client/DownloaderImpl.cpp'
--- src/qt/client/internal/local_client/DownloaderImpl.cpp 2016-07-22 00:17:24 +0000
+++ src/qt/client/internal/local_client/DownloaderImpl.cpp 2016-07-29 05:58:49 +0000
@@ -79,8 +79,10 @@
79 input_file_.reset(new QFile(filename_));79 input_file_.reset(new QFile(filename_));
80 if (!input_file_->open(QIODevice::ReadOnly))80 if (!input_file_->open(QIODevice::ReadOnly))
81 {81 {
82 handle_error("cannot open " + filename_ + ": " + input_file_->errorString());82 // LCOV_EXCL_START
83 handle_error("cannot open " + filename_ + ": " + input_file_->errorString(), input_file_->error());
83 return;84 return;
85 // LCOV_EXCL_STOP
84 }86 }
85 bytes_to_write_ = input_file_->size();87 bytes_to_write_ = input_file_->size();
8688
@@ -136,7 +138,7 @@
136 }138 }
137 case error:139 case error:
138 {140 {
139 make_exceptional_future(qf_, ResourceException(error_msg_));141 make_exceptional_future(qf_, ResourceException(error_msg_, error_code_));
140 break;142 break;
141 }143 }
142 default:144 default:
@@ -189,7 +191,8 @@
189191
190void DownloadWorker::on_error()192void DownloadWorker::on_error()
191{193{
192 handle_error(write_socket_->errorString());194 disconnect(write_socket_.get(), nullptr, this, nullptr);
195 handle_error(write_socket_->errorString(), write_socket_->error());
193}196}
194197
195// Read the next chunk of data from the input file and write it to the socket.198// Read the next chunk of data from the input file and write it to the socket.
@@ -203,25 +206,31 @@
203 auto bytes_read = input_file_->read(buf.data(), buf.size());206 auto bytes_read = input_file_->read(buf.data(), buf.size());
204 if (bytes_read == -1)207 if (bytes_read == -1)
205 {208 {
206 handle_error(filename_ + ": read error: " + input_file_->errorString());209 // LCOV_EXCL_START
210 handle_error(filename_ + ": read error: " + input_file_->errorString(), input_file_->error());
207 return;211 return;
212 // LCOV_EXCL_STOP
208 }213 }
209 buf.resize(bytes_read);214 buf.resize(bytes_read);
210215
211 auto bytes_written = write_socket_->write(buf);216 auto bytes_written = write_socket_->write(buf);
212 if (bytes_written == -1)217 if (bytes_written == -1)
213 {218 {
214 handle_error(filename_ + ": socket error: " + write_socket_->errorString());219 // LCOV_EXCL_START
220 handle_error(filename_ + ": socket error: " + write_socket_->errorString(), write_socket_->error());
221 // LCOV_EXCL_STOP
215 }222 }
216 else if (bytes_written != bytes_read)223 else if (bytes_written != bytes_read)
217 {224 {
225 // LCOV_EXCL_START
218 QString msg = filename_ + ": write error, requested " + bytes_read + " B, but wrote only "226 QString msg = filename_ + ": write error, requested " + bytes_read + " B, but wrote only "
219 + bytes_written + " B.";227 + bytes_written + " B.";
220 handle_error(msg);228 handle_error(msg, 0);
229 // LCOV_EXCL_STOP
221 }230 }
222}231}
223232
224void DownloadWorker::handle_error(QString const& msg)233void DownloadWorker::handle_error(QString const& msg, int error_code)
225{234{
226 if (state_ == in_progress)235 if (state_ == in_progress)
227 {236 {
@@ -229,6 +238,7 @@
229 }238 }
230 state_ = error;239 state_ = error;
231 error_msg_ = "Downloader: " + msg;240 error_msg_ = "Downloader: " + msg;
241 error_code_ = error_code;
232 do_finish();242 do_finish();
233}243}
234244
@@ -255,7 +265,7 @@
255 // LCOV_EXCL_START265 // LCOV_EXCL_START
256 QString msg = "Downloader: cannot create socket pair: "266 QString msg = "Downloader: cannot create socket pair: "
257 + QString::fromStdString(storage::internal::safe_strerror(errno));267 + QString::fromStdString(storage::internal::safe_strerror(errno));
258 make_exceptional_future(qf_, ResourceException(msg));268 make_exceptional_future(qf_, ResourceException(msg, errno));
259 return;269 return;
260 // LCOV_EXCL_STOP270 // LCOV_EXCL_STOP
261 }271 }
262272
=== modified file 'src/qt/client/internal/local_client/FileImpl.cpp'
--- src/qt/client/internal/local_client/FileImpl.cpp 2016-07-22 01:45:39 +0000
+++ src/qt/client/internal/local_client/FileImpl.cpp 2016-07-29 05:58:49 +0000
@@ -50,38 +50,52 @@
5050
51QString FileImpl::name() const51QString FileImpl::name() const
52{52{
53 lock_guard<mutex> guard(mutex_);53 lock_guard<decltype(mutex_)> guard(mutex_);
5454
55 if (deleted_)55 if (deleted_)
56 {56 {
57 throw deleted_ex("File::name()");57 throw deleted_ex("File::name()");
58 }58 }
59 if (!get_root())
60 {
61 throw RuntimeDestroyedException("File::name()");
62 }
59 return name_;63 return name_;
60}64}
6165
62int64_t FileImpl::size() const66int64_t FileImpl::size() const
63{67{
64 lock_guard<mutex> guard(mutex_);68 lock_guard<decltype(mutex_)> guard(mutex_);
6569
66 if (deleted_)70 if (deleted_)
67 {71 {
68 throw deleted_ex("File::size()");72 throw deleted_ex("File::size()");
69 }73 }
74 if (!get_root())
75 {
76 throw RuntimeDestroyedException("File::size()");
77 }
7078
71 try79 try
72 {80 {
73 boost::filesystem::path p = identity_.toStdString();81 boost::filesystem::path p = identity_.toStdString();
74 return file_size(p);82 return file_size(p);
75 }83 }
84 catch (boost::filesystem::filesystem_error const& e)
85 {
86 throw ResourceException(e.what(), e.code().value());
87 }
88 // LCOV_EXCL_START
76 catch (std::exception const& e)89 catch (std::exception const& e)
77 {90 {
78 throw ResourceException(e.what());91 throw ResourceException(e.what(), 0);
79 }92 }
93 // LCOV_EXCL_STOP
80}94}
8195
82QFuture<Uploader::SPtr> FileImpl::create_uploader(ConflictPolicy policy, int64_t size)96QFuture<Uploader::SPtr> FileImpl::create_uploader(ConflictPolicy policy, int64_t size)
83{97{
84 lock_guard<mutex> guard(mutex_);98 lock_guard<decltype(mutex_)> guard(mutex_);
8599
86 if (deleted_)100 if (deleted_)
87 {101 {
@@ -92,6 +106,11 @@
92 QString msg = "File::create_uploader(): size must be >= 0";106 QString msg = "File::create_uploader(): size must be >= 0";
93 return make_exceptional_future<shared_ptr<Uploader>>(InvalidArgumentException(msg));107 return make_exceptional_future<shared_ptr<Uploader>>(InvalidArgumentException(msg));
94 }108 }
109 auto root = get_root();
110 if (!root)
111 {
112 return make_exceptional_future<Uploader::SPtr>(RuntimeDestroyedException("File::create_uploader()"));
113 }
95114
96 auto file = dynamic_pointer_cast<File>(public_instance_.lock());115 auto file = dynamic_pointer_cast<File>(public_instance_.lock());
97 assert(file);116 assert(file);
@@ -102,12 +121,16 @@
102121
103QFuture<Downloader::SPtr> FileImpl::create_downloader()122QFuture<Downloader::SPtr> FileImpl::create_downloader()
104{123{
105 lock_guard<mutex> guard(mutex_);124 lock_guard<decltype(mutex_)> guard(mutex_);
106125
107 if (deleted_)126 if (deleted_)
108 {127 {
109 return make_exceptional_future<Downloader::SPtr>(deleted_ex("File::create_downloader()"));128 return make_exceptional_future<Downloader::SPtr>(deleted_ex("File::create_downloader()"));
110 }129 }
130 if (!get_root())
131 {
132 throw RuntimeDestroyedException("File::create_downloader()");
133 }
111134
112 auto pi = public_instance_.lock();135 auto pi = public_instance_.lock();
113 assert(pi);136 assert(pi);
114137
=== modified file 'src/qt/client/internal/local_client/FolderImpl.cpp'
--- src/qt/client/internal/local_client/FolderImpl.cpp 2016-07-22 01:45:39 +0000
+++ src/qt/client/internal/local_client/FolderImpl.cpp 2016-07-29 05:58:49 +0000
@@ -62,16 +62,45 @@
62{62{
63}63}
6464
65QString FolderImpl::name() const
66{
67 lock_guard<decltype(mutex_)> guard(mutex_);
68
69 if (deleted_)
70 {
71 throw deleted_ex("Folder::name()");
72 }
73 if (!get_root())
74 {
75 throw RuntimeDestroyedException("Folder::name()");
76 }
77 return name_;
78}
79
65QFuture<QVector<Item::SPtr>> FolderImpl::list() const80QFuture<QVector<Item::SPtr>> FolderImpl::list() const
66{81{
82 if (deleted_)
83 {
84 return make_exceptional_future<QVector<Item::SPtr>>(deleted_ex("Folder::list()"));
85 }
86 if (!get_root())
87 {
88 return make_exceptional_future<QVector<Item::SPtr>>(RuntimeDestroyedException("Folder::list()"));
89 }
90
67 auto This = dynamic_pointer_cast<FolderImpl const>(shared_from_this()); // Keep this folder alive while the lambda is alive.91 auto This = dynamic_pointer_cast<FolderImpl const>(shared_from_this()); // Keep this folder alive while the lambda is alive.
68 auto list = [This]()92 auto list = [This]()
69 {93 {
70 lock_guard<mutex> guard(This->mutex_);94 lock_guard<decltype(mutex_)> guard(This->mutex_);
7195
72 if (This->deleted_)96 if (This->deleted_)
73 {97 {
74 throw This->deleted_ex("Folder::list()");98 throw This->deleted_ex("Folder::list()"); // LCOV_EXCL_LINE
99 }
100 auto root = This->get_root();
101 if (!root)
102 {
103 throw RuntimeDestroyedException("Folder::list()"); // LCOV_EXCL_LINE
75 }104 }
76105
77 try106 try
@@ -90,11 +119,11 @@
90 QString path = QString::fromStdString(dirent.path().native());119 QString path = QString::fromStdString(dirent.path().native());
91 if (is_directory(s))120 if (is_directory(s))
92 {121 {
93 results.append(make_folder(path, This->root_));122 results.append(make_folder(path, root));
94 }123 }
95 else if (is_regular_file(s))124 else if (is_regular_file(s))
96 {125 {
97 results.append(FileImpl::make_file(path, This->root_));126 results.append(FileImpl::make_file(path, root));
98 }127 }
99 else128 else
100 {129 {
@@ -103,24 +132,48 @@
103 }132 }
104 return results;133 return results;
105 }134 }
135 // LCOV_EXCL_START
136 catch (StorageException const&)
137 {
138 throw;
139 }
140 catch (boost::filesystem::filesystem_error const& e)
141 {
142 throw ResourceException(QString("Folder::list(): ") + e.what(), e.code().value());
143 }
106 catch (std::exception const& e)144 catch (std::exception const& e)
107 {145 {
108 throw ResourceException(QString("Folder::list(): ") + e.what());146 throw ResourceException(QString("Folder::list(): ") + e.what(), errno);
109 }147 }
148 // LCOV_EXCL_STOP
110 };149 };
111 return QtConcurrent::run(list);150 return QtConcurrent::run(list);
112}151}
113152
114QFuture<QVector<Item::SPtr>> FolderImpl::lookup(QString const& name) const153QFuture<QVector<Item::SPtr>> FolderImpl::lookup(QString const& name) const
115{154{
155 if (deleted_)
156 {
157 return make_exceptional_future<QVector<Item::SPtr>>(deleted_ex("Folder::lookup()"));
158 }
159 if (!get_root())
160 {
161 return make_exceptional_future<QVector<Item::SPtr>>(RuntimeDestroyedException("Folder::lookup()"));
162 }
163
116 auto This = dynamic_pointer_cast<FolderImpl const>(shared_from_this()); // Keep this folder alive while the lambda is alive.164 auto This = dynamic_pointer_cast<FolderImpl const>(shared_from_this()); // Keep this folder alive while the lambda is alive.
117 auto lookup = [This, name]() -> QVector<Item::SPtr>165 auto lookup = [This, name]() -> QVector<Item::SPtr>
118 {166 {
119 lock_guard<mutex> guard(This->mutex_);167 lock_guard<decltype(mutex_)> guard(This->mutex_);
120168
121 if (This->deleted_)169 if (This->deleted_)
122 {170 {
123 throw This->deleted_ex("Folder::lookup()");171 throw This->deleted_ex("Folder::lookup()"); // LCOV_EXCL_LINE
172 }
173 auto root = This->get_root();
174 if (!root)
175 {
176 throw RuntimeDestroyedException("Folder::lookup()"); // LCOV_EXCL_LINE
124 }177 }
125178
126 try179 try
@@ -131,45 +184,56 @@
131 auto sanitized_name = sanitize(name, "Folder::lookup()");184 auto sanitized_name = sanitize(name, "Folder::lookup()");
132 if (is_reserved_path(sanitized_name))185 if (is_reserved_path(sanitized_name))
133 {186 {
134 throw NotExistsException("Folder::lookup(): no such item: " + name, name);187 throw NotExistsException("Folder::lookup(): no such item: \"" + name + "\"", name);
135 }188 }
136 p /= sanitized_name;189 p /= sanitized_name;
137 file_status s = status(p);190 file_status s = status(p);
138 if (is_directory(s))191 if (is_directory(s))
139 {192 {
140 QVector<Item::SPtr> v;193 QVector<Item::SPtr> v;
141 v.append(make_folder(QString::fromStdString(p.native()), This->root_));194 v.append(make_folder(QString::fromStdString(p.native()), root));
142 return v;195 return v;
143 }196 }
144 if (is_regular_file(s))197 if (is_regular_file(s))
145 {198 {
146 QVector<Item::SPtr> v;199 QVector<Item::SPtr> v;
147 v.append(FileImpl::make_file(QString::fromStdString(p.native()), This->root_));200 v.append(FileImpl::make_file(QString::fromStdString(p.native()), root));
148 return v;201 return v;
149 }202 }
150 throw NotExistsException("Folder::lookup(): no such item: " + name, name);203 throw NotExistsException("Folder::lookup(): no such item: \"" + name + "\"", name);
151 }204 }
152 catch (StorageException const&)205 catch (StorageException const& e)
153 {206 {
154 throw;207 throw;
155 }208 }
209 // LCOV_EXCL_START
210 catch (boost::filesystem::filesystem_error const& e)
211 {
212 throw ResourceException(QString("Folder::lookup(): ") + e.what(), e.code().value());
213 }
156 catch (std::exception const& e)214 catch (std::exception const& e)
157 {215 {
158 throw ResourceException(QString("Folder::lookup(): ") + e.what());216 throw ResourceException(QString("Folder::lookup(): ") + e.what(), errno);
159 }217 }
218 // LCOV_EXCL_STOP
160 };219 };
161 return QtConcurrent::run(lookup);220 return QtConcurrent::run(lookup);
162}221}
163222
164QFuture<Folder::SPtr> FolderImpl::create_folder(QString const& name)223QFuture<Folder::SPtr> FolderImpl::create_folder(QString const& name)
165{224{
166 lock_guard<mutex> guard(mutex_);225 lock_guard<decltype(mutex_)> guard(mutex_);
167226
168 QFutureInterface<Folder::SPtr> qf;227 QFutureInterface<Folder::SPtr> qf;
169 if (deleted_)228 if (deleted_)
170 {229 {
171 return make_exceptional_future<Folder::SPtr>(deleted_ex("Folder::create_folder()"));230 return make_exceptional_future<Folder::SPtr>(deleted_ex("Folder::create_folder()"));
172 }231 }
232 auto root = get_root();
233 if (!root)
234 {
235 return make_exceptional_future<Folder::SPtr>(RuntimeDestroyedException("Folder::create_folder()"));
236 }
173237
174 try238 try
175 {239 {
@@ -179,22 +243,39 @@
179 auto sanitized_name = sanitize(name, "Folder::create_folder()");243 auto sanitized_name = sanitize(name, "Folder::create_folder()");
180 if (is_reserved_path(sanitized_name))244 if (is_reserved_path(sanitized_name))
181 {245 {
182 QString msg = "Folder::create_folder(): names beginning with " + QString(TMPFILE_PREFIX) + " are reserved";246 QString msg = "Folder::create_folder(): names beginning with \"" + QString(TMPFILE_PREFIX) + "\" are reserved";
183 return make_exceptional_future<Folder::SPtr>(InvalidArgumentException(msg));247 throw InvalidArgumentException(msg);
184 }248 }
185 p /= sanitized_name;249 p /= sanitized_name;
250 if (exists(p))
251 {
252 QString msg = "Folder::create_folder(): item with name \"" + name + "\" exists already";
253 throw ExistsException(msg, native_identity() + "/" + name, name);
254 }
186 create_directory(p);255 create_directory(p);
187 return make_ready_future(make_folder(QString::fromStdString(p.native()), root_));256 return make_ready_future(make_folder(QString::fromStdString(p.native()), root));
188 }257 }
258 catch (StorageException const& e)
259 {
260 return make_exceptional_future<Folder::SPtr>(e);
261 }
262 catch (boost::filesystem::filesystem_error const& e)
263 {
264 return make_exceptional_future<Folder::SPtr>(ResourceException(QString("Folder::create_folder(): ") + e.what(),
265 e.code().value()));
266 }
267 // LCOV_EXCL_START
189 catch (std::exception const& e)268 catch (std::exception const& e)
190 {269 {
191 return make_exceptional_future<Folder::SPtr>(ResourceException(QString("Folder::create_folder: ") + e.what()));270 return make_exceptional_future<Folder::SPtr>(ResourceException(QString("Folder::create_folder(): ") + e.what(),
271 errno));
192 }272 }
273 // LCOV_EXCL_STOP
193}274}
194275
195QFuture<shared_ptr<Uploader>> FolderImpl::create_file(QString const& name, int64_t size)276QFuture<shared_ptr<Uploader>> FolderImpl::create_file(QString const& name, int64_t size)
196{277{
197 unique_lock<mutex> guard(mutex_);278 lock_guard<decltype(mutex_)> guard(mutex_);
198279
199 if (deleted_)280 if (deleted_)
200 {281 {
@@ -205,6 +286,11 @@
205 QString msg = "Folder::create_file(): size must be >= 0";286 QString msg = "Folder::create_file(): size must be >= 0";
206 return make_exceptional_future<shared_ptr<Uploader>>(InvalidArgumentException(msg));287 return make_exceptional_future<shared_ptr<Uploader>>(InvalidArgumentException(msg));
207 }288 }
289 auto root = get_root();
290 if (!root)
291 {
292 return make_exceptional_future<Uploader::SPtr>(RuntimeDestroyedException("Folder::create_file()"));
293 }
208294
209 try295 try
210 {296 {
@@ -214,27 +300,39 @@
214 auto sanitized_name = sanitize(name, "Folder::create_file()");300 auto sanitized_name = sanitize(name, "Folder::create_file()");
215 if (is_reserved_path(sanitized_name))301 if (is_reserved_path(sanitized_name))
216 {302 {
217 QString msg = "Folder::create_file(): names beginning with " + QString(TMPFILE_PREFIX) + " are reserved";303 QString msg = "Folder::create_file(): names beginning with \"" + QString(TMPFILE_PREFIX) + "\" are reserved";
218 return make_exceptional_future<Uploader::SPtr>(InvalidArgumentException(msg));304 throw InvalidArgumentException(msg);
219 }305 }
220 p /= sanitized_name;306 p /= sanitized_name;
221 if (exists(p))307 if (exists(p))
222 {308 {
223 QString msg = "Folder::create_file(): item with name \"" + name + "\" exists already";309 QString msg = "Folder::create_file(): item with name \"" + name + "\" exists already";
224 return make_exceptional_future<Uploader::SPtr>(ExistsException(msg, native_identity(), name));310 throw ExistsException(msg, native_identity() + "/" + name, name);
225 }311 }
226 auto impl = new UploaderImpl(shared_ptr<File>(),312 auto impl = new UploaderImpl(shared_ptr<File>(),
227 size,313 size,
228 QString::fromStdString(p.native()),314 QString::fromStdString(p.native()),
229 ConflictPolicy::error_if_conflict,315 ConflictPolicy::error_if_conflict,
230 root_);316 root);
231 Uploader::SPtr uploader(new Uploader(impl));317 Uploader::SPtr uploader(new Uploader(impl));
232 return make_ready_future(uploader);318 return make_ready_future(uploader);
233 }319 }
320 catch (StorageException const& e)
321 {
322 return make_exceptional_future<Uploader::SPtr>(e);
323 }
324 catch (boost::filesystem::filesystem_error const& e)
325 {
326 return make_exceptional_future<Uploader::SPtr>(ResourceException(QString("Folder::create_file(): ") + e.what(),
327 e.code().value()));
328 }
329 // LCOV_EXCL_START
234 catch (std::exception const& e)330 catch (std::exception const& e)
235 {331 {
236 return make_exceptional_future<Uploader::SPtr>(ResourceException(QString("Folder::create_file: ") + e.what()));332 return make_exceptional_future<Uploader::SPtr>(ResourceException(QString("Folder::create_file(): ") + e.what(),
333 errno));
237 }334 }
335 // LCOV_EXCL_STOP
238}336}
239337
240Folder::SPtr FolderImpl::make_folder(QString const& identity, weak_ptr<Root> root)338Folder::SPtr FolderImpl::make_folder(QString const& identity, weak_ptr<Root> root)
241339
=== modified file 'src/qt/client/internal/local_client/ItemImpl.cpp'
--- src/qt/client/internal/local_client/ItemImpl.cpp 2016-07-22 00:17:24 +0000
+++ src/qt/client/internal/local_client/ItemImpl.cpp 2016-07-29 05:58:49 +0000
@@ -64,102 +64,86 @@
6464
65ItemImpl::~ItemImpl() = default;65ItemImpl::~ItemImpl() = default;
6666
67QString ItemImpl::name() const
68{
69 lock_guard<mutex> guard(mutex_);
70
71 if (deleted_)
72 {
73 throw deleted_ex("Item::name()");
74 }
75 return name_;
76}
77
78QString ItemImpl::etag() const67QString ItemImpl::etag() const
79{68{
80 lock_guard<mutex> guard(mutex_);69 lock_guard<decltype(mutex_)> guard(mutex_);
8170
82 if (deleted_)71 if (deleted_)
83 {72 {
84 throw deleted_ex("Item::etag()");73 throw deleted_ex("Item::etag()");
85 }74 }
75 if (!get_root())
76 {
77 throw RuntimeDestroyedException("Item::etag()");
78 }
86 return etag_;79 return etag_;
87}80}
8881
89QVariantMap ItemImpl::metadata() const82QVariantMap ItemImpl::metadata() const
90{83{
91 lock_guard<mutex> guard(mutex_);84 lock_guard<decltype(mutex_)> guard(mutex_);
9285
93 if (deleted_)86 if (deleted_)
94 {87 {
95 throw deleted_ex("Item::metadata()");88 throw deleted_ex("Item::metadata()");
96 }89 }
90 if (!get_root())
91 {
92 throw RuntimeDestroyedException("Item::metadata()");
93 }
97 return metadata_;94 return metadata_;
98}95}
9996
100QDateTime ItemImpl::last_modified_time() const97QDateTime ItemImpl::last_modified_time() const
101{98{
102 lock_guard<mutex> guard(mutex_);99 lock_guard<decltype(mutex_)> guard(mutex_);
103100
104 if (deleted_)101 if (deleted_)
105 {102 {
106 throw deleted_ex("Item::last_modified_time()");103 throw deleted_ex("Item::last_modified_time()");
107 }104 }
105 if (!get_root())
106 {
107 throw RuntimeDestroyedException("Item::last_modified_time()");
108 }
108 return modified_time_;109 return modified_time_;
109}110}
110111
111namespace
112{
113
114using namespace boost::filesystem;
115
116void copy_recursively(path const& source, path const& target)
117{
118 auto s = status(source);
119 if (is_regular_file(s))
120 {
121 copy_file(source, target);
122 return;
123 }
124 else if (is_directory(s))
125 {
126 copy_directory(source, target); // Poorly named in boost; this creates the target dir without recursion
127 for (directory_iterator it(source); it != directory_iterator(); ++it)
128 {
129 path source_entry = it->path();
130 path target_entry = target;
131 target_entry /= source_entry.filename();
132 copy_recursively(source_entry, target_entry);
133 }
134 }
135 else
136 {
137 // Ignore everything that's not a directory or file.
138 }
139}
140
141} // namespace
142
143QFuture<shared_ptr<Item>> ItemImpl::copy(shared_ptr<Folder> const& new_parent, QString const& new_name)112QFuture<shared_ptr<Item>> ItemImpl::copy(shared_ptr<Folder> const& new_parent, QString const& new_name)
144{113{
114 if (!new_parent)
115 {
116 QString msg = "Item::copy(): new_parent cannot be nullptr";
117 return make_exceptional_future<shared_ptr<Item>>(InvalidArgumentException(msg));
118 }
119 if (!get_root())
120 {
121 return make_exceptional_future<shared_ptr<Item>>(RuntimeDestroyedException("Item::copy()"));
122 }
123
145 auto This = dynamic_pointer_cast<ItemImpl>(shared_from_this()); // Keep this item alive while the lambda is alive.124 auto This = dynamic_pointer_cast<ItemImpl>(shared_from_this()); // Keep this item alive while the lambda is alive.
146 auto copy = [This, new_parent, new_name]() -> Item::SPtr125 auto copy = [This, new_parent, new_name]() -> Item::SPtr
147 {126 {
148 auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);127 auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);
149128
150 lock(This->mutex_, new_parent_impl->mutex_);129 lock(This->mutex_, new_parent_impl->mutex_);
151 lock_guard<mutex> this_guard(This->mutex_, std::adopt_lock);130 lock_guard<decltype(mutex_)> this_guard(This->mutex_, std::adopt_lock);
152 lock_guard<mutex> other_guard(new_parent_impl->mutex_, adopt_lock);131 lock_guard<decltype(mutex_)> other_guard(new_parent_impl->mutex_, adopt_lock);
153132
154 if (This->deleted_ || new_parent_impl->deleted_)133 if (This->deleted_)
155 {134 {
156 throw This->deleted_ex("Item::copy");135 throw This->deleted_ex("Item::copy()");
157 }136 }
158137 if (new_parent_impl->deleted_)
159 if (This->root()->account() != new_parent->root()->account())138 {
139 throw new_parent_impl->deleted_ex("Item::copy()");
140 }
141
142 // TODO: This needs to deeply compare account identity because the client may have refreshed the accounts list.
143 if (This->root()->account() != new_parent->root()->account()) // Throws if account or runtime were destroyed.
160 {144 {
161 // Can't do cross-account copy.145 // Can't do cross-account copy.
162 QString msg = QString("Item::copy(): Source (") + This->name_ + ") and target ("146 QString msg = QString("Item::copy(): source (") + This->name_ + ") and target ("
163 + new_name + ") must belong to the same account";147 + new_name + ") must belong to the same account";
164 throw LogicException(msg);148 throw LogicException(msg);
165 }149 }
@@ -175,22 +159,22 @@
175 target_path /= sanitized_name;159 target_path /= sanitized_name;
176 if (is_reserved_path(target_path))160 if (is_reserved_path(target_path))
177 {161 {
178 QString msg = "Item::copy(): names beginning with " + QString(TMPFILE_PREFIX) + " are reserved";162 QString msg = "Item::copy(): names beginning with \"" + QString(TMPFILE_PREFIX) + "\" are reserved";
179 throw InvalidArgumentException(msg);163 throw InvalidArgumentException(msg);
180 }164 }
181165
166 if (exists(target_path))
167 {
168 QString msg = "Item::copy(): item with name \"" + new_name + "\" exists already";
169 throw ExistsException(msg, This->identity_, This->name_);
170 }
171
182 if (This->type_ == ItemType::file)172 if (This->type_ == ItemType::file)
183 {173 {
184 copy_file(source_path, target_path);174 copy_file(source_path, target_path);
185 return FileImpl::make_file(QString::fromStdString(target_path.native()), new_parent_impl->root_);175 return FileImpl::make_file(QString::fromStdString(target_path.native()), new_parent_impl->root_);
186 }176 }
187177
188 if (exists(target_path))
189 {
190 QString msg = "Item::copy(): item with name \"" + new_name + "\" exists already";
191 throw ExistsException(msg, This->identity_, This->name_);
192 }
193
194 // For recursive copy, we create a temporary directory in lieu of target_path and recursively copy178 // For recursive copy, we create a temporary directory in lieu of target_path and recursively copy
195 // everything into the temporary directory. This ensures that we don't invalidate directory iterators179 // everything into the temporary directory. This ensures that we don't invalidate directory iterators
196 // by creating things while we are iterating, potentially getting trapped in an infinite loop.180 // by creating things while we are iterating, potentially getting trapped in an infinite loop.
@@ -199,7 +183,7 @@
199 create_directories(tmp_path);183 create_directories(tmp_path);
200 for (directory_iterator it(source_path); it != directory_iterator(); ++it)184 for (directory_iterator it(source_path); it != directory_iterator(); ++it)
201 {185 {
202 if (tmp_path.compare(canonical(it->path())) == 0)186 if (is_reserved_path(it->path()))
203 {187 {
204 continue; // Don't recurse into the temporary directory188 continue; // Don't recurse into the temporary directory
205 }189 }
@@ -209,47 +193,72 @@
209 path source_entry = it->path();193 path source_entry = it->path();
210 path target_entry = tmp_path;194 path target_entry = tmp_path;
211 target_entry /= source_entry.filename();195 target_entry /= source_entry.filename();
212 copy_recursively(source_entry, target_entry);196 ItemImpl::copy_recursively(source_entry, target_entry);
213 }197 }
214 }198 }
215 rename(tmp_path, target_path);199 rename(tmp_path, target_path);
216 return FolderImpl::make_folder(QString::fromStdString(target_path.native()), new_parent_impl->root_);200 return FolderImpl::make_folder(QString::fromStdString(target_path.native()), new_parent_impl->root_);
217 }201 }
202 catch (StorageException const&)
203 {
204 throw;
205 }
206 catch (boost::filesystem::filesystem_error const& e)
207 {
208 throw ResourceException(QString("Item::copy(): ") + e.what(), e.code().value());
209 }
210 // LCOV_EXCL_START
218 catch (std::exception const& e)211 catch (std::exception const& e)
219 {212 {
220 throw ResourceException(QString("Item::copy(): ") + e.what());213 throw ResourceException(QString("Item::copy(): ") + e.what(), errno);
221 }214 }
215 // LCOV_EXCL_STOP
222 };216 };
223 return QtConcurrent::run(copy);217 return QtConcurrent::run(copy);
224}218}
225219
226QFuture<shared_ptr<Item>> ItemImpl::move(shared_ptr<Folder> const& new_parent, QString const& new_name)220QFuture<shared_ptr<Item>> ItemImpl::move(shared_ptr<Folder> const& new_parent, QString const& new_name)
227{221{
222 if (!new_parent)
223 {
224 QString msg = "Item::move(): new_parent cannot be nullptr";
225 return make_exceptional_future<shared_ptr<Item>>(InvalidArgumentException(msg));
226 }
227 if (!get_root())
228 {
229 return make_exceptional_future<shared_ptr<Item>>(RuntimeDestroyedException("Item::move()"));
230 }
231
228 auto This = dynamic_pointer_cast<ItemImpl>(shared_from_this()); // Keep this item alive while the lambda is alive.232 auto This = dynamic_pointer_cast<ItemImpl>(shared_from_this()); // Keep this item alive while the lambda is alive.
229 auto move = [This, new_parent, new_name]() -> Item::SPtr233 auto move = [This, new_parent, new_name]() -> Item::SPtr
230 {234 {
231 auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);235 auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);
232236
233 lock(This->mutex_, new_parent_impl->mutex_);237 lock(This->mutex_, new_parent_impl->mutex_);
234 lock_guard<mutex> this_guard(This->mutex_, std::adopt_lock);238 lock_guard<decltype(mutex_)> this_guard(This->mutex_, std::adopt_lock);
235 lock_guard<mutex> other_guard(new_parent_impl->mutex_, adopt_lock);239 lock_guard<decltype(mutex_)> other_guard(new_parent_impl->mutex_, adopt_lock);
236240
237 if (This->deleted_ || new_parent_impl->deleted_)241 if (This->deleted_)
238 {242 {
239 throw This->deleted_ex("Item::move");243 throw This->deleted_ex("Item::move()");
240 }244 }
241245 if (new_parent_impl->deleted_)
242 if (This->root()->account() != new_parent->root()->account())246 {
247 throw new_parent_impl->deleted_ex("Item::move()");
248 }
249
250 // TODO: This needs to deeply compare account identity because the client may have refreshed the accounts list.
251 if (This->root()->account() != new_parent->root()->account()) // Throws if account or runtime were destroyed.
243 {252 {
244 // Can't do cross-account move.253 // Can't do cross-account move.
245 QString msg = QString("Item::move(): Source (") + This->name_ + ") and target ("254 QString msg = QString("Item::move(): source (") + This->name_ + ") and target ("
246 + new_name + ") must belong to the same account";255 + new_name + ") must belong to the same account";
247 throw LogicException(msg);256 throw LogicException(msg);
248 }257 }
249 if (This->type_ == ItemType::root)258 if (This->type_ == ItemType::root)
250 {259 {
251 // Can't move a root.260 // Can't move a root.
252 throw LogicException("Item::move(): Cannot move root folder");261 throw LogicException("Item::move(): cannot move root folder");
253 }262 }
254263
255 try264 try
@@ -265,7 +274,7 @@
265 }274 }
266 if (is_reserved_path(target_path))275 if (is_reserved_path(target_path))
267 {276 {
268 QString msg = "Item::move(): names beginning with " + QString(TMPFILE_PREFIX) + " are reserved";277 QString msg = "Item::move(): names beginning with \"" + QString(TMPFILE_PREFIX) + "\" are reserved";
269 throw InvalidArgumentException(msg);278 throw InvalidArgumentException(msg);
270 }279 }
271 rename(This->native_identity().toStdString(), target_path);280 rename(This->native_identity().toStdString(), target_path);
@@ -276,25 +285,34 @@
276 }285 }
277 return FileImpl::make_file(QString::fromStdString(target_path.native()), new_parent_impl->root_);286 return FileImpl::make_file(QString::fromStdString(target_path.native()), new_parent_impl->root_);
278 }287 }
288 catch (StorageException const&)
289 {
290 throw;
291 }
292 catch (boost::filesystem::filesystem_error const& e)
293 {
294 throw ResourceException(QString("Item::move(): ") + e.what(), e.code().value());
295 }
296 // LCOV_EXCL_START
279 catch (std::exception const& e)297 catch (std::exception const& e)
280 {298 {
281 throw ResourceException(QString("Item::move(): ") + e.what());299 throw ResourceException(QString("Item::move(): ") + e.what(), errno);
282 }300 }
301 // LCOV_EXCL_STOP
283 };302 };
284 return QtConcurrent::run(move);303 return QtConcurrent::run(move);
285}304}
286305
287QFuture<QVector<Folder::SPtr>> ItemImpl::parents() const306QFuture<QVector<Folder::SPtr>> ItemImpl::parents() const
288{307{
289 lock_guard<mutex> guard(mutex_);308 lock_guard<decltype(mutex_)> guard(mutex_);
290309
291 QFutureInterface<QVector<Folder::SPtr>> qf;310 QFutureInterface<QVector<Folder::SPtr>> qf;
292 if (deleted_)311 if (deleted_)
293 {312 {
294 return make_exceptional_future<QVector<Folder::SPtr>>(deleted_ex("Item::parents()"));313 return make_exceptional_future<QVector<Folder::SPtr>>(deleted_ex("Item::parents()"));
295 }314 }
296315 auto root = get_root();
297 Root::SPtr root = root_.lock();
298 if (!root)316 if (!root)
299 {317 {
300 return make_exceptional_future<QVector<Folder::SPtr>>(RuntimeDestroyedException("Item::parents()"));318 return make_exceptional_future<QVector<Folder::SPtr>>(RuntimeDestroyedException("Item::parents()"));
@@ -309,7 +327,7 @@
309 QVector<Folder::SPtr> results;327 QVector<Folder::SPtr> results;
310 if (parent_path != root->native_identity())328 if (parent_path != root->native_identity())
311 {329 {
312 results.append(FolderImpl::make_folder(parent_path, root_));330 results.append(FolderImpl::make_folder(parent_path, root));
313 }331 }
314 else332 else
315 {333 {
@@ -320,12 +338,16 @@
320338
321QVector<QString> ItemImpl::parent_ids() const339QVector<QString> ItemImpl::parent_ids() const
322{340{
323 lock_guard<mutex> guard(mutex_);341 lock_guard<decltype(mutex_)> guard(mutex_);
324342
325 if (deleted_)343 if (deleted_)
326 {344 {
327 throw deleted_ex("Item::parent_ids()");345 throw deleted_ex("Item::parent_ids()");
328 }346 }
347 if (!get_root())
348 {
349 throw RuntimeDestroyedException("Item::parent_ids()");
350 }
329351
330 using namespace boost::filesystem;352 using namespace boost::filesystem;
331353
@@ -343,33 +365,63 @@
343 auto This = dynamic_pointer_cast<ItemImpl>(shared_from_this()); // Keep this item alive while the lambda is alive.365 auto This = dynamic_pointer_cast<ItemImpl>(shared_from_this()); // Keep this item alive while the lambda is alive.
344 auto destroy = [This]()366 auto destroy = [This]()
345 {367 {
346 lock_guard<mutex> guard(This->mutex_);368 lock_guard<decltype(mutex_)> guard(This->mutex_);
347369
348 if (This->deleted_)370 if (This->deleted_)
349 {371 {
350 throw This->deleted_ex("Item::delete_item()");372 throw This->deleted_ex("Item::delete_item()");
351 }373 }
374 if (!This->get_root())
375 {
376 throw RuntimeDestroyedException("Item::delete_item()");
377 }
352378
353 try379 try
354 {380 {
355 boost::filesystem::remove_all(This->native_identity().toStdString());381 boost::filesystem::remove_all(This->native_identity().toStdString());
356 This->deleted_ = true;382 This->deleted_ = true;
357 }383 }
384 catch (boost::filesystem::filesystem_error const& e)
385 {
386 throw ResourceException(QString("Item::delete_item(): ") + e.what(), e.code().value());
387 }
388 // LCOV_EXCL_START
358 catch (std::exception const& e)389 catch (std::exception const& e)
359 {390 {
360 throw ResourceException(QString("Item::delete_item(): ") + e.what());391 throw ResourceException(QString("Item::delete_item(): ") + e.what(), errno);
361 }392 }
393 // LCOV_EXCL_STOP
362 };394 };
363 return QtConcurrent::run(destroy);395 return QtConcurrent::run(destroy);
364}396}
365397
366QDateTime ItemImpl::creation_time() const398QDateTime ItemImpl::creation_time() const
367{399{
400 lock_guard<decltype(mutex_)> guard(mutex_);
401
402 if (deleted_)
403 {
404 throw deleted_ex("Item::creation_time()");
405 }
406 if (!get_root())
407 {
408 throw RuntimeDestroyedException("Item::creation_time()");
409 }
368 return QDateTime();410 return QDateTime();
369}411}
370412
371MetadataMap ItemImpl::native_metadata() const413MetadataMap ItemImpl::native_metadata() const
372{414{
415 lock_guard<decltype(mutex_)> guard(mutex_);
416
417 if (deleted_)
418 {
419 throw deleted_ex("Item::native_metadata()");
420 }
421 if (!get_root())
422 {
423 throw RuntimeDestroyedException("Item::native_metadata()");
424 }
373 return MetadataMap();425 return MetadataMap();
374}426}
375427
@@ -383,8 +435,8 @@
383 }435 }
384436
385 lock(mutex_, other_impl->mutex_);437 lock(mutex_, other_impl->mutex_);
386 lock_guard<mutex> this_guard(mutex_, std::adopt_lock);438 lock_guard<decltype(mutex_)> this_guard(mutex_, std::adopt_lock);
387 lock_guard<mutex> other_guard(other_impl->mutex_, adopt_lock);439 lock_guard<decltype(mutex_)> other_guard(other_impl->mutex_, adopt_lock);
388440
389 if (deleted_ || other_impl->deleted_)441 if (deleted_ || other_impl->deleted_)
390 {442 {
@@ -395,7 +447,7 @@
395447
396void ItemImpl::set_timestamps() noexcept448void ItemImpl::set_timestamps() noexcept
397{449{
398 lock_guard<mutex> guard(mutex_);450 lock_guard<decltype(mutex_)> guard(mutex_);
399451
400 string id = identity_.toStdString();452 string id = identity_.toStdString();
401 // Use nano-second resolution for the ETag, if the file system supports it.453 // Use nano-second resolution for the ETag, if the file system supports it.
@@ -412,7 +464,7 @@
412464
413bool ItemImpl::has_conflict() const noexcept465bool ItemImpl::has_conflict() const noexcept
414{466{
415 lock_guard<mutex> guard(mutex_);467 lock_guard<decltype(mutex_)> guard(mutex_);
416468
417 string id = identity_.toStdString();469 string id = identity_.toStdString();
418 struct stat st;470 struct stat st;
@@ -425,11 +477,6 @@
425 return etag_ != new_etag;477 return etag_ != new_etag;
426}478}
427479
428unique_lock<mutex> ItemImpl::get_lock()
429{
430 return unique_lock<mutex>(mutex_);
431}
432
433// Throw if name contains more than one path component.480// Throw if name contains more than one path component.
434// Otherwise, return the relative path for the name.481// Otherwise, return the relative path for the name.
435// This is to make sure that calling, say, create_file()482// This is to make sure that calling, say, create_file()
@@ -467,10 +514,41 @@
467514
468DeletedException ItemImpl::deleted_ex(QString const& method) const noexcept515DeletedException ItemImpl::deleted_ex(QString const& method) const noexcept
469{516{
470 QString msg = method + ": " + identity_ + " was deleted previously";517 QString msg = method + ": \"" + identity_ + "\" was deleted previously";
471 return DeletedException(msg, identity_, name_);518 return DeletedException(msg, identity_, name_);
472}519}
473520
521void ItemImpl::copy_recursively(boost::filesystem::path const& source, boost::filesystem::path const& target)
522{
523 using namespace boost::filesystem;
524
525 if (is_reserved_path(source))
526 {
527 return; // Don't copy temporary directories.
528 }
529
530 auto s = status(source);
531 if (is_regular_file(s))
532 {
533 copy_file(source, target);
534 }
535 else if (is_directory(s))
536 {
537 copy_directory(source, target); // Poorly named in boost; this creates the target dir without recursion
538 for (directory_iterator it(source); it != directory_iterator(); ++it)
539 {
540 path source_entry = it->path();
541 path target_entry = target;
542 target_entry /= source_entry.filename();
543 copy_recursively(source_entry, target_entry);
544 }
545 }
546 else
547 {
548 // Ignore everything that's not a directory or file.
549 }
550}
551
474} // namespace local_client552} // namespace local_client
475} // namespace internal553} // namespace internal
476} // namespace client554} // namespace client
477555
=== modified file 'src/qt/client/internal/local_client/RootImpl.cpp'
--- src/qt/client/internal/local_client/RootImpl.cpp 2016-07-15 03:34:00 +0000
+++ src/qt/client/internal/local_client/RootImpl.cpp 2016-07-29 05:58:49 +0000
@@ -68,27 +68,58 @@
6868
69QString RootImpl::name() const69QString RootImpl::name() const
70{70{
71 lock_guard<decltype(mutex_)> guard(mutex_);
72
73 if (!get_root())
74 {
75 throw RuntimeDestroyedException("Root::name()");
76 }
71 return "";77 return "";
72}78}
7379
74QFuture<QVector<Folder::SPtr>> RootImpl::parents() const80QFuture<QVector<Folder::SPtr>> RootImpl::parents() const
75{81{
82 lock_guard<decltype(mutex_)> guard(mutex_);
83
84 if (!get_root())
85 {
86 throw RuntimeDestroyedException("Root::parents()");
87 }
76 return make_ready_future(QVector<Folder::SPtr>()); // For the root, we return an empty vector.88 return make_ready_future(QVector<Folder::SPtr>()); // For the root, we return an empty vector.
77}89}
7890
79QVector<QString> RootImpl::parent_ids() const91QVector<QString> RootImpl::parent_ids() const
80{92{
93 lock_guard<decltype(mutex_)> guard(mutex_);
94
95 if (!get_root())
96 {
97 throw RuntimeDestroyedException("Root::parent_ids()");
98 }
81 return QVector<QString>(); // For the root, we return an empty vector.99 return QVector<QString>(); // For the root, we return an empty vector.
82}100}
83101
84QFuture<void> RootImpl::delete_item()102QFuture<void> RootImpl::delete_item()
85{103{
104 lock_guard<decltype(mutex_)> guard(mutex_);
105
106 if (!get_root())
107 {
108 throw RuntimeDestroyedException("Root::delete_item()");
109 }
86 // Cannot delete root.110 // Cannot delete root.
87 return make_exceptional_future(LogicException("Root::delete_item(): Cannot delete root folder"));111 return make_exceptional_future(LogicException("Root::delete_item(): Cannot delete root folder"));
88}112}
89113
90QFuture<int64_t> RootImpl::free_space_bytes() const114QFuture<int64_t> RootImpl::free_space_bytes() const
91{115{
116 lock_guard<decltype(mutex_)> guard(mutex_);
117
118 if (!get_root())
119 {
120 throw RuntimeDestroyedException("Root::free_space_bytes()");
121 }
122
92 using namespace boost::filesystem;123 using namespace boost::filesystem;
93124
94 try125 try
@@ -96,14 +127,29 @@
96 space_info si = space(identity_.toStdString());127 space_info si = space(identity_.toStdString());
97 return make_ready_future<int64_t>(si.available);128 return make_ready_future<int64_t>(si.available);
98 }129 }
130 // LCOV_EXCL_START
131 catch (boost::filesystem::filesystem_error const& e)
132 {
133 return make_exceptional_future<int64_t>(ResourceException(QString("Root::free_space_bytes(): ") + e.what(),
134 e.code().value()));
135 }
99 catch (std::exception const& e)136 catch (std::exception const& e)
100 {137 {
101 return make_exceptional_future<int64_t>(ResourceException(QString("Root::free_space_bytes(): ") + e.what()));138 return make_exceptional_future<int64_t>(ResourceException(QString("Root::free_space_bytes(): ") + e.what(),
139 errno));
102 }140 }
141 // LCOV_EXCL_STOP
103}142}
104143
105QFuture<int64_t> RootImpl::used_space_bytes() const144QFuture<int64_t> RootImpl::used_space_bytes() const
106{145{
146 lock_guard<decltype(mutex_)> guard(mutex_);
147
148 if (!get_root())
149 {
150 throw RuntimeDestroyedException("Root::used_space_bytes()");
151 }
152
107 using namespace boost::filesystem;153 using namespace boost::filesystem;
108154
109 try155 try
@@ -111,14 +157,30 @@
111 space_info si = space(identity_.toStdString());157 space_info si = space(identity_.toStdString());
112 return make_ready_future<int64_t>(si.capacity - si.available);158 return make_ready_future<int64_t>(si.capacity - si.available);
113 }159 }
160 // LCOV_EXCL_START
161 catch (boost::filesystem::filesystem_error const& e)
162 {
163 return make_exceptional_future<int64_t>(ResourceException(QString("Root::used_space_bytes(): ") + e.what(),
164 e.code().value()));
165 }
114 catch (std::exception const& e)166 catch (std::exception const& e)
115 {167 {
116 return make_exceptional_future<int64_t>(ResourceException(QString("Root::used_space_bytes(): ") + e.what()));168 return make_exceptional_future<int64_t>(ResourceException(QString("Root::used_space_bytes(): ") + e.what(),
169 errno));
117 }170 }
171 // LCOV_EXCL_STOP
118}172}
119173
120QFuture<Item::SPtr> RootImpl::get(QString native_identity) const174QFuture<Item::SPtr> RootImpl::get(QString native_identity) const
121{175{
176 lock_guard<decltype(mutex_)> guard(mutex_);
177
178 auto root = get_root();
179 if (!root)
180 {
181 return make_exceptional_future<Item::SPtr>(RuntimeDestroyedException("Root::get()"));
182 }
183
122 using namespace boost::filesystem;184 using namespace boost::filesystem;
123185
124 QFutureInterface<Item::SPtr> qf;186 QFutureInterface<Item::SPtr> qf;
@@ -127,13 +189,13 @@
127 path id_path = native_identity.toStdString();189 path id_path = native_identity.toStdString();
128 if (!id_path.is_absolute())190 if (!id_path.is_absolute())
129 {191 {
130 QString msg = "Root::get(): identity must be an absolute path";192 QString msg = "Root::get(): identity \"" + native_identity + "\" must be an absolute path";
131 return make_exceptional_future<Item::SPtr>(InvalidArgumentException(msg));193 throw InvalidArgumentException(msg);
132 }194 }
133195
134 // Make sure that native_identity is contained in or equal to the root path.196 // Make sure that native_identity is contained in or equal to the root path.
135 id_path = canonical(id_path);197 id_path = canonical(id_path);
136 auto root_path = path(root()->native_identity().toStdString());198 auto root_path = path(root->native_identity().toStdString());
137 auto id_len = std::distance(id_path.begin(), id_path.end());199 auto id_len = std::distance(id_path.begin(), id_path.end());
138 auto root_len = std::distance(root_path.begin(), root_path.end());200 auto root_len = std::distance(root_path.begin(), root_path.end());
139 if (id_len < root_len || !std::equal(root_path.begin(), root_path.end(), id_path.begin()))201 if (id_len < root_len || !std::equal(root_path.begin(), root_path.end(), id_path.begin()))
@@ -141,14 +203,14 @@
141 // Too few components, or wrong path prefix. Therefore, native_identity can't203 // Too few components, or wrong path prefix. Therefore, native_identity can't
142 // possibly point at something below the root.204 // possibly point at something below the root.
143 QString msg = QString("Root::get(): identity \"") + native_identity + "\" points outside the root folder";205 QString msg = QString("Root::get(): identity \"") + native_identity + "\" points outside the root folder";
144 return make_exceptional_future<Item::SPtr>(InvalidArgumentException(msg));206 throw InvalidArgumentException(msg);
145 }207 }
146208
147 // Don't allow reserved files to be found.209 // Don't allow reserved files to be found.
148 if (is_reserved_path(id_path))210 if (is_reserved_path(id_path))
149 {211 {
150 QString msg = "Root::get(): no such item: " + native_identity;212 QString msg = "Root::get(): no such item: \"" + native_identity + "\"";
151 return make_exceptional_future<Item::SPtr>(NotExistsException(msg, native_identity));213 throw NotExistsException(msg, native_identity);
152 }214 }
153215
154 file_status s = status(id_path);216 file_status s = status(id_path);
@@ -157,21 +219,31 @@
157 {219 {
158 if (id_path == root_path)220 if (id_path == root_path)
159 {221 {
160 return make_ready_future<Item::SPtr>(make_root(path, account_));222 return make_ready_future<Item::SPtr>(make_root(path, account()));
161 }223 }
162 return make_ready_future<Item::SPtr>(make_folder(path, root_));224 return make_ready_future<Item::SPtr>(make_folder(path, root));
163 }225 }
164 if (is_regular_file(s))226 if (is_regular_file(s))
165 {227 {
166 return make_ready_future<Item::SPtr>(FileImpl::make_file(path, root_));228 return make_ready_future<Item::SPtr>(FileImpl::make_file(path, root));
167 }229 }
168 QString msg = "Root::get(): no such item: " + native_identity;230 QString msg = "Root::get(): no such item: \"" + native_identity + "\"";
169 return make_exceptional_future<Item::SPtr>(NotExistsException(msg, native_identity));231 throw NotExistsException(msg, native_identity);
170 }232 }
233 catch (StorageException const& e)
234 {
235 return make_exceptional_future<Item::SPtr>(e);
236 }
237 catch (boost::filesystem::filesystem_error const& e)
238 {
239 return make_exceptional_future<Item::SPtr>(QString("Root::get(): ") + e.what(), e, native_identity);
240 }
241 // LCOV_EXCL_START
171 catch (std::exception const& e)242 catch (std::exception const& e)
172 {243 {
173 return make_exceptional_future<Item::SPtr>(ResourceException(QString("Root::get(): ") + e.what()));244 return make_exceptional_future<Item::SPtr>(ResourceException(QString("Root::get(): ") + e.what(), errno));
174 }245 }
246 // LCOV_EXCL_STOP
175}247}
176248
177Root::SPtr RootImpl::make_root(QString const& identity, std::weak_ptr<Account> const& account)249Root::SPtr RootImpl::make_root(QString const& identity, std::weak_ptr<Account> const& account)
178250
=== modified file 'src/qt/client/internal/local_client/RuntimeImpl.cpp'
--- src/qt/client/internal/local_client/RuntimeImpl.cpp 2016-07-12 02:22:05 +0000
+++ src/qt/client/internal/local_client/RuntimeImpl.cpp 2016-07-29 05:58:49 +0000
@@ -68,14 +68,19 @@
6868
69void RuntimeImpl::shutdown()69void RuntimeImpl::shutdown()
70{70{
71 if (destroyed_.exchange(true))71 if (destroyed_)
72 {72 {
73 return;73 return;
74 }74 }
75 destroyed_ = true;
75}76}
7677
77QFuture<QVector<Account::SPtr>> RuntimeImpl::accounts()78QFuture<QVector<Account::SPtr>> RuntimeImpl::accounts()
78{79{
80 if (destroyed_)
81 {
82 throw RuntimeDestroyedException("Runtime::accounts()");
83 }
7984
80 char const* user = g_get_user_name();85 char const* user = g_get_user_name();
81 assert(*user != '\0');86 assert(*user != '\0');
8287
=== modified file 'src/qt/client/internal/local_client/UploaderImpl.cpp'
--- src/qt/client/internal/local_client/UploaderImpl.cpp 2016-07-26 01:51:26 +0000
+++ src/qt/client/internal/local_client/UploaderImpl.cpp 2016-07-29 05:58:49 +0000
@@ -93,8 +93,6 @@
93 // Monitor read socket for ready-to-read, disconnected, and error events.93 // Monitor read socket for ready-to-read, disconnected, and error events.
94 connect(read_socket_.get(), &QLocalSocket::readyRead, this, &UploadWorker::on_bytes_ready);94 connect(read_socket_.get(), &QLocalSocket::readyRead, this, &UploadWorker::on_bytes_ready);
95 connect(read_socket_.get(), &QIODevice::readChannelFinished, this, &UploadWorker::on_read_channel_finished);95 connect(read_socket_.get(), &QIODevice::readChannelFinished, this, &UploadWorker::on_read_channel_finished);
96 connect(read_socket_.get(), static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error),
97 this, &UploadWorker::on_error);
9896
99 using namespace boost::filesystem;97 using namespace boost::filesystem;
10098
@@ -114,10 +112,11 @@
114 tmp_fd_.reset(mkstemp(const_cast<char*>(tmpfile.data())));112 tmp_fd_.reset(mkstemp(const_cast<char*>(tmpfile.data())));
115 if (tmp_fd_.get() == -1)113 if (tmp_fd_.get() == -1)
116 {114 {
115 int error_code = errno;
117 worker_initialized_.reportFinished();116 worker_initialized_.reportFinished();
118 QString msg = "cannot create temp file \"" + QString::fromStdString(tmpfile)117 QString msg = "cannot create temp file \"" + QString::fromStdString(tmpfile)
119 + "\": " + QString::fromStdString(storage::internal::safe_strerror(errno));118 + "\": " + QString::fromStdString(storage::internal::safe_strerror(errno));
120 handle_error(msg);119 handle_error(msg, error_code);
121 return;120 return;
122 }121 }
123 output_file_.reset(new QFile(QString::fromStdString(tmpfile)));122 output_file_.reset(new QFile(QString::fromStdString(tmpfile)));
@@ -140,11 +139,6 @@
140139
141void UploadWorker::do_finish()140void UploadWorker::do_finish()
142{141{
143 if (qf_.future().isFinished())
144 {
145 return; // Future was set previously, no point in continuing.
146 }
147
148 switch (state_)142 switch (state_)
149 {143 {
150 case in_progress:144 case in_progress:
@@ -165,8 +159,10 @@
165 }159 }
166 case error:160 case error:
167 {161 {
168 make_exceptional_future(qf_, ResourceException(error_msg_));162 // LCOV_EXCL_START
163 make_exceptional_future(qf_, ResourceException(error_msg_, error_code_));
169 break;164 break;
165 // LCOV_EXCL_STOP
170 }166 }
171 default:167 default:
172 {168 {
@@ -201,14 +197,14 @@
201 auto bytes_written = output_file_->write(buf);197 auto bytes_written = output_file_->write(buf);
202 if (bytes_written == -1)198 if (bytes_written == -1)
203 {199 {
204 handle_error("socket error: " + output_file_->errorString()); // LCOV_EXCL_LINE200 handle_error("socket error: " + output_file_->errorString(), output_file_->error()); // LCOV_EXCL_LINE
205 }201 }
206 else if (bytes_written != buf.size())202 else if (bytes_written != buf.size())
207 {203 {
208 // LCOV_EXCL_START204 // LCOV_EXCL_START
209 QString msg = "write error, requested " + QString::number(buf.size()) + " B, but wrote only "205 QString msg = "write error, requested " + QString::number(buf.size()) + " B, but wrote only "
210 + bytes_written + " B.";206 + bytes_written + " B.";
211 handle_error(msg);207 handle_error(msg, 0);
212 // LCOV_EXCL_STOP208 // LCOV_EXCL_STOP
213 }209 }
214 }210 }
@@ -220,11 +216,6 @@
220 do_finish();216 do_finish();
221}217}
222218
223void UploadWorker::on_error()
224{
225 handle_error(read_socket_->errorString());
226}
227
228void UploadWorker::finalize()219void UploadWorker::finalize()
229{220{
230 auto file = file_.lock();221 auto file = file_.lock();
@@ -259,7 +250,7 @@
259 state_ = error;250 state_ = error;
260 QString msg = "Uploader::finish_upload(): cannot close tmp file: "251 QString msg = "Uploader::finish_upload(): cannot close tmp file: "
261 + QString::fromStdString(storage::internal::safe_strerror(errno));252 + QString::fromStdString(storage::internal::safe_strerror(errno));
262 make_exceptional_future(qf_, ResourceException(msg));253 make_exceptional_future(qf_, ResourceException(msg, errno));
263 return;254 return;
264 // LCOV_EXCL_STOP255 // LCOV_EXCL_STOP
265 }256 }
@@ -273,7 +264,7 @@
273 // LCOV_EXCL_START264 // LCOV_EXCL_START
274 state_ = error;265 state_ = error;
275 QString msg = "Uploader::finish_upload(): cannot flush output file: " + output_file_->errorString();266 QString msg = "Uploader::finish_upload(): cannot flush output file: " + output_file_->errorString();
276 make_exceptional_future(qf_, ResourceException(msg));267 make_exceptional_future(qf_, ResourceException(msg, output_file_->error()));
277 return;268 return;
278 // LCOV_EXCL_STOP269 // LCOV_EXCL_STOP
279 }270 }
@@ -287,11 +278,12 @@
287 if (linkat(-1, old_path.c_str(), tmp_fd_.get(), new_path.c_str(), AT_SYMLINK_FOLLOW) == -1)278 if (linkat(-1, old_path.c_str(), tmp_fd_.get(), new_path.c_str(), AT_SYMLINK_FOLLOW) == -1)
288 {279 {
289 // LCOV_EXCL_START280 // LCOV_EXCL_START
281 int error_code = errno;
290 state_ = error;282 state_ = error;
291 QString msg = "Uploader::finish_upload(): linkat \"" + QString::fromStdString(old_path)283 QString msg = "Uploader::finish_upload(): linkat \"" + QString::fromStdString(old_path)
292 + "\" to \"" + file->native_identity() + "\" failed: "284 + "\" to \"" + file->native_identity() + "\" failed: "
293 + QString::fromStdString(storage::internal::safe_strerror(errno));285 + QString::fromStdString(storage::internal::safe_strerror(errno));
294 make_exceptional_future(qf_, ResourceException(msg));286 make_exceptional_future(qf_, ResourceException(msg, error_code));
295 return;287 return;
296 // LCOV_EXCL_STOP288 // LCOV_EXCL_STOP
297 }289 }
@@ -302,11 +294,12 @@
302 auto old_path = output_file_->fileName().toStdString();294 auto old_path = output_file_->fileName().toStdString();
303 if (rename(old_path.c_str(), new_path.c_str()) == -1)295 if (rename(old_path.c_str(), new_path.c_str()) == -1)
304 {296 {
297 int error_code = errno;
305 state_ = error;298 state_ = error;
306 QString msg = "Uploader::finish_upload(): rename \"" + QString::fromStdString(old_path)299 QString msg = "Uploader::finish_upload(): rename \"" + QString::fromStdString(old_path)
307 + "\" to \"" + file->native_identity() + "\" failed: "300 + "\" to \"" + file->native_identity() + "\" failed: "
308 + QString::fromStdString(storage::internal::safe_strerror(errno));301 + QString::fromStdString(storage::internal::safe_strerror(errno));
309 make_exceptional_future(qf_, ResourceException(msg));302 make_exceptional_future(qf_, ResourceException(msg, error_code));
310 return;303 return;
311 }304 }
312 // LCOV_EXCL_STOP305 // LCOV_EXCL_STOP
@@ -318,7 +311,8 @@
318 make_ready_future(qf_, file);311 make_ready_future(qf_, file);
319}312}
320313
321void UploadWorker::handle_error(QString const& msg)314// LCOV_EXCL_START
315void UploadWorker::handle_error(QString const& msg, int error_code)
322{316{
323 if (state_ == in_progress)317 if (state_ == in_progress)
324 {318 {
@@ -327,8 +321,10 @@
327 }321 }
328 state_ = error;322 state_ = error;
329 error_msg_ = "Uploader: " + msg;323 error_msg_ = "Uploader: " + msg;
324 error_code_ = error_code;
330 do_finish();325 do_finish();
331}326}
327// LCOV_EXCL_STOP
332328
333UploadThread::UploadThread(UploadWorker* worker)329UploadThread::UploadThread(UploadWorker* worker)
334 : worker_(worker)330 : worker_(worker)
@@ -357,7 +353,7 @@
357 // LCOV_EXCL_START353 // LCOV_EXCL_START
358 QString msg = "Uploader: cannot create socket pair: "354 QString msg = "Uploader: cannot create socket pair: "
359 + QString::fromStdString(storage::internal::safe_strerror(errno));355 + QString::fromStdString(storage::internal::safe_strerror(errno));
360 make_exceptional_future(qf_, ResourceException(msg));356 make_exceptional_future(qf_, ResourceException(msg, errno));
361 return;357 return;
362 // LCOV_EXCL_STOP358 // LCOV_EXCL_STOP
363 }359 }
@@ -405,6 +401,7 @@
405 if (write_socket_->state() == QLocalSocket::ConnectedState)401 if (write_socket_->state() == QLocalSocket::ConnectedState)
406 {402 {
407 write_socket_->disconnectFromServer();403 write_socket_->disconnectFromServer();
404 write_socket_->close();
408 }405 }
409 return qf_.future();406 return qf_.future();
410}407}
411408
=== modified file 'src/qt/client/internal/remote_client/AccountImpl.cpp'
--- src/qt/client/internal/remote_client/AccountImpl.cpp 2016-07-27 02:19:17 +0000
+++ src/qt/client/internal/remote_client/AccountImpl.cpp 2016-07-29 05:58:49 +0000
@@ -65,16 +65,19 @@
6565
66QString AccountImpl::owner() const66QString AccountImpl::owner() const
67{67{
68 runtime(); // Throws if runtime was destroyed.
68 return owner_;69 return owner_;
69}70}
7071
71QString AccountImpl::owner_id() const72QString AccountImpl::owner_id() const
72{73{
74 runtime(); // Throws if runtime was destroyed.
73 return owner_id_;75 return owner_id_;
74}76}
7577
76QString AccountImpl::description() const78QString AccountImpl::description() const
77{79{
80 runtime(); // Throws if runtime was destroyed.
78 return description_;81 return description_;
79}82}
8083
@@ -84,6 +87,16 @@
8487
85 auto process_reply = [this](decltype(reply) const& reply, QFutureInterface<QVector<Root::SPtr>>& qf)88 auto process_reply = [this](decltype(reply) const& reply, QFutureInterface<QVector<Root::SPtr>>& qf)
86 {89 {
90 try
91 {
92 this->runtime();
93 }
94 catch (RuntimeDestroyedException const& e)
95 {
96 make_exceptional_future(qf, RuntimeDestroyedException("Account::roots()"));
97 return;
98 }
99
87 QVector<shared_ptr<Root>> roots;100 QVector<shared_ptr<Root>> roots;
88 auto metadata = reply.value();101 auto metadata = reply.value();
89 for (auto const& md : metadata)102 for (auto const& md : metadata)
90103
=== modified file 'src/qt/client/internal/remote_client/FileImpl.cpp'
--- src/qt/client/internal/remote_client/FileImpl.cpp 2016-07-22 01:45:39 +0000
+++ src/qt/client/internal/remote_client/FileImpl.cpp 2016-07-29 05:58:49 +0000
@@ -52,6 +52,10 @@
52 {52 {
53 throw deleted_ex("File::size()");53 throw deleted_ex("File::size()");
54 }54 }
55 if (!get_root())
56 {
57 throw RuntimeDestroyedException("File::size()");
58 }
55 return 0; // TODO59 return 0; // TODO
56}60}
5761
@@ -66,18 +70,19 @@
66 QString msg = "File::create_uploader(): size must be >= 0";70 QString msg = "File::create_uploader(): size must be >= 0";
67 return make_exceptional_future<shared_ptr<Uploader>>(InvalidArgumentException(msg));71 return make_exceptional_future<shared_ptr<Uploader>>(InvalidArgumentException(msg));
68 }72 }
73 if (!get_root())
74 {
75 return make_exceptional_future<shared_ptr<Uploader>>(RuntimeDestroyedException("File::create_uploader()"));
76 }
6977
70 QString old_etag = policy == ConflictPolicy::overwrite ? "" : md_.etag;78 QString old_etag = policy == ConflictPolicy::overwrite ? "" : md_.etag;
71 auto prov = provider();79 auto prov = provider();
72 if (!prov)
73 {
74 return make_exceptional_future<shared_ptr<Uploader>>(RuntimeDestroyedException("File::create_uploader"));
75 }
76 auto reply = prov->Update(md_.item_id, size, old_etag);80 auto reply = prov->Update(md_.item_id, size, old_etag);
7781
78 auto process_reply = [this, size, old_etag, prov](decltype(reply) const& reply, QFutureInterface<std::shared_ptr<Uploader>>& qf)82 auto process_reply = [this, size, old_etag, prov](decltype(reply) const& reply,
83 QFutureInterface<std::shared_ptr<Uploader>>& qf)
79 {84 {
80 auto root = root_.lock();85 auto root = get_root();
81 if (!root)86 if (!root)
82 {87 {
83 make_exceptional_future<shared_ptr<Uploader>>(RuntimeDestroyedException("File::create_uploader()"));88 make_exceptional_future<shared_ptr<Uploader>>(RuntimeDestroyedException("File::create_uploader()"));
@@ -107,17 +112,24 @@
107 {112 {
108 return make_exceptional_future<shared_ptr<Downloader>>(deleted_ex("File::create_downloader()"));113 return make_exceptional_future<shared_ptr<Downloader>>(deleted_ex("File::create_downloader()"));
109 }114 }
115 if (!get_root())
116 {
117 return make_exceptional_future<shared_ptr<Downloader>>(RuntimeDestroyedException("File::create_downloader()"));
118 }
110119
111 auto prov = provider();120 auto prov = provider();
112 if (!prov)
113 {
114 return make_exceptional_future<shared_ptr<Downloader>>(RuntimeDestroyedException("File::create_downloader"));
115 }
116 auto reply = prov->Download(md_.item_id);121 auto reply = prov->Download(md_.item_id);
117122
118 auto process_reply = [this, prov](QDBusPendingReply<QString, QDBusUnixFileDescriptor> const& reply,123 auto process_reply = [this, prov](QDBusPendingReply<QString, QDBusUnixFileDescriptor> const& reply,
119 QFutureInterface<std::shared_ptr<Downloader>>& qf)124 QFutureInterface<std::shared_ptr<Downloader>>& qf)
120 {125 {
126 auto root = get_root();
127 if (!root)
128 {
129 make_exceptional_future<shared_ptr<Uploader>>(RuntimeDestroyedException("File::create_downloader()"));
130 return;
131 }
132
121 auto download_id = reply.argumentAt<0>();133 auto download_id = reply.argumentAt<0>();
122 auto fd = reply.argumentAt<1>();134 auto fd = reply.argumentAt<1>();
123 if (fd.fileDescriptor() < 0)135 if (fd.fileDescriptor() < 0)
124136
=== modified file 'src/qt/client/internal/remote_client/FolderImpl.cpp'
--- src/qt/client/internal/remote_client/FolderImpl.cpp 2016-07-27 02:19:17 +0000
+++ src/qt/client/internal/remote_client/FolderImpl.cpp 2016-07-29 05:58:49 +0000
@@ -61,6 +61,10 @@
61 {61 {
62 return make_exceptional_future<QVector<shared_ptr<Item>>>(deleted_ex("Folder::list()"));62 return make_exceptional_future<QVector<shared_ptr<Item>>>(deleted_ex("Folder::list()"));
63 }63 }
64 if (!get_root())
65 {
66 return make_exceptional_future<QVector<shared_ptr<Item>>>(RuntimeDestroyedException("Folder::list()"));
67 }
6468
65 auto prov = provider();69 auto prov = provider();
66 if (!prov)70 if (!prov)
@@ -74,7 +78,7 @@
74 function<void(decltype(reply) const&, QFutureInterface<QVector<shared_ptr<Item>>>&)> process_reply78 function<void(decltype(reply) const&, QFutureInterface<QVector<shared_ptr<Item>>>&)> process_reply
75 = [this, prov, &process_reply](decltype(reply) const& reply, QFutureInterface<QVector<shared_ptr<Item>>>& qf)79 = [this, prov, &process_reply](decltype(reply) const& reply, QFutureInterface<QVector<shared_ptr<Item>>>& qf)
76 {80 {
77 auto root = root_.lock();81 auto root = get_root();
78 if (!root)82 if (!root)
79 {83 {
80 make_exceptional_future(qf, RuntimeDestroyedException("Folder::list()"));84 make_exceptional_future(qf, RuntimeDestroyedException("Folder::list()"));
@@ -117,17 +121,17 @@
117 {121 {
118 return make_exceptional_future<QVector<shared_ptr<Item>>>(deleted_ex("Folder::lookup()"));122 return make_exceptional_future<QVector<shared_ptr<Item>>>(deleted_ex("Folder::lookup()"));
119 }123 }
120124 if (!get_root())
121 auto prov = provider();
122 if (!prov)
123 {125 {
124 return make_exceptional_future<QVector<shared_ptr<Item>>>(RuntimeDestroyedException("Folder::lookup()"));126 return make_exceptional_future<QVector<shared_ptr<Item>>>(RuntimeDestroyedException("Folder::lookup()"));
125 }127 }
128
129 auto prov = provider();
126 auto reply = prov->Lookup(md_.item_id, name);130 auto reply = prov->Lookup(md_.item_id, name);
127131
128 auto process_reply = [this, name](decltype(reply) const& reply, QFutureInterface<QVector<shared_ptr<Item>>>& qf)132 auto process_reply = [this, name](decltype(reply) const& reply, QFutureInterface<QVector<shared_ptr<Item>>>& qf)
129 {133 {
130 auto root = root_.lock();134 auto root = get_root();
131 if (!root)135 if (!root)
132 {136 {
133 make_exceptional_future(qf, RuntimeDestroyedException("Folder::lookup()"));137 make_exceptional_future(qf, RuntimeDestroyedException("Folder::lookup()"));
@@ -163,17 +167,17 @@
163 {167 {
164 return make_exceptional_future<shared_ptr<Folder>>(deleted_ex("Folder::create_folder()"));168 return make_exceptional_future<shared_ptr<Folder>>(deleted_ex("Folder::create_folder()"));
165 }169 }
166170 if (!get_root())
167 auto prov = provider();
168 if (!prov)
169 {171 {
170 return make_exceptional_future<shared_ptr<Folder>>(RuntimeDestroyedException("Folder::create_folder()"));172 return make_exceptional_future<shared_ptr<Folder>>(RuntimeDestroyedException("Folder::create_folder()"));
171 }173 }
174
175 auto prov = provider();
172 auto reply = prov->CreateFolder(md_.item_id, name);176 auto reply = prov->CreateFolder(md_.item_id, name);
173177
174 auto process_reply = [this](decltype(reply) const& reply, QFutureInterface<shared_ptr<Folder>>& qf)178 auto process_reply = [this](decltype(reply) const& reply, QFutureInterface<shared_ptr<Folder>>& qf)
175 {179 {
176 auto root = root_.lock();180 auto root = get_root();
177 if (!root)181 if (!root)
178 {182 {
179 make_exceptional_future(qf, RuntimeDestroyedException("Folder::create_folder()"));183 make_exceptional_future(qf, RuntimeDestroyedException("Folder::create_folder()"));
@@ -208,17 +212,17 @@
208 QString msg = "Folder::create_file(): size must be >= 0";212 QString msg = "Folder::create_file(): size must be >= 0";
209 return make_exceptional_future<shared_ptr<Uploader>>(InvalidArgumentException(msg));213 return make_exceptional_future<shared_ptr<Uploader>>(InvalidArgumentException(msg));
210 }214 }
211215 if (!get_root())
212 auto prov = provider();
213 if (!prov)
214 {216 {
215 return make_exceptional_future<shared_ptr<Uploader>>(RuntimeDestroyedException("Folder::create_file()"));217 return make_exceptional_future<shared_ptr<Uploader>>(RuntimeDestroyedException("Folder::create_file()"));
216 }218 }
217219
220 auto prov = provider();
218 auto reply = prov->CreateFile(md_.item_id, name, size, "application/octet-stream", false);221 auto reply = prov->CreateFile(md_.item_id, name, size, "application/octet-stream", false);
222
219 auto process_reply = [this, size](decltype(reply) const& reply, QFutureInterface<shared_ptr<Uploader>>& qf)223 auto process_reply = [this, size](decltype(reply) const& reply, QFutureInterface<shared_ptr<Uploader>>& qf)
220 {224 {
221 auto root = root_.lock();225 auto root = get_root();
222 if (!root)226 if (!root)
223 {227 {
224 make_exceptional_future(qf, RuntimeDestroyedException("Folder::create_file()"));228 make_exceptional_future(qf, RuntimeDestroyedException("Folder::create_file()"));
225229
=== modified file 'src/qt/client/internal/remote_client/ItemImpl.cpp'
--- src/qt/client/internal/remote_client/ItemImpl.cpp 2016-07-27 02:19:17 +0000
+++ src/qt/client/internal/remote_client/ItemImpl.cpp 2016-07-29 05:58:49 +0000
@@ -54,6 +54,10 @@
54 {54 {
55 throw deleted_ex("Item::name()");55 throw deleted_ex("Item::name()");
56 }56 }
57 if (!get_root())
58 {
59 throw RuntimeDestroyedException("Item::name()");
60 }
57 return md_.name;61 return md_.name;
58}62}
5963
@@ -63,6 +67,10 @@
63 {67 {
64 throw deleted_ex("Item::etag()");68 throw deleted_ex("Item::etag()");
65 }69 }
70 if (!get_root())
71 {
72 throw RuntimeDestroyedException("Item::etag()");
73 }
66 return md_.etag;74 return md_.etag;
67}75}
6876
@@ -72,6 +80,10 @@
72 {80 {
73 throw deleted_ex("Item::metadata()");81 throw deleted_ex("Item::metadata()");
74 }82 }
83 if (!get_root())
84 {
85 throw RuntimeDestroyedException("Item::metadata()");
86 }
75 // TODO: need to agree on metadata representation87 // TODO: need to agree on metadata representation
76 return QVariantMap();88 return QVariantMap();
77}89}
@@ -82,6 +94,10 @@
82 {94 {
83 throw deleted_ex("Item::last_modified_time()");95 throw deleted_ex("Item::last_modified_time()");
84 }96 }
97 if (!get_root())
98 {
99 throw RuntimeDestroyedException("Item::last_modified_time()");
100 }
85 // TODO: need to agree on metadata representation101 // TODO: need to agree on metadata representation
86 return QDateTime();102 return QDateTime();
87}103}
@@ -92,17 +108,17 @@
92 {108 {
93 return make_exceptional_future<shared_ptr<Item>>(deleted_ex("Item::copy()"));109 return make_exceptional_future<shared_ptr<Item>>(deleted_ex("Item::copy()"));
94 }110 }
111 if (!get_root())
112 {
113 return make_exceptional_future<shared_ptr<Item>>(RuntimeDestroyedException("Item::copy()"));
114 }
95115
96 auto prov = provider();116 auto prov = provider();
97 if (!prov)
98 {
99 return make_exceptional_future<shared_ptr<Item>>(RuntimeDestroyedException("Item::copy()"));
100 }
101 auto reply = prov->Copy(md_.item_id, new_parent->native_identity(), new_name);117 auto reply = prov->Copy(md_.item_id, new_parent->native_identity(), new_name);
102118
103 auto process_reply = [this](decltype(reply) const& reply, QFutureInterface<std::shared_ptr<Item>>& qf)119 auto process_reply = [this](decltype(reply) const& reply, QFutureInterface<std::shared_ptr<Item>>& qf)
104 {120 {
105 auto root = root_.lock();121 auto root = get_root();
106 if (!root)122 if (!root)
107 {123 {
108 make_exceptional_future(qf, RuntimeDestroyedException("Item::copy()"));124 make_exceptional_future(qf, RuntimeDestroyedException("Item::copy()"));
@@ -132,6 +148,10 @@
132 {148 {
133 return make_exceptional_future<shared_ptr<Item>>(deleted_ex("Item::move()"));149 return make_exceptional_future<shared_ptr<Item>>(deleted_ex("Item::move()"));
134 }150 }
151 if (!get_root())
152 {
153 return make_exceptional_future<shared_ptr<Item>>(RuntimeDestroyedException("Item::copy()"));
154 }
135155
136 auto prov = provider();156 auto prov = provider();
137 if (!prov)157 if (!prov)
@@ -142,10 +162,10 @@
142162
143 auto process_reply = [this](decltype(reply) const& reply, QFutureInterface<std::shared_ptr<Item>>& qf)163 auto process_reply = [this](decltype(reply) const& reply, QFutureInterface<std::shared_ptr<Item>>& qf)
144 {164 {
145 auto root = root_.lock();165 auto root = get_root();
146 if (!root)166 if (!root)
147 {167 {
148 make_exceptional_future(qf, RuntimeDestroyedException("Item::copy()"));168 make_exceptional_future(qf, RuntimeDestroyedException("Item::move()"));
149 return;169 return;
150 }170 }
151171
@@ -170,6 +190,10 @@
170 {190 {
171 return make_exceptional_future<QVector<Folder::SPtr>>(deleted_ex("Item::parents()"));191 return make_exceptional_future<QVector<Folder::SPtr>>(deleted_ex("Item::parents()"));
172 }192 }
193 if (!get_root())
194 {
195 return make_exceptional_future<QVector<Folder::SPtr>>(RuntimeDestroyedException("Item::parents()"));
196 }
173 // TODO, need different metadata representation, affects xml197 // TODO, need different metadata representation, affects xml
174 return QFuture<QVector<Folder::SPtr>>();198 return QFuture<QVector<Folder::SPtr>>();
175}199}
@@ -180,6 +204,10 @@
180 {204 {
181 throw deleted_ex("Item::parent_ids()");205 throw deleted_ex("Item::parent_ids()");
182 }206 }
207 if (!get_root())
208 {
209 RuntimeDestroyedException("Item::parent_ids()");
210 }
183 // TODO, need different metadata representation, affects xml211 // TODO, need different metadata representation, affects xml
184 return QVector<QString>();212 return QVector<QString>();
185}213}
@@ -190,12 +218,12 @@
190 {218 {
191 return make_exceptional_future(deleted_ex("Item::delete_item()"));219 return make_exceptional_future(deleted_ex("Item::delete_item()"));
192 }220 }
221 if (!get_root())
222 {
223 return make_exceptional_future(RuntimeDestroyedException("Item::parents()"));
224 }
193225
194 auto prov = provider();226 auto prov = provider();
195 if (!prov)
196 {
197 return make_exceptional_future(RuntimeDestroyedException("Item::delete_item()"));
198 }
199 auto reply = prov->Delete(md_.item_id);227 auto reply = prov->Delete(md_.item_id);
200228
201 auto process_reply = [this](decltype(reply) const&, QFutureInterface<void>& qf)229 auto process_reply = [this](decltype(reply) const&, QFutureInterface<void>& qf)
202230
=== modified file 'src/qt/client/internal/remote_client/RootImpl.cpp'
--- src/qt/client/internal/remote_client/RootImpl.cpp 2016-07-27 02:19:17 +0000
+++ src/qt/client/internal/remote_client/RootImpl.cpp 2016-07-29 05:58:49 +0000
@@ -50,45 +50,70 @@
5050
51QFuture<QVector<Folder::SPtr>> RootImpl::parents() const51QFuture<QVector<Folder::SPtr>> RootImpl::parents() const
52{52{
53 if (!get_root())
54 {
55 return make_exceptional_future<QVector<Folder::SPtr>>(RuntimeDestroyedException("Root::parents()"));
56 }
53 return make_ready_future(QVector<Folder::SPtr>()); // For the root, we return an empty vector.57 return make_ready_future(QVector<Folder::SPtr>()); // For the root, we return an empty vector.
54}58}
5559
56QVector<QString> RootImpl::parent_ids() const60QVector<QString> RootImpl::parent_ids() const
57{61{
62 if (!get_root())
63 {
64 return make_exceptional_future<QVector<QString>>(RuntimeDestroyedException("Root::parent_ids()"));
65 }
58 return QVector<QString>(); // For the root, we return an empty vector.66 return QVector<QString>(); // For the root, we return an empty vector.
59}67}
6068
61QFuture<void> RootImpl::delete_item()69QFuture<void> RootImpl::delete_item()
62{70{
71 if (!get_root())
72 {
73 return make_exceptional_future(RuntimeDestroyedException("Root::delete_item()"));
74 }
63 // Cannot delete root.75 // Cannot delete root.
64 return make_exceptional_future(LogicException("Root::delete_item(): root item cannot be deleted"));76 return make_exceptional_future(LogicException("Root::delete_item(): root item cannot be deleted"));
65}77}
6678
67QFuture<int64_t> RootImpl::free_space_bytes() const79QFuture<int64_t> RootImpl::free_space_bytes() const
68{80{
81 if (!get_root())
82 {
83 return make_exceptional_future<int64_t>(RuntimeDestroyedException("Root::free_space_bytes()"));
84 }
69 // TODO, need to refresh metadata here instead.85 // TODO, need to refresh metadata here instead.
70 return make_ready_future(int64_t(1));86 return make_ready_future(int64_t(1));
71}87}
7288
73QFuture<int64_t> RootImpl::used_space_bytes() const89QFuture<int64_t> RootImpl::used_space_bytes() const
74{90{
91 if (!get_root())
92 {
93 return make_exceptional_future<int64_t>(RuntimeDestroyedException("Root::used_space_bytes()"));
94 }
75 // TODO, need to refresh metadata here instead.95 // TODO, need to refresh metadata here instead.
76 return make_ready_future(int64_t(1));96 return make_ready_future(int64_t(1));
77}97}
7898
79QFuture<Item::SPtr> RootImpl::get(QString native_identity) const99QFuture<Item::SPtr> RootImpl::get(QString native_identity) const
80{100{
101 if (!get_root())
102 {
103 return make_exceptional_future<Item::SPtr>(RuntimeDestroyedException("Root::get()"));
104 }
105
81 auto prov = provider();106 auto prov = provider();
82 if (!prov)
83 {
84 return make_exceptional_future<Item::SPtr>(RuntimeDestroyedException("Root::get()"));
85 }
86 auto reply = prov->Metadata(native_identity);107 auto reply = prov->Metadata(native_identity);
87108
88 auto process_reply = [this](decltype(reply) const& reply, QFutureInterface<Item::SPtr>& qf)109 auto process_reply = [this](decltype(reply) const& reply, QFutureInterface<Item::SPtr>& qf)
89 {110 {
90 auto account = account_.lock();111 shared_ptr<Account> acc;
91 if (!account)112 try
113 {
114 acc = account();
115 }
116 catch (RuntimeDestroyedException const&)
92 {117 {
93 make_exceptional_future<Item::SPtr>(qf, RuntimeDestroyedException("Root::get()"));118 make_exceptional_future<Item::SPtr>(qf, RuntimeDestroyedException("Root::get()"));
94 return;119 return;
@@ -98,11 +123,11 @@
98 Item::SPtr item;123 Item::SPtr item;
99 if (md.type == ItemType::root)124 if (md.type == ItemType::root)
100 {125 {
101 item = make_root(md, account);126 item = make_root(md, acc);
102 }127 }
103 else128 else
104 {129 {
105 assert(root_.lock()); // Account owns the root, so it can't go away.130 // acc owns the root, so the root weak_ptr is guaranteed to be lockable.
106 item = ItemImpl::make_item(md, root_);131 item = ItemImpl::make_item(md, root_);
107 }132 }
108 make_ready_future(qf, item);133 make_ready_future(qf, item);
109134
=== modified file 'src/qt/client/internal/remote_client/RuntimeImpl.cpp'
--- src/qt/client/internal/remote_client/RuntimeImpl.cpp 2016-07-22 00:17:24 +0000
+++ src/qt/client/internal/remote_client/RuntimeImpl.cpp 2016-07-29 05:58:49 +0000
@@ -19,7 +19,6 @@
19#include <unity/storage/qt/client/internal/remote_client/RuntimeImpl.h>19#include <unity/storage/qt/client/internal/remote_client/RuntimeImpl.h>
2020
21#include <unity/storage/qt/client/Account.h>21#include <unity/storage/qt/client/Account.h>
22#include <unity/storage/qt/client/Exceptions.h>
23#include <unity/storage/qt/client/internal/make_future.h>22#include <unity/storage/qt/client/internal/make_future.h>
24#include <unity/storage/qt/client/internal/remote_client/AccountImpl.h>23#include <unity/storage/qt/client/internal/remote_client/AccountImpl.h>
25#include <unity/storage/qt/client/internal/remote_client/dbusmarshal.h>24#include <unity/storage/qt/client/internal/remote_client/dbusmarshal.h>
@@ -81,15 +80,21 @@
8180
82void RuntimeImpl::shutdown()81void RuntimeImpl::shutdown()
83{82{
84 if (destroyed_.exchange(true))83 if (destroyed_)
85 {84 {
86 return;85 return;
87 }86 }
87 destroyed_ = true;
88 conn_.disconnectFromBus(conn_.name());88 conn_.disconnectFromBus(conn_.name());
89}89}
9090
91QFuture<QVector<Account::SPtr>> RuntimeImpl::accounts()91QFuture<QVector<Account::SPtr>> RuntimeImpl::accounts()
92{92{
93 if (destroyed_)
94 {
95 return make_exceptional_future(qf_, RuntimeDestroyedException("Runtime::accounts()"));
96 }
97
93 if (!manager_)98 if (!manager_)
94 {99 {
95 manager_.reset(new OnlineAccounts::Manager("", conn_));100 manager_.reset(new OnlineAccounts::Manager("", conn_));
@@ -110,6 +115,12 @@
110115
111void RuntimeImpl::manager_ready()116void RuntimeImpl::manager_ready()
112{117{
118 if (destroyed_)
119 {
120 make_exceptional_future(qf_, RuntimeDestroyedException("Runtime::accounts()"));
121 return;
122 }
123
113 timer_.stop();124 timer_.stop();
114 try125 try
115 {126 {
@@ -133,7 +144,7 @@
133144
134void RuntimeImpl::timeout()145void RuntimeImpl::timeout()
135{146{
136 make_exceptional_future(qf_, ResourceException("timeout error")); // TODO147 make_exceptional_future(qf_, ResourceException("Runtime::accounts(): timeout retrieving Online accounts", 0));
137}148}
138149
139} // namespace local_client150} // namespace local_client
140151
=== modified file 'src/qt/client/internal/remote_client/UploaderImpl.cpp'
--- src/qt/client/internal/remote_client/UploaderImpl.cpp 2016-07-22 01:45:39 +0000
+++ src/qt/client/internal/remote_client/UploaderImpl.cpp 2016-07-29 05:58:49 +0000
@@ -51,14 +51,14 @@
51 , fd_(fd)51 , fd_(fd)
52 , size_(size)52 , size_(size)
53 , old_etag_(old_etag)53 , old_etag_(old_etag)
54 , root_(root)54 , root_(root.lock())
55 , provider_(provider)55 , provider_(provider)
56 , write_socket_(new QLocalSocket)56 , write_socket_(new QLocalSocket)
57{57{
58 assert(!upload_id.isEmpty());58 assert(!upload_id.isEmpty());
59 assert(fd.isValid());59 assert(fd.isValid());
60 assert(size >= 0);60 assert(size >= 0);
61 assert(root_.lock());61 assert(root_);
62 assert(provider);62 assert(provider);
63 assert(fd.isValid());63 assert(fd.isValid());
64 write_socket_->setSocketDescriptor(fd_.fileDescriptor(), QLocalSocket::ConnectedState, QIODevice::WriteOnly);64 write_socket_->setSocketDescriptor(fd_.fileDescriptor(), QLocalSocket::ConnectedState, QIODevice::WriteOnly);
@@ -79,13 +79,6 @@
79 auto reply = provider_->FinishUpload(upload_id_);79 auto reply = provider_->FinishUpload(upload_id_);
80 auto process_reply = [this](decltype(reply) const& reply, QFutureInterface<shared_ptr<File>>& qf)80 auto process_reply = [this](decltype(reply) const& reply, QFutureInterface<shared_ptr<File>>& qf)
81 {81 {
82 auto root = root_.lock();
83 if (!root)
84 {
85 make_exceptional_future(qf, RuntimeDestroyedException("Uploader::finish_upload()"));
86 return;
87 }
88
89 auto md = reply.value();82 auto md = reply.value();
90 if (md.type != ItemType::file)83 if (md.type != ItemType::file)
91 {84 {
@@ -95,7 +88,7 @@
95 make_exceptional_future(qf, LocalCommsException(msg));88 make_exceptional_future(qf, LocalCommsException(msg));
96 return;89 return;
97 }90 }
98 make_ready_future(qf, FileImpl::make_file(md, root));91 make_ready_future(qf, FileImpl::make_file(md, root_));
99 };92 };
10093
101 auto handler = new Handler<shared_ptr<File>>(this, reply, process_reply);94 auto handler = new Handler<shared_ptr<File>>(this, reply, process_reply);
10295
=== modified file 'tests/local-client/local-client_test.cpp'
--- tests/local-client/local-client_test.cpp 2016-07-27 02:19:17 +0000
+++ tests/local-client/local-client_test.cpp 2016-07-29 05:58:49 +0000
@@ -18,7 +18,12 @@
1818
19#include <unity/storage/qt/client/client-api.h>19#include <unity/storage/qt/client/client-api.h>
2020
21<<<<<<< TREE
21#include <unity/storage/qt/client/internal/local_client/boost_filesystem.h>22#include <unity/storage/qt/client/internal/local_client/boost_filesystem.h>
23=======
24#include <unity/storage/qt/client/internal/boost_filesystem.h>
25#include <unity/storage/qt/client/internal/local_client/tmpfile-prefix.h>
26>>>>>>> MERGE-SOURCE
2227
23#include <gtest/gtest.h>28#include <gtest/gtest.h>
24#include <QCoreApplication>29#include <QCoreApplication>
@@ -34,6 +39,8 @@
3439
35#include <fstream>40#include <fstream>
3641
42Q_DECLARE_METATYPE(QLocalSocket::LocalSocketState)
43
37using namespace unity::storage;44using namespace unity::storage;
38using namespace unity::storage::qt::client;45using namespace unity::storage::qt::client;
39using namespace std;46using namespace std;
@@ -42,31 +49,61 @@
4249
43// Bunch of helper function to reduce the amount of noise in the tests.50// Bunch of helper function to reduce the amount of noise in the tests.
4451
52template<typename T>
53void wait(T fut)
54{
55 QFutureWatcher<decltype(fut.result())> w;
56 QSignalSpy spy(&w, &decltype(w)::finished);
57 w.setFuture(fut);
58 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
59}
60
61template<>
62void wait(QFuture<void> fut)
63{
64 QFutureWatcher<void> w;
65 QSignalSpy spy(&w, &decltype(w)::finished);
66 w.setFuture(fut);
67 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
68}
69
70template <typename T>
71T call(QFuture<T> fut)
72{
73 wait(fut);
74 return fut.result();
75}
76
77template <>
78void call(QFuture<void> fut)
79{
80 wait(fut);
81 fut.waitForFinished();
82}
83
45Account::SPtr get_account(Runtime::SPtr const& runtime)84Account::SPtr get_account(Runtime::SPtr const& runtime)
46{85{
47 auto accounts = runtime->accounts().result();86 auto accounts = call(runtime->accounts());
48 assert(accounts.size() == 1);
49 return accounts[0];87 return accounts[0];
50}88}
5189
52Root::SPtr get_root(Runtime::SPtr const& runtime)90Root::SPtr get_root(Runtime::SPtr const& runtime)
53{91{
54 auto acc = get_account(runtime);92 auto acc = get_account(runtime);
55 auto roots = acc->roots().result();93 auto roots = call(acc->roots());
56 assert(roots.size() == 1);
57 return roots[0];94 return roots[0];
58}95}
5996
60Folder::SPtr get_parent(Item::SPtr const& item)97Folder::SPtr get_parent(Item::SPtr const& item)
61{98{
62 assert(item->type() != ItemType::root);99 assert(item->type() != ItemType::root);
63 auto parents = item->parents().result();100 auto parents = call(item->parents());
64 return parents[0];101 return parents[0];
65}102}
66103
67void clear_folder(Folder::SPtr folder)104void clear_folder(Folder::SPtr folder)
68{105{
69 auto items = folder->list().result();106 auto items = call(folder->list());
70 for (auto i : items)107 for (auto i : items)
71 {108 {
72 i->delete_item().waitForFinished();109 i->delete_item().waitForFinished();
@@ -81,7 +118,7 @@
81 return buf == expected;118 return buf == expected;
82}119}
83120
84void write_file(Folder::SPtr const& folder, QString const& name, QByteArray const& contents)121File::SPtr write_file(Folder::SPtr const& folder, QString const& name, QByteArray const& contents)
85{122{
86 QString ofile = folder->native_identity() + "/" + name;123 QString ofile = folder->native_identity() + "/" + name;
87 QFile f(ofile);124 QFile f(ofile);
@@ -90,6 +127,23 @@
90 {127 {
91 assert(f.write(contents));128 assert(f.write(contents));
92 }129 }
130 f.close();
131 auto items = call(folder->lookup(name));
132 return dynamic_pointer_cast<File>(items[0]);
133}
134
135File::SPtr make_deleted_file(Folder::SPtr parent, QString const& name)
136{
137 auto file = write_file(parent, name, "bytes");
138 call(file->delete_item());
139 return file;
140}
141
142Folder::SPtr make_deleted_folder(Folder::SPtr parent, QString const& name)
143{
144 auto folder = call(parent->create_folder(name));
145 call(folder->delete_item());
146 return folder;
93}147}
94148
95TEST(Runtime, lifecycle)149TEST(Runtime, lifecycle)
@@ -104,7 +158,7 @@
104 auto runtime = Runtime::create();158 auto runtime = Runtime::create();
105159
106 auto acc = get_account(runtime);160 auto acc = get_account(runtime);
107 EXPECT_EQ(runtime.get(), acc->runtime());161 EXPECT_EQ(runtime, acc->runtime());
108 auto owner = acc->owner();162 auto owner = acc->owner();
109 EXPECT_EQ(QString(g_get_user_name()), owner);163 EXPECT_EQ(QString(g_get_user_name()), owner);
110 auto owner_id = acc->owner_id();164 auto owner_id = acc->owner_id();
@@ -118,11 +172,11 @@
118 auto runtime = Runtime::create();172 auto runtime = Runtime::create();
119173
120 auto acc = get_account(runtime);174 auto acc = get_account(runtime);
121 auto roots = acc->roots().result();175 auto roots = call(acc->roots());
122 EXPECT_EQ(1, roots.size());176 EXPECT_EQ(1, roots.size());
123177
124 // Get roots again, to get coverage for lazy initialization.178 // Get roots again, to get coverage for lazy initialization.
125 roots = acc->roots().result();179 roots = call(acc->roots());
126 ASSERT_EQ(1, roots.size());180 ASSERT_EQ(1, roots.size());
127}181}
128182
@@ -132,28 +186,36 @@
132186
133 auto acc = get_account(runtime);187 auto acc = get_account(runtime);
134 auto root = get_root(runtime);188 auto root = get_root(runtime);
135 EXPECT_EQ(acc.get(), root->account());189 EXPECT_EQ(acc, root->account());
136 EXPECT_EQ(ItemType::root, root->type());190 EXPECT_EQ(ItemType::root, root->type());
137 EXPECT_EQ("", root->name());191 EXPECT_EQ("", root->name());
138 EXPECT_NE("", root->etag());192 EXPECT_NE("", root->etag());
139193
140 auto parents = root->parents().result();194 {
141 EXPECT_TRUE(parents.isEmpty());195 auto parents = call(root->parents());
142 EXPECT_TRUE(root->parent_ids().isEmpty());196 EXPECT_TRUE(parents.isEmpty());
197 EXPECT_TRUE(root->parent_ids().isEmpty());
198 }
143199
144 // get(<root-path>) must return the root.200 {
145 auto item = root->get(root->native_identity()).result();201 // get(<root-path>) must return the root.
146 EXPECT_NE(nullptr, dynamic_pointer_cast<Root>(item));202 auto item = call(root->get(root->native_identity()));
147 EXPECT_TRUE(root->equal_to(item));203 EXPECT_NE(nullptr, dynamic_pointer_cast<Root>(item));
204 EXPECT_TRUE(root->equal_to(item));
205 }
148206
149 // Free and used space can be anything, but must be > 0.207 // Free and used space can be anything, but must be > 0.
150 auto free_space = root->free_space_bytes().result();208 {
151 cerr << "bytes free: " << free_space << endl;209 auto free_space = call(root->free_space_bytes());
152 EXPECT_GT(free_space, 0);210 cerr << "bytes free: " << free_space << endl;
211 EXPECT_GT(free_space, 0);
212 }
153213
154 auto used_space = root->used_space_bytes().result();214 {
155 cerr << "bytes used: " << used_space << endl;215 auto used_space = call(root->used_space_bytes());
156 EXPECT_GT(used_space, 0);216 cerr << "bytes used: " << used_space << endl;
217 EXPECT_GT(used_space, 0);
218 }
157}219}
158220
159TEST(Folder, basic)221TEST(Folder, basic)
@@ -164,31 +226,43 @@
164 auto root = get_root(runtime);226 auto root = get_root(runtime);
165 clear_folder(root);227 clear_folder(root);
166228
167 auto items = root->list().result();229 auto items = call(root->list());
168 EXPECT_TRUE(items.isEmpty());230 EXPECT_TRUE(items.isEmpty());
169231
170 // Create a file and check that it was created with correct type, name, and size 0.232 // Create a file and check that it was created with correct type, name, and size 0.
171 auto uploader = root->create_file("file1", 0).result();233 auto uploader = call(root->create_file("file1", 0));
172 auto file = uploader->finish_upload().result();234 auto file = call(uploader->finish_upload());
173 EXPECT_EQ(ItemType::file, file->type());235 EXPECT_EQ(ItemType::file, file->type());
174 EXPECT_EQ("file1", file->name());236 EXPECT_EQ("file1", file->name());
175 EXPECT_EQ(0, file->size());237 EXPECT_EQ(0, file->size());
176 EXPECT_EQ(root->native_identity() + "/file1", file->native_identity());238 EXPECT_EQ(root->native_identity() + "/file1", file->native_identity());
177239
178 // Create a folder and check that it was created with correct type and name.240 // Create a folder and check that it was created with correct type and name.
179 auto folder = root->create_folder("folder1").result();241 auto folder = call(root->create_folder("folder1"));
180 EXPECT_EQ(ItemType::folder, folder->type());242 EXPECT_EQ(ItemType::folder, folder->type());
181 EXPECT_EQ("folder1", folder->name());243 EXPECT_EQ("folder1", folder->name());
182 EXPECT_EQ(root->native_identity() + "/folder1", folder->native_identity());244 EXPECT_EQ(root->native_identity() + "/folder1", folder->native_identity());
183245
184 // Check that we can find both file1 and folder1.246 // Check that we can find both file1 and folder1.
185 auto item = root->lookup("file1").result()[0];247 auto item = call(root->lookup("file1"))[0];
186 file = dynamic_pointer_cast<File>(item);248 file = dynamic_pointer_cast<File>(item);
187 ASSERT_NE(nullptr, file);249 ASSERT_NE(nullptr, file);
188 EXPECT_EQ("file1", file->name());250 EXPECT_EQ("file1", file->name());
189 EXPECT_EQ(0, file->size());251 EXPECT_EQ(0, file->size());
190252
191 item = root->lookup("folder1").result()[0];253 item = call(root->lookup("folder1"))[0];
254 folder = dynamic_pointer_cast<Folder>(item);
255 ASSERT_NE(nullptr, folder);
256 ASSERT_EQ(nullptr, dynamic_pointer_cast<Root>(folder));
257 EXPECT_EQ("folder1", folder->name());
258
259 item = call(root->get(file->native_identity()));
260 file = dynamic_pointer_cast<File>(item);
261 ASSERT_NE(nullptr, file);
262 EXPECT_EQ("file1", file->name());
263 EXPECT_EQ(0, file->size());
264
265 item = call(root->get(folder->native_identity()));
192 folder = dynamic_pointer_cast<Folder>(item);266 folder = dynamic_pointer_cast<Folder>(item);
193 ASSERT_NE(nullptr, folder);267 ASSERT_NE(nullptr, folder);
194 ASSERT_EQ(nullptr, dynamic_pointer_cast<Root>(folder));268 ASSERT_EQ(nullptr, dynamic_pointer_cast<Root>(folder));
@@ -224,8 +298,8 @@
224 EXPECT_EQ(root->native_identity(), folder->parent_ids()[0]);298 EXPECT_EQ(root->native_identity(), folder->parent_ids()[0]);
225299
226 // Delete the file and check that only the directory is left.300 // Delete the file and check that only the directory is left.
227 file->delete_item().waitForFinished();301 call(file->delete_item());
228 items = root->list().result();302 items = call(root->list());
229 ASSERT_EQ(1, items.size());303 ASSERT_EQ(1, items.size());
230 folder = dynamic_pointer_cast<Folder>(items[0]);304 folder = dynamic_pointer_cast<Folder>(items[0]);
231 ASSERT_NE(nullptr, folder);305 ASSERT_NE(nullptr, folder);
@@ -233,7 +307,7 @@
233307
234 // Delete the folder and check that the root is empty.308 // Delete the folder and check that the root is empty.
235 folder->delete_item().waitForFinished();309 folder->delete_item().waitForFinished();
236 items = root->list().result();310 items = call(root->list());
237 ASSERT_EQ(0, items.size());311 ASSERT_EQ(0, items.size());
238}312}
239313
@@ -245,8 +319,8 @@
245 auto root = get_root(runtime);319 auto root = get_root(runtime);
246 clear_folder(root);320 clear_folder(root);
247321
248 auto d1 = root->create_folder("d1").result();322 auto d1 = call(root->create_folder("d1"));
249 auto d2 = d1->create_folder("d2").result();323 auto d2 = call(d1->create_folder("d2"));
250324
251 // Parent of d2 must be d1.325 // Parent of d2 must be d1.
252 EXPECT_TRUE(get_parent(d2)->equal_to(d1));326 EXPECT_TRUE(get_parent(d2)->equal_to(d1));
@@ -254,7 +328,7 @@
254328
255 // Delete is recursive329 // Delete is recursive
256 d1->delete_item().waitForFinished();330 d1->delete_item().waitForFinished();
257 auto items = root->list().result();331 auto items = call(root->list());
258 ASSERT_EQ(0, items.size());332 ASSERT_EQ(0, items.size());
259}333}
260334
@@ -269,91 +343,73 @@
269 {343 {
270 // Upload a few bytes.344 // Upload a few bytes.
271 QByteArray const contents = "Hello\n";345 QByteArray const contents = "Hello\n";
272 auto uploader = root->create_file("new_file", contents.size()).result();346 auto uploader = call(root->create_file("new_file", contents.size()));
273 auto written = uploader->socket()->write(contents);347 auto written = uploader->socket()->write(contents);
274 ASSERT_EQ(contents.size(), written);348 ASSERT_EQ(contents.size(), written);
275349
276 auto file_fut = uploader->finish_upload();350 auto file = call(uploader->finish_upload());
277 QFutureWatcher<File::SPtr> w;
278 QSignalSpy spy(&w, &decltype(w)::finished);
279 w.setFuture(file_fut);
280 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
281
282 auto file = file_fut.result();
283 EXPECT_EQ(contents.size(), file->size());351 EXPECT_EQ(contents.size(), file->size());
284 ASSERT_TRUE(content_matches(file, contents));352 ASSERT_TRUE(content_matches(file, contents));
285353
286 // Calling finish_upload() more than once must return the original future.354 // Calling finish_upload() more than once must return the original future.
287 auto file2 = uploader->finish_upload().result();355 auto file2 = call(uploader->finish_upload());
288 EXPECT_TRUE(file2->equal_to(file));356 EXPECT_TRUE(file2->equal_to(file));
289357
290 // Calling cancel() after finish_upload must do nothing.358 // Calling cancel() after finish_upload must do nothing.
291 uploader->cancel();359 uploader->cancel();
292 file2 = uploader->finish_upload().result();360 file2 = call(uploader->finish_upload());
293 EXPECT_TRUE(file2->equal_to(file));361 EXPECT_TRUE(file2->equal_to(file));
294362
295 file->delete_item().waitForFinished();363 call(file->delete_item());
296 }364 }
297365
298 {366 {
299 // Upload exactly 64 KB.367 // Upload exactly 64 KB.
300 QByteArray const contents(64 * 1024, 'a');368 QByteArray const contents(64 * 1024, 'a');
301 auto uploader = root->create_file("new_file", contents.size()).result();369 auto uploader = call(root->create_file("new_file", contents.size()));
302 auto written = uploader->socket()->write(contents);370 auto written = uploader->socket()->write(contents);
303 ASSERT_EQ(contents.size(), written);371 ASSERT_EQ(contents.size(), written);
304372
305 auto file_fut = uploader->finish_upload();373 auto file = call(uploader->finish_upload());
306 QFutureWatcher<File::SPtr> w;
307 QSignalSpy spy(&w, &decltype(w)::finished);
308 w.setFuture(file_fut);
309 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
310
311 auto file = file_fut.result();
312 EXPECT_EQ(contents.size(), file->size());374 EXPECT_EQ(contents.size(), file->size());
313 ASSERT_TRUE(content_matches(file, contents));375 ASSERT_TRUE(content_matches(file, contents));
314376
315 file->delete_item().waitForFinished();377 call(file->delete_item());
316 }378 }
317379
318 {380 {
319 // Upload 1000 KBj381 // Upload 1000 KBj
320 QByteArray const contents(1000 * 1024, 'a');382 QByteArray const contents(1000 * 1024, 'a');
321 auto uploader = root->create_file("new_file", contents.size()).result();383 auto uploader = call(root->create_file("new_file", contents.size()));
322 auto written = uploader->socket()->write(contents);384 auto written = uploader->socket()->write(contents);
323 ASSERT_EQ(contents.size(), written);385 ASSERT_EQ(contents.size(), written);
324386
325 auto file_fut = uploader->finish_upload();387 auto file = call(uploader->finish_upload());
326 QFutureWatcher<File::SPtr> w;
327 QSignalSpy spy(&w, &decltype(w)::finished);
328 w.setFuture(file_fut);
329 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
330
331 auto file = file_fut.result();
332 EXPECT_EQ(contents.size(), file->size());388 EXPECT_EQ(contents.size(), file->size());
333 ASSERT_TRUE(content_matches(file, contents));389 ASSERT_TRUE(content_matches(file, contents));
334390
335 file->delete_item().waitForFinished();391 call(file->delete_item());
336 }392 }
337393
338 {394 {
339 // Upload empty file.395 // Upload empty file.
340 auto uploader = root->create_file("new_file", 0).result();396 auto uploader = call(root->create_file("new_file", 0));
341 auto file = uploader->finish_upload().result();397 auto file = call(uploader->finish_upload());
342 ASSERT_EQ(0, file->size());398 ASSERT_EQ(0, file->size());
343399
344 // Again, and check that the ETag is different.400 // Again, and check that the ETag is different.
345 auto old_etag = file->etag();401 auto old_etag = file->etag();
346 sleep(1);402 sleep(1);
347 uploader = file->create_uploader(ConflictPolicy::overwrite, 0).result();403 uploader = call(file->create_uploader(ConflictPolicy::overwrite, 0));
348 file = uploader->finish_upload().result();404 file = call(uploader->finish_upload());
349 EXPECT_NE(old_etag, file->etag());405 EXPECT_NE(old_etag, file->etag());
350406
351 file->delete_item().waitForFinished();407 call(file->delete_item());
352 }408 }
353409
354 {410 {
355 // Let the uploader go out of scope and check that the file was not created.411 // Let the uploader go out of scope and check that the file was not created.
356 root->create_file("new_file", 0).result();412 call(root->create_file("new_file", 0));
357 boost::filesystem::path path(TEST_DIR "/storage-framework/new_file");413 boost::filesystem::path path(TEST_DIR "/storage-framework/new_file");
358 auto status = boost::filesystem::status(path);414 auto status = boost::filesystem::status(path);
359 ASSERT_FALSE(boost::filesystem::exists(status));415 ASSERT_FALSE(boost::filesystem::exists(status));
@@ -369,47 +425,25 @@
369 clear_folder(root);425 clear_folder(root);
370426
371 // Make a new file first.427 // Make a new file first.
372 auto uploader = root->create_file("new_file", 0).result();428 auto uploader = call(root->create_file("new_file", 0));
373 auto file_fut = uploader->finish_upload();429 auto file = call(uploader->finish_upload());
374 {
375 QFutureWatcher<File::SPtr> w;
376 QSignalSpy spy(&w, &decltype(w)::finished);
377 w.setFuture(file_fut);
378 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
379 }
380 auto file = file_fut.result();
381 EXPECT_EQ(0, file->size());430 EXPECT_EQ(0, file->size());
382 auto old_etag = file->etag();431 auto old_etag = file->etag();
383432
384 // Create uploader for the file and write nothing.433 // Create uploader for the file and write nothing.
385 uploader = file->create_uploader(ConflictPolicy::overwrite, 0).result();434 uploader = call(file->create_uploader(ConflictPolicy::overwrite, 0));
386 file_fut = uploader->finish_upload();435 file = call(uploader->finish_upload());
387 {
388 QFutureWatcher<File::SPtr> w;
389 QSignalSpy spy(&w, &decltype(w)::finished);
390 w.setFuture(file_fut);
391 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
392 }
393 file = file_fut.result();
394 EXPECT_EQ(0, file->size());436 EXPECT_EQ(0, file->size());
395437
396 // Same test again, but this time, we write a bunch of data.438 // Same test again, but this time, we write a bunch of data.
397 std::string s(1000000, 'a');439 std::string s(1000000, 'a');
398 uploader = file->create_uploader(ConflictPolicy::overwrite, s.size()).result();440 uploader = call(file->create_uploader(ConflictPolicy::overwrite, s.size()));
399 uploader->socket()->write(&s[0], s.size());441 uploader->socket()->write(&s[0], s.size());
400442
401 // Need to sleep here, otherwise it is possible for the443 // Need to sleep here, otherwise it is possible for the
402 // upload to finish within the granularity of the file system time stamps.444 // upload to finish within the granularity of the file system time stamps.
403 sleep(1);445 sleep(1);
404 file_fut = uploader->finish_upload();446 file = call(uploader->finish_upload());
405 {
406 QFutureWatcher<File::SPtr> w;
407 QSignalSpy spy(&w, &decltype(w)::finished);
408 w.setFuture(file_fut);
409 // Now that we have disconnected, the future must become ready.
410 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
411 }
412 file = file_fut.result();
413 EXPECT_EQ(1000000, file->size());447 EXPECT_EQ(1000000, file->size());
414 EXPECT_NE(old_etag, file->etag());448 EXPECT_NE(old_etag, file->etag());
415449
@@ -425,12 +459,12 @@
425 clear_folder(root);459 clear_folder(root);
426460
427 {461 {
428 auto uploader = root->create_file("new_file", 20).result();462 auto uploader = call(root->create_file("new_file", 20));
429463
430 // We haven't called finish_upload(), so the cancel is guaranteed464 // We haven't called finish_upload(), so the cancel is guaranteed
431 // to catch the uploader in the in_progress state.465 // to catch the uploader in the in_progress state.
432 uploader->cancel();466 uploader->cancel();
433 EXPECT_THROW(uploader->finish_upload().result(), CancelledException);467 EXPECT_THROW(call(uploader->finish_upload()), CancelledException);
434468
435 boost::filesystem::path path(TEST_DIR "/storage-framework/new_file");469 boost::filesystem::path path(TEST_DIR "/storage-framework/new_file");
436 auto status = boost::filesystem::status(path);470 auto status = boost::filesystem::status(path);
@@ -440,33 +474,25 @@
440 {474 {
441 // Create a file with a few bytes.475 // Create a file with a few bytes.
442 QByteArray original_contents = "Hello World!\n";476 QByteArray original_contents = "Hello World!\n";
443 write_file(root, "new_file", original_contents);477 auto file = write_file(root, "new_file", original_contents);
444 auto file = dynamic_pointer_cast<File>(root->lookup("new_file").result()[0]);
445 ASSERT_NE(nullptr, file);
446478
447 // Create an uploader for the file and write a bunch of bytes.479 // Create an uploader for the file and write a bunch of bytes.
448 auto uploader = file->create_uploader(ConflictPolicy::overwrite, original_contents.size()).result();480 auto uploader = call(file->create_uploader(ConflictPolicy::overwrite, original_contents.size()));
449 QByteArray const contents(1024 * 1024, 'a');481 QByteArray const contents(1024 * 1024, 'a');
450 auto written = uploader->socket()->write(contents);482 auto written = uploader->socket()->write(contents);
451 ASSERT_EQ(contents.size(), written);483 ASSERT_EQ(contents.size(), written);
452484
453 // No finish_upload() here, so the transfer is still in progress. Now cancel.485 // No finish_upload() here, so the transfer is still in progress. Now cancel.
454 auto cancel_fut = uploader->cancel();486 uploader->cancel();
455 {
456 QFutureWatcher<void> w;
457 QSignalSpy spy(&w, &decltype(w)::finished);
458 w.setFuture(cancel_fut);
459 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
460 }
461487
462 // finish_upload() must indicate that the upload was cancelled.488 // finish_upload() must indicate that the upload was cancelled.
463 EXPECT_THROW(uploader->finish_upload().result(), CancelledException);489 EXPECT_THROW(call(uploader->finish_upload()), CancelledException);
464490
465 // The original file contents must still be intact.491 // The original file contents must still be intact.
466 EXPECT_EQ(original_contents.size(), file->size());492 EXPECT_EQ(original_contents.size(), file->size());
467 ASSERT_TRUE(content_matches(file, original_contents));493 ASSERT_TRUE(content_matches(file, original_contents));
468494
469 file->delete_item().waitForFinished();495 call(file->delete_item());
470 }496 }
471}497}
472498
@@ -480,10 +506,8 @@
480506
481 // Make a new file on disk.507 // Make a new file on disk.
482 QByteArray const contents = "";508 QByteArray const contents = "";
483 write_file(root, "new_file", contents);509 auto file = write_file(root, "new_file", contents);
484 auto file = dynamic_pointer_cast<File>(root->lookup("new_file").result()[0]);510 auto uploader = call(file->create_uploader(ConflictPolicy::error_if_conflict, contents.size()));
485 ASSERT_NE(nullptr, file);
486 auto uploader = file->create_uploader(ConflictPolicy::error_if_conflict, contents.size()).result();
487511
488 // Touch the file on disk to give it a new time stamp.512 // Touch the file on disk to give it a new time stamp.
489 sleep(1);513 sleep(1);
@@ -492,7 +516,7 @@
492 try516 try
493 {517 {
494 // Must get an exception because the time stamps no longer match.518 // Must get an exception because the time stamps no longer match.
495 uploader->finish_upload().result();519 call(uploader->finish_upload());
496 FAIL();520 FAIL();
497 }521 }
498 catch (ConflictException const&)522 catch (ConflictException const&)
@@ -500,7 +524,52 @@
500 // TODO: check exception details.524 // TODO: check exception details.
501 }525 }
502526
503 file->delete_item().waitForFinished();527 call(file->delete_item());
528}
529
530TEST(File, upload_error)
531{
532 auto runtime = Runtime::create();
533
534 auto acc = get_account(runtime);
535 auto root = get_root(runtime);
536 clear_folder(root);
537
538 auto uploader = call(root->create_file("new_file", 0));
539 // Make new_file, so it gets in the way during finish_upload().
540 write_file(root, "new_file", "");
541
542 try
543 {
544 call(uploader->finish_upload());
545 FAIL();
546 }
547 catch (ExistsException const& e)
548 {
549 EXPECT_TRUE(e.error_message().startsWith("Uploader::finish_upload(): item with name \""));
550 EXPECT_TRUE(e.error_message().endsWith("\" exists already"));
551 EXPECT_EQ(TEST_DIR "/storage-framework/new_file", e.native_identity()) << e.native_identity().toStdString();
552 EXPECT_EQ("new_file", e.name());
553 }
554}
555
556TEST(File, create_uploader_bad_arg)
557{
558 auto runtime = Runtime::create();
559
560 auto acc = get_account(runtime);
561 auto root = get_root(runtime);
562 clear_folder(root);
563
564 auto file = write_file(root, "new_file", 0);
565 try
566 {
567 call(file->create_uploader(ConflictPolicy::overwrite, -1));
568 }
569 catch (InvalidArgumentException const& e)
570 {
571 EXPECT_EQ("File::create_uploader(): size must be >= 0", e.error_message());
572 }
504}573}
505574
506TEST(File, download)575TEST(File, download)
@@ -514,22 +583,15 @@
514 {583 {
515 // Download a few bytes.584 // Download a few bytes.
516 QByteArray const contents = "Hello\n";585 QByteArray const contents = "Hello\n";
517 write_file(root, "file", contents);586 auto file = write_file(root, "file", contents);
518587
519 auto item = root->lookup("file").result()[0];588 auto downloader = call(file->create_downloader());
520 File::SPtr file = dynamic_pointer_cast<File>(item);
521 ASSERT_FALSE(file == nullptr);
522
523 auto downloader = file->create_downloader().result();
524 EXPECT_TRUE(file->equal_to(downloader->file()));589 EXPECT_TRUE(file->equal_to(downloader->file()));
525590
526 auto socket = downloader->socket();591 auto socket = downloader->socket();
527 QByteArray buf;592 QByteArray buf;
528 do593 do
529 {594 {
530 // Need to pump the event loop while the socket does its thing.
531 QSignalSpy spy(downloader->socket().get(), &QIODevice::readyRead);
532 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
533 auto bytes_to_read = socket->bytesAvailable();595 auto bytes_to_read = socket->bytesAvailable();
534 buf.append(socket->read(bytes_to_read));596 buf.append(socket->read(bytes_to_read));
535 } while (buf.size() < contents.size());597 } while (buf.size() < contents.size());
@@ -538,7 +600,7 @@
538 QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);600 QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);
539 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));601 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
540602
541 ASSERT_NO_THROW(downloader->finish_download().waitForFinished());603 ASSERT_NO_THROW(call(downloader->finish_download()));
542604
543 // Contents must match.605 // Contents must match.
544 EXPECT_EQ(contents, buf);606 EXPECT_EQ(contents, buf);
@@ -547,13 +609,9 @@
547 {609 {
548 // Download exactly 64 KB.610 // Download exactly 64 KB.
549 QByteArray const contents(64 * 1024, 'a');611 QByteArray const contents(64 * 1024, 'a');
550 write_file(root, "file", contents);612 auto file = write_file(root, "file", contents);
551613
552 auto item = root->lookup("file").result()[0];614 auto downloader = call(file->create_downloader());
553 File::SPtr file = dynamic_pointer_cast<File>(item);
554 ASSERT_FALSE(file == nullptr);
555
556 auto downloader = file->create_downloader().result();
557 EXPECT_TRUE(file->equal_to(downloader->file()));615 EXPECT_TRUE(file->equal_to(downloader->file()));
558616
559 auto socket = downloader->socket();617 auto socket = downloader->socket();
@@ -561,9 +619,12 @@
561 do619 do
562 {620 {
563 // Need to pump the event loop while the socket does its thing.621 // Need to pump the event loop while the socket does its thing.
564 QSignalSpy spy(downloader->socket().get(), &QIODevice::readyRead);
565 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
566 auto bytes_to_read = socket->bytesAvailable();622 auto bytes_to_read = socket->bytesAvailable();
623 if (bytes_to_read == 0)
624 {
625 QSignalSpy spy(downloader->socket().get(), &QIODevice::readyRead);
626 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
627 }
567 buf.append(socket->read(bytes_to_read));628 buf.append(socket->read(bytes_to_read));
568 } while (buf.size() < contents.size());629 } while (buf.size() < contents.size());
569630
@@ -571,7 +632,7 @@
571 QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);632 QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);
572 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));633 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
573634
574 ASSERT_NO_THROW(downloader->finish_download().waitForFinished());635 ASSERT_NO_THROW(call(downloader->finish_download()));
575636
576 // Contents must match637 // Contents must match
577 EXPECT_EQ(contents, buf);638 EXPECT_EQ(contents, buf);
@@ -580,13 +641,9 @@
580 {641 {
581 // Download 1 MB + 1 bytes.642 // Download 1 MB + 1 bytes.
582 QByteArray const contents(1024 * 1024 + 1, 'a');643 QByteArray const contents(1024 * 1024 + 1, 'a');
583 write_file(root, "file", contents);644 auto file = write_file(root, "file", contents);
584645
585 auto item = root->lookup("file").result()[0];646 auto downloader = call(file->create_downloader());
586 File::SPtr file = dynamic_pointer_cast<File>(item);
587 ASSERT_FALSE(file == nullptr);
588
589 auto downloader = file->create_downloader().result();
590 EXPECT_TRUE(file->equal_to(downloader->file()));647 EXPECT_TRUE(file->equal_to(downloader->file()));
591648
592 auto socket = downloader->socket();649 auto socket = downloader->socket();
@@ -594,17 +651,20 @@
594 do651 do
595 {652 {
596 // Need to pump the event loop while the socket does its thing.653 // Need to pump the event loop while the socket does its thing.
597 QSignalSpy spy(downloader->socket().get(), &QIODevice::readyRead);
598 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
599 auto bytes_to_read = socket->bytesAvailable();654 auto bytes_to_read = socket->bytesAvailable();
655 if (bytes_to_read == 0)
656 {
657 QSignalSpy spy(downloader->socket().get(), &QIODevice::readyRead);
658 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
659 }
600 buf.append(socket->read(bytes_to_read));660 buf.append(socket->read(bytes_to_read));
601 } while (buf.size() < contents.size());661 } while (buf.size() < contents.size());
602662
603 // Wait for disconnected signal.663 // Wait for disconnected signal.
604 QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);664 QSignalSpy spy(socket.get(), &QLocalSocket::disconnected);
605 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));665 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
606666
607 ASSERT_NO_THROW(downloader->finish_download().waitForFinished());667 ASSERT_NO_THROW(call(downloader->finish_download()));
608668
609 // Contents must match669 // Contents must match
610 EXPECT_EQ(contents, buf);670 EXPECT_EQ(contents, buf);
@@ -613,85 +673,84 @@
613 {673 {
614 // Download file containing zero bytes674 // Download file containing zero bytes
615 QByteArray const contents;675 QByteArray const contents;
616 write_file(root, "file", contents);676 auto file = write_file(root, "file", contents);
617677
618 auto item = root->lookup("file").result()[0];678 auto downloader = call(file->create_downloader());
619 File::SPtr file = dynamic_pointer_cast<File>(item);
620 ASSERT_FALSE(file == nullptr);
621
622 auto downloader = file->create_downloader().result();
623 EXPECT_TRUE(file->equal_to(downloader->file()));679 EXPECT_TRUE(file->equal_to(downloader->file()));
624680
625 auto socket = downloader->socket();681 auto socket = downloader->socket();
626682
627 // No readyRead every arrives in this case, just wait for disconnected.683 // No readyRead ever arrives in this case, just wait for disconnected.
628 QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);684 if (socket->state() != QLocalSocket::UnconnectedState)
629 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));685 {
686 QSignalSpy spy(socket.get(), &QLocalSocket::disconnected);
687 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
688 }
630689
631 ASSERT_NO_THROW(downloader->finish_download().waitForFinished());690 ASSERT_NO_THROW(call(downloader->finish_download()));
632 }691 }
633692
634 {693 {
635 // Don't ever call read on empty file.694 // Don't ever call read on empty file.
636 QByteArray const contents;695 QByteArray const contents;
637 write_file(root, "file", contents);696 auto file = write_file(root, "file", contents);
638697
639 auto item = root->lookup("file").result()[0];698 auto downloader = call(file->create_downloader());
640 File::SPtr file = dynamic_pointer_cast<File>(item);
641 ASSERT_FALSE(file == nullptr);
642
643 auto downloader = file->create_downloader().result();
644 EXPECT_TRUE(file->equal_to(downloader->file()));699 EXPECT_TRUE(file->equal_to(downloader->file()));
645700
701 auto socket = downloader->socket();
702
646 // No readyRead ever arrives in this case, just wait for disconnected.703 // No readyRead ever arrives in this case, just wait for disconnected.
647 QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);704 if (socket->state() != QLocalSocket::UnconnectedState)
648 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));705 {
706 QSignalSpy spy(socket.get(), &QLocalSocket::disconnected);
707 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
708 }
649709
650 // This succeeds because the provider disconnects as soon710 // This succeeds because the provider disconnects as soon
651 // as it realizes that there is nothing to write.711 // as it realizes that there is nothing to write.
652 ASSERT_NO_THROW(downloader->finish_download().waitForFinished());712 ASSERT_NO_THROW(call(downloader->finish_download()));
653 }713 }
654714
655 {715 {
656 // Don't ever call read on small file.716 // Don't ever call read on small file.
657 QByteArray const contents("some contents");717 QByteArray const contents("some contents");
658 write_file(root, "file", contents);718 auto file = write_file(root, "file", contents);
659719
660 auto item = root->lookup("file").result()[0];720 auto downloader = call(file->create_downloader());
661 File::SPtr file = dynamic_pointer_cast<File>(item);
662 ASSERT_FALSE(file == nullptr);
663
664 auto downloader = file->create_downloader().result();
665 EXPECT_TRUE(file->equal_to(downloader->file()));721 EXPECT_TRUE(file->equal_to(downloader->file()));
666722
723 auto socket = downloader->socket();
724
667 // Wait for disconnected.725 // Wait for disconnected.
668 QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);726 if (socket->state() != QLocalSocket::UnconnectedState)
669 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));727 {
728 QSignalSpy spy(socket.get(), &QLocalSocket::disconnected);
729 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
730 }
670731
671 // This succeeds because the provider has written everything and disconnected.732 // This succeeds because the provider has written everything and disconnected.
672 ASSERT_NO_THROW(downloader->finish_download().waitForFinished());733 ASSERT_NO_THROW(call(downloader->finish_download()));
673 }734 }
674735
675 {736 {
676 // Don't ever call read on large file.737 // Don't ever call read on large file.
677 QByteArray const contents(1024 * 1024, 'a');738 QByteArray const contents(1024 * 1024, 'a');
678 write_file(root, "file", contents);739 auto file = write_file(root, "file", contents);
679740
680 auto item = root->lookup("file").result()[0];741 auto downloader = call(file->create_downloader());
681 File::SPtr file = dynamic_pointer_cast<File>(item);
682 ASSERT_FALSE(file == nullptr);
683
684 auto downloader = file->create_downloader().result();
685 EXPECT_TRUE(file->equal_to(downloader->file()));742 EXPECT_TRUE(file->equal_to(downloader->file()));
686743
744 auto socket = downloader->socket();
745
687 // Wait for first readyRead. Not all data fits into the socket buffer.746 // Wait for first readyRead. Not all data fits into the socket buffer.
688 QSignalSpy spy(downloader->socket().get(), &QLocalSocket::readyRead);747 QSignalSpy spy(socket.get(), &QLocalSocket::readyRead);
689 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));748 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
690749
691 // This fails because the provider still has data left to write.750 // This fails because the provider still has data left to write.
692 try751 try
693 {752 {
694 downloader->finish_download().waitForFinished();753 call(downloader->finish_download());
695 FAIL();754 FAIL();
696 }755 }
697 catch (StorageException const& e)756 catch (StorageException const& e)
@@ -703,25 +762,17 @@
703 {762 {
704 // Let downloader go out of scope.763 // Let downloader go out of scope.
705 QByteArray const contents(1024 * 1024, 'a');764 QByteArray const contents(1024 * 1024, 'a');
706 write_file(root, "file", contents);765 auto file = write_file(root, "file", contents);
707766
708 auto item = root->lookup("file").result()[0];767 auto downloader = call(file->create_downloader());
709 File::SPtr file = dynamic_pointer_cast<File>(item);
710 ASSERT_FALSE(file == nullptr);
711
712 auto downloader = file->create_downloader().result();
713 }768 }
714769
715 {770 {
716 // Let downloader future go out of scope.771 // Let downloader future go out of scope.
717 QByteArray const contents(1024 * 1024, 'a');772 QByteArray const contents(1024 * 1024, 'a');
718 write_file(root, "file", contents);773 auto file = write_file(root, "file", contents);
719774
720 auto item = root->lookup("file").result()[0];775 file->create_downloader();
721 File::SPtr file = dynamic_pointer_cast<File>(item);
722 ASSERT_FALSE(file == nullptr);
723
724 auto downloader_fut = file->create_downloader();
725 }776 }
726}777}
727778
@@ -736,65 +787,111 @@
736 {787 {
737 // Download enough bytes to prevent a single write in the provider from completing the download.788 // Download enough bytes to prevent a single write in the provider from completing the download.
738 QByteArray const contents(1024 * 1024, 'a');789 QByteArray const contents(1024 * 1024, 'a');
739 write_file(root, "file", contents);790 auto file = write_file(root, "file", contents);
740791
741 auto item = root->lookup("file").result()[0];792 auto downloader = call(file->create_downloader());
742 File::SPtr file = dynamic_pointer_cast<File>(item);
743 ASSERT_FALSE(file == nullptr);
744
745 auto download_fut = file->create_downloader();
746 {
747 QFutureWatcher<Downloader::SPtr> w;
748 QSignalSpy spy(&w, &decltype(w)::finished);
749 w.setFuture(download_fut);
750 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
751 }
752 auto downloader = download_fut.result();
753793
754 // We haven't read anything, so the cancel is guaranteed to catch the794 // We haven't read anything, so the cancel is guaranteed to catch the
755 // downloader in the in_progress state.795 // downloader in the in_progress state.
756 auto cancel_fut = downloader->cancel();796 downloader->cancel();
757 {797 ASSERT_THROW(call(downloader->finish_download()), CancelledException);
758 QFutureWatcher<void> w;
759 QSignalSpy spy(&w, &decltype(w)::finished);
760 w.setFuture(cancel_fut);
761 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
762 }
763 ASSERT_THROW(downloader->finish_download().waitForFinished(), CancelledException);
764 }798 }
765799
766 {800 {
767 // Download a few bytes.801 // Download a few bytes.
768 QByteArray const contents = "Hello\n";802 QByteArray const contents = "Hello\n";
769 write_file(root, "file", contents);803 auto file = write_file(root, "file", contents);
770
771 auto item = root->lookup("file").result()[0];
772 File::SPtr file = dynamic_pointer_cast<File>(item);
773 ASSERT_FALSE(file == nullptr);
774804
775 // Finish the download.805 // Finish the download.
776 auto downloader = file->create_downloader().result();806 auto downloader = call(file->create_downloader());
777 auto socket = downloader->socket();807 auto socket = downloader->socket();
778 QByteArray buf;808 QByteArray buf;
779 do809 do
780 {810 {
781 // Need to pump the event loop while the socket does its thing.811 // Need to pump the event loop while the socket does its thing.
782 QSignalSpy spy(downloader->socket().get(), &QIODevice::readyRead);
783 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
784 auto bytes_to_read = socket->bytesAvailable();812 auto bytes_to_read = socket->bytesAvailable();
813 if (bytes_to_read == 0)
814 {
815 QSignalSpy spy(downloader->socket().get(), &QIODevice::readyRead);
816 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
817 }
785 buf.append(socket->read(bytes_to_read));818 buf.append(socket->read(bytes_to_read));
786 } while (buf.size() < contents.size());819 } while (buf.size() < contents.size());
787820
788 // Wait for disconnected signal.821 // Wait for disconnected signal.
789 QSignalSpy spy(downloader->socket().get(), &QLocalSocket::disconnected);822 if (socket->state() != QLocalSocket::UnconnectedState)
790 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));823 {
824 QSignalSpy spy(socket.get(), &QLocalSocket::disconnected);
825 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
826 }
791827
792 // Now send the cancel. The download is finished already, and the cancel828 // Now send the cancel. The download is finished already, and the cancel
793 // is too late, so finish_download() must report that the download829 // is too late, so finish_download() must report that the download
794 // worked OK.830 // worked OK.
795 downloader->cancel();831 downloader->cancel();
796 ASSERT_NO_THROW(downloader->finish_download().waitForFinished());832 ASSERT_NO_THROW(call(downloader->finish_download()));
797 }833 }
834}
835
836TEST(File, download_error)
837{
838 auto runtime = Runtime::create();
839
840 auto acc = get_account(runtime);
841 auto root = get_root(runtime);
842 clear_folder(root);
843
844 QByteArray const contents(1024 * 1024, 'a');
845 auto file = write_file(root, "file", contents);
846
847 auto downloader = call(file->create_downloader());
848 EXPECT_TRUE(file->equal_to(downloader->file()));
849
850 auto socket = downloader->socket();
851
852 {
853 // Wait for first readyRead. Not all data fits into the socket buffer.
854 QSignalSpy spy(socket.get(), &QLocalSocket::readyRead);
855 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
856 }
857
858 {
859 // Now close the socket, to force an error at the writing end.
860 // This gives us coverage of the error handling logic in the download worker.
861 socket->abort();
862 // Wait a little, to give the worker a chance to notice the problem.
863 // We don't wait for a signal here because all attempts to disable the
864 // socket (via close(), abort(), or disconnectFromServer() also
865 // stop the stateChanged signal from arriving.
866 QTimer timer;
867 QSignalSpy spy(&timer, &QTimer::timeout);
868 timer.start(1000);
869 spy.wait();
870 }
871
872 try
873 {
874 call(downloader->finish_download());
875 FAIL();
876 }
877 catch (ResourceException const& e)
878 {
879 EXPECT_TRUE(e.error_message().startsWith("Downloader: QLocalSocket: "));
880 }
881}
882
883TEST(File, size_error)
884{
885 auto runtime = Runtime::create();
886
887 auto acc = get_account(runtime);
888 auto root = get_root(runtime);
889 clear_folder(root);
890
891 auto file = write_file(root, "file", "");
892
893 ASSERT_EQ(0, system("rm " TEST_DIR "/storage-framework/file"));
894 EXPECT_THROW(file->size(), ResourceException);
798}895}
799896
800TEST(Item, move)897TEST(Item, move)
@@ -807,25 +904,22 @@
807904
808 // Check that rename works within the same folder.905 // Check that rename works within the same folder.
809 QByteArray const contents = "Hello\n";906 QByteArray const contents = "Hello\n";
810 write_file(root, "f1", contents);907 auto f1 = write_file(root, "f1", contents);
811 auto f1 = root->lookup("f1").result()[0];908 auto f2 = call(f1->move(root, "f2"));
812 auto f2 = f1->move(root, "f2").result();
813 EXPECT_EQ("f2", f2->name());
814 EXPECT_THROW(f1->name(), DeletedException); // TODO: check exception details.
815909
816 // File must be found under new name.910 // File must be found under new name.
817 auto items = root->list().result();911 auto items = call(root->list());
818 ASSERT_EQ(1, items.size());912 ASSERT_EQ(1, items.size());
819 f2 = dynamic_pointer_cast<File>(items[0]);913 f2 = dynamic_pointer_cast<File>(items[0]);
820 ASSERT_FALSE(f2 == nullptr);914 ASSERT_FALSE(f2 == nullptr);
821915
822 // Make a folder and move f2 into it.916 // Make a folder and move f2 into it.
823 auto folder = root->create_folder("folder").result();917 auto folder = call(root->create_folder("folder"));
824 f2 = f2->move(folder, "f2").result();918 f2 = call(f2->move(folder, "f2"));
825 EXPECT_TRUE(get_parent(f2)->equal_to(folder));919 EXPECT_TRUE(get_parent(f2)->equal_to(folder));
826920
827 // Move the folder921 // Move the folder
828 auto item = folder->move(root, "folder2").result();922 auto item = call(folder->move(root, "folder2"));
829 folder = dynamic_pointer_cast<Folder>(item);923 folder = dynamic_pointer_cast<Folder>(item);
830 EXPECT_EQ("folder2", folder->name());924 EXPECT_EQ("folder2", folder->name());
831}925}
@@ -839,10 +933,8 @@
839 clear_folder(root);933 clear_folder(root);
840934
841 QByteArray const contents = "hello\n";935 QByteArray const contents = "hello\n";
842 write_file(root, "file", contents);936 auto item = write_file(root, "file", contents);
843937 auto copied_item = call(item->copy(root, "copy_of_file"));
844 auto item = root->lookup("file").result()[0];
845 auto copied_item = item->copy(root, "copy_of_file").result();
846 EXPECT_EQ("copy_of_file", copied_item->name());938 EXPECT_EQ("copy_of_file", copied_item->name());
847 File::SPtr copied_file = dynamic_pointer_cast<File>(item);939 File::SPtr copied_file = dynamic_pointer_cast<File>(item);
848 ASSERT_NE(nullptr, copied_file);940 ASSERT_NE(nullptr, copied_file);
@@ -862,7 +954,9 @@
862 // folder/empty_folder954 // folder/empty_folder
863 // folder/non_empty_folder955 // folder/non_empty_folder
864 // folder/non_empty_folder/nested_file956 // folder/non_empty_folder/nested_file
957 // folder/non_empty_folder/<TMPFILE_PREFIX>1234-1234-1234-1234
865 // folder/file958 // folder/file
959 // folder/<TMPFILE_PREFIX>1234-1234-1234-1234
866960
867 string root_path = root->native_identity().toStdString();961 string root_path = root->native_identity().toStdString();
868 ASSERT_EQ(0, mkdir((root_path + "/folder").c_str(), 0700));962 ASSERT_EQ(0, mkdir((root_path + "/folder").c_str(), 0700));
@@ -871,23 +965,26 @@
871 ofstream(root_path + "/folder/non_empty_folder/nested_file");965 ofstream(root_path + "/folder/non_empty_folder/nested_file");
872 ofstream(root_path + "/folder/file");966 ofstream(root_path + "/folder/file");
873967
968 // Add dirs that look like a tmp dirs, to get coverage on skipping those.
969 ASSERT_EQ(0, mkdir((root_path + "/folder/" + TMPFILE_PREFIX "1234-1234-1234-1234").c_str(), 0700));
970 ASSERT_EQ(0, mkdir((root_path + "/folder/non_empty_folder/" + TMPFILE_PREFIX "1234-1234-1234-1234").c_str(), 0700));
874 // Copy folder to folder2971 // Copy folder to folder2
875 auto folder = dynamic_pointer_cast<Folder>(root->lookup("folder").result()[0]);972 auto folder = dynamic_pointer_cast<Folder>(call(root->lookup("folder"))[0]);
876 ASSERT_NE(nullptr, folder);973 ASSERT_NE(nullptr, folder);
877 auto item = folder->copy(root, "folder2").result();974 auto item = call(folder->copy(root, "folder2"));
878975
879 // Verify that folder2 now contains the same structure as folder.976 // Verify that folder2 now contains the same structure as folder.
880 auto folder2 = dynamic_pointer_cast<Folder>(item);977 auto folder2 = dynamic_pointer_cast<Folder>(item);
881 ASSERT_NE(nullptr, folder2);978 ASSERT_NE(nullptr, folder2);
882 EXPECT_NO_THROW(folder2->lookup("empty_folder").result()[0]);979 EXPECT_NO_THROW(call(folder2->lookup("empty_folder"))[0]);
883 item = folder2->lookup("non_empty_folder").result()[0];980 item = call(folder2->lookup("non_empty_folder"))[0];
884 auto non_empty_folder = dynamic_pointer_cast<Folder>(item);981 auto non_empty_folder = dynamic_pointer_cast<Folder>(item);
885 ASSERT_NE(nullptr, non_empty_folder);982 ASSERT_NE(nullptr, non_empty_folder);
886 EXPECT_NO_THROW(non_empty_folder->lookup("nested_file").result()[0]);983 EXPECT_NO_THROW(call(non_empty_folder->lookup("nested_file"))[0]);
887 EXPECT_NO_THROW(folder2->lookup("file").result()[0]);984 EXPECT_NO_THROW(call(folder2->lookup("file"))[0]);
888}985}
889986
890TEST(Item, modified_time)987TEST(Item, time)
891{988{
892 auto runtime = Runtime::create();989 auto runtime = Runtime::create();
893990
@@ -897,19 +994,15 @@
897994
898 auto now = QDateTime::currentDateTimeUtc();995 auto now = QDateTime::currentDateTimeUtc();
899 sleep(1);996 sleep(1);
900 auto uploader = root->create_file("file", 0).result();997 auto uploader = call(root->create_file("file", 0));
901 auto file_fut = uploader->finish_upload();998 auto file = call(uploader->finish_upload());
902 {
903 QFutureWatcher<File::SPtr> w;
904 QSignalSpy spy(&w, &decltype(w)::finished);
905 w.setFuture(file_fut);
906 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
907 }
908 auto file = file_fut.result();
909 auto t = file->last_modified_time();999 auto t = file->last_modified_time();
910 // Rough check that the time is sane.1000 // Rough check that the time is sane.
911 EXPECT_LE(now, t);1001 EXPECT_LE(now, t);
912 EXPECT_LE(t, now.addSecs(5));1002 EXPECT_LE(t, now.addSecs(5));
1003
1004 auto creation_time = file->creation_time();
1005 EXPECT_FALSE(creation_time.isValid());
913}1006}
9141007
915TEST(Item, comparison)1008TEST(Item, comparison)
@@ -921,41 +1014,1436 @@
921 clear_folder(root);1014 clear_folder(root);
9221015
923 // Create two files.1016 // Create two files.
924 auto uploader = root->create_file("file1", 0).result();1017 auto uploader = call(root->create_file("file1", 0));
925 auto file_fut = uploader->finish_upload();1018 auto file1 = call(uploader->finish_upload());
926 {
927 QFutureWatcher<File::SPtr> w;
928 QSignalSpy spy(&w, &decltype(w)::finished);
929 w.setFuture(file_fut);
930 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
931 }
932 auto file1 = file_fut.result();
9331019
934 uploader = root->create_file("file2", 0).result();1020 uploader = call(root->create_file("file2", 0));
935 file_fut = uploader->finish_upload();1021 auto file2 = call(uploader->finish_upload());
936 {
937 QFutureWatcher<File::SPtr> w;
938 QSignalSpy spy(&w, &decltype(w)::finished);
939 w.setFuture(file_fut);
940 ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME));
941 }
942 auto file2 = file_fut.result();
9431022
944 EXPECT_FALSE(file1->equal_to(file2));1023 EXPECT_FALSE(file1->equal_to(file2));
9451024
946 // Retrieve file1 via lookup, so we get a different proxy.1025 // Retrieve file1 via lookup, so we get a different proxy.
947 auto item = root->lookup("file1").result()[0];1026 auto item = call(root->lookup("file1"))[0];
948 auto other_file1 = dynamic_pointer_cast<File>(item);1027 auto other_file1 = dynamic_pointer_cast<File>(item);
949 EXPECT_NE(file1, other_file1); // Compares shared_ptr values1028 EXPECT_NE(file1, other_file1); // Compares shared_ptr values
950 EXPECT_TRUE(file1->equal_to(other_file1)); // Deep comparison1029 EXPECT_TRUE(file1->equal_to(other_file1)); // Deep comparison
1030
1031 // Comparing against a deleted file must return false.
1032 call(file1->delete_item());
1033 EXPECT_FALSE(file1->equal_to(file2));
1034 EXPECT_FALSE(file2->equal_to(file1));
1035
1036 // Delete file2 as well and compare again.
1037 call(file2->delete_item());
1038 EXPECT_FALSE(file1->equal_to(file2));
1039}
1040
1041TEST(Item, exceptions)
1042{
1043 auto runtime = Runtime::create();
1044
1045 auto acc = get_account(runtime);
1046 auto root = get_root(runtime);
1047 clear_folder(root);
1048
1049 try
1050 {
1051 call(root->copy(nullptr, "new name"));
1052 FAIL();
1053 }
1054 catch (InvalidArgumentException const& e)
1055 {
1056 EXPECT_EQ("Item::copy(): new_parent cannot be nullptr", e.error_message());
1057 }
1058
1059 auto file = write_file(root, "file", 0);
1060
1061 try
1062 {
1063 call(file->copy(root, TMPFILE_PREFIX "copy_of_file"));
1064 FAIL();
1065 }
1066 catch (InvalidArgumentException const& e)
1067 {
1068 EXPECT_EQ("Item::copy(): names beginning with \".storage-framework-\" are reserved", e.error_message());
1069 }
1070
1071 try
1072 {
1073 call(file->copy(root, file->name()));
1074 FAIL();
1075 }
1076 catch (ExistsException const& e)
1077 {
1078 EXPECT_EQ("Item::copy(): item with name \"file\" exists already", e.error_message());
1079 EXPECT_EQ("file", e.name());
1080 EXPECT_EQ(TEST_DIR "/storage-framework/file", e.native_identity());
1081 }
1082
1083 try
1084 {
1085 auto file = write_file(root, "file", "");
1086 ASSERT_EQ(0, unlink(file->native_identity().toStdString().c_str()));
1087
1088 call(file->copy(root, file->name()));
1089 FAIL();
1090 }
1091 catch (ResourceException const& e)
1092 {
1093 EXPECT_TRUE(e.error_message().startsWith("Item::copy(): "));
1094 }
1095
1096 try
1097 {
1098 auto file = write_file(root, "file", "");
1099 ASSERT_EQ(0, unlink(file->native_identity().toStdString().c_str()));
1100
1101 call(file->move(root, "new_name"));
1102 FAIL();
1103 }
1104 catch (ResourceException const& e)
1105 {
1106 EXPECT_TRUE(e.error_message().startsWith("Item::move(): "));
1107 }
1108
1109 try
1110 {
1111 auto file = write_file(root, "file", "");
1112 ASSERT_EQ(0, unlink(file->native_identity().toStdString().c_str()));
1113 ASSERT_EQ(0, system("chmod -x " TEST_DIR "/storage-framework"));
1114
1115 call(file->delete_item());
1116 ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
1117 FAIL();
1118 }
1119 catch (ResourceException const& e)
1120 {
1121 ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
1122 EXPECT_TRUE(e.error_message().startsWith("Item::delete_item(): "));
1123 }
1124 catch (std::exception const&)
1125 {
1126 ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
1127 FAIL();
1128 }
1129
1130 try
1131 {
1132 call(root->move(nullptr, "new name"));
1133 FAIL();
1134 }
1135 catch (InvalidArgumentException const& e)
1136 {
1137 EXPECT_EQ("Item::move(): new_parent cannot be nullptr", e.error_message());
1138 }
1139
1140 try
1141 {
1142 call(root->move(root, "new name"));
1143 FAIL();
1144 }
1145 catch (LogicException const& e)
1146 {
1147 EXPECT_EQ("Item::move(): cannot move root folder", e.error_message());
1148 }
1149
1150 try
1151 {
1152 auto file = write_file(root, "file", "");
1153 call(file->move(root, file->name()));
1154 FAIL();
1155 }
1156 catch (ExistsException const& e)
1157 {
1158 EXPECT_EQ("Item::move(): item with name \"file\" exists already", e.error_message());
1159 EXPECT_EQ("file", e.name());
1160 EXPECT_EQ(TEST_DIR "/storage-framework/file", e.native_identity());
1161 }
1162
1163 try
1164 {
1165 call(file->move(root, TMPFILE_PREFIX "copy_of_file"));
1166 FAIL();
1167 }
1168 catch (InvalidArgumentException const& e)
1169 {
1170 EXPECT_EQ("Item::move(): names beginning with \".storage-framework-\" are reserved", e.error_message());
1171 }
1172
1173 try
1174 {
1175 call(root->lookup("abc/def"));
1176 }
1177 catch (InvalidArgumentException const& e)
1178 {
1179 EXPECT_EQ("Folder::lookup(): name \"abc/def\" contains more than one path component",
1180 e.error_message()) << e.what();
1181 }
1182
1183 try
1184 {
1185 call(root->create_folder(".."));
1186 }
1187 catch (InvalidArgumentException const& e)
1188 {
1189 EXPECT_EQ("Folder::create_folder(): invalid name: \"..\"", e.error_message()) << e.what();
1190 }
1191}
1192
1193TEST(Folder, exceptions)
1194{
1195 auto runtime = Runtime::create();
1196
1197 auto acc = get_account(runtime);
1198 auto root = get_root(runtime);
1199 clear_folder(root);
1200
1201 try
1202 {
1203 write_file(root, TMPFILE_PREFIX "file", "");
1204 call(root->lookup(TMPFILE_PREFIX "file"));
1205 FAIL();
1206 }
1207 catch (NotExistsException const& e)
1208 {
1209 string cmd = "rm ";
1210 cmd += string(TEST_DIR) + "/storage-framework/" + TMPFILE_PREFIX "file";
1211 ASSERT_EQ(0, system(cmd.c_str()));
1212 EXPECT_EQ("Folder::lookup(): no such item: \".storage-framework-file\"", e.error_message());
1213 EXPECT_EQ(".storage-framework-file", e.key());
1214 }
1215
1216 {
1217 auto fifo_id = root->native_identity() + "/fifo";
1218 string cmd = "mkfifo " + fifo_id.toStdString();
1219 ASSERT_EQ(0, system(cmd.c_str()));
1220
1221 try
1222 {
1223 call(root->lookup("fifo"));
1224 FAIL();
1225 }
1226 catch (NotExistsException const& e)
1227 {
1228 EXPECT_EQ("Folder::lookup(): no such item: \"fifo\"", e.error_message()) << e.what();
1229 EXPECT_EQ("fifo", e.key());
1230 }
1231
1232 cmd = "rm " + fifo_id.toStdString();
1233 ASSERT_EQ(0, system(cmd.c_str()));
1234 }
1235
1236 try
1237 {
1238 call(root->lookup("no_such_file"));
1239 FAIL();
1240 }
1241 catch (NotExistsException const& e)
1242 {
1243 EXPECT_EQ("Folder::lookup(): no such item: \"no_such_file\"", e.error_message());
1244 EXPECT_EQ("no_such_file", e.key());
1245 }
1246
1247 try
1248 {
1249 call(root->create_folder(TMPFILE_PREFIX "folder"));
1250 FAIL();
1251 }
1252 catch (InvalidArgumentException const& e)
1253 {
1254 EXPECT_EQ("Folder::create_folder(): names beginning with \".storage-framework-\" are reserved", e.error_message());
1255 }
1256
1257 try
1258 {
1259 EXPECT_NO_THROW(call(root->create_folder("folder")));
1260 call(root->create_folder("folder"));
1261 FAIL();
1262 }
1263 catch (ExistsException const& e)
1264 {
1265 EXPECT_EQ("Folder::create_folder(): item with name \"folder\" exists already", e.error_message());
1266 EXPECT_EQ("folder", e.name());
1267 EXPECT_EQ(TEST_DIR "/storage-framework/folder", e.native_identity());
1268 }
1269
1270 try
1271 {
1272 call(root->create_file("new_file", -1));
1273 FAIL();
1274 }
1275 catch (InvalidArgumentException const& e)
1276 {
1277 EXPECT_EQ("Folder::create_file(): size must be >= 0", e.error_message());
1278 }
1279
1280 try
1281 {
1282 call(root->create_file(TMPFILE_PREFIX "new_file", 0));
1283 FAIL();
1284 }
1285 catch (InvalidArgumentException const& e)
1286 {
1287 EXPECT_EQ("Folder::create_file(): names beginning with \".storage-framework-\" are reserved", e.error_message());
1288 }
1289
1290 try
1291 {
1292 write_file(root, "file", "");
1293 call(root->create_file("file", 0));
1294 FAIL();
1295 }
1296 catch (ExistsException const& e)
1297 {
1298 EXPECT_EQ("Folder::create_file(): item with name \"file\" exists already", e.error_message());
1299 EXPECT_EQ("file", e.name());
1300 EXPECT_EQ(TEST_DIR "/storage-framework/file", e.native_identity());
1301 }
1302
1303 ASSERT_EQ(0, system("chmod -x " TEST_DIR "/storage-framework"));
1304 try
1305 {
1306 call(root->create_file("new_file", 0));
1307 ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
1308 FAIL();
1309 }
1310 catch (ResourceException const& e)
1311 {
1312 EXPECT_TRUE(e.error_message().startsWith("Folder::create_file(): "));
1313 EXPECT_EQ(EACCES, e.error_code());
1314 ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
1315 }
1316 catch (std::exception const& e)
1317 {
1318 ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
1319 FAIL();
1320 }
1321
1322 ASSERT_EQ(0, system("chmod -x " TEST_DIR "/storage-framework"));
1323 try
1324 {
1325 call(root->create_folder("new_folder"));
1326 ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
1327 FAIL();
1328 }
1329 catch (ResourceException const& e)
1330 {
1331 EXPECT_TRUE(e.error_message().startsWith("Folder::create_folder(): "));
1332 EXPECT_EQ(EACCES, e.error_code());
1333 ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
1334 }
1335 catch (std::exception const& e)
1336 {
1337 ASSERT_EQ(0, system("chmod +x " TEST_DIR "/storage-framework"));
1338 FAIL();
1339 }
1340
1341 try
1342 {
1343 call(root->create_file("new_file", -1));
1344 }
1345 catch (InvalidArgumentException const& e)
1346 {
1347 EXPECT_EQ("Folder::create_file(): size must be >= 0", e.error_message());
1348 }
1349}
1350
1351TEST(Root, root_exceptions)
1352{
1353 auto runtime = Runtime::create();
1354
1355 auto acc = get_account(runtime);
1356 auto root = get_root(runtime);
1357 clear_folder(root);
1358
1359 try
1360 {
1361 call(root->delete_item());
1362 FAIL();
1363 }
1364 catch (LogicException const& e)
1365 {
1366 EXPECT_EQ("Root::delete_item(): Cannot delete root folder", e.error_message());
1367 }
1368
1369 try
1370 {
1371 call(root->get("abc"));
1372 FAIL();
1373 }
1374 catch (InvalidArgumentException const& e)
1375 {
1376 EXPECT_EQ("Root::get(): identity \"abc\" must be an absolute path", e.error_message());
1377 }
1378
1379 try
1380 {
1381 call(root->get("/etc"));
1382 FAIL();
1383 }
1384 catch (InvalidArgumentException const& e)
1385 {
1386 EXPECT_EQ("Root::get(): identity \"/etc\" points outside the root folder", e.error_message());
1387 }
1388
1389 {
1390 auto folder = call(root->create_folder("folder"));
1391 auto file = write_file(folder, "testfile", "hello");
1392
1393 // Remove permission from folder.
1394 string cmd = "chmod -x " + folder->native_identity().toStdString();
1395 ASSERT_EQ(0, system(cmd.c_str()));
1396
1397 try
1398 {
1399 file = dynamic_pointer_cast<File>(call(root->get(file->native_identity())));
1400 FAIL();
1401 }
1402 catch (PermissionException const& e)
1403 {
1404 EXPECT_TRUE(e.error_message().startsWith("Root::get(): "));
1405 EXPECT_TRUE(e.error_message().contains("Permission denied"));
1406 }
1407
1408 cmd = "chmod +x " + folder->native_identity().toStdString();
1409 ASSERT_EQ(0, system(cmd.c_str()));
1410
1411 clear_folder(root);
1412 }
1413
1414 {
1415 auto file = write_file(root, "testfile", "hello");
1416
1417 QString id = file->native_identity();
1418 id.append("_doesnt_exist");
1419
1420 try
1421 {
1422 file = dynamic_pointer_cast<File>(call(root->get(id)));
1423 FAIL();
1424 }
1425 catch (NotExistsException const& e)
1426 {
1427 EXPECT_EQ(id, e.key());
1428 }
1429
1430 clear_folder(root);
1431 }
1432
1433 {
1434 auto fifo_id = root->native_identity() + "/fifo";
1435 string cmd = "mkfifo " + fifo_id.toStdString();
1436 ASSERT_EQ(0, system(cmd.c_str()));
1437
1438 try
1439 {
1440 call(root->get(fifo_id));
1441 FAIL();
1442 }
1443 catch (NotExistsException const& e)
1444 {
1445 EXPECT_EQ(fifo_id, e.key());
1446 }
1447
1448 cmd = "rm " + fifo_id.toStdString();
1449 ASSERT_EQ(0, system(cmd.c_str()));
1450 }
1451
1452 {
1453 string reserved_name = TMPFILE_PREFIX "somefile";
1454 string full_path = string(TEST_DIR) + "/storage-framework/" + reserved_name;
1455 string cmd = "touch ";
1456 cmd += full_path;
1457 ASSERT_EQ(0, system(cmd.c_str()));
1458
1459 auto reserved_id = QString::fromStdString(full_path);
1460 try
1461 {
1462 call(root->get(reserved_id));
1463 FAIL();
1464 }
1465 catch (NotExistsException const& e)
1466 {
1467 EXPECT_EQ(reserved_id, e.key());
1468 }
1469
1470 clear_folder(root);
1471 }
1472}
1473
1474TEST(Item, deleted_exceptions)
1475{
1476 auto runtime = Runtime::create();
1477
1478 auto acc = get_account(runtime);
1479 auto root = get_root(runtime);
1480 clear_folder(root);
1481
1482 try
1483 {
1484 auto file = make_deleted_file(root, "file");
1485 file->etag();
1486 FAIL();
1487 }
1488 catch (DeletedException const& e)
1489 {
1490 EXPECT_EQ("file", e.name());
1491 EXPECT_TRUE(e.error_message().startsWith("Item::etag(): "));
1492 EXPECT_TRUE(e.error_message().endsWith(" was deleted previously"));
1493 EXPECT_EQ(TEST_DIR "/storage-framework/file", e.native_identity());
1494 }
1495
1496 try
1497 {
1498 auto file = make_deleted_file(root, "file");
1499 file->metadata();
1500 FAIL();
1501 }
1502 catch (DeletedException const& e)
1503 {
1504 EXPECT_EQ("file", e.name());
1505 EXPECT_TRUE(e.error_message().startsWith("Item::metadata(): "));
1506 }
1507
1508 try
1509 {
1510 auto file = make_deleted_file(root, "file");
1511 file->last_modified_time();
1512 FAIL();
1513 }
1514 catch (DeletedException const& e)
1515 {
1516 EXPECT_EQ("file", e.name());
1517 EXPECT_TRUE(e.error_message().startsWith("Item::last_modified_time(): "));
1518 }
1519
1520 try
1521 {
1522 // Copying deleted file must fail.
1523 auto file = make_deleted_file(root, "file");
1524 call(file->copy(root, "copy_of_file"));
1525 FAIL();
1526 }
1527 catch (DeletedException const& e)
1528 {
1529 EXPECT_EQ("file", e.name());
1530 EXPECT_TRUE(e.error_message().startsWith("Item::copy(): "));
1531 }
1532
1533 try
1534 {
1535 // Copying file into deleted folder must fail.
1536
1537 // Make target folder.
1538 auto folder = call(root->create_folder("folder"));
1539
1540 // Make a file in the root.
1541 auto uploader = call(root->create_file("file", 0));
1542 auto file = call(uploader->finish_upload());
1543
1544 // Delete folder.
1545 call(folder->delete_item());
1546
1547 call(file->copy(folder, "file"));
1548 FAIL();
1549 }
1550 catch (DeletedException const& e)
1551 {
1552 EXPECT_EQ("folder", e.name());
1553 EXPECT_TRUE(e.error_message().startsWith("Item::copy(): "));
1554 }
1555 clear_folder(root);
1556
1557 try
1558 {
1559 // Moving deleted file must fail.
1560 auto file = make_deleted_file(root, "file");
1561 call(file->move(root, "moved_file"));
1562 FAIL();
1563 }
1564 catch (DeletedException const& e)
1565 {
1566 EXPECT_EQ("file", e.name());
1567 EXPECT_TRUE(e.error_message().startsWith("Item::move(): "));
1568 }
1569
1570 try
1571 {
1572 // Moving file into deleted folder must fail.
1573
1574 // Make target folder.
1575 auto folder = call(root->create_folder("folder"));
1576
1577 // Make a file in the root.
1578 auto uploader = call(root->create_file("file", 0));
1579 auto file = call(uploader->finish_upload());
1580
1581 // Delete folder.
1582 call(folder->delete_item());
1583
1584 call(file->move(folder, "file"));
1585 FAIL();
1586 }
1587 catch (DeletedException const& e)
1588 {
1589 EXPECT_EQ("folder", e.name());
1590 EXPECT_TRUE(e.error_message().startsWith("Item::move(): "));
1591 }
1592 clear_folder(root);
1593
1594 try
1595 {
1596 auto file = make_deleted_file(root, "file");
1597 call(file->parents());
1598 FAIL();
1599 }
1600 catch (DeletedException const& e)
1601 {
1602 EXPECT_EQ("file", e.name());
1603 EXPECT_TRUE(e.error_message().startsWith("Item::parents(): "));
1604 }
1605
1606 try
1607 {
1608 auto file = make_deleted_file(root, "file");
1609 file->parent_ids();
1610 FAIL();
1611 }
1612 catch (DeletedException const& e)
1613 {
1614 EXPECT_EQ("file", e.name());
1615 EXPECT_TRUE(e.error_message().startsWith("Item::parent_ids(): "));
1616 }
1617
1618 try
1619 {
1620 // Deleting a deleted item must fail.
1621 auto file = make_deleted_file(root, "file");
1622 call(file->delete_item());
1623 FAIL();
1624 }
1625 catch (DeletedException const& e)
1626 {
1627 EXPECT_EQ("file", e.name());
1628 EXPECT_TRUE(e.error_message().startsWith("Item::delete_item(): "));
1629 }
1630}
1631
1632TEST(Folder, deleted_exceptions)
1633{
1634 auto runtime = Runtime::create();
1635
1636 auto acc = get_account(runtime);
1637 auto root = get_root(runtime);
1638 clear_folder(root);
1639
1640 try
1641 {
1642 auto folder = make_deleted_folder(root, "folder");
1643 folder->name();
1644 FAIL();
1645 }
1646 catch (DeletedException const& e)
1647 {
1648 EXPECT_EQ("folder", e.name());
1649 EXPECT_TRUE(e.error_message().startsWith("Folder::name(): "));
1650 }
1651
1652 try
1653 {
1654 auto folder = make_deleted_folder(root, "folder");
1655 call(folder->list());
1656 FAIL();
1657 }
1658 catch (DeletedException const& e)
1659 {
1660 EXPECT_EQ("folder", e.name());
1661 EXPECT_TRUE(e.error_message().startsWith("Folder::list(): "));
1662 }
1663
1664 try
1665 {
1666 auto folder = make_deleted_folder(root, "folder");
1667 call(folder->lookup("something"));
1668 FAIL();
1669 }
1670 catch (DeletedException const& e)
1671 {
1672 EXPECT_EQ("folder", e.name());
1673 EXPECT_TRUE(e.error_message().startsWith("Folder::lookup(): "));
1674 }
1675
1676 try
1677 {
1678 auto folder = make_deleted_folder(root, "folder");
1679 call(folder->list());
1680 FAIL();
1681 }
1682 catch (DeletedException const& e)
1683 {
1684 EXPECT_EQ("folder", e.name());
1685 EXPECT_TRUE(e.error_message().startsWith("Folder::list(): "));
1686 }
1687
1688 try
1689 {
1690 auto folder = make_deleted_folder(root, "folder");
1691 call(folder->create_folder("nested_folder"));
1692 FAIL();
1693 }
1694 catch (DeletedException const& e)
1695 {
1696 EXPECT_EQ("folder", e.name());
1697 EXPECT_TRUE(e.error_message().startsWith("Folder::create_folder(): "));
1698 }
1699
1700 try
1701 {
1702 auto folder = make_deleted_folder(root, "folder");
1703 call(folder->create_file("nested_file", 0));
1704 FAIL();
1705 }
1706 catch (DeletedException const& e)
1707 {
1708 EXPECT_EQ("folder", e.name());
1709 EXPECT_TRUE(e.error_message().startsWith("Folder::create_file(): "));
1710 }
1711
1712 try
1713 {
1714 auto folder = make_deleted_folder(root, "folder");
1715 folder->creation_time();
1716 FAIL();
1717 }
1718 catch (DeletedException const& e)
1719 {
1720 EXPECT_EQ("folder", e.name());
1721 EXPECT_TRUE(e.error_message().startsWith("Item::creation_time(): ")) << e.what();
1722 }
1723
1724 try
1725 {
1726 auto folder = make_deleted_folder(root, "folder");
1727 folder->native_metadata();
1728 FAIL();
1729 }
1730 catch (DeletedException const& e)
1731 {
1732 EXPECT_EQ("folder", e.name());
1733 EXPECT_TRUE(e.error_message().startsWith("Item::native_metadata(): ")) << e.what();
1734 }
1735}
1736
1737TEST(File, deleted_exceptions)
1738{
1739 auto runtime = Runtime::create();
1740
1741 auto acc = get_account(runtime);
1742 auto root = get_root(runtime);
1743 clear_folder(root);
1744
1745 try
1746 {
1747 auto file = make_deleted_file(root, "file");
1748 file->name();
1749 FAIL();
1750 }
1751 catch (DeletedException const& e)
1752 {
1753 EXPECT_EQ("file", e.name());
1754 EXPECT_TRUE(e.error_message().startsWith("File::name(): "));
1755 EXPECT_TRUE(e.error_message().endsWith(" was deleted previously"));
1756 EXPECT_EQ(TEST_DIR "/storage-framework/file", e.native_identity());
1757 }
1758
1759 try
1760 {
1761 auto file = make_deleted_file(root, "file");
1762 file->size();
1763 FAIL();
1764 }
1765 catch (DeletedException const& e)
1766 {
1767 EXPECT_EQ("file", e.name());
1768 EXPECT_TRUE(e.error_message().startsWith("File::size(): "));
1769 EXPECT_TRUE(e.error_message().endsWith(" was deleted previously"));
1770 EXPECT_EQ(TEST_DIR "/storage-framework/file", e.native_identity());
1771 }
1772
1773 try
1774 {
1775 auto file = make_deleted_file(root, "file");
1776 call(file->create_uploader(ConflictPolicy::overwrite, 0));
1777 FAIL();
1778 }
1779 catch (DeletedException const& e)
1780 {
1781 EXPECT_EQ("file", e.name());
1782 EXPECT_TRUE(e.error_message().startsWith("File::create_uploader(): "));
1783 EXPECT_TRUE(e.error_message().endsWith(" was deleted previously"));
1784 EXPECT_EQ(TEST_DIR "/storage-framework/file", e.native_identity());
1785 }
1786
1787 try
1788 {
1789 auto file = make_deleted_file(root, "file");
1790 call(file->create_downloader());
1791 FAIL();
1792 }
1793 catch (DeletedException const& e)
1794 {
1795 EXPECT_EQ("file", e.name());
1796 EXPECT_TRUE(e.error_message().startsWith("File::create_downloader(): "));
1797 EXPECT_TRUE(e.error_message().endsWith(" was deleted previously"));
1798 EXPECT_EQ(TEST_DIR "/storage-framework/file", e.native_identity());
1799 }
1800}
1801
1802TEST(Runtime, runtime_destroyed_exceptions)
1803{
1804 // Gettting an account after shutting down the runtime must fail.
1805 {
1806 auto runtime = Runtime::create();
1807 auto acc = get_account(runtime);
1808 runtime->shutdown();
1809 try
1810 {
1811 acc->runtime();
1812 FAIL();
1813 }
1814 catch (RuntimeDestroyedException const& e)
1815 {
1816 EXPECT_EQ("Account::runtime(): Runtime was destroyed previously", e.error_message());
1817 }
1818 }
1819
1820 // Getting an account after destroying the runtime must fail.
1821 {
1822 auto runtime = Runtime::create();
1823 auto acc = get_account(runtime);
1824 runtime.reset();
1825 try
1826 {
1827 acc->runtime();
1828 FAIL();
1829 }
1830 catch (RuntimeDestroyedException const& e)
1831 {
1832 EXPECT_EQ("Account::runtime(): Runtime was destroyed previously", e.error_message());
1833 }
1834 }
1835
1836 // Getting accounts after shutting down the runtime must fail.
1837 {
1838 auto runtime = Runtime::create();
1839 runtime->shutdown();
1840 try
1841 {
1842 runtime->accounts();
1843 FAIL();
1844 }
1845 catch (RuntimeDestroyedException const& e)
1846 {
1847 EXPECT_EQ("Runtime::accounts(): Runtime was destroyed previously", e.error_message());
1848 }
1849 }
1850
1851 // Getting the account from a root with a destroyed runtime must fail.
1852 {
1853 auto runtime = Runtime::create();
1854 auto acc = get_account(runtime);
1855 auto root = get_root(runtime);
1856 runtime.reset();
1857 try
1858 {
1859 root->account();
1860 FAIL();
1861 }
1862 catch (RuntimeDestroyedException const& e)
1863 {
1864 EXPECT_EQ("Root::account(): Runtime was destroyed previously", e.error_message());
1865 }
1866 }
1867
1868 // Getting the account from a root with a destroyed account must fail.
1869 {
1870 auto runtime = Runtime::create();
1871 auto acc = get_account(runtime);
1872 auto root = get_root(runtime);
1873 runtime.reset();
1874 acc.reset();
1875 try
1876 {
1877 root->account();
1878 FAIL();
1879 }
1880 catch (RuntimeDestroyedException const& e)
1881 {
1882 EXPECT_EQ("Root::account(): Runtime was destroyed previously", e.error_message());
1883 }
1884 }
1885
1886 // Getting the root from an item with a destroyed runtime must fail.
1887 {
1888 auto runtime = Runtime::create();
1889 auto acc = get_account(runtime);
1890 auto root = get_root(runtime);
1891 clear_folder(root);
1892
1893 auto file = write_file(root, "file", "");
1894 runtime.reset();
1895 try
1896 {
1897 file->root();
1898 FAIL();
1899 }
1900 catch (RuntimeDestroyedException const& e)
1901 {
1902 EXPECT_EQ("Item::root(): Runtime was destroyed previously", e.error_message());
1903 }
1904 }
1905
1906 // Getting the root from an item with a destroyed root must fail.
1907 {
1908 auto runtime = Runtime::create();
1909 auto acc = get_account(runtime);
1910 auto root = get_root(runtime);
1911 clear_folder(root);
1912
1913 auto file = write_file(root, "file", "");
1914 runtime.reset();
1915 acc.reset();
1916 root.reset();
1917 try
1918 {
1919 file->root();
1920 FAIL();
1921 }
1922 catch (RuntimeDestroyedException const& e)
1923 {
1924 EXPECT_EQ("Item::root(): Runtime was destroyed previously", e.error_message());
1925 }
1926 }
1927
1928 // etag() with destroyed runtime must fail.
1929 {
1930 auto runtime = Runtime::create();
1931 auto acc = get_account(runtime);
1932 auto root = get_root(runtime);
1933 clear_folder(root);
1934
1935 auto file = write_file(root, "file", "");
1936 runtime->shutdown();
1937 try
1938 {
1939 file->etag();
1940 FAIL();
1941 }
1942 catch (RuntimeDestroyedException const& e)
1943 {
1944 EXPECT_EQ("Item::etag(): Runtime was destroyed previously", e.error_message());
1945 }
1946 }
1947
1948 // metadata() with destroyed runtime must fail.
1949 {
1950 auto runtime = Runtime::create();
1951 auto acc = get_account(runtime);
1952 auto root = get_root(runtime);
1953 clear_folder(root);
1954
1955 auto file = write_file(root, "file", "");
1956 runtime->shutdown();
1957 try
1958 {
1959 file->metadata();
1960 FAIL();
1961 }
1962 catch (RuntimeDestroyedException const& e)
1963 {
1964 EXPECT_EQ("Item::metadata(): Runtime was destroyed previously", e.error_message());
1965 }
1966 }
1967
1968 // last_modified_time() with destroyed runtime must fail.
1969 {
1970 auto runtime = Runtime::create();
1971 auto acc = get_account(runtime);
1972 auto root = get_root(runtime);
1973 clear_folder(root);
1974
1975 auto file = write_file(root, "file", "");
1976 runtime->shutdown();
1977 try
1978 {
1979 file->last_modified_time();
1980 FAIL();
1981 }
1982 catch (RuntimeDestroyedException const& e)
1983 {
1984 EXPECT_EQ("Item::last_modified_time(): Runtime was destroyed previously", e.error_message());
1985 }
1986 }
1987
1988 // copy() with destroyed runtime must fail.
1989 {
1990 auto runtime = Runtime::create();
1991 auto acc = get_account(runtime);
1992 auto root = get_root(runtime);
1993 clear_folder(root);
1994
1995 auto file = write_file(root, "file", "");
1996 runtime->shutdown();
1997 try
1998 {
1999 call(file->copy(root, "file2"));
2000 FAIL();
2001 }
2002 catch (RuntimeDestroyedException const& e)
2003 {
2004 EXPECT_EQ("Item::copy(): Runtime was destroyed previously", e.error_message());
2005 }
2006 }
2007
2008 // move() with destroyed runtime must fail.
2009 {
2010 auto runtime = Runtime::create();
2011 auto acc = get_account(runtime);
2012 auto root = get_root(runtime);
2013 clear_folder(root);
2014
2015 auto file = write_file(root, "file", "");
2016 runtime->shutdown();
2017 try
2018 {
2019 call(file->move(root, "file2"));
2020 FAIL();
2021 }
2022 catch (RuntimeDestroyedException const& e)
2023 {
2024 EXPECT_EQ("Item::move(): Runtime was destroyed previously", e.error_message());
2025 }
2026 }
2027
2028 // parents() on root with destroyed runtime must fail.
2029 {
2030 auto runtime = Runtime::create();
2031 auto acc = get_account(runtime);
2032 auto root = get_root(runtime);
2033 clear_folder(root);
2034
2035 auto file = write_file(root, "file", "");
2036 runtime->shutdown();
2037 try
2038 {
2039 call(root->parents());
2040 FAIL();
2041 }
2042 catch (RuntimeDestroyedException const& e)
2043 {
2044 EXPECT_EQ("Root::parents(): Runtime was destroyed previously", e.error_message());
2045 }
2046 }
2047
2048 // parents() on file with destroyed runtime must fail.
2049 {
2050 auto runtime = Runtime::create();
2051 auto acc = get_account(runtime);
2052 auto root = get_root(runtime);
2053 clear_folder(root);
2054
2055 auto file = write_file(root, "file", "");
2056 runtime->shutdown();
2057 try
2058 {
2059 call(file->parents());
2060 FAIL();
2061 }
2062 catch (RuntimeDestroyedException const& e)
2063 {
2064 EXPECT_EQ("Item::parents(): Runtime was destroyed previously", e.error_message());
2065 }
2066 }
2067
2068 // parent_ids() with destroyed runtime must fail.
2069 {
2070 auto runtime = Runtime::create();
2071 auto acc = get_account(runtime);
2072 auto root = get_root(runtime);
2073 clear_folder(root);
2074
2075 auto file = write_file(root, "file", "");
2076 runtime->shutdown();
2077 try
2078 {
2079 file->parent_ids();
2080 FAIL();
2081 }
2082 catch (RuntimeDestroyedException const& e)
2083 {
2084 EXPECT_EQ("Item::parent_ids(): Runtime was destroyed previously", e.error_message());
2085 }
2086 }
2087
2088 // parent_ids() on root with destroyed runtime must fail.
2089 {
2090 auto runtime = Runtime::create();
2091 auto acc = get_account(runtime);
2092 auto root = get_root(runtime);
2093 clear_folder(root);
2094
2095 runtime->shutdown();
2096 try
2097 {
2098 root->parent_ids();
2099 FAIL();
2100 }
2101 catch (RuntimeDestroyedException const& e)
2102 {
2103 EXPECT_EQ("Root::parent_ids(): Runtime was destroyed previously", e.error_message());
2104 }
2105 }
2106
2107 // delete_item() with destroyed runtime must fail.
2108 {
2109 auto runtime = Runtime::create();
2110 auto acc = get_account(runtime);
2111 auto root = get_root(runtime);
2112 clear_folder(root);
2113
2114 auto file = write_file(root, "file", "");
2115 runtime->shutdown();
2116 try
2117 {
2118 call(file->delete_item());
2119 FAIL();
2120 }
2121 catch (RuntimeDestroyedException const& e)
2122 {
2123 EXPECT_EQ("Item::delete_item(): Runtime was destroyed previously", e.error_message());
2124 }
2125 }
2126
2127 // delete_item() on root with destroyed runtime must fail.
2128 {
2129 auto runtime = Runtime::create();
2130 auto acc = get_account(runtime);
2131 auto root = get_root(runtime);
2132 clear_folder(root);
2133
2134 runtime->shutdown();
2135 try
2136 {
2137 call(root->delete_item());
2138 FAIL();
2139 }
2140 catch (RuntimeDestroyedException const& e)
2141 {
2142 EXPECT_EQ("Root::delete_item(): Runtime was destroyed previously", e.error_message());
2143 }
2144 }
2145
2146 // creation_time() with destroyed runtime must fail.
2147 {
2148 auto runtime = Runtime::create();
2149 auto acc = get_account(runtime);
2150 auto root = get_root(runtime);
2151 clear_folder(root);
2152
2153 auto file = write_file(root, "file", "");
2154 runtime->shutdown();
2155 try
2156 {
2157 file->creation_time();
2158 FAIL();
2159 }
2160 catch (RuntimeDestroyedException const& e)
2161 {
2162 EXPECT_EQ("Item::creation_time(): Runtime was destroyed previously", e.error_message());
2163 }
2164 }
2165
2166 // native_metadata() with destroyed runtime must fail.
2167 {
2168 auto runtime = Runtime::create();
2169 auto acc = get_account(runtime);
2170 auto root = get_root(runtime);
2171 clear_folder(root);
2172
2173 auto file = write_file(root, "file", "");
2174 runtime->shutdown();
2175 try
2176 {
2177 file->native_metadata();
2178 FAIL();
2179 }
2180 catch (RuntimeDestroyedException const& e)
2181 {
2182 EXPECT_EQ("Item::native_metadata(): Runtime was destroyed previously", e.error_message());
2183 }
2184 }
2185
2186 // name() on root with destroyed runtime must fail.
2187 {
2188 auto runtime = Runtime::create();
2189 auto acc = get_account(runtime);
2190 auto root = get_root(runtime);
2191 clear_folder(root);
2192
2193 runtime->shutdown();
2194 try
2195 {
2196 root->name();
2197 FAIL();
2198 }
2199 catch (RuntimeDestroyedException const& e)
2200 {
2201 EXPECT_EQ("Root::name(): Runtime was destroyed previously", e.error_message());
2202 }
2203 }
2204
2205 // name() on folder with destroyed runtime must fail.
2206 {
2207 auto runtime = Runtime::create();
2208 auto acc = get_account(runtime);
2209 auto root = get_root(runtime);
2210 clear_folder(root);
2211
2212 auto folder = call(root->create_folder("folder"));
2213 runtime->shutdown();
2214 try
2215 {
2216 folder->name();
2217 FAIL();
2218 }
2219 catch (RuntimeDestroyedException const& e)
2220 {
2221 EXPECT_EQ("Folder::name(): Runtime was destroyed previously", e.error_message());
2222 }
2223 }
2224
2225 // name() on file with destroyed runtime must fail.
2226 {
2227 auto runtime = Runtime::create();
2228 auto acc = get_account(runtime);
2229 auto root = get_root(runtime);
2230 clear_folder(root);
2231
2232 auto file = write_file(root, "file", "");
2233 runtime->shutdown();
2234 try
2235 {
2236 file->name();
2237 FAIL();
2238 }
2239 catch (RuntimeDestroyedException const& e)
2240 {
2241 EXPECT_EQ("File::name(): Runtime was destroyed previously", e.error_message());
2242 }
2243 }
2244
2245 // list() with destroyed runtime must fail.
2246 {
2247 auto runtime = Runtime::create();
2248 auto acc = get_account(runtime);
2249 auto root = get_root(runtime);
2250 clear_folder(root);
2251
2252 runtime->shutdown();
2253 try
2254 {
2255 call(root->list());
2256 FAIL();
2257 }
2258 catch (RuntimeDestroyedException const& e)
2259 {
2260 EXPECT_EQ("Folder::list(): Runtime was destroyed previously", e.error_message());
2261 }
2262 }
2263
2264 // lookup() with destroyed runtime must fail.
2265 {
2266 auto runtime = Runtime::create();
2267 auto acc = get_account(runtime);
2268 auto root = get_root(runtime);
2269 clear_folder(root);
2270
2271 runtime->shutdown();
2272 try
2273 {
2274 call(root->lookup("file"));
2275 FAIL();
2276 }
2277 catch (RuntimeDestroyedException const& e)
2278 {
2279 EXPECT_EQ("Folder::lookup(): Runtime was destroyed previously", e.error_message());
2280 }
2281 }
2282
2283 // create_folder() with destroyed runtime must fail.
2284 {
2285 auto runtime = Runtime::create();
2286 auto acc = get_account(runtime);
2287 auto root = get_root(runtime);
2288 clear_folder(root);
2289
2290 runtime->shutdown();
2291 try
2292 {
2293 call(root->create_folder("folder"));
2294 FAIL();
2295 }
2296 catch (RuntimeDestroyedException const& e)
2297 {
2298 EXPECT_EQ("Folder::create_folder(): Runtime was destroyed previously", e.error_message());
2299 }
2300 }
2301
2302 // create_file() with destroyed runtime must fail.
2303 {
2304 auto runtime = Runtime::create();
2305 auto acc = get_account(runtime);
2306 auto root = get_root(runtime);
2307 clear_folder(root);
2308
2309 runtime->shutdown();
2310 try
2311 {
2312 call(root->create_file("file", 0));
2313 FAIL();
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: