2
* Copyright (C) 2016 Canonical Ltd
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU Lesser General Public License version 3 as
6
* published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU Lesser General Public License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Authors: James Henstridge <james.henstridge@canonical.com>
19
#include <unity/storage/provider/ProviderBase.h>
20
#include <unity/storage/provider/internal/DBusPeerCache.h>
21
#include <unity/storage/provider/internal/AccountData.h>
22
#include <unity/storage/provider/internal/ProviderInterface.h>
23
#include <unity/storage/provider/internal/dbusmarshal.h>
24
// generated DBus service adaptor
25
#include "../../src/provider/provideradaptor.h"
27
#include "TestProvider.h"
28
#include "ProviderClient.h"
30
#include <utils/DBusEnvironment.h>
32
#include <gtest/gtest.h>
33
#include <OnlineAccounts/Account>
34
#include <OnlineAccounts/Manager>
35
#include <QCoreApplication>
36
#include <QDBusConnection>
37
#include <QDBusConnectionInterface>
39
#include <QSocketNotifier>
42
#include <sys/types.h>
49
using unity::storage::ItemType;
50
using namespace unity::storage::provider;
54
const auto SERVICE_CONNECTION_NAME = QStringLiteral("service-session-bus");
55
const auto BUS_PATH = QStringLiteral("/provider");
56
const auto PROVIDER_IFACE = QStringLiteral("com.canonical.StorageFramework.Provider");
57
const char PROVIDER_ERROR[] = "com.canonical.StorageFramework.Provider.Error";
62
class ProviderInterfaceTest : public ::testing::Test
65
QDBusConnection const& connection()
67
return dbus_->connection();
70
void make_provider(unique_ptr<ProviderBase>&& provider)
72
account_manager_->waitForReady();
73
OnlineAccounts::Account* account = account_manager_->account(
75
ASSERT_NE(nullptr, account);
77
auto peer_cache = make_shared<internal::DBusPeerCache>(*service_connection_);
78
auto account_data = make_shared<internal::AccountData>(
79
move(provider), peer_cache, *service_connection_, account);
80
provider_interface_.reset(new internal::ProviderInterface(account_data));
81
new ProviderAdaptor(provider_interface_.get());
82
service_connection_->registerObject(BUS_PATH, provider_interface_.get());
84
client_.reset(new ProviderClient(service_connection_->baseService(),
89
void wait_for(QDBusPendingCall const& call) {
90
QDBusPendingCallWatcher watcher(call);
91
QSignalSpy spy(&watcher, &QDBusPendingCallWatcher::finished);
92
ASSERT_TRUE(spy.wait());
98
dbus_.reset(new DBusEnvironment);
99
dbus_->start_services();
100
service_connection_.reset(
101
new QDBusConnection(QDBusConnection::connectToBus(
102
dbus_->busAddress(), SERVICE_CONNECTION_NAME)));
103
account_manager_.reset(new OnlineAccounts::Manager(
104
"", *service_connection_));
107
void TearDown() override
110
provider_interface_.reset();
111
service_connection_.reset();
112
QDBusConnection::disconnectFromBus(SERVICE_CONNECTION_NAME);
116
unique_ptr<DBusEnvironment> dbus_;
117
unique_ptr<QDBusConnection> service_connection_;
118
unique_ptr<OnlineAccounts::Manager> account_manager_;
119
unique_ptr<internal::ProviderInterface> provider_interface_;
120
unique_ptr<ProviderClient> client_;
124
TEST_F(ProviderInterfaceTest, roots)
126
make_provider(unique_ptr<ProviderBase>(new TestProvider));
128
auto reply = client_->Roots();
130
ASSERT_TRUE(reply.isValid());
131
EXPECT_EQ(1, reply.value().size());
132
auto root = reply.value()[0];
133
EXPECT_EQ("root_id", root.item_id);
134
EXPECT_EQ("", root.parent_id);
135
EXPECT_EQ("Root", root.name);
136
EXPECT_EQ("etag", root.etag);
137
EXPECT_EQ(ItemType::root, root.type);
140
TEST_F(ProviderInterfaceTest, list)
142
make_provider(unique_ptr<ProviderBase>(new TestProvider));
144
auto reply = client_->List("root_id", "");
146
ASSERT_TRUE(reply.isValid());
147
auto items = reply.argumentAt<0>();
148
QString page_token = reply.argumentAt<1>();
149
ASSERT_EQ(2, items.size());
150
EXPECT_EQ("child1_id", items[0].item_id);
151
EXPECT_EQ("child2_id", items[1].item_id);
152
EXPECT_EQ("page_token", page_token);
154
reply = client_->List("root_id", page_token);
156
ASSERT_TRUE(reply.isValid());
157
items = reply.argumentAt<0>();
158
page_token = reply.argumentAt<1>();
159
ASSERT_EQ(2, items.size());
160
EXPECT_EQ("child3_id", items[0].item_id);
161
EXPECT_EQ("child4_id", items[1].item_id);
162
EXPECT_EQ("", page_token);
164
// Try a bad page token
165
reply = client_->List("root_id", "bad_page_token");
167
EXPECT_TRUE(reply.isError());
168
EXPECT_EQ(PROVIDER_ERROR, reply.error().name());
169
EXPECT_EQ("Unknown page token", reply.error().message());
171
reply = client_->List("no_such_folder_id", "");
173
EXPECT_TRUE(reply.isError());
174
EXPECT_EQ(PROVIDER_ERROR, reply.error().name());
175
EXPECT_EQ("Unknown folder", reply.error().message());
178
TEST_F(ProviderInterfaceTest, lookup)
180
make_provider(unique_ptr<ProviderBase>(new TestProvider));
182
auto reply = client_->Lookup("root_id", "Filename");
184
ASSERT_TRUE(reply.isValid());
185
auto items = reply.value();
186
ASSERT_EQ(1, items.size());
187
auto item = items[0];
188
EXPECT_EQ("child_id", item.item_id);
189
EXPECT_EQ("root_id", item.parent_id);
190
EXPECT_EQ("Filename", item.name);
191
EXPECT_EQ(ItemType::file, item.type);
194
TEST_F(ProviderInterfaceTest, metadata)
196
make_provider(unique_ptr<ProviderBase>(new TestProvider));
198
auto reply = client_->Metadata("root_id");
200
ASSERT_TRUE(reply.isValid());
201
auto item = reply.value();
202
EXPECT_EQ("root_id", item.item_id);
203
EXPECT_EQ("", item.parent_id);
204
EXPECT_EQ("Root", item.name);
205
EXPECT_EQ(ItemType::root, item.type);
208
TEST_F(ProviderInterfaceTest, create_folder)
210
make_provider(unique_ptr<ProviderBase>(new TestProvider));
212
auto reply = client_->CreateFolder("root_id", "New Folder");
214
ASSERT_TRUE(reply.isValid());
215
auto item = reply.value();
216
EXPECT_EQ("new_folder_id", item.item_id);
217
EXPECT_EQ("root_id", item.parent_id);
218
EXPECT_EQ("New Folder", item.name);
219
EXPECT_EQ(ItemType::folder, item.type);
222
TEST_F(ProviderInterfaceTest, create_file)
224
make_provider(unique_ptr<ProviderBase>(new TestProvider));
226
const std::string file_contents = "Hello world!";
228
QDBusUnixFileDescriptor socket;
230
auto reply = client_->CreateFile("parent_id", "file name", file_contents.size(), "text/plain", false);
232
ASSERT_TRUE(reply.isValid());
233
upload_id = reply.argumentAt<0>();
234
socket = reply.argumentAt<1>();
237
auto app = QCoreApplication::instance();
238
QSocketNotifier notifier(socket.fileDescriptor(), QSocketNotifier::Write);
239
size_t total_written = 0;
241
¬ifier, &QSocketNotifier::activated,
242
[&file_contents, app, ¬ifier, &total_written](int fd) {
243
ssize_t n_written = write(fd, file_contents.data() + total_written, file_contents.size() - total_written);
247
notifier.setEnabled(false);
250
total_written += n_written;
251
if (total_written == file_contents.size())
253
notifier.setEnabled(false);
257
notifier.setEnabled(true);
259
socket.setFileDescriptor(-1);
261
auto reply = client_->FinishUpload(upload_id);
263
ASSERT_TRUE(reply.isValid());
264
auto item = reply.value();
265
EXPECT_EQ("new_file_id", item.item_id);
266
EXPECT_EQ("parent_id", item.parent_id);
267
EXPECT_EQ("file name", item.name);
270
TEST_F(ProviderInterfaceTest, update)
272
make_provider(unique_ptr<ProviderBase>(new TestProvider));
274
const std::string file_contents = "Hello world!";
276
QDBusUnixFileDescriptor socket;
278
auto reply = client_->Update("item_id", file_contents.size(), "old_etag");
280
ASSERT_TRUE(reply.isValid());
281
upload_id = reply.argumentAt<0>();
282
socket = reply.argumentAt<1>();
285
auto app = QCoreApplication::instance();
286
QSocketNotifier notifier(socket.fileDescriptor(), QSocketNotifier::Write);
287
size_t total_written = 0;
289
¬ifier, &QSocketNotifier::activated,
290
[&file_contents, app, ¬ifier, &total_written](int fd) {
291
ssize_t n_written = write(fd, file_contents.data() + total_written, file_contents.size() - total_written);
295
notifier.setEnabled(false);
298
total_written += n_written;
299
if (total_written == file_contents.size())
301
notifier.setEnabled(false);
305
notifier.setEnabled(true);
307
socket.setFileDescriptor(-1);
309
auto reply = client_->FinishUpload(upload_id);
311
ASSERT_TRUE(reply.isValid());
312
auto item = reply.value();
313
EXPECT_EQ("item_id", item.item_id);
316
TEST_F(ProviderInterfaceTest, upload_short_write)
318
make_provider(unique_ptr<ProviderBase>(new TestProvider));
321
QDBusUnixFileDescriptor socket;
323
auto reply = client_->Update("item_id", 100, "old_etag");
325
ASSERT_TRUE(reply.isValid());
326
upload_id = reply.argumentAt<0>();
327
socket = reply.argumentAt<1>();
329
auto reply = client_->FinishUpload(upload_id);
331
ASSERT_TRUE(reply.isError());
332
EXPECT_EQ(PROVIDER_ERROR, reply.error().name());
333
EXPECT_EQ("wrong number of bytes written", reply.error().message());
336
TEST_F(ProviderInterfaceTest, upload_long_write)
338
make_provider(unique_ptr<ProviderBase>(new TestProvider));
340
const std::string file_contents = "Hello world!";
342
QDBusUnixFileDescriptor socket;
344
auto reply = client_->Update("item_id", file_contents.size() - 5, "old_etag");
346
ASSERT_TRUE(reply.isValid());
347
upload_id = reply.argumentAt<0>();
348
socket = reply.argumentAt<1>();
351
auto app = QCoreApplication::instance();
352
QSocketNotifier notifier(socket.fileDescriptor(), QSocketNotifier::Write);
353
size_t total_written = 0;
355
¬ifier, &QSocketNotifier::activated,
356
[&file_contents, app, ¬ifier, &total_written](int fd) {
357
ssize_t n_written = write(fd, file_contents.data() + total_written, file_contents.size() - total_written);
361
notifier.setEnabled(false);
364
total_written += n_written;
365
if (total_written == file_contents.size())
367
notifier.setEnabled(false);
371
notifier.setEnabled(true);
373
socket.setFileDescriptor(-1);
375
auto reply = client_->FinishUpload(upload_id);
377
ASSERT_TRUE(reply.isError());
378
EXPECT_EQ("too many bytes written", reply.error().message().toStdString());
381
TEST_F(ProviderInterfaceTest, cancel_upload)
383
make_provider(unique_ptr<ProviderBase>(new TestProvider));
386
QDBusUnixFileDescriptor socket;
388
auto reply = client_->Update("item_id", 100, "old_etag");
390
ASSERT_TRUE(reply.isValid());
391
upload_id = reply.argumentAt<0>();
392
socket = reply.argumentAt<1>();
394
auto reply = client_->CancelUpload(upload_id);
396
ASSERT_TRUE(reply.isValid());
399
TEST_F(ProviderInterfaceTest, finish_upload_unknown)
401
make_provider(unique_ptr<ProviderBase>(new TestProvider));
403
auto reply = client_->FinishUpload("no-such-upload");
405
ASSERT_TRUE(reply.isError());
406
EXPECT_EQ(PROVIDER_ERROR, reply.error().name());
407
EXPECT_EQ("map::at", reply.error().message());
410
TEST_F(ProviderInterfaceTest, download)
412
make_provider(unique_ptr<ProviderBase>(new TestProvider));
415
QDBusUnixFileDescriptor socket;
417
auto reply = client_->Download("item_id");
419
ASSERT_TRUE(reply.isValid());
420
download_id = reply.argumentAt<0>();
421
socket = reply.argumentAt<1>();
425
auto app = QCoreApplication::instance();
426
QSocketNotifier notifier(socket.fileDescriptor(), QSocketNotifier::Read);
428
¬ifier, &QSocketNotifier::activated,
429
[&data, app, ¬ifier](int fd) {
431
ssize_t n_read = read(fd, buf, sizeof(buf));
434
// Error or end of file
435
notifier.setEnabled(false);
440
data += string(buf, n_read);
443
notifier.setEnabled(true);
445
auto reply = client_->FinishDownload(download_id);
447
ASSERT_TRUE(reply.isValid());
449
// Also check that we got the expected data from the socket.
450
EXPECT_EQ("Hello world", data);
453
TEST_F(ProviderInterfaceTest, download_short_read)
455
make_provider(unique_ptr<ProviderBase>(new TestProvider));
458
QDBusUnixFileDescriptor socket;
460
auto reply = client_->Download("item_id");
462
ASSERT_TRUE(reply.isValid());
463
download_id = reply.argumentAt<0>();
464
socket = reply.argumentAt<1>();
466
auto reply = client_->FinishDownload(download_id);
468
ASSERT_TRUE(reply.isError());
469
EXPECT_EQ(PROVIDER_ERROR, reply.error().name());
470
EXPECT_EQ("Not all data read", reply.error().message());
473
TEST_F(ProviderInterfaceTest, finish_download_unknown)
475
make_provider(unique_ptr<ProviderBase>(new TestProvider));
477
auto reply = client_->FinishDownload("no-such-download");
479
ASSERT_TRUE(reply.isError());
480
EXPECT_EQ(PROVIDER_ERROR, reply.error().name());
481
EXPECT_EQ("map::at", reply.error().message());
484
TEST_F(ProviderInterfaceTest, delete_)
486
make_provider(unique_ptr<ProviderBase>(new TestProvider));
488
auto reply = client_->Delete("item_id");
490
ASSERT_TRUE(reply.isValid());
493
TEST_F(ProviderInterfaceTest, move)
495
make_provider(unique_ptr<ProviderBase>(new TestProvider));
497
auto reply = client_->Move("child_id", "new_parent_id", "New name");
499
ASSERT_TRUE(reply.isValid());
500
auto item = reply.value();
501
EXPECT_EQ("child_id", item.item_id);
502
EXPECT_EQ("new_parent_id", item.parent_id);
503
EXPECT_EQ("New name", item.name);
504
EXPECT_EQ(ItemType::file, item.type);
507
TEST_F(ProviderInterfaceTest, copy)
509
make_provider(unique_ptr<ProviderBase>(new TestProvider));
511
auto reply = client_->Copy("child_id", "new_parent_id", "New name");
513
ASSERT_TRUE(reply.isValid());
514
auto item = reply.value();
515
EXPECT_EQ("new_id", item.item_id);
516
EXPECT_EQ("new_parent_id", item.parent_id);
517
EXPECT_EQ("New name", item.name);
518
EXPECT_EQ(ItemType::file, item.type);
522
int main(int argc, char **argv)
524
QCoreApplication app(argc, argv);
525
qDBusRegisterMetaType<unity::storage::internal::ItemMetadata>();
526
qDBusRegisterMetaType<QList<unity::storage::internal::ItemMetadata>>();
527
qDBusRegisterMetaType<unity::storage::provider::Item>();
528
qDBusRegisterMetaType<std::vector<unity::storage::provider::Item>>();
529
::testing::InitGoogleTest(&argc, argv);
530
return RUN_ALL_TESTS();