Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / sync / internal_api / attachments / attachment_service_impl_unittest.cc
blob0ca4007581b9ccafd43e39feff725a364ac66cf2
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"
7 #include "base/bind.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/thread_task_runner_handle.h"
12 #include "base/timer/mock_timer.h"
13 #include "sync/api/attachments/attachment_store_backend.h"
14 #include "sync/internal_api/public/attachments/attachment_util.h"
15 #include "sync/internal_api/public/attachments/fake_attachment_downloader.h"
16 #include "sync/internal_api/public/attachments/fake_attachment_uploader.h"
17 #include "testing/gmock/include/gmock/gmock-matchers.h"
18 #include "testing/gtest/include/gtest/gtest.h"
20 namespace syncer {
22 namespace {
24 class MockAttachmentStoreBackend
25 : public AttachmentStoreBackend,
26 public base::SupportsWeakPtr<MockAttachmentStoreBackend> {
27 public:
28 MockAttachmentStoreBackend(
29 const scoped_refptr<base::SequencedTaskRunner>& callback_task_runner)
30 : AttachmentStoreBackend(callback_task_runner) {}
32 ~MockAttachmentStoreBackend() override {}
34 void Init(const AttachmentStore::InitCallback& callback) override {}
36 void Read(AttachmentStore::Component component,
37 const AttachmentIdList& ids,
38 const AttachmentStore::ReadCallback& callback) override {
39 read_ids.push_back(ids);
40 read_callbacks.push_back(callback);
43 void Write(AttachmentStore::Component component,
44 const AttachmentList& attachments,
45 const AttachmentStore::WriteCallback& callback) override {
46 write_attachments.push_back(attachments);
47 write_callbacks.push_back(callback);
50 void SetReference(AttachmentStore::Component component,
51 const AttachmentIdList& ids) override {
52 set_reference_ids.push_back(std::make_pair(component, ids));
55 void DropReference(AttachmentStore::Component component,
56 const AttachmentIdList& ids,
57 const AttachmentStore::DropCallback& callback) override {
58 ASSERT_EQ(AttachmentStore::SYNC, component);
59 drop_ids.push_back(ids);
62 void ReadMetadataById(
63 AttachmentStore::Component component,
64 const AttachmentIdList& ids,
65 const AttachmentStore::ReadMetadataCallback& callback) override {
66 NOTREACHED();
69 void ReadMetadata(
70 AttachmentStore::Component component,
71 const AttachmentStore::ReadMetadataCallback& callback) override {
72 NOTREACHED();
75 // Respond to Read request. Attachments found in local_attachments should be
76 // returned, everything else should be reported unavailable.
77 void RespondToRead(const AttachmentIdSet& local_attachments) {
78 scoped_refptr<base::RefCountedString> data = new base::RefCountedString();
79 AttachmentStore::ReadCallback callback = read_callbacks.back();
80 AttachmentIdList ids = read_ids.back();
81 read_callbacks.pop_back();
82 read_ids.pop_back();
84 scoped_ptr<AttachmentMap> attachments(new AttachmentMap());
85 scoped_ptr<AttachmentIdList> unavailable_attachments(
86 new AttachmentIdList());
87 for (AttachmentIdList::const_iterator iter = ids.begin(); iter != ids.end();
88 ++iter) {
89 if (local_attachments.find(*iter) != local_attachments.end()) {
90 Attachment attachment = Attachment::CreateFromParts(*iter, data);
91 attachments->insert(std::make_pair(*iter, attachment));
92 } else {
93 unavailable_attachments->push_back(*iter);
96 AttachmentStore::Result result = unavailable_attachments->empty()
97 ? AttachmentStore::SUCCESS
98 : AttachmentStore::UNSPECIFIED_ERROR;
100 base::MessageLoop::current()->PostTask(
101 FROM_HERE,
102 base::Bind(callback,
103 result,
104 base::Passed(&attachments),
105 base::Passed(&unavailable_attachments)));
108 // Respond to Write request with |result|.
109 void RespondToWrite(const AttachmentStore::Result& result) {
110 AttachmentStore::WriteCallback callback = write_callbacks.back();
111 write_callbacks.pop_back();
112 write_attachments.pop_back();
113 base::MessageLoop::current()->PostTask(FROM_HERE,
114 base::Bind(callback, result));
117 std::vector<AttachmentIdList> read_ids;
118 std::vector<AttachmentStore::ReadCallback> read_callbacks;
119 std::vector<AttachmentList> write_attachments;
120 std::vector<AttachmentStore::WriteCallback> write_callbacks;
121 std::vector<std::pair<AttachmentStore::Component, AttachmentIdList>>
122 set_reference_ids;
123 std::vector<AttachmentIdList> drop_ids;
125 private:
126 DISALLOW_COPY_AND_ASSIGN(MockAttachmentStoreBackend);
129 class MockAttachmentDownloader
130 : public AttachmentDownloader,
131 public base::SupportsWeakPtr<MockAttachmentDownloader> {
132 public:
133 MockAttachmentDownloader() {}
135 void DownloadAttachment(const AttachmentId& id,
136 const DownloadCallback& callback) override {
137 ASSERT_TRUE(download_requests.find(id) == download_requests.end());
138 download_requests.insert(std::make_pair(id, callback));
141 // Multiple requests to download will be active at the same time.
142 // RespondToDownload should respond to only one of them.
143 void RespondToDownload(const AttachmentId& id, const DownloadResult& result) {
144 ASSERT_TRUE(download_requests.find(id) != download_requests.end());
145 scoped_ptr<Attachment> attachment;
146 if (result == DOWNLOAD_SUCCESS) {
147 scoped_refptr<base::RefCountedString> data = new base::RefCountedString();
148 attachment.reset(new Attachment(Attachment::CreateFromParts(id, data)));
150 base::MessageLoop::current()->PostTask(
151 FROM_HERE,
152 base::Bind(download_requests[id], result, base::Passed(&attachment)));
154 download_requests.erase(id);
157 std::map<AttachmentId, DownloadCallback> download_requests;
159 DISALLOW_COPY_AND_ASSIGN(MockAttachmentDownloader);
162 class MockAttachmentUploader
163 : public AttachmentUploader,
164 public base::SupportsWeakPtr<MockAttachmentUploader> {
165 public:
166 MockAttachmentUploader() {}
168 // AttachmentUploader implementation.
169 void UploadAttachment(const Attachment& attachment,
170 const UploadCallback& callback) override {
171 const AttachmentId id = attachment.GetId();
172 ASSERT_TRUE(upload_requests.find(id) == upload_requests.end());
173 upload_requests.insert(std::make_pair(id, callback));
176 void RespondToUpload(const AttachmentId& id, const UploadResult& result) {
177 ASSERT_TRUE(upload_requests.find(id) != upload_requests.end());
178 base::MessageLoop::current()->PostTask(
179 FROM_HERE, base::Bind(upload_requests[id], result, id));
180 upload_requests.erase(id);
183 std::map<AttachmentId, UploadCallback> upload_requests;
185 DISALLOW_COPY_AND_ASSIGN(MockAttachmentUploader);
188 } // namespace
190 class AttachmentServiceImplTest : public testing::Test,
191 public AttachmentService::Delegate {
192 protected:
193 AttachmentServiceImplTest() {}
195 void SetUp() override {
196 network_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock());
197 InitializeAttachmentService(make_scoped_ptr(new MockAttachmentUploader()),
198 make_scoped_ptr(new MockAttachmentDownloader()),
199 this);
202 void TearDown() override {
203 attachment_service_.reset();
204 RunLoop();
205 ASSERT_FALSE(attachment_store_backend_);
206 ASSERT_FALSE(attachment_uploader_);
207 ASSERT_FALSE(attachment_downloader_);
210 // AttachmentService::Delegate implementation.
211 void OnAttachmentUploaded(const AttachmentId& attachment_id) override {
212 on_attachment_uploaded_list_.push_back(attachment_id);
215 void InitializeAttachmentService(
216 scoped_ptr<MockAttachmentUploader> uploader,
217 scoped_ptr<MockAttachmentDownloader> downloader,
218 AttachmentService::Delegate* delegate) {
219 // Initialize mock attachment store
220 scoped_refptr<base::SingleThreadTaskRunner> runner =
221 base::ThreadTaskRunnerHandle::Get();
222 scoped_ptr<MockAttachmentStoreBackend> attachment_store_backend(
223 new MockAttachmentStoreBackend(runner));
224 attachment_store_backend_ = attachment_store_backend->AsWeakPtr();
225 scoped_ptr<AttachmentStore> attachment_store =
226 AttachmentStore::CreateMockStoreForTest(
227 attachment_store_backend.Pass());
229 if (uploader.get()) {
230 attachment_uploader_ = uploader->AsWeakPtr();
232 if (downloader.get()) {
233 attachment_downloader_ = downloader->AsWeakPtr();
235 attachment_service_.reset(new AttachmentServiceImpl(
236 attachment_store->CreateAttachmentStoreForSync(), uploader.Pass(),
237 downloader.Pass(), delegate, base::TimeDelta::FromMinutes(1),
238 base::TimeDelta::FromMinutes(8)));
240 scoped_ptr<base::MockTimer> timer_to_pass(
241 new base::MockTimer(false, false));
242 mock_timer_ = timer_to_pass.get();
243 attachment_service_->SetTimerForTest(timer_to_pass.Pass());
246 AttachmentService* attachment_service() { return attachment_service_.get(); }
248 base::MockTimer* mock_timer() { return mock_timer_; }
250 AttachmentService::GetOrDownloadCallback download_callback() {
251 return base::Bind(&AttachmentServiceImplTest::DownloadDone,
252 base::Unretained(this));
255 void DownloadDone(const AttachmentService::GetOrDownloadResult& result,
256 scoped_ptr<AttachmentMap> attachments) {
257 download_results_.push_back(result);
258 last_download_attachments_ = attachments.Pass();
261 void RunLoop() {
262 base::RunLoop run_loop;
263 run_loop.RunUntilIdle();
266 void RunLoopAndFireTimer() {
267 RunLoop();
268 if (mock_timer()->IsRunning()) {
269 mock_timer()->Fire();
270 RunLoop();
274 static AttachmentIdSet AttachmentIdSetFromList(
275 const AttachmentIdList& id_list) {
276 AttachmentIdSet id_set;
277 std::copy(id_list.begin(), id_list.end(),
278 std::inserter(id_set, id_set.end()));
279 return id_set;
282 const std::vector<AttachmentService::GetOrDownloadResult>&
283 download_results() const {
284 return download_results_;
287 const AttachmentMap& last_download_attachments() const {
288 return *last_download_attachments_.get();
291 net::NetworkChangeNotifier* network_change_notifier() {
292 return network_change_notifier_.get();
295 MockAttachmentStoreBackend* store() {
296 return attachment_store_backend_.get();
299 MockAttachmentDownloader* downloader() {
300 return attachment_downloader_.get();
303 MockAttachmentUploader* uploader() {
304 return attachment_uploader_.get();
307 const std::vector<AttachmentId>& on_attachment_uploaded_list() const {
308 return on_attachment_uploaded_list_;
311 private:
312 base::MessageLoop message_loop_;
313 scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
314 base::WeakPtr<MockAttachmentStoreBackend> attachment_store_backend_;
315 base::WeakPtr<MockAttachmentDownloader> attachment_downloader_;
316 base::WeakPtr<MockAttachmentUploader> attachment_uploader_;
317 scoped_ptr<AttachmentServiceImpl> attachment_service_;
318 base::MockTimer* mock_timer_; // not owned
320 std::vector<AttachmentService::GetOrDownloadResult> download_results_;
321 scoped_ptr<AttachmentMap> last_download_attachments_;
322 std::vector<AttachmentId> on_attachment_uploaded_list_;
325 TEST_F(AttachmentServiceImplTest, GetOrDownload_EmptyAttachmentList) {
326 AttachmentIdList attachment_ids;
327 attachment_service()->GetOrDownloadAttachments(attachment_ids,
328 download_callback());
329 RunLoop();
330 store()->RespondToRead(AttachmentIdSet());
332 RunLoop();
333 EXPECT_EQ(1U, download_results().size());
334 EXPECT_EQ(0U, last_download_attachments().size());
337 TEST_F(AttachmentServiceImplTest, GetOrDownload_Local) {
338 AttachmentIdList attachment_ids;
339 attachment_ids.push_back(AttachmentId::Create(0, 0));
340 attachment_service()->GetOrDownloadAttachments(attachment_ids,
341 download_callback());
342 AttachmentIdSet local_attachments;
343 local_attachments.insert(attachment_ids[0]);
344 RunLoop();
345 EXPECT_EQ(1U, store()->set_reference_ids.size());
346 EXPECT_EQ(AttachmentStore::MODEL_TYPE, store()->set_reference_ids[0].first);
347 store()->RespondToRead(local_attachments);
349 RunLoop();
350 EXPECT_EQ(1U, download_results().size());
351 EXPECT_EQ(1U, last_download_attachments().size());
352 EXPECT_TRUE(last_download_attachments().find(attachment_ids[0]) !=
353 last_download_attachments().end());
356 TEST_F(AttachmentServiceImplTest, GetOrDownload_LocalRemoteUnavailable) {
357 // Create attachment list with 4 ids.
358 AttachmentIdList attachment_ids;
359 attachment_ids.push_back(AttachmentId::Create(0, 0));
360 attachment_ids.push_back(AttachmentId::Create(0, 0));
361 attachment_ids.push_back(AttachmentId::Create(0, 0));
362 attachment_ids.push_back(AttachmentId::Create(0, 0));
363 // Call attachment service.
364 attachment_service()->GetOrDownloadAttachments(attachment_ids,
365 download_callback());
366 RunLoop();
367 // Ensure AttachmentStore is called.
368 EXPECT_FALSE(store()->read_ids.empty());
370 // Make AttachmentStore return only attachment 0.
371 AttachmentIdSet local_attachments;
372 local_attachments.insert(attachment_ids[0]);
373 store()->RespondToRead(local_attachments);
374 RunLoop();
375 // Ensure Downloader called with right attachment ids
376 EXPECT_EQ(3U, downloader()->download_requests.size());
378 // Make downloader return attachment 1.
379 downloader()->RespondToDownload(attachment_ids[1],
380 AttachmentDownloader::DOWNLOAD_SUCCESS);
381 RunLoop();
382 // Ensure consumer callback is not called.
383 EXPECT_TRUE(download_results().empty());
384 // Make AttachmentStore acknowledge writing attachment 1.
385 store()->RespondToWrite(AttachmentStore::SUCCESS);
386 RunLoop();
387 // Ensure consumer callback is not called.
388 EXPECT_TRUE(download_results().empty());
390 // Make downloader return attachment 2.
391 downloader()->RespondToDownload(attachment_ids[2],
392 AttachmentDownloader::DOWNLOAD_SUCCESS);
393 RunLoop();
394 // Ensure consumer callback is not called.
395 EXPECT_TRUE(download_results().empty());
396 // Make AttachmentStore fail writing attachment 2.
397 store()->RespondToWrite(AttachmentStore::UNSPECIFIED_ERROR);
398 RunLoop();
399 // Ensure consumer callback is not called.
400 EXPECT_TRUE(download_results().empty());
402 // Make downloader fail attachment 3.
403 downloader()->RespondToDownload(
404 attachment_ids[3], AttachmentDownloader::DOWNLOAD_UNSPECIFIED_ERROR);
405 RunLoop();
407 // Ensure callback is called
408 EXPECT_FALSE(download_results().empty());
409 // There should be only two attachments returned, 0 and 1.
410 EXPECT_EQ(2U, last_download_attachments().size());
411 EXPECT_TRUE(last_download_attachments().find(attachment_ids[0]) !=
412 last_download_attachments().end());
413 EXPECT_TRUE(last_download_attachments().find(attachment_ids[1]) !=
414 last_download_attachments().end());
415 EXPECT_TRUE(last_download_attachments().find(attachment_ids[2]) ==
416 last_download_attachments().end());
417 EXPECT_TRUE(last_download_attachments().find(attachment_ids[3]) ==
418 last_download_attachments().end());
421 TEST_F(AttachmentServiceImplTest, GetOrDownload_NoDownloader) {
422 // No downloader.
423 InitializeAttachmentService(
424 make_scoped_ptr<MockAttachmentUploader>(new MockAttachmentUploader()),
425 make_scoped_ptr<MockAttachmentDownloader>(NULL),
426 this);
428 AttachmentIdList attachment_ids;
429 attachment_ids.push_back(AttachmentId::Create(0, 0));
430 attachment_service()->GetOrDownloadAttachments(attachment_ids,
431 download_callback());
432 RunLoop();
433 EXPECT_FALSE(store()->read_ids.empty());
435 AttachmentIdSet local_attachments;
436 store()->RespondToRead(local_attachments);
437 RunLoop();
438 ASSERT_EQ(1U, download_results().size());
439 EXPECT_EQ(AttachmentService::GET_UNSPECIFIED_ERROR, download_results()[0]);
440 EXPECT_TRUE(last_download_attachments().empty());
443 TEST_F(AttachmentServiceImplTest, UploadAttachments_Success) {
444 AttachmentIdList attachment_ids;
445 const unsigned num_attachments = 3;
446 for (unsigned i = 0; i < num_attachments; ++i) {
447 attachment_ids.push_back(AttachmentId::Create(0, 0));
449 attachment_service()->UploadAttachments(attachment_ids);
450 RunLoop();
451 ASSERT_EQ(1U, store()->set_reference_ids.size());
452 EXPECT_EQ(AttachmentStore::SYNC, store()->set_reference_ids[0].first);
453 for (unsigned i = 0; i < num_attachments; ++i) {
454 RunLoopAndFireTimer();
455 // See that the service has issued a read for at least one of the
456 // attachments.
457 ASSERT_GE(store()->read_ids.size(), 1U);
458 store()->RespondToRead(AttachmentIdSetFromList(attachment_ids));
459 RunLoop();
460 ASSERT_GE(uploader()->upload_requests.size(), 1U);
461 uploader()->RespondToUpload(uploader()->upload_requests.begin()->first,
462 AttachmentUploader::UPLOAD_SUCCESS);
464 RunLoop();
465 ASSERT_EQ(0U, store()->read_ids.size());
466 ASSERT_EQ(0U, uploader()->upload_requests.size());
468 // See that all the attachments were uploaded.
469 ASSERT_EQ(attachment_ids.size(), on_attachment_uploaded_list().size());
470 for (auto iter = attachment_ids.begin(); iter != attachment_ids.end();
471 ++iter) {
472 EXPECT_THAT(on_attachment_uploaded_list(), testing::Contains(*iter));
474 EXPECT_EQ(num_attachments, store()->drop_ids.size());
477 TEST_F(AttachmentServiceImplTest, UploadAttachments_Success_NoDelegate) {
478 InitializeAttachmentService(make_scoped_ptr(new MockAttachmentUploader()),
479 make_scoped_ptr(new MockAttachmentDownloader()),
480 NULL); // No delegate.
482 AttachmentIdList attachment_ids;
483 attachment_ids.push_back(AttachmentId::Create(0, 0));
484 attachment_service()->UploadAttachments(attachment_ids);
485 RunLoopAndFireTimer();
486 ASSERT_EQ(1U, store()->read_ids.size());
487 ASSERT_EQ(0U, uploader()->upload_requests.size());
488 store()->RespondToRead(AttachmentIdSetFromList(attachment_ids));
489 RunLoop();
490 ASSERT_EQ(0U, store()->read_ids.size());
491 ASSERT_EQ(1U, uploader()->upload_requests.size());
492 uploader()->RespondToUpload(*attachment_ids.begin(),
493 AttachmentUploader::UPLOAD_SUCCESS);
494 RunLoop();
495 ASSERT_TRUE(on_attachment_uploaded_list().empty());
498 TEST_F(AttachmentServiceImplTest, UploadAttachments_SomeMissingFromStore) {
499 AttachmentIdList attachment_ids;
500 attachment_ids.push_back(AttachmentId::Create(0, 0));
501 attachment_ids.push_back(AttachmentId::Create(0, 0));
502 attachment_service()->UploadAttachments(attachment_ids);
503 RunLoopAndFireTimer();
504 ASSERT_GE(store()->read_ids.size(), 1U);
506 ASSERT_EQ(0U, uploader()->upload_requests.size());
507 store()->RespondToRead(AttachmentIdSetFromList(attachment_ids));
508 RunLoop();
509 ASSERT_EQ(1U, uploader()->upload_requests.size());
511 uploader()->RespondToUpload(uploader()->upload_requests.begin()->first,
512 AttachmentUploader::UPLOAD_SUCCESS);
513 RunLoopAndFireTimer();
514 ASSERT_EQ(1U, on_attachment_uploaded_list().size());
515 ASSERT_GE(store()->read_ids.size(), 1U);
516 // Not found!
517 store()->RespondToRead(AttachmentIdSet());
518 RunLoop();
519 // No upload requests since the read failed.
520 ASSERT_EQ(0U, uploader()->upload_requests.size());
521 EXPECT_EQ(attachment_ids.size(), store()->drop_ids.size());
524 TEST_F(AttachmentServiceImplTest, UploadAttachments_AllMissingFromStore) {
525 AttachmentIdList attachment_ids;
526 const unsigned num_attachments = 2;
527 for (unsigned i = 0; i < num_attachments; ++i) {
528 attachment_ids.push_back(AttachmentId::Create(0, 0));
530 attachment_service()->UploadAttachments(attachment_ids);
532 for (unsigned i = 0; i < num_attachments; ++i) {
533 RunLoopAndFireTimer();
534 ASSERT_GE(store()->read_ids.size(), 1U);
535 // None found!
536 store()->RespondToRead(AttachmentIdSet());
538 RunLoop();
540 // Nothing uploaded.
541 EXPECT_EQ(0U, uploader()->upload_requests.size());
542 // See that the delegate was never called.
543 ASSERT_EQ(0U, on_attachment_uploaded_list().size());
544 EXPECT_EQ(num_attachments, store()->drop_ids.size());
547 TEST_F(AttachmentServiceImplTest, UploadAttachments_NoUploader) {
548 InitializeAttachmentService(make_scoped_ptr<MockAttachmentUploader>(NULL),
549 make_scoped_ptr(new MockAttachmentDownloader()),
550 this);
552 AttachmentIdList attachment_ids;
553 attachment_ids.push_back(AttachmentId::Create(0, 0));
554 attachment_service()->UploadAttachments(attachment_ids);
555 RunLoop();
556 EXPECT_EQ(0U, store()->read_ids.size());
557 ASSERT_EQ(0U, on_attachment_uploaded_list().size());
558 EXPECT_EQ(0U, store()->drop_ids.size());
561 // Upload three attachments. For one of them, server responds with error.
562 TEST_F(AttachmentServiceImplTest, UploadAttachments_OneUploadFails) {
563 AttachmentIdList attachment_ids;
564 const unsigned num_attachments = 3;
565 for (unsigned i = 0; i < num_attachments; ++i) {
566 attachment_ids.push_back(AttachmentId::Create(0, 0));
568 attachment_service()->UploadAttachments(attachment_ids);
570 for (unsigned i = 0; i < 3; ++i) {
571 RunLoopAndFireTimer();
572 ASSERT_GE(store()->read_ids.size(), 1U);
573 store()->RespondToRead(AttachmentIdSetFromList(attachment_ids));
574 RunLoop();
575 ASSERT_EQ(1U, uploader()->upload_requests.size());
576 AttachmentUploader::UploadResult result =
577 AttachmentUploader::UPLOAD_SUCCESS;
578 // Fail the 2nd one.
579 if (i == 2U) {
580 result = AttachmentUploader::UPLOAD_UNSPECIFIED_ERROR;
581 } else {
582 result = AttachmentUploader::UPLOAD_SUCCESS;
584 uploader()->RespondToUpload(uploader()->upload_requests.begin()->first,
585 result);
586 RunLoop();
588 ASSERT_EQ(2U, on_attachment_uploaded_list().size());
589 EXPECT_EQ(num_attachments, store()->drop_ids.size());
592 // Attempt an upload, respond with transient error to trigger backoff, issue
593 // network disconnect/connect events and see that backoff is cleared.
594 TEST_F(AttachmentServiceImplTest,
595 UploadAttachments_ResetBackoffAfterNetworkChange) {
596 AttachmentIdList attachment_ids;
597 attachment_ids.push_back(AttachmentId::Create(0, 0));
598 attachment_service()->UploadAttachments(attachment_ids);
600 RunLoopAndFireTimer();
601 ASSERT_EQ(1U, store()->read_ids.size());
602 store()->RespondToRead(AttachmentIdSetFromList(attachment_ids));
603 RunLoop();
604 ASSERT_EQ(1U, uploader()->upload_requests.size());
606 uploader()->RespondToUpload(uploader()->upload_requests.begin()->first,
607 AttachmentUploader::UPLOAD_TRANSIENT_ERROR);
608 RunLoop();
610 // See that we are in backoff.
611 ASSERT_TRUE(mock_timer()->IsRunning());
612 ASSERT_GT(mock_timer()->GetCurrentDelay(), base::TimeDelta());
614 // Issue a network disconnect event.
615 network_change_notifier()->NotifyObserversOfNetworkChangeForTests(
616 net::NetworkChangeNotifier::CONNECTION_NONE);
617 RunLoop();
619 // Still in backoff.
620 ASSERT_TRUE(mock_timer()->IsRunning());
621 ASSERT_GT(mock_timer()->GetCurrentDelay(), base::TimeDelta());
623 // Issue a network connect event.
624 net::NetworkChangeNotifier::NotifyObserversOfNetworkChangeForTests(
625 net::NetworkChangeNotifier::CONNECTION_WIFI);
626 RunLoop();
628 // No longer in backoff.
629 ASSERT_TRUE(mock_timer()->IsRunning());
630 ASSERT_EQ(base::TimeDelta(), mock_timer()->GetCurrentDelay());
633 } // namespace syncer