| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "sync/api/attachments/attachment_service_impl.h" | |
| 6 | |
| 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 "sync/internal_api/public/attachments/fake_attachment_downloader.h" | |
| 12 #include "sync/internal_api/public/attachments/fake_attachment_uploader.h" | |
| 13 #include "testing/gtest/include/gtest/gtest.h" | |
| 14 | |
| 15 namespace syncer { | |
| 16 | |
| 17 class MockAttachmentStore : public AttachmentStore, | |
| 18 public base::SupportsWeakPtr<MockAttachmentStore> { | |
| 19 public: | |
| 20 MockAttachmentStore() {} | |
| 21 | |
| 22 virtual void Read(const AttachmentIdList& ids, | |
| 23 const ReadCallback& callback) OVERRIDE { | |
| 24 read_ids.push_back(ids); | |
| 25 read_callbacks.push_back(callback); | |
| 26 } | |
| 27 | |
| 28 virtual void Write(const AttachmentList& attachments, | |
| 29 const WriteCallback& callback) OVERRIDE { | |
| 30 write_attachments.push_back(attachments); | |
| 31 write_callbacks.push_back(callback); | |
| 32 } | |
| 33 | |
| 34 virtual void Drop(const AttachmentIdList& ids, | |
| 35 const DropCallback& callback) OVERRIDE { | |
| 36 NOTREACHED(); | |
| 37 } | |
| 38 | |
| 39 // Respond to Read request. Attachments found in local_attachments should be | |
| 40 // returned, everything else should be reported unavailable. | |
| 41 void RespondToRead(const AttachmentIdSet& local_attachments) { | |
| 42 scoped_refptr<base::RefCountedString> data = new base::RefCountedString(); | |
| 43 ReadCallback callback = read_callbacks.back(); | |
| 44 AttachmentIdList ids = read_ids.back(); | |
| 45 read_callbacks.pop_back(); | |
| 46 read_ids.pop_back(); | |
| 47 | |
| 48 scoped_ptr<AttachmentMap> attachments(new AttachmentMap()); | |
| 49 scoped_ptr<AttachmentIdList> unavailable_attachments( | |
| 50 new AttachmentIdList()); | |
| 51 for (AttachmentIdList::const_iterator iter = ids.begin(); iter != ids.end(); | |
| 52 ++iter) { | |
| 53 if (local_attachments.find(*iter) != local_attachments.end()) { | |
| 54 Attachment attachment = Attachment::CreateWithId(*iter, data); | |
| 55 attachments->insert(std::make_pair(*iter, attachment)); | |
| 56 } else { | |
| 57 unavailable_attachments->push_back(*iter); | |
| 58 } | |
| 59 } | |
| 60 Result result = | |
| 61 unavailable_attachments->empty() ? SUCCESS : UNSPECIFIED_ERROR; | |
| 62 | |
| 63 base::MessageLoop::current()->PostTask( | |
| 64 FROM_HERE, | |
| 65 base::Bind(callback, | |
| 66 result, | |
| 67 base::Passed(&attachments), | |
| 68 base::Passed(&unavailable_attachments))); | |
| 69 } | |
| 70 | |
| 71 // Respond to Write request with |result|. | |
| 72 void RespondToWrite(const Result& result) { | |
| 73 WriteCallback callback = write_callbacks.back(); | |
| 74 AttachmentList attachments = write_attachments.back(); | |
| 75 write_callbacks.pop_back(); | |
| 76 write_attachments.pop_back(); | |
| 77 base::MessageLoop::current()->PostTask(FROM_HERE, | |
| 78 base::Bind(callback, result)); | |
| 79 } | |
| 80 | |
| 81 std::vector<AttachmentIdList> read_ids; | |
| 82 std::vector<ReadCallback> read_callbacks; | |
| 83 std::vector<AttachmentList> write_attachments; | |
| 84 std::vector<WriteCallback> write_callbacks; | |
| 85 | |
| 86 DISALLOW_COPY_AND_ASSIGN(MockAttachmentStore); | |
| 87 }; | |
| 88 | |
| 89 class MockAttachmentDownloader | |
| 90 : public AttachmentDownloader, | |
| 91 public base::SupportsWeakPtr<MockAttachmentDownloader> { | |
| 92 public: | |
| 93 MockAttachmentDownloader() {} | |
| 94 | |
| 95 virtual void DownloadAttachment(const AttachmentId& id, | |
| 96 const DownloadCallback& callback) OVERRIDE { | |
| 97 ASSERT_TRUE(download_requests.find(id) == download_requests.end()); | |
| 98 download_requests.insert(std::make_pair(id, callback)); | |
| 99 } | |
| 100 | |
| 101 // Multiple requests to download will be active at the same time. | |
| 102 // RespondToDownload should respond to only one of them. | |
| 103 void RespondToDownload(const AttachmentId& id, const DownloadResult& result) { | |
| 104 ASSERT_TRUE(download_requests.find(id) != download_requests.end()); | |
| 105 scoped_ptr<Attachment> attachment; | |
| 106 if (result == DOWNLOAD_SUCCESS) { | |
| 107 scoped_refptr<base::RefCountedString> data = new base::RefCountedString(); | |
| 108 attachment.reset(new Attachment(Attachment::CreateWithId(id, data))); | |
| 109 } | |
| 110 base::MessageLoop::current()->PostTask( | |
| 111 FROM_HERE, | |
| 112 base::Bind(download_requests[id], result, base::Passed(&attachment))); | |
| 113 | |
| 114 download_requests.erase(id); | |
| 115 } | |
| 116 | |
| 117 std::map<AttachmentId, DownloadCallback> download_requests; | |
| 118 | |
| 119 DISALLOW_COPY_AND_ASSIGN(MockAttachmentDownloader); | |
| 120 }; | |
| 121 | |
| 122 class MockAttachmentUploader | |
| 123 : public AttachmentUploader, | |
| 124 public base::SupportsWeakPtr<MockAttachmentUploader> { | |
| 125 public: | |
| 126 MockAttachmentUploader() {} | |
| 127 | |
| 128 // AttachmentUploader implementation. | |
| 129 virtual void UploadAttachment(const Attachment& attachment, | |
| 130 const UploadCallback& callback) OVERRIDE { | |
| 131 const AttachmentId id = attachment.GetId(); | |
| 132 ASSERT_TRUE(upload_requests.find(id) == upload_requests.end()); | |
| 133 upload_requests.insert(std::make_pair(id, callback)); | |
| 134 } | |
| 135 | |
| 136 void RespondToUpload(const AttachmentId& id, const UploadResult& result) { | |
| 137 ASSERT_TRUE(upload_requests.find(id) != upload_requests.end()); | |
| 138 base::MessageLoop::current()->PostTask( | |
| 139 FROM_HERE, base::Bind(upload_requests[id], result, id)); | |
| 140 upload_requests.erase(id); | |
| 141 } | |
| 142 | |
| 143 std::map<AttachmentId, UploadCallback> upload_requests; | |
| 144 | |
| 145 DISALLOW_COPY_AND_ASSIGN(MockAttachmentUploader); | |
| 146 }; | |
| 147 | |
| 148 class AttachmentServiceImplTest : public testing::Test, | |
| 149 public AttachmentService::Delegate { | |
| 150 protected: | |
| 151 AttachmentServiceImplTest() {} | |
| 152 | |
| 153 virtual void SetUp() OVERRIDE { | |
| 154 InitializeAttachmentService(make_scoped_ptr(new MockAttachmentUploader()), | |
| 155 make_scoped_ptr(new MockAttachmentDownloader()), | |
| 156 this); | |
| 157 } | |
| 158 | |
| 159 virtual void TearDown() OVERRIDE { | |
| 160 attachment_service_.reset(); | |
| 161 ASSERT_FALSE(attachment_store_); | |
| 162 ASSERT_FALSE(attachment_uploader_); | |
| 163 ASSERT_FALSE(attachment_downloader_); | |
| 164 } | |
| 165 | |
| 166 // AttachmentService::Delegate implementation. | |
| 167 virtual void OnAttachmentUploaded( | |
| 168 const AttachmentId& attachment_id) OVERRIDE { | |
| 169 on_attachment_uploaded_list_.push_back(attachment_id); | |
| 170 } | |
| 171 | |
| 172 void InitializeAttachmentService( | |
| 173 scoped_ptr<MockAttachmentUploader> uploader, | |
| 174 scoped_ptr<MockAttachmentDownloader> downloader, | |
| 175 AttachmentService::Delegate* delegate) { | |
| 176 scoped_ptr<MockAttachmentStore> attachment_store(new MockAttachmentStore()); | |
| 177 attachment_store_ = attachment_store->AsWeakPtr(); | |
| 178 | |
| 179 if (uploader.get()) { | |
| 180 attachment_uploader_ = uploader->AsWeakPtr(); | |
| 181 } | |
| 182 if (downloader.get()) { | |
| 183 attachment_downloader_ = downloader->AsWeakPtr(); | |
| 184 } | |
| 185 attachment_service_.reset( | |
| 186 new AttachmentServiceImpl(attachment_store.PassAs<AttachmentStore>(), | |
| 187 uploader.PassAs<AttachmentUploader>(), | |
| 188 downloader.PassAs<AttachmentDownloader>(), | |
| 189 delegate)); | |
| 190 } | |
| 191 | |
| 192 AttachmentService* attachment_service() { return attachment_service_.get(); } | |
| 193 | |
| 194 AttachmentService::GetOrDownloadCallback download_callback() { | |
| 195 return base::Bind(&AttachmentServiceImplTest::DownloadDone, | |
| 196 base::Unretained(this)); | |
| 197 } | |
| 198 | |
| 199 AttachmentService::StoreCallback store_callback() { | |
| 200 return base::Bind(&AttachmentServiceImplTest::StoreDone, | |
| 201 base::Unretained(this)); | |
| 202 } | |
| 203 | |
| 204 void DownloadDone(const AttachmentService::GetOrDownloadResult& result, | |
| 205 scoped_ptr<AttachmentMap> attachments) { | |
| 206 download_results_.push_back(result); | |
| 207 last_download_attachments_ = attachments.Pass(); | |
| 208 } | |
| 209 | |
| 210 void StoreDone(const AttachmentService::StoreResult& result) { | |
| 211 store_results_.push_back(result); | |
| 212 } | |
| 213 | |
| 214 void RunLoop() { | |
| 215 base::RunLoop run_loop; | |
| 216 run_loop.RunUntilIdle(); | |
| 217 } | |
| 218 | |
| 219 const std::vector<AttachmentService::GetOrDownloadResult>& | |
| 220 download_results() const { | |
| 221 return download_results_; | |
| 222 } | |
| 223 | |
| 224 const AttachmentMap& last_download_attachments() const { | |
| 225 return *last_download_attachments_.get(); | |
| 226 } | |
| 227 | |
| 228 const std::vector<AttachmentService::StoreResult>& store_results() const { | |
| 229 return store_results_; | |
| 230 } | |
| 231 | |
| 232 MockAttachmentStore* store() { return attachment_store_.get(); } | |
| 233 | |
| 234 MockAttachmentDownloader* downloader() { | |
| 235 return attachment_downloader_.get(); | |
| 236 } | |
| 237 | |
| 238 MockAttachmentUploader* uploader() { | |
| 239 return attachment_uploader_.get(); | |
| 240 } | |
| 241 | |
| 242 const std::vector<AttachmentId>& on_attachment_uploaded_list() const { | |
| 243 return on_attachment_uploaded_list_; | |
| 244 } | |
| 245 | |
| 246 private: | |
| 247 base::MessageLoop message_loop_; | |
| 248 base::WeakPtr<MockAttachmentStore> attachment_store_; | |
| 249 base::WeakPtr<MockAttachmentDownloader> attachment_downloader_; | |
| 250 base::WeakPtr<MockAttachmentUploader> attachment_uploader_; | |
| 251 scoped_ptr<AttachmentService> attachment_service_; | |
| 252 | |
| 253 std::vector<AttachmentService::GetOrDownloadResult> download_results_; | |
| 254 scoped_ptr<AttachmentMap> last_download_attachments_; | |
| 255 std::vector<AttachmentId> on_attachment_uploaded_list_; | |
| 256 | |
| 257 std::vector<AttachmentService::StoreResult> store_results_; | |
| 258 }; | |
| 259 | |
| 260 TEST_F(AttachmentServiceImplTest, GetOrDownload_EmptyAttachmentList) { | |
| 261 AttachmentIdList attachment_ids; | |
| 262 attachment_service()->GetOrDownloadAttachments(attachment_ids, | |
| 263 download_callback()); | |
| 264 store()->RespondToRead(AttachmentIdSet()); | |
| 265 | |
| 266 RunLoop(); | |
| 267 EXPECT_EQ(1U, download_results().size()); | |
| 268 EXPECT_EQ(0U, last_download_attachments().size()); | |
| 269 } | |
| 270 | |
| 271 TEST_F(AttachmentServiceImplTest, GetOrDownload_Local) { | |
| 272 AttachmentIdList attachment_ids; | |
| 273 attachment_ids.push_back(AttachmentId::Create()); | |
| 274 attachment_service()->GetOrDownloadAttachments(attachment_ids, | |
| 275 download_callback()); | |
| 276 AttachmentIdSet local_attachments; | |
| 277 local_attachments.insert(attachment_ids[0]); | |
| 278 store()->RespondToRead(local_attachments); | |
| 279 | |
| 280 RunLoop(); | |
| 281 EXPECT_EQ(1U, download_results().size()); | |
| 282 EXPECT_EQ(1U, last_download_attachments().size()); | |
| 283 EXPECT_TRUE(last_download_attachments().find(attachment_ids[0]) != | |
| 284 last_download_attachments().end()); | |
| 285 } | |
| 286 | |
| 287 TEST_F(AttachmentServiceImplTest, GetOrDownload_LocalRemoteUnavailable) { | |
| 288 // Create attachment list with 3 ids. | |
| 289 AttachmentIdList attachment_ids; | |
| 290 attachment_ids.push_back(AttachmentId::Create()); | |
| 291 attachment_ids.push_back(AttachmentId::Create()); | |
| 292 attachment_ids.push_back(AttachmentId::Create()); | |
| 293 // Call attachment service. | |
| 294 attachment_service()->GetOrDownloadAttachments(attachment_ids, | |
| 295 download_callback()); | |
| 296 // Ensure AttachmentStore is called. | |
| 297 EXPECT_FALSE(store()->read_ids.empty()); | |
| 298 | |
| 299 // make AttachmentStore return only attachment 0. | |
| 300 AttachmentIdSet local_attachments; | |
| 301 local_attachments.insert(attachment_ids[0]); | |
| 302 store()->RespondToRead(local_attachments); | |
| 303 RunLoop(); | |
| 304 // Ensure Downloader called with right attachment ids | |
| 305 EXPECT_EQ(2U, downloader()->download_requests.size()); | |
| 306 | |
| 307 // Make downloader return attachment 1. | |
| 308 downloader()->RespondToDownload(attachment_ids[1], | |
| 309 AttachmentDownloader::DOWNLOAD_SUCCESS); | |
| 310 RunLoop(); | |
| 311 // Ensure consumer callback is not called. | |
| 312 EXPECT_TRUE(download_results().empty()); | |
| 313 | |
| 314 // Make downloader fail attachment 2. | |
| 315 downloader()->RespondToDownload( | |
| 316 attachment_ids[2], AttachmentDownloader::DOWNLOAD_UNSPECIFIED_ERROR); | |
| 317 RunLoop(); | |
| 318 // Ensure callback is called | |
| 319 EXPECT_FALSE(download_results().empty()); | |
| 320 // There should be only two attachments returned, 0 and 1. | |
| 321 EXPECT_EQ(2U, last_download_attachments().size()); | |
| 322 EXPECT_TRUE(last_download_attachments().find(attachment_ids[0]) != | |
| 323 last_download_attachments().end()); | |
| 324 EXPECT_TRUE(last_download_attachments().find(attachment_ids[1]) != | |
| 325 last_download_attachments().end()); | |
| 326 EXPECT_TRUE(last_download_attachments().find(attachment_ids[2]) == | |
| 327 last_download_attachments().end()); | |
| 328 } | |
| 329 | |
| 330 TEST_F(AttachmentServiceImplTest, GetOrDownload_NoDownloader) { | |
| 331 // No downloader. | |
| 332 InitializeAttachmentService( | |
| 333 make_scoped_ptr<MockAttachmentUploader>(new MockAttachmentUploader()), | |
| 334 make_scoped_ptr<MockAttachmentDownloader>(NULL), | |
| 335 this); | |
| 336 | |
| 337 AttachmentIdList attachment_ids; | |
| 338 attachment_ids.push_back(AttachmentId::Create()); | |
| 339 attachment_service()->GetOrDownloadAttachments(attachment_ids, | |
| 340 download_callback()); | |
| 341 EXPECT_FALSE(store()->read_ids.empty()); | |
| 342 | |
| 343 AttachmentIdSet local_attachments; | |
| 344 store()->RespondToRead(local_attachments); | |
| 345 RunLoop(); | |
| 346 ASSERT_EQ(1U, download_results().size()); | |
| 347 EXPECT_EQ(AttachmentService::GET_UNSPECIFIED_ERROR, download_results()[0]); | |
| 348 EXPECT_TRUE(last_download_attachments().empty()); | |
| 349 } | |
| 350 | |
| 351 TEST_F(AttachmentServiceImplTest, StoreAttachments_Success) { | |
| 352 scoped_refptr<base::RefCountedString> data = new base::RefCountedString(); | |
| 353 Attachment attachment(Attachment::Create(data)); | |
| 354 AttachmentList attachments; | |
| 355 attachments.push_back(attachment); | |
| 356 attachment_service()->StoreAttachments(attachments, store_callback()); | |
| 357 EXPECT_EQ(1U, store()->write_attachments.size()); | |
| 358 EXPECT_EQ(1U, uploader()->upload_requests.size()); | |
| 359 | |
| 360 store()->RespondToWrite(AttachmentStore::SUCCESS); | |
| 361 uploader()->RespondToUpload(attachment.GetId(), | |
| 362 AttachmentUploader::UPLOAD_SUCCESS); | |
| 363 RunLoop(); | |
| 364 ASSERT_EQ(1U, store_results().size()); | |
| 365 EXPECT_EQ(AttachmentService::STORE_SUCCESS, store_results()[0]); | |
| 366 ASSERT_EQ(1U, on_attachment_uploaded_list().size()); | |
| 367 EXPECT_EQ(attachment.GetId(), on_attachment_uploaded_list()[0]); | |
| 368 } | |
| 369 | |
| 370 TEST_F(AttachmentServiceImplTest, | |
| 371 StoreAttachments_StoreFailsWithUnspecifiedError) { | |
| 372 scoped_refptr<base::RefCountedString> data = new base::RefCountedString(); | |
| 373 Attachment attachment(Attachment::Create(data)); | |
| 374 AttachmentList attachments; | |
| 375 attachments.push_back(attachment); | |
| 376 attachment_service()->StoreAttachments(attachments, store_callback()); | |
| 377 EXPECT_EQ(1U, store()->write_attachments.size()); | |
| 378 EXPECT_EQ(1U, uploader()->upload_requests.size()); | |
| 379 | |
| 380 store()->RespondToWrite(AttachmentStore::UNSPECIFIED_ERROR); | |
| 381 uploader()->RespondToUpload(attachment.GetId(), | |
| 382 AttachmentUploader::UPLOAD_SUCCESS); | |
| 383 RunLoop(); | |
| 384 ASSERT_EQ(1U, store_results().size()); | |
| 385 EXPECT_EQ(AttachmentService::STORE_UNSPECIFIED_ERROR, store_results()[0]); | |
| 386 ASSERT_EQ(1U, on_attachment_uploaded_list().size()); | |
| 387 EXPECT_EQ(attachment.GetId(), on_attachment_uploaded_list()[0]); | |
| 388 } | |
| 389 | |
| 390 TEST_F(AttachmentServiceImplTest, | |
| 391 StoreAttachments_UploadFailsWithUnspecifiedError) { | |
| 392 scoped_refptr<base::RefCountedString> data = new base::RefCountedString(); | |
| 393 Attachment attachment(Attachment::Create(data)); | |
| 394 AttachmentList attachments; | |
| 395 attachments.push_back(attachment); | |
| 396 attachment_service()->StoreAttachments(attachments, store_callback()); | |
| 397 EXPECT_EQ(1U, store()->write_attachments.size()); | |
| 398 EXPECT_EQ(1U, uploader()->upload_requests.size()); | |
| 399 | |
| 400 store()->RespondToWrite(AttachmentStore::SUCCESS); | |
| 401 uploader()->RespondToUpload(attachment.GetId(), | |
| 402 AttachmentUploader::UPLOAD_UNSPECIFIED_ERROR); | |
| 403 RunLoop(); | |
| 404 ASSERT_EQ(1U, store_results().size()); | |
| 405 // Even though the upload failed, the Store operation is successful. | |
| 406 EXPECT_EQ(AttachmentService::STORE_SUCCESS, store_results()[0]); | |
| 407 EXPECT_TRUE(on_attachment_uploaded_list().empty()); | |
| 408 } | |
| 409 | |
| 410 TEST_F(AttachmentServiceImplTest, StoreAttachments_NoDelegate) { | |
| 411 InitializeAttachmentService(make_scoped_ptr(new MockAttachmentUploader()), | |
| 412 make_scoped_ptr(new MockAttachmentDownloader()), | |
| 413 NULL); // No delegate. | |
| 414 | |
| 415 scoped_refptr<base::RefCountedString> data = new base::RefCountedString(); | |
| 416 Attachment attachment(Attachment::Create(data)); | |
| 417 AttachmentList attachments; | |
| 418 attachments.push_back(attachment); | |
| 419 attachment_service()->StoreAttachments(attachments, store_callback()); | |
| 420 EXPECT_EQ(1U, store()->write_attachments.size()); | |
| 421 EXPECT_EQ(1U, uploader()->upload_requests.size()); | |
| 422 | |
| 423 store()->RespondToWrite(AttachmentStore::SUCCESS); | |
| 424 uploader()->RespondToUpload(attachment.GetId(), | |
| 425 AttachmentUploader::UPLOAD_SUCCESS); | |
| 426 RunLoop(); | |
| 427 ASSERT_EQ(1U, store_results().size()); | |
| 428 EXPECT_EQ(AttachmentService::STORE_SUCCESS, store_results()[0]); | |
| 429 EXPECT_TRUE(on_attachment_uploaded_list().empty()); | |
| 430 } | |
| 431 | |
| 432 TEST_F(AttachmentServiceImplTest, StoreAttachments_NoUploader) { | |
| 433 // No uploader. | |
| 434 InitializeAttachmentService(make_scoped_ptr<MockAttachmentUploader>(NULL), | |
| 435 make_scoped_ptr(new MockAttachmentDownloader()), | |
| 436 this); | |
| 437 | |
| 438 scoped_refptr<base::RefCountedString> data = new base::RefCountedString(); | |
| 439 Attachment attachment(Attachment::Create(data)); | |
| 440 AttachmentList attachments; | |
| 441 attachments.push_back(attachment); | |
| 442 attachment_service()->StoreAttachments(attachments, store_callback()); | |
| 443 EXPECT_EQ(1U, store()->write_attachments.size()); | |
| 444 | |
| 445 store()->RespondToWrite(AttachmentStore::SUCCESS); | |
| 446 RunLoop(); | |
| 447 ASSERT_EQ(1U, store_results().size()); | |
| 448 EXPECT_EQ(AttachmentService::STORE_SUCCESS, store_results()[0]); | |
| 449 EXPECT_TRUE(on_attachment_uploaded_list().empty()); | |
| 450 } | |
| 451 | |
| 452 } // namespace syncer | |
| OLD | NEW |