1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "sync/internal_api/public/attachments/attachment_service_impl.h"
8 #include "base/memory/weak_ptr.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/run_loop.h"
11 #include "base/timer/mock_timer.h"
12 #include "sync/internal_api/public/attachments/attachment_util.h"
13 #include "sync/internal_api/public/attachments/fake_attachment_downloader.h"
14 #include "sync/internal_api/public/attachments/fake_attachment_uploader.h"
15 #include "testing/gmock/include/gmock/gmock-matchers.h"
16 #include "testing/gtest/include/gtest/gtest.h"
22 class MockAttachmentStore
: public AttachmentStore
,
23 public base::SupportsWeakPtr
<MockAttachmentStore
> {
25 MockAttachmentStore() {}
27 void Init(const InitCallback
& callback
) override
{
30 void Read(const AttachmentIdList
& ids
,
31 const ReadCallback
& callback
) override
{
32 read_ids
.push_back(ids
);
33 read_callbacks
.push_back(callback
);
36 void Write(const AttachmentList
& attachments
,
37 const WriteCallback
& callback
) override
{
38 write_attachments
.push_back(attachments
);
39 write_callbacks
.push_back(callback
);
42 void Drop(const AttachmentIdList
& ids
,
43 const DropCallback
& callback
) override
{
47 void ReadMetadata(const AttachmentIdList
& ids
,
48 const ReadMetadataCallback
& callback
) override
{
52 void ReadAllMetadata(const ReadMetadataCallback
& callback
) override
{
56 // Respond to Read request. Attachments found in local_attachments should be
57 // returned, everything else should be reported unavailable.
58 void RespondToRead(const AttachmentIdSet
& local_attachments
) {
59 scoped_refptr
<base::RefCountedString
> data
= new base::RefCountedString();
60 ReadCallback callback
= read_callbacks
.back();
61 AttachmentIdList ids
= read_ids
.back();
62 read_callbacks
.pop_back();
65 scoped_ptr
<AttachmentMap
> attachments(new AttachmentMap());
66 scoped_ptr
<AttachmentIdList
> unavailable_attachments(
67 new AttachmentIdList());
68 for (AttachmentIdList::const_iterator iter
= ids
.begin(); iter
!= ids
.end();
70 if (local_attachments
.find(*iter
) != local_attachments
.end()) {
71 uint32_t crc32c
= ComputeCrc32c(data
);
72 Attachment attachment
=
73 Attachment::CreateFromParts(*iter
, data
, crc32c
);
74 attachments
->insert(std::make_pair(*iter
, attachment
));
76 unavailable_attachments
->push_back(*iter
);
80 unavailable_attachments
->empty() ? SUCCESS
: UNSPECIFIED_ERROR
;
82 base::MessageLoop::current()->PostTask(
86 base::Passed(&attachments
),
87 base::Passed(&unavailable_attachments
)));
90 // Respond to Write request with |result|.
91 void RespondToWrite(const Result
& result
) {
92 WriteCallback callback
= write_callbacks
.back();
93 AttachmentList attachments
= write_attachments
.back();
94 write_callbacks
.pop_back();
95 write_attachments
.pop_back();
96 base::MessageLoop::current()->PostTask(FROM_HERE
,
97 base::Bind(callback
, result
));
100 std::vector
<AttachmentIdList
> read_ids
;
101 std::vector
<ReadCallback
> read_callbacks
;
102 std::vector
<AttachmentList
> write_attachments
;
103 std::vector
<WriteCallback
> write_callbacks
;
106 ~MockAttachmentStore() override
{}
108 DISALLOW_COPY_AND_ASSIGN(MockAttachmentStore
);
111 class MockAttachmentDownloader
112 : public AttachmentDownloader
,
113 public base::SupportsWeakPtr
<MockAttachmentDownloader
> {
115 MockAttachmentDownloader() {}
117 void DownloadAttachment(const AttachmentId
& id
,
118 const DownloadCallback
& callback
) override
{
119 ASSERT_TRUE(download_requests
.find(id
) == download_requests
.end());
120 download_requests
.insert(std::make_pair(id
, callback
));
123 // Multiple requests to download will be active at the same time.
124 // RespondToDownload should respond to only one of them.
125 void RespondToDownload(const AttachmentId
& id
, const DownloadResult
& result
) {
126 ASSERT_TRUE(download_requests
.find(id
) != download_requests
.end());
127 scoped_ptr
<Attachment
> attachment
;
128 if (result
== DOWNLOAD_SUCCESS
) {
129 scoped_refptr
<base::RefCountedString
> data
= new base::RefCountedString();
130 uint32_t crc32c
= ComputeCrc32c(data
);
132 new Attachment(Attachment::CreateFromParts(id
, data
, crc32c
)));
134 base::MessageLoop::current()->PostTask(
136 base::Bind(download_requests
[id
], result
, base::Passed(&attachment
)));
138 download_requests
.erase(id
);
141 std::map
<AttachmentId
, DownloadCallback
> download_requests
;
143 DISALLOW_COPY_AND_ASSIGN(MockAttachmentDownloader
);
146 class MockAttachmentUploader
147 : public AttachmentUploader
,
148 public base::SupportsWeakPtr
<MockAttachmentUploader
> {
150 MockAttachmentUploader() {}
152 // AttachmentUploader implementation.
153 void UploadAttachment(const Attachment
& attachment
,
154 const UploadCallback
& callback
) override
{
155 const AttachmentId id
= attachment
.GetId();
156 ASSERT_TRUE(upload_requests
.find(id
) == upload_requests
.end());
157 upload_requests
.insert(std::make_pair(id
, callback
));
160 void RespondToUpload(const AttachmentId
& id
, const UploadResult
& result
) {
161 ASSERT_TRUE(upload_requests
.find(id
) != upload_requests
.end());
162 base::MessageLoop::current()->PostTask(
163 FROM_HERE
, base::Bind(upload_requests
[id
], result
, id
));
164 upload_requests
.erase(id
);
167 std::map
<AttachmentId
, UploadCallback
> upload_requests
;
169 DISALLOW_COPY_AND_ASSIGN(MockAttachmentUploader
);
174 class AttachmentServiceImplTest
: public testing::Test
,
175 public AttachmentService::Delegate
{
177 AttachmentServiceImplTest() {}
179 void SetUp() override
{
180 network_change_notifier_
.reset(net::NetworkChangeNotifier::CreateMock());
181 InitializeAttachmentService(make_scoped_ptr(new MockAttachmentUploader()),
182 make_scoped_ptr(new MockAttachmentDownloader()),
186 void TearDown() override
{
187 attachment_service_
.reset();
188 ASSERT_FALSE(attachment_store_
);
189 ASSERT_FALSE(attachment_uploader_
);
190 ASSERT_FALSE(attachment_downloader_
);
193 // AttachmentService::Delegate implementation.
194 void OnAttachmentUploaded(const AttachmentId
& attachment_id
) override
{
195 on_attachment_uploaded_list_
.push_back(attachment_id
);
198 void InitializeAttachmentService(
199 scoped_ptr
<MockAttachmentUploader
> uploader
,
200 scoped_ptr
<MockAttachmentDownloader
> downloader
,
201 AttachmentService::Delegate
* delegate
) {
202 scoped_refptr
<MockAttachmentStore
> attachment_store(
203 new MockAttachmentStore());
204 attachment_store_
= attachment_store
->AsWeakPtr();
206 if (uploader
.get()) {
207 attachment_uploader_
= uploader
->AsWeakPtr();
209 if (downloader
.get()) {
210 attachment_downloader_
= downloader
->AsWeakPtr();
212 attachment_service_
.reset(
213 new AttachmentServiceImpl(attachment_store
,
217 base::TimeDelta::FromMinutes(1),
218 base::TimeDelta::FromMinutes(8)));
220 scoped_ptr
<base::MockTimer
> timer_to_pass(
221 new base::MockTimer(false, false));
222 mock_timer_
= timer_to_pass
.get();
223 attachment_service_
->SetTimerForTest(timer_to_pass
.Pass());
226 AttachmentService
* attachment_service() { return attachment_service_
.get(); }
228 base::MockTimer
* mock_timer() { return mock_timer_
; }
230 AttachmentService::GetOrDownloadCallback
download_callback() {
231 return base::Bind(&AttachmentServiceImplTest::DownloadDone
,
232 base::Unretained(this));
235 void DownloadDone(const AttachmentService::GetOrDownloadResult
& result
,
236 scoped_ptr
<AttachmentMap
> attachments
) {
237 download_results_
.push_back(result
);
238 last_download_attachments_
= attachments
.Pass();
242 base::RunLoop run_loop
;
243 run_loop
.RunUntilIdle();
246 void RunLoopAndFireTimer() {
248 if (mock_timer()->IsRunning()) {
249 mock_timer()->Fire();
254 const std::vector
<AttachmentService::GetOrDownloadResult
>&
255 download_results() const {
256 return download_results_
;
259 const AttachmentMap
& last_download_attachments() const {
260 return *last_download_attachments_
.get();
263 net::NetworkChangeNotifier
* network_change_notifier() {
264 return network_change_notifier_
.get();
267 MockAttachmentStore
* store() { return attachment_store_
.get(); }
269 MockAttachmentDownloader
* downloader() {
270 return attachment_downloader_
.get();
273 MockAttachmentUploader
* uploader() {
274 return attachment_uploader_
.get();
277 const std::vector
<AttachmentId
>& on_attachment_uploaded_list() const {
278 return on_attachment_uploaded_list_
;
282 base::MessageLoop message_loop_
;
283 scoped_ptr
<net::NetworkChangeNotifier
> network_change_notifier_
;
284 base::WeakPtr
<MockAttachmentStore
> attachment_store_
;
285 base::WeakPtr
<MockAttachmentDownloader
> attachment_downloader_
;
286 base::WeakPtr
<MockAttachmentUploader
> attachment_uploader_
;
287 scoped_ptr
<AttachmentServiceImpl
> attachment_service_
;
288 base::MockTimer
* mock_timer_
; // not owned
290 std::vector
<AttachmentService::GetOrDownloadResult
> download_results_
;
291 scoped_ptr
<AttachmentMap
> last_download_attachments_
;
292 std::vector
<AttachmentId
> on_attachment_uploaded_list_
;
295 TEST_F(AttachmentServiceImplTest
, GetStore
) {
296 EXPECT_EQ(store(), attachment_service()->GetStore());
299 TEST_F(AttachmentServiceImplTest
, GetOrDownload_EmptyAttachmentList
) {
300 AttachmentIdList attachment_ids
;
301 attachment_service()->GetOrDownloadAttachments(attachment_ids
,
302 download_callback());
303 store()->RespondToRead(AttachmentIdSet());
306 EXPECT_EQ(1U, download_results().size());
307 EXPECT_EQ(0U, last_download_attachments().size());
310 TEST_F(AttachmentServiceImplTest
, GetOrDownload_Local
) {
311 AttachmentIdList attachment_ids
;
312 attachment_ids
.push_back(AttachmentId::Create());
313 attachment_service()->GetOrDownloadAttachments(attachment_ids
,
314 download_callback());
315 AttachmentIdSet local_attachments
;
316 local_attachments
.insert(attachment_ids
[0]);
317 store()->RespondToRead(local_attachments
);
320 EXPECT_EQ(1U, download_results().size());
321 EXPECT_EQ(1U, last_download_attachments().size());
322 EXPECT_TRUE(last_download_attachments().find(attachment_ids
[0]) !=
323 last_download_attachments().end());
326 TEST_F(AttachmentServiceImplTest
, GetOrDownload_LocalRemoteUnavailable
) {
327 // Create attachment list with 4 ids.
328 AttachmentIdList attachment_ids
;
329 attachment_ids
.push_back(AttachmentId::Create());
330 attachment_ids
.push_back(AttachmentId::Create());
331 attachment_ids
.push_back(AttachmentId::Create());
332 attachment_ids
.push_back(AttachmentId::Create());
333 // Call attachment service.
334 attachment_service()->GetOrDownloadAttachments(attachment_ids
,
335 download_callback());
336 // Ensure AttachmentStore is called.
337 EXPECT_FALSE(store()->read_ids
.empty());
339 // Make AttachmentStore return only attachment 0.
340 AttachmentIdSet local_attachments
;
341 local_attachments
.insert(attachment_ids
[0]);
342 store()->RespondToRead(local_attachments
);
344 // Ensure Downloader called with right attachment ids
345 EXPECT_EQ(3U, downloader()->download_requests
.size());
347 // Make downloader return attachment 1.
348 downloader()->RespondToDownload(attachment_ids
[1],
349 AttachmentDownloader::DOWNLOAD_SUCCESS
);
351 // Ensure consumer callback is not called.
352 EXPECT_TRUE(download_results().empty());
353 // Make AttachmentStore acknowledge writing attachment 1.
354 store()->RespondToWrite(AttachmentStore::SUCCESS
);
356 // Ensure consumer callback is not called.
357 EXPECT_TRUE(download_results().empty());
359 // Make downloader return attachment 2.
360 downloader()->RespondToDownload(attachment_ids
[2],
361 AttachmentDownloader::DOWNLOAD_SUCCESS
);
363 // Ensure consumer callback is not called.
364 EXPECT_TRUE(download_results().empty());
365 // Make AttachmentStore fail writing attachment 2.
366 store()->RespondToWrite(AttachmentStore::UNSPECIFIED_ERROR
);
368 // Ensure consumer callback is not called.
369 EXPECT_TRUE(download_results().empty());
371 // Make downloader fail attachment 3.
372 downloader()->RespondToDownload(
373 attachment_ids
[3], AttachmentDownloader::DOWNLOAD_UNSPECIFIED_ERROR
);
376 // Ensure callback is called
377 EXPECT_FALSE(download_results().empty());
378 // There should be only two attachments returned, 0 and 1.
379 EXPECT_EQ(2U, last_download_attachments().size());
380 EXPECT_TRUE(last_download_attachments().find(attachment_ids
[0]) !=
381 last_download_attachments().end());
382 EXPECT_TRUE(last_download_attachments().find(attachment_ids
[1]) !=
383 last_download_attachments().end());
384 EXPECT_TRUE(last_download_attachments().find(attachment_ids
[2]) ==
385 last_download_attachments().end());
386 EXPECT_TRUE(last_download_attachments().find(attachment_ids
[3]) ==
387 last_download_attachments().end());
390 TEST_F(AttachmentServiceImplTest
, GetOrDownload_NoDownloader
) {
392 InitializeAttachmentService(
393 make_scoped_ptr
<MockAttachmentUploader
>(new MockAttachmentUploader()),
394 make_scoped_ptr
<MockAttachmentDownloader
>(NULL
),
397 AttachmentIdList attachment_ids
;
398 attachment_ids
.push_back(AttachmentId::Create());
399 attachment_service()->GetOrDownloadAttachments(attachment_ids
,
400 download_callback());
401 EXPECT_FALSE(store()->read_ids
.empty());
403 AttachmentIdSet local_attachments
;
404 store()->RespondToRead(local_attachments
);
406 ASSERT_EQ(1U, download_results().size());
407 EXPECT_EQ(AttachmentService::GET_UNSPECIFIED_ERROR
, download_results()[0]);
408 EXPECT_TRUE(last_download_attachments().empty());
411 TEST_F(AttachmentServiceImplTest
, UploadAttachments_Success
) {
412 AttachmentIdSet attachment_ids
;
413 const unsigned num_attachments
= 3;
414 for (unsigned i
= 0; i
< num_attachments
; ++i
) {
415 attachment_ids
.insert(AttachmentId::Create());
417 attachment_service()->UploadAttachments(attachment_ids
);
419 for (unsigned i
= 0; i
< num_attachments
; ++i
) {
420 RunLoopAndFireTimer();
421 // See that the service has issued a read for at least one of the
423 ASSERT_GE(store()->read_ids
.size(), 1U);
424 store()->RespondToRead(attachment_ids
);
426 ASSERT_GE(uploader()->upload_requests
.size(), 1U);
427 uploader()->RespondToUpload(uploader()->upload_requests
.begin()->first
,
428 AttachmentUploader::UPLOAD_SUCCESS
);
431 ASSERT_EQ(0U, store()->read_ids
.size());
432 ASSERT_EQ(0U, uploader()->upload_requests
.size());
434 // See that all the attachments were uploaded.
435 ASSERT_EQ(attachment_ids
.size(), on_attachment_uploaded_list().size());
436 AttachmentIdSet::const_iterator iter
= attachment_ids
.begin();
437 const AttachmentIdSet::const_iterator end
= attachment_ids
.end();
438 for (iter
= attachment_ids
.begin(); iter
!= end
; ++iter
) {
439 EXPECT_THAT(on_attachment_uploaded_list(), testing::Contains(*iter
));
443 TEST_F(AttachmentServiceImplTest
, UploadAttachments_Success_NoDelegate
) {
444 InitializeAttachmentService(make_scoped_ptr(new MockAttachmentUploader()),
445 make_scoped_ptr(new MockAttachmentDownloader()),
446 NULL
); // No delegate.
448 AttachmentIdSet attachment_ids
;
449 attachment_ids
.insert(AttachmentId::Create());
450 attachment_service()->UploadAttachments(attachment_ids
);
451 RunLoopAndFireTimer();
452 ASSERT_EQ(1U, store()->read_ids
.size());
453 ASSERT_EQ(0U, uploader()->upload_requests
.size());
454 store()->RespondToRead(attachment_ids
);
456 ASSERT_EQ(0U, store()->read_ids
.size());
457 ASSERT_EQ(1U, uploader()->upload_requests
.size());
458 uploader()->RespondToUpload(*attachment_ids
.begin(),
459 AttachmentUploader::UPLOAD_SUCCESS
);
461 ASSERT_TRUE(on_attachment_uploaded_list().empty());
464 TEST_F(AttachmentServiceImplTest
, UploadAttachments_SomeMissingFromStore
) {
465 AttachmentIdSet attachment_ids
;
466 attachment_ids
.insert(AttachmentId::Create());
467 attachment_ids
.insert(AttachmentId::Create());
468 attachment_service()->UploadAttachments(attachment_ids
);
469 RunLoopAndFireTimer();
470 ASSERT_GE(store()->read_ids
.size(), 1U);
472 ASSERT_EQ(0U, uploader()->upload_requests
.size());
473 store()->RespondToRead(attachment_ids
);
475 ASSERT_EQ(1U, uploader()->upload_requests
.size());
477 uploader()->RespondToUpload(uploader()->upload_requests
.begin()->first
,
478 AttachmentUploader::UPLOAD_SUCCESS
);
479 RunLoopAndFireTimer();
480 ASSERT_EQ(1U, on_attachment_uploaded_list().size());
481 ASSERT_GE(store()->read_ids
.size(), 1U);
483 store()->RespondToRead(AttachmentIdSet());
485 // No upload requests since the read failed.
486 ASSERT_EQ(0U, uploader()->upload_requests
.size());
489 TEST_F(AttachmentServiceImplTest
, UploadAttachments_AllMissingFromStore
) {
490 AttachmentIdSet attachment_ids
;
491 const unsigned num_attachments
= 2;
492 for (unsigned i
= 0; i
< num_attachments
; ++i
) {
493 attachment_ids
.insert(AttachmentId::Create());
495 attachment_service()->UploadAttachments(attachment_ids
);
497 for (unsigned i
= 0; i
< num_attachments
; ++i
) {
498 RunLoopAndFireTimer();
499 ASSERT_GE(store()->read_ids
.size(), 1U);
501 store()->RespondToRead(AttachmentIdSet());
506 EXPECT_EQ(0U, uploader()->upload_requests
.size());
507 // See that the delegate was never called.
508 ASSERT_EQ(0U, on_attachment_uploaded_list().size());
511 TEST_F(AttachmentServiceImplTest
, UploadAttachments_NoUploader
) {
512 InitializeAttachmentService(make_scoped_ptr
<MockAttachmentUploader
>(NULL
),
513 make_scoped_ptr(new MockAttachmentDownloader()),
516 AttachmentIdSet attachment_ids
;
517 attachment_ids
.insert(AttachmentId::Create());
518 attachment_service()->UploadAttachments(attachment_ids
);
520 EXPECT_EQ(0U, store()->read_ids
.size());
521 ASSERT_EQ(0U, on_attachment_uploaded_list().size());
524 // Upload three attachments. For one of them, server responds with error.
525 TEST_F(AttachmentServiceImplTest
, UploadAttachments_OneUploadFails
) {
526 AttachmentIdSet attachment_ids
;
527 const unsigned num_attachments
= 3;
528 for (unsigned i
= 0; i
< num_attachments
; ++i
) {
529 attachment_ids
.insert(AttachmentId::Create());
531 attachment_service()->UploadAttachments(attachment_ids
);
533 for (unsigned i
= 0; i
< 3; ++i
) {
534 RunLoopAndFireTimer();
535 ASSERT_GE(store()->read_ids
.size(), 1U);
536 store()->RespondToRead(attachment_ids
);
538 ASSERT_EQ(1U, uploader()->upload_requests
.size());
539 AttachmentUploader::UploadResult result
=
540 AttachmentUploader::UPLOAD_SUCCESS
;
543 result
= AttachmentUploader::UPLOAD_UNSPECIFIED_ERROR
;
545 result
= AttachmentUploader::UPLOAD_SUCCESS
;
547 uploader()->RespondToUpload(uploader()->upload_requests
.begin()->first
,
551 ASSERT_EQ(2U, on_attachment_uploaded_list().size());
554 // Attempt an upload, respond with transient error to trigger backoff, issue
555 // network disconnect/connect events and see that backoff is cleared.
556 TEST_F(AttachmentServiceImplTest
,
557 UploadAttachments_ResetBackoffAfterNetworkChange
) {
558 AttachmentIdSet attachment_ids
;
559 attachment_ids
.insert(AttachmentId::Create());
560 attachment_service()->UploadAttachments(attachment_ids
);
562 RunLoopAndFireTimer();
563 ASSERT_EQ(1U, store()->read_ids
.size());
564 store()->RespondToRead(attachment_ids
);
566 ASSERT_EQ(1U, uploader()->upload_requests
.size());
568 uploader()->RespondToUpload(uploader()->upload_requests
.begin()->first
,
569 AttachmentUploader::UPLOAD_TRANSIENT_ERROR
);
572 // See that we are in backoff.
573 ASSERT_TRUE(mock_timer()->IsRunning());
574 ASSERT_GT(mock_timer()->GetCurrentDelay(), base::TimeDelta());
576 // Issue a network disconnect event.
577 network_change_notifier()->NotifyObserversOfNetworkChangeForTests(
578 net::NetworkChangeNotifier::CONNECTION_NONE
);
582 ASSERT_TRUE(mock_timer()->IsRunning());
583 ASSERT_GT(mock_timer()->GetCurrentDelay(), base::TimeDelta());
585 // Issue a network connect event.
586 net::NetworkChangeNotifier::NotifyObserversOfNetworkChangeForTests(
587 net::NetworkChangeNotifier::CONNECTION_WIFI
);
590 // No longer in backoff.
591 ASSERT_TRUE(mock_timer()->IsRunning());
592 ASSERT_EQ(base::TimeDelta(), mock_timer()->GetCurrentDelay());
595 } // namespace syncer