Home | History | Annotate | Download | only in attachments
      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     NOTREACHED();
     31   }
     32 
     33   virtual void Drop(const AttachmentIdList& ids,
     34                     const DropCallback& callback) OVERRIDE {
     35     NOTREACHED();
     36   }
     37 
     38   // Respond to Read request. Attachments found in local_attachments should be
     39   // returned, everything else should be reported unavailable.
     40   void RespondToRead(const AttachmentIdSet& local_attachments) {
     41     scoped_refptr<base::RefCountedString> data = new base::RefCountedString();
     42     ReadCallback callback = read_callbacks.back();
     43     AttachmentIdList ids = read_ids.back();
     44     read_callbacks.pop_back();
     45     read_ids.pop_back();
     46 
     47     scoped_ptr<AttachmentMap> attachments(new AttachmentMap());
     48     scoped_ptr<AttachmentIdList> unavailable_attachments(
     49         new AttachmentIdList());
     50     for (AttachmentIdList::const_iterator iter = ids.begin(); iter != ids.end();
     51          ++iter) {
     52       if (local_attachments.find(*iter) != local_attachments.end()) {
     53         Attachment attachment = Attachment::CreateWithId(*iter, data);
     54         attachments->insert(std::make_pair(*iter, attachment));
     55       } else {
     56         unavailable_attachments->push_back(*iter);
     57       }
     58     }
     59     Result result =
     60         unavailable_attachments->empty() ? SUCCESS : UNSPECIFIED_ERROR;
     61 
     62     base::MessageLoop::current()->PostTask(
     63         FROM_HERE,
     64         base::Bind(callback,
     65                    result,
     66                    base::Passed(&attachments),
     67                    base::Passed(&unavailable_attachments)));
     68   }
     69 
     70   std::vector<AttachmentIdList> read_ids;
     71   std::vector<ReadCallback> read_callbacks;
     72 
     73   DISALLOW_COPY_AND_ASSIGN(MockAttachmentStore);
     74 };
     75 
     76 class MockAttachmentDownloader
     77     : public AttachmentDownloader,
     78       public base::SupportsWeakPtr<MockAttachmentDownloader> {
     79  public:
     80   MockAttachmentDownloader() {}
     81 
     82   virtual void DownloadAttachment(const AttachmentId& id,
     83                                   const DownloadCallback& callback) OVERRIDE {
     84     ASSERT_TRUE(download_requests.find(id) == download_requests.end());
     85     download_requests.insert(std::make_pair(id, callback));
     86   }
     87 
     88   // Multiple requests to download will be active at the same time.
     89   // RespondToDownload should respond to only one of them.
     90   void RespondToDownload(const AttachmentId& id, const DownloadResult& result) {
     91     ASSERT_TRUE(download_requests.find(id) != download_requests.end());
     92     scoped_ptr<Attachment> attachment;
     93     if (result == DOWNLOAD_SUCCESS) {
     94       scoped_refptr<base::RefCountedString> data = new base::RefCountedString();
     95       attachment.reset(new Attachment(Attachment::CreateWithId(id, data)));
     96     }
     97     base::MessageLoop::current()->PostTask(
     98         FROM_HERE,
     99         base::Bind(download_requests[id], result, base::Passed(&attachment)));
    100 
    101     download_requests.erase(id);
    102   }
    103 
    104   std::map<AttachmentId, DownloadCallback> download_requests;
    105 
    106   DISALLOW_COPY_AND_ASSIGN(MockAttachmentDownloader);
    107 };
    108 
    109 class AttachmentServiceImplTest : public testing::Test {
    110  protected:
    111   AttachmentServiceImplTest() {}
    112 
    113   virtual void SetUp() OVERRIDE {
    114     scoped_ptr<MockAttachmentStore> attachment_store(new MockAttachmentStore());
    115     scoped_ptr<AttachmentUploader> attachment_uploader(
    116         new FakeAttachmentUploader());
    117     scoped_ptr<MockAttachmentDownloader> attachment_downloader(
    118         new MockAttachmentDownloader());
    119 
    120     attachment_store_ = attachment_store->AsWeakPtr();
    121     attachment_downloader_ = attachment_downloader->AsWeakPtr();
    122 
    123     attachment_service_.reset(new AttachmentServiceImpl(
    124         attachment_store.PassAs<AttachmentStore>(),
    125         attachment_uploader.Pass(),
    126         attachment_downloader.PassAs<AttachmentDownloader>(),
    127         NULL));
    128   }
    129 
    130   virtual void TearDown() OVERRIDE {
    131     attachment_service_.reset();
    132     ASSERT_FALSE(attachment_store_);
    133     ASSERT_FALSE(attachment_downloader_);
    134   }
    135 
    136   AttachmentService* attachment_service() { return attachment_service_.get(); }
    137 
    138   AttachmentService::GetOrDownloadCallback download_callback() {
    139     return base::Bind(&AttachmentServiceImplTest::DownloadDone,
    140                       base::Unretained(this));
    141   }
    142 
    143   void DownloadDone(const AttachmentService::GetOrDownloadResult& result,
    144                     scoped_ptr<AttachmentMap> attachments) {
    145     download_results_.push_back(result);
    146     last_download_attachments_ = attachments.Pass();
    147   }
    148 
    149   void RunLoop() {
    150     base::RunLoop run_loop;
    151     run_loop.RunUntilIdle();
    152   }
    153 
    154   const std::vector<AttachmentService::GetOrDownloadResult>&
    155   download_results() {
    156     return download_results_;
    157   }
    158 
    159   AttachmentMap* last_download_attachments() {
    160     return last_download_attachments_.get();
    161   }
    162 
    163   MockAttachmentStore* store() { return attachment_store_.get(); }
    164 
    165   MockAttachmentDownloader* downloader() {
    166     return attachment_downloader_.get();
    167   }
    168 
    169  private:
    170   base::MessageLoop message_loop_;
    171   base::WeakPtr<MockAttachmentStore> attachment_store_;
    172   base::WeakPtr<MockAttachmentDownloader> attachment_downloader_;
    173   scoped_ptr<AttachmentService> attachment_service_;
    174 
    175   std::vector<AttachmentService::GetOrDownloadResult> download_results_;
    176   scoped_ptr<AttachmentMap> last_download_attachments_;
    177 };
    178 
    179 TEST_F(AttachmentServiceImplTest, GetOrDownload_EmptyAttachmentList) {
    180   AttachmentIdList attachment_ids;
    181   attachment_service()->GetOrDownloadAttachments(attachment_ids,
    182                                                  download_callback());
    183   store()->RespondToRead(AttachmentIdSet());
    184 
    185   RunLoop();
    186   EXPECT_EQ(1U, download_results().size());
    187   EXPECT_EQ(0U, last_download_attachments()->size());
    188 }
    189 
    190 TEST_F(AttachmentServiceImplTest, GetOrDownload_Local) {
    191   AttachmentIdList attachment_ids;
    192   attachment_ids.push_back(AttachmentId::Create());
    193   attachment_service()->GetOrDownloadAttachments(attachment_ids,
    194                                                  download_callback());
    195   AttachmentIdSet local_attachments;
    196   local_attachments.insert(attachment_ids[0]);
    197   store()->RespondToRead(local_attachments);
    198 
    199   RunLoop();
    200   EXPECT_EQ(1U, download_results().size());
    201   EXPECT_EQ(1U, last_download_attachments()->size());
    202   EXPECT_TRUE(last_download_attachments()->find(attachment_ids[0]) !=
    203               last_download_attachments()->end());
    204 }
    205 
    206 TEST_F(AttachmentServiceImplTest, GetOrDownload_LocalRemoteUnavailable) {
    207   // Create attachment list with 3 ids.
    208   AttachmentIdList attachment_ids;
    209   attachment_ids.push_back(AttachmentId::Create());
    210   attachment_ids.push_back(AttachmentId::Create());
    211   attachment_ids.push_back(AttachmentId::Create());
    212   // Call attachment service.
    213   attachment_service()->GetOrDownloadAttachments(attachment_ids,
    214                                                  download_callback());
    215   // Ensure AttachmentStore is called.
    216   EXPECT_FALSE(store()->read_ids.empty());
    217 
    218   // make AttachmentStore return only attachment 0.
    219   AttachmentIdSet local_attachments;
    220   local_attachments.insert(attachment_ids[0]);
    221   store()->RespondToRead(local_attachments);
    222   RunLoop();
    223   // Ensure Downloader called with right attachment ids
    224   EXPECT_EQ(2U, downloader()->download_requests.size());
    225 
    226   // Make downloader return attachment 1.
    227   downloader()->RespondToDownload(attachment_ids[1],
    228                                   AttachmentDownloader::DOWNLOAD_SUCCESS);
    229   RunLoop();
    230   // Ensure consumer callback is not called.
    231   EXPECT_TRUE(download_results().empty());
    232 
    233   // Make downloader fail attachment 2.
    234   downloader()->RespondToDownload(
    235       attachment_ids[2], AttachmentDownloader::DOWNLOAD_UNSPECIFIED_ERROR);
    236   RunLoop();
    237   // Ensure callback is called
    238   EXPECT_FALSE(download_results().empty());
    239   // There should be only two attachments returned, 0 and 1.
    240   EXPECT_EQ(2U, last_download_attachments()->size());
    241   EXPECT_TRUE(last_download_attachments()->find(attachment_ids[0]) !=
    242               last_download_attachments()->end());
    243   EXPECT_TRUE(last_download_attachments()->find(attachment_ids[1]) !=
    244               last_download_attachments()->end());
    245   EXPECT_TRUE(last_download_attachments()->find(attachment_ids[2]) ==
    246               last_download_attachments()->end());
    247 }
    248 
    249 }  // namespace syncer
    250