#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
+#include "base/timer/mock_timer.h"
#include "sync/internal_api/public/attachments/fake_attachment_downloader.h"
#include "sync/internal_api/public/attachments/fake_attachment_uploader.h"
+#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace syncer {
std::vector<AttachmentList> write_attachments;
std::vector<WriteCallback> write_callbacks;
+ private:
+ virtual ~MockAttachmentStore() {}
+
DISALLOW_COPY_AND_ASSIGN(MockAttachmentStore);
};
AttachmentServiceImplTest() {}
virtual void SetUp() OVERRIDE {
+ network_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock());
InitializeAttachmentService(make_scoped_ptr(new MockAttachmentUploader()),
make_scoped_ptr(new MockAttachmentDownloader()),
this);
scoped_ptr<MockAttachmentUploader> uploader,
scoped_ptr<MockAttachmentDownloader> downloader,
AttachmentService::Delegate* delegate) {
- scoped_ptr<MockAttachmentStore> attachment_store(new MockAttachmentStore());
+ scoped_refptr<MockAttachmentStore> attachment_store(
+ new MockAttachmentStore());
attachment_store_ = attachment_store->AsWeakPtr();
if (uploader.get()) {
attachment_downloader_ = downloader->AsWeakPtr();
}
attachment_service_.reset(
- new AttachmentServiceImpl(attachment_store.PassAs<AttachmentStore>(),
+ new AttachmentServiceImpl(attachment_store,
uploader.PassAs<AttachmentUploader>(),
downloader.PassAs<AttachmentDownloader>(),
- delegate));
+ delegate,
+ base::TimeDelta::FromMinutes(1),
+ base::TimeDelta::FromMinutes(8)));
+
+ scoped_ptr<base::MockTimer> timer_to_pass(
+ new base::MockTimer(false, false));
+ mock_timer_ = timer_to_pass.get();
+ attachment_service_->SetTimerForTest(timer_to_pass.PassAs<base::Timer>());
}
AttachmentService* attachment_service() { return attachment_service_.get(); }
+ base::MockTimer* mock_timer() { return mock_timer_; }
+
AttachmentService::GetOrDownloadCallback download_callback() {
return base::Bind(&AttachmentServiceImplTest::DownloadDone,
base::Unretained(this));
}
- AttachmentService::StoreCallback store_callback() {
- return base::Bind(&AttachmentServiceImplTest::StoreDone,
- base::Unretained(this));
- }
-
void DownloadDone(const AttachmentService::GetOrDownloadResult& result,
scoped_ptr<AttachmentMap> attachments) {
download_results_.push_back(result);
last_download_attachments_ = attachments.Pass();
}
- void StoreDone(const AttachmentService::StoreResult& result) {
- store_results_.push_back(result);
- }
-
void RunLoop() {
base::RunLoop run_loop;
run_loop.RunUntilIdle();
}
+ void RunLoopAndFireTimer() {
+ RunLoop();
+ if (mock_timer()->IsRunning()) {
+ mock_timer()->Fire();
+ }
+ RunLoop();
+ }
+
const std::vector<AttachmentService::GetOrDownloadResult>&
download_results() const {
return download_results_;
return *last_download_attachments_.get();
}
- const std::vector<AttachmentService::StoreResult>& store_results() const {
- return store_results_;
+ net::NetworkChangeNotifier* network_change_notifier() {
+ return network_change_notifier_.get();
}
MockAttachmentStore* store() { return attachment_store_.get(); }
private:
base::MessageLoop message_loop_;
+ scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
base::WeakPtr<MockAttachmentStore> attachment_store_;
base::WeakPtr<MockAttachmentDownloader> attachment_downloader_;
base::WeakPtr<MockAttachmentUploader> attachment_uploader_;
- scoped_ptr<AttachmentService> attachment_service_;
+ scoped_ptr<AttachmentServiceImpl> attachment_service_;
+ base::MockTimer* mock_timer_; // not owned
std::vector<AttachmentService::GetOrDownloadResult> download_results_;
scoped_ptr<AttachmentMap> last_download_attachments_;
std::vector<AttachmentId> on_attachment_uploaded_list_;
-
- std::vector<AttachmentService::StoreResult> store_results_;
};
+TEST_F(AttachmentServiceImplTest, GetStore) {
+ EXPECT_EQ(store(), attachment_service()->GetStore());
+}
+
TEST_F(AttachmentServiceImplTest, GetOrDownload_EmptyAttachmentList) {
AttachmentIdList attachment_ids;
attachment_service()->GetOrDownloadAttachments(attachment_ids,
EXPECT_TRUE(last_download_attachments().empty());
}
-TEST_F(AttachmentServiceImplTest, StoreAttachments_Success) {
- scoped_refptr<base::RefCountedString> data = new base::RefCountedString();
- Attachment attachment(Attachment::Create(data));
- AttachmentList attachments;
- attachments.push_back(attachment);
- attachment_service()->StoreAttachments(attachments, store_callback());
- EXPECT_EQ(1U, store()->write_attachments.size());
- EXPECT_EQ(1U, uploader()->upload_requests.size());
-
- store()->RespondToWrite(AttachmentStore::SUCCESS);
- uploader()->RespondToUpload(attachment.GetId(),
- AttachmentUploader::UPLOAD_SUCCESS);
+TEST_F(AttachmentServiceImplTest, UploadAttachments_Success) {
+ AttachmentIdSet attachment_ids;
+ const unsigned num_attachments = 3;
+ for (unsigned i = 0; i < num_attachments; ++i) {
+ attachment_ids.insert(AttachmentId::Create());
+ }
+ attachment_service()->UploadAttachments(attachment_ids);
+
+ for (unsigned i = 0; i < num_attachments; ++i) {
+ RunLoopAndFireTimer();
+ // See that the service has issued a read for at least one of the
+ // attachments.
+ ASSERT_GE(store()->read_ids.size(), 1U);
+ store()->RespondToRead(attachment_ids);
+ RunLoop();
+ ASSERT_GE(uploader()->upload_requests.size(), 1U);
+ uploader()->RespondToUpload(uploader()->upload_requests.begin()->first,
+ AttachmentUploader::UPLOAD_SUCCESS);
+ }
RunLoop();
- ASSERT_EQ(1U, store_results().size());
- EXPECT_EQ(AttachmentService::STORE_SUCCESS, store_results()[0]);
- ASSERT_EQ(1U, on_attachment_uploaded_list().size());
- EXPECT_EQ(attachment.GetId(), on_attachment_uploaded_list()[0]);
+ ASSERT_EQ(0U, store()->read_ids.size());
+ ASSERT_EQ(0U, uploader()->upload_requests.size());
+
+ // See that all the attachments were uploaded.
+ ASSERT_EQ(attachment_ids.size(), on_attachment_uploaded_list().size());
+ AttachmentIdSet::const_iterator iter = attachment_ids.begin();
+ const AttachmentIdSet::const_iterator end = attachment_ids.end();
+ for (iter = attachment_ids.begin(); iter != end; ++iter) {
+ EXPECT_THAT(on_attachment_uploaded_list(), testing::Contains(*iter));
+ }
}
-TEST_F(AttachmentServiceImplTest,
- StoreAttachments_StoreFailsWithUnspecifiedError) {
- scoped_refptr<base::RefCountedString> data = new base::RefCountedString();
- Attachment attachment(Attachment::Create(data));
- AttachmentList attachments;
- attachments.push_back(attachment);
- attachment_service()->StoreAttachments(attachments, store_callback());
- EXPECT_EQ(1U, store()->write_attachments.size());
- EXPECT_EQ(1U, uploader()->upload_requests.size());
-
- store()->RespondToWrite(AttachmentStore::UNSPECIFIED_ERROR);
- uploader()->RespondToUpload(attachment.GetId(),
+TEST_F(AttachmentServiceImplTest, UploadAttachments_Success_NoDelegate) {
+ InitializeAttachmentService(make_scoped_ptr(new MockAttachmentUploader()),
+ make_scoped_ptr(new MockAttachmentDownloader()),
+ NULL); // No delegate.
+
+ AttachmentIdSet attachment_ids;
+ attachment_ids.insert(AttachmentId::Create());
+ attachment_service()->UploadAttachments(attachment_ids);
+ RunLoopAndFireTimer();
+ ASSERT_EQ(1U, store()->read_ids.size());
+ ASSERT_EQ(0U, uploader()->upload_requests.size());
+ store()->RespondToRead(attachment_ids);
+ RunLoop();
+ ASSERT_EQ(0U, store()->read_ids.size());
+ ASSERT_EQ(1U, uploader()->upload_requests.size());
+ uploader()->RespondToUpload(*attachment_ids.begin(),
AttachmentUploader::UPLOAD_SUCCESS);
RunLoop();
- ASSERT_EQ(1U, store_results().size());
- EXPECT_EQ(AttachmentService::STORE_UNSPECIFIED_ERROR, store_results()[0]);
- ASSERT_EQ(1U, on_attachment_uploaded_list().size());
- EXPECT_EQ(attachment.GetId(), on_attachment_uploaded_list()[0]);
+ ASSERT_TRUE(on_attachment_uploaded_list().empty());
}
-TEST_F(AttachmentServiceImplTest,
- StoreAttachments_UploadFailsWithUnspecifiedError) {
- scoped_refptr<base::RefCountedString> data = new base::RefCountedString();
- Attachment attachment(Attachment::Create(data));
- AttachmentList attachments;
- attachments.push_back(attachment);
- attachment_service()->StoreAttachments(attachments, store_callback());
- EXPECT_EQ(1U, store()->write_attachments.size());
- EXPECT_EQ(1U, uploader()->upload_requests.size());
-
- store()->RespondToWrite(AttachmentStore::SUCCESS);
- uploader()->RespondToUpload(attachment.GetId(),
- AttachmentUploader::UPLOAD_UNSPECIFIED_ERROR);
+TEST_F(AttachmentServiceImplTest, UploadAttachments_SomeMissingFromStore) {
+ AttachmentIdSet attachment_ids;
+ attachment_ids.insert(AttachmentId::Create());
+ attachment_ids.insert(AttachmentId::Create());
+ attachment_service()->UploadAttachments(attachment_ids);
+ RunLoopAndFireTimer();
+ ASSERT_GE(store()->read_ids.size(), 1U);
+
+ ASSERT_EQ(0U, uploader()->upload_requests.size());
+ store()->RespondToRead(attachment_ids);
RunLoop();
- ASSERT_EQ(1U, store_results().size());
- // Even though the upload failed, the Store operation is successful.
- EXPECT_EQ(AttachmentService::STORE_SUCCESS, store_results()[0]);
- EXPECT_TRUE(on_attachment_uploaded_list().empty());
-}
+ ASSERT_EQ(1U, uploader()->upload_requests.size());
-TEST_F(AttachmentServiceImplTest, StoreAttachments_NoDelegate) {
- InitializeAttachmentService(make_scoped_ptr(new MockAttachmentUploader()),
- make_scoped_ptr(new MockAttachmentDownloader()),
- NULL); // No delegate.
+ uploader()->RespondToUpload(uploader()->upload_requests.begin()->first,
+ AttachmentUploader::UPLOAD_SUCCESS);
+ RunLoopAndFireTimer();
+ ASSERT_EQ(1U, on_attachment_uploaded_list().size());
+ ASSERT_GE(store()->read_ids.size(), 1U);
+ // Not found!
+ store()->RespondToRead(AttachmentIdSet());
+ RunLoop();
+ // No upload requests since the read failed.
+ ASSERT_EQ(0U, uploader()->upload_requests.size());
+}
- scoped_refptr<base::RefCountedString> data = new base::RefCountedString();
- Attachment attachment(Attachment::Create(data));
- AttachmentList attachments;
- attachments.push_back(attachment);
- attachment_service()->StoreAttachments(attachments, store_callback());
- EXPECT_EQ(1U, store()->write_attachments.size());
- EXPECT_EQ(1U, uploader()->upload_requests.size());
+TEST_F(AttachmentServiceImplTest, UploadAttachments_AllMissingFromStore) {
+ AttachmentIdSet attachment_ids;
+ const unsigned num_attachments = 2;
+ for (unsigned i = 0; i < num_attachments; ++i) {
+ attachment_ids.insert(AttachmentId::Create());
+ }
+ attachment_service()->UploadAttachments(attachment_ids);
- store()->RespondToWrite(AttachmentStore::SUCCESS);
- uploader()->RespondToUpload(attachment.GetId(),
- AttachmentUploader::UPLOAD_SUCCESS);
+ for (unsigned i = 0; i < num_attachments; ++i) {
+ RunLoopAndFireTimer();
+ ASSERT_GE(store()->read_ids.size(), 1U);
+ // None found!
+ store()->RespondToRead(AttachmentIdSet());
+ }
RunLoop();
- ASSERT_EQ(1U, store_results().size());
- EXPECT_EQ(AttachmentService::STORE_SUCCESS, store_results()[0]);
- EXPECT_TRUE(on_attachment_uploaded_list().empty());
+
+ // Nothing uploaded.
+ EXPECT_EQ(0U, uploader()->upload_requests.size());
+ // See that the delegate was never called.
+ ASSERT_EQ(0U, on_attachment_uploaded_list().size());
}
-TEST_F(AttachmentServiceImplTest, StoreAttachments_NoUploader) {
- // No uploader.
+TEST_F(AttachmentServiceImplTest, UploadAttachments_NoUploader) {
InitializeAttachmentService(make_scoped_ptr<MockAttachmentUploader>(NULL),
make_scoped_ptr(new MockAttachmentDownloader()),
this);
- scoped_refptr<base::RefCountedString> data = new base::RefCountedString();
- Attachment attachment(Attachment::Create(data));
- AttachmentList attachments;
- attachments.push_back(attachment);
- attachment_service()->StoreAttachments(attachments, store_callback());
- EXPECT_EQ(1U, store()->write_attachments.size());
+ AttachmentIdSet attachment_ids;
+ attachment_ids.insert(AttachmentId::Create());
+ attachment_service()->UploadAttachments(attachment_ids);
+ RunLoop();
+ EXPECT_EQ(0U, store()->read_ids.size());
+ ASSERT_EQ(0U, on_attachment_uploaded_list().size());
+}
- store()->RespondToWrite(AttachmentStore::SUCCESS);
+// Upload three attachments. For one of them, server responds with error.
+TEST_F(AttachmentServiceImplTest, UploadAttachments_OneUploadFails) {
+ AttachmentIdSet attachment_ids;
+ const unsigned num_attachments = 3;
+ for (unsigned i = 0; i < num_attachments; ++i) {
+ attachment_ids.insert(AttachmentId::Create());
+ }
+ attachment_service()->UploadAttachments(attachment_ids);
+
+ for (unsigned i = 0; i < 3; ++i) {
+ RunLoopAndFireTimer();
+ ASSERT_GE(store()->read_ids.size(), 1U);
+ store()->RespondToRead(attachment_ids);
+ RunLoop();
+ ASSERT_EQ(1U, uploader()->upload_requests.size());
+ AttachmentUploader::UploadResult result =
+ AttachmentUploader::UPLOAD_SUCCESS;
+ // Fail the 2nd one.
+ if (i == 2U) {
+ result = AttachmentUploader::UPLOAD_UNSPECIFIED_ERROR;
+ } else {
+ result = AttachmentUploader::UPLOAD_SUCCESS;
+ }
+ uploader()->RespondToUpload(uploader()->upload_requests.begin()->first,
+ result);
+ RunLoop();
+ }
+ ASSERT_EQ(2U, on_attachment_uploaded_list().size());
+}
+
+// Attempt an upload, respond with transient error to trigger backoff, issue
+// network disconnect/connect events and see that backoff is cleared.
+TEST_F(AttachmentServiceImplTest,
+ UploadAttachments_ResetBackoffAfterNetworkChange) {
+ AttachmentIdSet attachment_ids;
+ attachment_ids.insert(AttachmentId::Create());
+ attachment_service()->UploadAttachments(attachment_ids);
+
+ RunLoopAndFireTimer();
+ ASSERT_EQ(1U, store()->read_ids.size());
+ store()->RespondToRead(attachment_ids);
+ RunLoop();
+ ASSERT_EQ(1U, uploader()->upload_requests.size());
+
+ uploader()->RespondToUpload(uploader()->upload_requests.begin()->first,
+ AttachmentUploader::UPLOAD_TRANSIENT_ERROR);
RunLoop();
- ASSERT_EQ(1U, store_results().size());
- EXPECT_EQ(AttachmentService::STORE_SUCCESS, store_results()[0]);
- EXPECT_TRUE(on_attachment_uploaded_list().empty());
+
+ // See that we are in backoff.
+ ASSERT_TRUE(mock_timer()->IsRunning());
+ ASSERT_GT(mock_timer()->GetCurrentDelay(), base::TimeDelta());
+
+ // Issue a network disconnect event.
+ network_change_notifier()->NotifyObserversOfNetworkChangeForTests(
+ net::NetworkChangeNotifier::CONNECTION_NONE);
+ RunLoop();
+
+ // Still in backoff.
+ ASSERT_TRUE(mock_timer()->IsRunning());
+ ASSERT_GT(mock_timer()->GetCurrentDelay(), base::TimeDelta());
+
+ // Issue a network connect event.
+ net::NetworkChangeNotifier::NotifyObserversOfNetworkChangeForTests(
+ net::NetworkChangeNotifier::CONNECTION_WIFI);
+ RunLoop();
+
+ // No longer in backoff.
+ ASSERT_TRUE(mock_timer()->IsRunning());
+ ASSERT_EQ(base::TimeDelta(), mock_timer()->GetCurrentDelay());
}
} // namespace syncer