Merge lp:~jamesh/storage-provider-webdav/download-job into lp:storage-provider-webdav/devel
- download-job
- Merge into devel
Status: | Merged |
---|---|
Approved by: | James Henstridge |
Approved revision: | 22 |
Merged at revision: | 14 |
Proposed branch: | lp:~jamesh/storage-provider-webdav/download-job |
Merge into: | lp:storage-provider-webdav/devel |
Diff against target: |
544 lines (+480/-1) 5 files modified
src/CMakeLists.txt (+1/-0) src/DavDownloadJob.cpp (+183/-0) src/DavDownloadJob.h (+51/-0) src/DavProvider.cpp (+5/-0) tests/davprovider/davprovider_test.cpp (+240/-1) |
To merge this branch: | bzr merge lp:~jamesh/storage-provider-webdav/download-job |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
unity-api-1-bot | continuous-integration | Approve | |
Michi Henning (community) | Approve | ||
Review via email: mp+310275@code.launchpad.net |
Commit message
Implement download() D-Bus method.
Description of the change
Implement download() d-bus method.
unity-api-1-bot (unity-api-1-bot) wrote : | # |
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Continuous integration, rev:17
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Continuous integration, rev:17
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Continuous integration, rev:19
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
PASSED: Continuous integration, rev:20
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Continuous integration, rev:21
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
PASSED: Continuous integration, rev:21
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Michi Henning (michihenning) wrote : | # |
This looks good! My only request is to add a test that compares the actually read contents with the expected contents (instead of comparing only the size). Without this, we have no guarantee that the data was read actually matches.
Michi Henning (michihenning) wrote : | # |
Loverly, thanks! :-)
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Continuous integration, rev:22
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
unity-api-1-bot (unity-api-1-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
unity-api-1-bot (unity-api-1-bot) : | # |
Preview Diff
1 | === modified file 'src/CMakeLists.txt' |
2 | --- src/CMakeLists.txt 2016-10-31 07:33:19 +0000 |
3 | +++ src/CMakeLists.txt 2016-11-10 07:22:07 +0000 |
4 | @@ -1,6 +1,7 @@ |
5 | |
6 | add_library(dav-provider-lib STATIC |
7 | DavProvider.cpp |
8 | + DavDownloadJob.cpp |
9 | DavUploadJob.cpp |
10 | MultiStatusParser.cpp |
11 | item_id.cpp |
12 | |
13 | === added file 'src/DavDownloadJob.cpp' |
14 | --- src/DavDownloadJob.cpp 1970-01-01 00:00:00 +0000 |
15 | +++ src/DavDownloadJob.cpp 2016-11-10 07:22:07 +0000 |
16 | @@ -0,0 +1,183 @@ |
17 | +#include "DavDownloadJob.h" |
18 | +#include "DavProvider.h" |
19 | +#include "RetrieveMetadataHandler.h" |
20 | +#include "item_id.h" |
21 | + |
22 | +#include <unity/storage/provider/Exceptions.h> |
23 | +#include <unity/storage/provider/metadata_keys.h> |
24 | + |
25 | +#include <unistd.h> |
26 | +#include <cassert> |
27 | +#include <cstdint> |
28 | +#include <string> |
29 | + |
30 | +using namespace std; |
31 | +using namespace unity::storage::provider; |
32 | +using unity::storage::ItemType; |
33 | + |
34 | +namespace |
35 | +{ |
36 | + |
37 | +string make_download_id() |
38 | +{ |
39 | + static int counter = 0; |
40 | + return to_string(counter++); |
41 | +} |
42 | + |
43 | +constexpr int CHUNK_SIZE = 64 * 1024; |
44 | + |
45 | +} |
46 | + |
47 | +DavDownloadJob::DavDownloadJob(DavProvider const& provider, |
48 | + string const& item_id, |
49 | + string const& match_etag, |
50 | + Context const& ctx) |
51 | + : QObject(), DownloadJob(make_download_id()), provider_(provider), |
52 | + item_id_(item_id) |
53 | +{ |
54 | + QUrl base_url = provider.base_url(ctx); |
55 | + QNetworkRequest request(id_to_url(item_id, base_url)); |
56 | + if (!match_etag.empty()) |
57 | + { |
58 | + request.setRawHeader(QByteArrayLiteral("If-Match"), |
59 | + QByteArray::fromStdString(match_etag)); |
60 | + } |
61 | + |
62 | + reply_ = provider.send_request( |
63 | + request, QByteArrayLiteral("GET"), nullptr, ctx); |
64 | + assert(reply_ != nullptr); |
65 | + reply_->setReadBufferSize(CHUNK_SIZE); |
66 | + connect(reply_, &QNetworkReply::finished, |
67 | + this, &DavDownloadJob::onReplyFinished); |
68 | + connect(reply_, &QIODevice::readyRead, |
69 | + this, &DavDownloadJob::onReplyReadyRead); |
70 | + connect(reply_, &QIODevice::readChannelFinished, |
71 | + this, &DavDownloadJob::onReplyReadChannelFinished); |
72 | + writer_.setSocketDescriptor( |
73 | + dup(write_socket()), QLocalSocket::ConnectedState, QIODevice::WriteOnly); |
74 | + connect(&writer_, &QIODevice::bytesWritten, |
75 | + this, &DavDownloadJob::onSocketBytesWritten); |
76 | +} |
77 | + |
78 | +DavDownloadJob::~DavDownloadJob() |
79 | +{ |
80 | + if (!reply_.isNull()) |
81 | + { |
82 | + reply_->deleteLater(); |
83 | + } |
84 | +} |
85 | + |
86 | +void DavDownloadJob::onReplyFinished() |
87 | +{ |
88 | + // If we haven't seen HTTP response headers and are in an error |
89 | + // state, report that. |
90 | + if (!seen_header_ && reply_->error() != QNetworkReply::NoError) |
91 | + { |
92 | + handle_error(RemoteCommsException("Error connecting to server: " + |
93 | + reply_->errorString().toStdString())); |
94 | + } |
95 | +} |
96 | + |
97 | +void DavDownloadJob::onReplyReadyRead() |
98 | +{ |
99 | + if (error_) |
100 | + { |
101 | + return; |
102 | + } |
103 | + |
104 | + if (!seen_header_) |
105 | + { |
106 | + seen_header_ = true; |
107 | + auto status = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); |
108 | + if (status != 200) |
109 | + { |
110 | + handle_error(RemoteCommsException("Unexpected status code: " + |
111 | + to_string(status))); |
112 | + return; |
113 | + } |
114 | + } |
115 | + |
116 | + maybe_send_chunk(); |
117 | +} |
118 | + |
119 | +void DavDownloadJob::onReplyReadChannelFinished() |
120 | +{ |
121 | + if (error_) |
122 | + { |
123 | + return; |
124 | + } |
125 | + |
126 | + read_channel_finished_ = true; |
127 | + maybe_send_chunk(); |
128 | +} |
129 | + |
130 | +void DavDownloadJob::onSocketBytesWritten(int64_t bytes) |
131 | +{ |
132 | + if (error_) |
133 | + { |
134 | + return; |
135 | + } |
136 | + |
137 | + bytes_written_ += bytes; |
138 | + maybe_send_chunk(); |
139 | +} |
140 | + |
141 | +void DavDownloadJob::maybe_send_chunk() |
142 | +{ |
143 | + assert(bytes_written_ <= bytes_read_); |
144 | + // If there are outstanding writes, do nothing. |
145 | + if (bytes_written_ < bytes_read_) |
146 | + { |
147 | + return; |
148 | + } |
149 | + // If there are no bytes available, return. |
150 | + if (reply_->bytesAvailable() == 0) |
151 | + { |
152 | + // If we've reached the end of the input, and all data has been |
153 | + // written out, signal completion. |
154 | + if (read_channel_finished_ && bytes_written_ == bytes_read_) |
155 | + { |
156 | + writer_.close(); |
157 | + report_complete(); |
158 | + } |
159 | + return; |
160 | + } |
161 | + |
162 | + char buffer[CHUNK_SIZE]; |
163 | + int n_read = reply_->read(buffer, CHUNK_SIZE); |
164 | + if (n_read < 0) |
165 | + { |
166 | + handle_error(RemoteCommsException("Failed to read from server")); |
167 | + return; |
168 | + } |
169 | + bytes_read_ += n_read; |
170 | + |
171 | + int n_written = writer_.write(buffer, n_read); |
172 | + if (n_written < 0) |
173 | + { |
174 | + handle_error(ResourceException("Error writing to socket", 0)); |
175 | + return; |
176 | + } |
177 | +} |
178 | + |
179 | +void DavDownloadJob::handle_error(StorageException const& exc) |
180 | +{ |
181 | + error_ = true; |
182 | + reply_->close(); |
183 | + writer_.close(); |
184 | + report_error(std::make_exception_ptr(exc)); |
185 | +} |
186 | + |
187 | + |
188 | +boost::future<void> DavDownloadJob::cancel() |
189 | +{ |
190 | + reply_->abort(); |
191 | + writer_.close(); |
192 | + return boost::make_ready_future(); |
193 | +} |
194 | + |
195 | +boost::future<void> DavDownloadJob::finish() |
196 | +{ |
197 | + return boost::make_exceptional_future<void>( |
198 | + LogicException("finish called before all data sent")); |
199 | +} |
200 | |
201 | === added file 'src/DavDownloadJob.h' |
202 | --- src/DavDownloadJob.h 1970-01-01 00:00:00 +0000 |
203 | +++ src/DavDownloadJob.h 2016-11-10 07:22:07 +0000 |
204 | @@ -0,0 +1,51 @@ |
205 | +#pragma once |
206 | + |
207 | +#include <QLocalSocket> |
208 | +#include <QNetworkReply> |
209 | +#include <QObject> |
210 | +#include <QPointer> |
211 | +#include <QUrl> |
212 | +#include <unity/storage/provider/Exceptions.h> |
213 | +#include <unity/storage/provider/ProviderBase.h> |
214 | +#include <unity/storage/provider/DownloadJob.h> |
215 | + |
216 | +#include <cstdint> |
217 | +#include <memory> |
218 | +#include <string> |
219 | + |
220 | +class DavProvider; |
221 | + |
222 | +class DavDownloadJob : public QObject, public unity::storage::provider::DownloadJob |
223 | +{ |
224 | + Q_OBJECT |
225 | +public: |
226 | + DavDownloadJob(DavProvider const& provider, std::string const& item_id, |
227 | + std::string const& match_etag, |
228 | + unity::storage::provider::Context const& ctx); |
229 | + ~DavDownloadJob(); |
230 | + |
231 | + boost::future<void> cancel() override; |
232 | + boost::future<void> finish() override; |
233 | + |
234 | +private Q_SLOTS: |
235 | + void onReplyFinished(); |
236 | + void onReplyReadyRead(); |
237 | + void onReplyReadChannelFinished(); |
238 | + |
239 | + void onSocketBytesWritten(int64_t bytes); |
240 | + |
241 | +private: |
242 | + void maybe_send_chunk(); |
243 | + void handle_error(unity::storage::provider::StorageException const& exc); |
244 | + |
245 | + DavProvider const& provider_; |
246 | + std::string const item_id_; |
247 | + QLocalSocket writer_; |
248 | + QPointer<QNetworkReply> reply_; |
249 | + |
250 | + bool seen_header_ = false; |
251 | + bool error_ = false; |
252 | + bool read_channel_finished_ = false; |
253 | + int64_t bytes_read_ = 0; |
254 | + int64_t bytes_written_ = 0; |
255 | +}; |
256 | |
257 | === modified file 'src/DavProvider.cpp' |
258 | --- src/DavProvider.cpp 2016-09-30 12:41:56 +0000 |
259 | +++ src/DavProvider.cpp 2016-11-10 07:22:07 +0000 |
260 | @@ -4,6 +4,7 @@ |
261 | #include "ListHandler.h" |
262 | #include "LookupHandler.h" |
263 | #include "MetadataHandler.h" |
264 | +#include "DavDownloadJob.h" |
265 | #include "DavUploadJob.h" |
266 | #include "item_id.h" |
267 | |
268 | @@ -87,6 +88,10 @@ |
269 | boost::future<unique_ptr<DownloadJob>> DavProvider::download( |
270 | string const& item_id, Context const& ctx) |
271 | { |
272 | + boost::promise<unique_ptr<DownloadJob>> p; |
273 | + p.set_value(unique_ptr<DownloadJob>(new DavDownloadJob( |
274 | + *this, item_id, string(), ctx))); |
275 | + return p.get_future(); |
276 | } |
277 | |
278 | boost::future<void> DavProvider::delete_item( |
279 | |
280 | === modified file 'tests/davprovider/davprovider_test.cpp' |
281 | --- tests/davprovider/davprovider_test.cpp 2016-11-03 08:33:58 +0000 |
282 | +++ tests/davprovider/davprovider_test.cpp 2016-11-10 07:22:07 +0000 |
283 | @@ -12,6 +12,7 @@ |
284 | #include <QTemporaryDir> |
285 | #include <QTimer> |
286 | #include <unity/storage/qt/client/Account.h> |
287 | +#include <unity/storage/qt/client/Downloader.h> |
288 | #include <unity/storage/qt/client/Exceptions.h> |
289 | #include <unity/storage/qt/client/Root.h> |
290 | #include <unity/storage/qt/client/Uploader.h> |
291 | @@ -45,7 +46,7 @@ |
292 | "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor " |
293 | "in reprehenderit in voluptate velit esse cillum dolore eu fugiat " |
294 | "nulla pariatur. Excepteur sint occaecat cupidatat non proident, " |
295 | - "sunt in culpa qui officia deserunt mollit anim id est laborum."; |
296 | + "sunt in culpa qui officia deserunt mollit anim id est laborum.\n"; |
297 | |
298 | constexpr int SIGNAL_WAIT_TIME = 30000; |
299 | |
300 | @@ -666,6 +667,244 @@ |
301 | } |
302 | } |
303 | |
304 | +TEST_F(DavProviderTests, download) |
305 | +{ |
306 | + int const segments = 1000; |
307 | + string large_contents; |
308 | + large_contents.reserve(file_contents.size() * segments); |
309 | + for (int i = 0; i < segments; i++) |
310 | + { |
311 | + large_contents += file_contents; |
312 | + } |
313 | + string const full_path = local_file("foo.txt"); |
314 | + { |
315 | + int fd = open(full_path.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0644); |
316 | + ASSERT_GT(fd, 0); |
317 | + ASSERT_EQ(large_contents.size(), write(fd, &large_contents[0], large_contents.size())) << strerror(errno); |
318 | + ASSERT_EQ(0, close(fd)); |
319 | + } |
320 | + |
321 | + auto account = get_client(); |
322 | + shared_ptr<Root> root; |
323 | + { |
324 | + QFutureWatcher<QVector<shared_ptr<Root>>> watcher; |
325 | + QSignalSpy spy(&watcher, &decltype(watcher)::finished); |
326 | + watcher.setFuture(account->roots()); |
327 | + if (spy.count() == 0) |
328 | + { |
329 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
330 | + } |
331 | + auto roots = watcher.result(); |
332 | + ASSERT_EQ(1, roots.size()); |
333 | + root = roots[0]; |
334 | + } |
335 | + |
336 | + shared_ptr<File> file; |
337 | + { |
338 | + QFutureWatcher<shared_ptr<Item>> watcher; |
339 | + QSignalSpy spy(&watcher, &decltype(watcher)::finished); |
340 | + watcher.setFuture(root->get("foo.txt")); |
341 | + if (spy.count() == 0) |
342 | + { |
343 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
344 | + } |
345 | + file = dynamic_pointer_cast<File>(watcher.result()); |
346 | + } |
347 | + ASSERT_NE(nullptr, file.get()); |
348 | + |
349 | + shared_ptr<Downloader> downloader; |
350 | + { |
351 | + QFutureWatcher<shared_ptr<Downloader>> watcher; |
352 | + QSignalSpy spy(&watcher, &decltype(watcher)::finished); |
353 | + watcher.setFuture(file->create_downloader()); |
354 | + if (spy.count() == 0) |
355 | + { |
356 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
357 | + } |
358 | + downloader = watcher.result(); |
359 | + } |
360 | + |
361 | + int64_t n_read = 0; |
362 | + auto socket = downloader->socket(); |
363 | + QObject::connect(socket.get(), &QIODevice::readyRead, |
364 | + [socket, &large_contents, &n_read]() { |
365 | + auto bytes = socket->readAll(); |
366 | + string const expected = large_contents.substr( |
367 | + n_read, bytes.size()); |
368 | + EXPECT_EQ(expected, bytes.toStdString()); |
369 | + n_read += bytes.size(); |
370 | + }); |
371 | + { |
372 | + QSignalSpy spy(socket.get(), &QIODevice::readChannelFinished); |
373 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
374 | + } |
375 | + |
376 | + { |
377 | + QFutureWatcher<void> watcher; |
378 | + QSignalSpy spy(&watcher, &decltype(watcher)::finished); |
379 | + watcher.setFuture(downloader->finish_download()); |
380 | + if (spy.count() == 0) |
381 | + { |
382 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
383 | + } |
384 | + watcher.waitForFinished(); // to check for errors |
385 | + } |
386 | + |
387 | + EXPECT_EQ(large_contents.size(), n_read); |
388 | +} |
389 | + |
390 | +TEST_F(DavProviderTests, download_short_read) |
391 | +{ |
392 | + int const segments = 1000; |
393 | + { |
394 | + string full_path = local_file("foo.txt"); |
395 | + int fd = open(full_path.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0644); |
396 | + ASSERT_GT(fd, 0); |
397 | + for (int i = 0; i < segments; i++) |
398 | + { |
399 | + ASSERT_EQ(file_contents.size(), write(fd, &file_contents[0], file_contents.size())) << strerror(errno); |
400 | + } |
401 | + ASSERT_EQ(0, close(fd)); |
402 | + } |
403 | + |
404 | + auto account = get_client(); |
405 | + shared_ptr<Root> root; |
406 | + { |
407 | + QFutureWatcher<QVector<shared_ptr<Root>>> watcher; |
408 | + QSignalSpy spy(&watcher, &decltype(watcher)::finished); |
409 | + watcher.setFuture(account->roots()); |
410 | + if (spy.count() == 0) |
411 | + { |
412 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
413 | + } |
414 | + auto roots = watcher.result(); |
415 | + ASSERT_EQ(1, roots.size()); |
416 | + root = roots[0]; |
417 | + } |
418 | + |
419 | + shared_ptr<File> file; |
420 | + { |
421 | + QFutureWatcher<shared_ptr<Item>> watcher; |
422 | + QSignalSpy spy(&watcher, &decltype(watcher)::finished); |
423 | + watcher.setFuture(root->get("foo.txt")); |
424 | + if (spy.count() == 0) |
425 | + { |
426 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
427 | + } |
428 | + file = dynamic_pointer_cast<File>(watcher.result()); |
429 | + } |
430 | + ASSERT_NE(nullptr, file.get()); |
431 | + |
432 | + shared_ptr<Downloader> downloader; |
433 | + { |
434 | + QFutureWatcher<shared_ptr<Downloader>> watcher; |
435 | + QSignalSpy spy(&watcher, &decltype(watcher)::finished); |
436 | + watcher.setFuture(file->create_downloader()); |
437 | + if (spy.count() == 0) |
438 | + { |
439 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
440 | + } |
441 | + downloader = watcher.result(); |
442 | + } |
443 | + |
444 | + { |
445 | + QFutureWatcher<void> watcher; |
446 | + QSignalSpy spy(&watcher, &decltype(watcher)::finished); |
447 | + watcher.setFuture(downloader->finish_download()); |
448 | + if (spy.count() == 0) |
449 | + { |
450 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
451 | + } |
452 | + |
453 | + try |
454 | + { |
455 | + watcher.waitForFinished(); // to check for errors |
456 | + FAIL(); |
457 | + } |
458 | + catch (LogicException const& e) |
459 | + { |
460 | + EXPECT_EQ("finish called before all data sent", e.error_message()); |
461 | + } |
462 | + } |
463 | +} |
464 | + |
465 | +TEST_F(DavProviderTests, download_not_found) |
466 | +{ |
467 | + auto account = get_client(); |
468 | + make_file("foo.txt"); |
469 | + |
470 | + shared_ptr<Root> root; |
471 | + { |
472 | + QFutureWatcher<QVector<shared_ptr<Root>>> watcher; |
473 | + QSignalSpy spy(&watcher, &decltype(watcher)::finished); |
474 | + watcher.setFuture(account->roots()); |
475 | + if (spy.count() == 0) |
476 | + { |
477 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
478 | + } |
479 | + auto roots = watcher.result(); |
480 | + ASSERT_EQ(1, roots.size()); |
481 | + root = roots[0]; |
482 | + } |
483 | + |
484 | + shared_ptr<File> file; |
485 | + { |
486 | + QFutureWatcher<shared_ptr<Item>> watcher; |
487 | + QSignalSpy spy(&watcher, &decltype(watcher)::finished); |
488 | + watcher.setFuture(root->get("foo.txt")); |
489 | + if (spy.count() == 0) |
490 | + { |
491 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
492 | + } |
493 | + file = dynamic_pointer_cast<File>(watcher.result()); |
494 | + } |
495 | + ASSERT_NE(nullptr, file.get()); |
496 | + |
497 | + ASSERT_EQ(0, unlink(local_file("foo.txt").c_str())); |
498 | + |
499 | + shared_ptr<Downloader> downloader; |
500 | + { |
501 | + QFutureWatcher<shared_ptr<Downloader>> watcher; |
502 | + QSignalSpy spy(&watcher, &decltype(watcher)::finished); |
503 | + watcher.setFuture(file->create_downloader()); |
504 | + if (spy.count() == 0) |
505 | + { |
506 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
507 | + } |
508 | + downloader = watcher.result(); |
509 | + } |
510 | + |
511 | + auto socket = downloader->socket(); |
512 | + QObject::connect(socket.get(), &QIODevice::readyRead, |
513 | + [socket]() { |
514 | + socket->readAll(); |
515 | + }); |
516 | + { |
517 | + QSignalSpy spy(socket.get(), &QIODevice::readChannelFinished); |
518 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
519 | + } |
520 | + |
521 | + { |
522 | + QFutureWatcher<void> watcher; |
523 | + QSignalSpy spy(&watcher, &decltype(watcher)::finished); |
524 | + watcher.setFuture(downloader->finish_download()); |
525 | + if (spy.count() == 0) |
526 | + { |
527 | + ASSERT_TRUE(spy.wait(SIGNAL_WAIT_TIME)); |
528 | + } |
529 | + |
530 | + try |
531 | + { |
532 | + watcher.waitForFinished(); // to check for errors |
533 | + FAIL(); |
534 | + } |
535 | + catch (RemoteCommsException const& e) |
536 | + { |
537 | + EXPECT_EQ("Unexpected status code: 404", e.error_message()); |
538 | + } |
539 | + } |
540 | +} |
541 | + |
542 | int main(int argc, char**argv) |
543 | { |
544 | QCoreApplication app(argc, argv); |
FAILED: Continuous integration, rev:16 /jenkins. canonical. com/unity- api-1/job/ lp-storage- provider- webdav- ci/15/ /jenkins. canonical. com/unity- api-1/job/ build/1023/ console /jenkins. canonical. com/unity- api-1/job/ build-0- fetch/1030 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= vivid+overlay/ 821 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= vivid+overlay/ 821/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 821 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 821/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= yakkety/ 821 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=amd64, release= yakkety/ 821/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= vivid+overlay/ 821/console /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 821/console /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=armhf, release= yakkety/ 821/console /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= vivid+overlay/ 821 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= vivid+overlay/ 821/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 821 /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 821/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/unity- api-1/job/ build-2- binpkg/ arch=i386, release= yakkety/ 821/console
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild: /jenkins. canonical. com/unity- api-1/job/ lp-storage- provider- webdav- ci/15/rebuild
https:/