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