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 #ifndef SYNC_INTERNAL_API_ATTACHMENTS_ATTACHMENT_STORE_TEST_TEMPLATE_H_
6 #define SYNC_INTERNAL_API_ATTACHMENTS_ATTACHMENT_STORE_TEST_TEMPLATE_H_
8 #include "sync/api/attachments/attachment_store.h"
10 #include "base/bind.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/memory/ref_counted_memory.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/run_loop.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "sync/api/attachments/attachment.h"
17 #include "sync/internal_api/public/attachments/attachment_util.h"
18 #include "sync/protocol/sync.pb.h"
19 #include "testing/gmock/include/gmock/gmock-matchers.h"
20 #include "testing/gtest/include/gtest/gtest.h"
22 // AttachmentStoreTest defines tests for AttachmentStore. To instantiate these
23 // tests for a particular implementation you need to:
24 // - Include this file in test.
25 // - Create factory class for attachment store that implements factory method.
26 // - add INSTANTIATE_TYPED_TEST_CASE_P statement.
27 // Here is how to do it for MyAttachmentStore:
29 // class MyAttachmentStoreFactory {
31 // scoped_refptr<AttachmentStore> CreateAttachmentStore() {
32 // return new MyAttachmentStore();
36 // INSTANTIATE_TYPED_TEST_CASE_P(My,
37 // AttachmentStoreTest,
38 // MyAttachmentStoreFactory);
42 const char kTestData1
[] = "test data 1";
43 const char kTestData2
[] = "test data 2";
45 template <typename AttachmentStoreFactory
>
46 class AttachmentStoreTest
: public testing::Test
{
48 AttachmentStoreFactory attachment_store_factory
;
49 base::MessageLoop message_loop
;
50 scoped_ptr
<AttachmentStore
> store
;
51 scoped_ptr
<AttachmentStoreForSync
> store_for_sync
;
52 AttachmentStore::Result result
;
53 scoped_ptr
<AttachmentMap
> attachments
;
54 scoped_ptr
<AttachmentIdList
> failed_attachment_ids
;
55 scoped_ptr
<AttachmentMetadataList
> attachment_metadata
;
57 AttachmentStore::ReadCallback read_callback
;
58 AttachmentStore::WriteCallback write_callback
;
59 AttachmentStore::DropCallback drop_callback
;
60 AttachmentStore::ReadMetadataCallback read_metadata_callback
;
62 scoped_refptr
<base::RefCountedString
> some_data1
;
63 scoped_refptr
<base::RefCountedString
> some_data2
;
65 AttachmentStoreTest() {}
67 void SetUp() override
{
68 store
= attachment_store_factory
.CreateAttachmentStore();
69 store_for_sync
= store
->CreateAttachmentStoreForSync();
72 read_callback
= base::Bind(&AttachmentStoreTest::CopyResultAttachments
,
73 base::Unretained(this),
76 &failed_attachment_ids
);
77 write_callback
= base::Bind(
78 &AttachmentStoreTest::CopyResult
, base::Unretained(this), &result
);
79 drop_callback
= write_callback
;
80 read_metadata_callback
=
81 base::Bind(&AttachmentStoreTest::CopyResultMetadata
,
82 base::Unretained(this), &result
, &attachment_metadata
);
84 some_data1
= new base::RefCountedString
;
85 some_data1
->data() = kTestData1
;
87 some_data2
= new base::RefCountedString
;
88 some_data2
->data() = kTestData2
;
91 void ClearAndPumpLoop() {
93 base::RunLoop().RunUntilIdle();
98 result
= AttachmentStore::UNSPECIFIED_ERROR
;
100 failed_attachment_ids
.reset();
101 attachment_metadata
.reset();
104 void CopyResult(AttachmentStore::Result
* destination_result
,
105 const AttachmentStore::Result
& source_result
) {
106 *destination_result
= source_result
;
109 void CopyResultAttachments(
110 AttachmentStore::Result
* destination_result
,
111 scoped_ptr
<AttachmentMap
>* destination_attachments
,
112 scoped_ptr
<AttachmentIdList
>* destination_failed_attachment_ids
,
113 const AttachmentStore::Result
& source_result
,
114 scoped_ptr
<AttachmentMap
> source_attachments
,
115 scoped_ptr
<AttachmentIdList
> source_failed_attachment_ids
) {
116 CopyResult(destination_result
, source_result
);
117 *destination_attachments
= source_attachments
.Pass();
118 *destination_failed_attachment_ids
= source_failed_attachment_ids
.Pass();
121 void CopyResultMetadata(
122 AttachmentStore::Result
* destination_result
,
123 scoped_ptr
<AttachmentMetadataList
>* destination_metadata
,
124 const AttachmentStore::Result
& source_result
,
125 scoped_ptr
<AttachmentMetadataList
> source_metadata
) {
126 CopyResult(destination_result
, source_result
);
127 *destination_metadata
= source_metadata
.Pass();
131 TYPED_TEST_CASE_P(AttachmentStoreTest
);
133 // Verify that CreateAttachmentStoreForSync() creates valid object.
134 TYPED_TEST_P(AttachmentStoreTest
, CreateAttachmentStoreForSync
) {
135 scoped_ptr
<AttachmentStoreForSync
> attachment_store_for_sync
=
136 this->store
->CreateAttachmentStoreForSync();
137 EXPECT_NE(nullptr, attachment_store_for_sync
);
140 // Verify that we do not overwrite existing attachments and that we do not treat
142 TYPED_TEST_P(AttachmentStoreTest
, Write_NoOverwriteNoError
) {
143 // Create two attachments with the same id but different data.
144 Attachment attachment1
= Attachment::Create(this->some_data1
);
145 Attachment attachment2
=
146 Attachment::CreateFromParts(attachment1
.GetId(), this->some_data2
);
148 // Write the first one.
149 AttachmentList some_attachments
;
150 some_attachments
.push_back(attachment1
);
151 this->store
->Write(some_attachments
, this->write_callback
);
152 this->ClearAndPumpLoop();
153 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
155 // Write the second one.
156 some_attachments
.clear();
157 some_attachments
.push_back(attachment2
);
158 this->store
->Write(some_attachments
, this->write_callback
);
159 this->ClearAndPumpLoop();
160 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
162 // Read it back and see that it was not overwritten.
163 AttachmentIdList some_attachment_ids
;
164 some_attachment_ids
.push_back(attachment1
.GetId());
165 this->store
->Read(some_attachment_ids
, this->read_callback
);
166 this->ClearAndPumpLoop();
167 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
168 EXPECT_EQ(1U, this->attachments
->size());
169 EXPECT_EQ(0U, this->failed_attachment_ids
->size());
170 AttachmentMap::const_iterator a1
=
171 this->attachments
->find(attachment1
.GetId());
172 EXPECT_TRUE(a1
!= this->attachments
->end());
173 EXPECT_TRUE(attachment1
.GetData()->Equals(a1
->second
.GetData()));
176 // Verify that we can write some attachments and read them back.
177 TYPED_TEST_P(AttachmentStoreTest
, Write_RoundTrip
) {
178 Attachment attachment1
= Attachment::Create(this->some_data1
);
179 Attachment attachment2
= Attachment::Create(this->some_data2
);
180 AttachmentList some_attachments
;
181 some_attachments
.push_back(attachment1
);
182 some_attachments
.push_back(attachment2
);
184 this->store
->Write(some_attachments
, this->write_callback
);
185 this->ClearAndPumpLoop();
186 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
188 AttachmentIdList some_attachment_ids
;
189 some_attachment_ids
.push_back(attachment1
.GetId());
190 some_attachment_ids
.push_back(attachment2
.GetId());
191 this->store
->Read(some_attachment_ids
, this->read_callback
);
192 this->ClearAndPumpLoop();
193 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
194 EXPECT_EQ(2U, this->attachments
->size());
195 EXPECT_EQ(0U, this->failed_attachment_ids
->size());
197 AttachmentMap::const_iterator a1
=
198 this->attachments
->find(attachment1
.GetId());
199 EXPECT_TRUE(a1
!= this->attachments
->end());
200 EXPECT_TRUE(attachment1
.GetData()->Equals(a1
->second
.GetData()));
202 AttachmentMap::const_iterator a2
=
203 this->attachments
->find(attachment2
.GetId());
204 EXPECT_TRUE(a2
!= this->attachments
->end());
205 EXPECT_TRUE(attachment2
.GetData()->Equals(a2
->second
.GetData()));
208 // Try to read two attachments when only one exists.
209 TYPED_TEST_P(AttachmentStoreTest
, Read_OneNotFound
) {
210 Attachment attachment1
= Attachment::Create(this->some_data1
);
211 Attachment attachment2
= Attachment::Create(this->some_data2
);
213 AttachmentList some_attachments
;
214 // Write attachment1 only.
215 some_attachments
.push_back(attachment1
);
216 this->store
->Write(some_attachments
, this->write_callback
);
217 this->ClearAndPumpLoop();
218 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
220 // Try to read both attachment1 and attachment2.
221 AttachmentIdList ids
;
222 ids
.push_back(attachment1
.GetId());
223 ids
.push_back(attachment2
.GetId());
224 this->store
->Read(ids
, this->read_callback
);
225 this->ClearAndPumpLoop();
227 // See that only attachment1 was read.
228 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR
, this->result
);
229 EXPECT_EQ(1U, this->attachments
->size());
230 EXPECT_EQ(1U, this->failed_attachment_ids
->size());
233 // Try to drop two attachments when only one exists. Verify that no error occurs
234 // and that the existing attachment was dropped.
235 TYPED_TEST_P(AttachmentStoreTest
, Drop_DropTwoButOnlyOneExists
) {
236 // First, create two attachments.
237 Attachment attachment1
= Attachment::Create(this->some_data1
);
238 Attachment attachment2
= Attachment::Create(this->some_data2
);
239 AttachmentList some_attachments
;
240 some_attachments
.push_back(attachment1
);
241 some_attachments
.push_back(attachment2
);
242 this->store
->Write(some_attachments
, this->write_callback
);
243 this->ClearAndPumpLoop();
244 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
246 // Drop attachment1 only.
247 AttachmentIdList ids
;
248 ids
.push_back(attachment1
.GetId());
249 this->store
->Drop(ids
, this->drop_callback
);
250 this->ClearAndPumpLoop();
251 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
253 // See that attachment1 is gone.
254 this->store
->Read(ids
, this->read_callback
);
255 this->ClearAndPumpLoop();
256 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR
, this->result
);
257 EXPECT_EQ(0U, this->attachments
->size());
258 EXPECT_EQ(1U, this->failed_attachment_ids
->size());
259 EXPECT_EQ(attachment1
.GetId(), (*this->failed_attachment_ids
)[0]);
261 // Drop both attachment1 and attachment2.
263 ids
.push_back(attachment1
.GetId());
264 ids
.push_back(attachment2
.GetId());
265 this->store
->Drop(ids
, this->drop_callback
);
266 this->ClearAndPumpLoop();
267 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
269 // See that attachment2 is now gone.
271 ids
.push_back(attachment2
.GetId());
272 this->store
->Read(ids
, this->read_callback
);
273 this->ClearAndPumpLoop();
274 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR
, this->result
);
275 EXPECT_EQ(0U, this->attachments
->size());
276 EXPECT_EQ(1U, this->failed_attachment_ids
->size());
277 EXPECT_EQ(attachment2
.GetId(), (*this->failed_attachment_ids
)[0]);
280 // Verify that attempting to drop an attachment that does not exist is not an
282 TYPED_TEST_P(AttachmentStoreTest
, Drop_DoesNotExist
) {
283 Attachment attachment1
= Attachment::Create(this->some_data1
);
284 AttachmentList some_attachments
;
285 some_attachments
.push_back(attachment1
);
286 this->store
->Write(some_attachments
, this->write_callback
);
287 this->ClearAndPumpLoop();
288 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
290 // Drop the attachment.
291 AttachmentIdList ids
;
292 ids
.push_back(attachment1
.GetId());
293 this->store
->Drop(ids
, this->drop_callback
);
294 this->ClearAndPumpLoop();
295 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
297 // See that it's gone.
298 this->store
->Read(ids
, this->read_callback
);
299 this->ClearAndPumpLoop();
300 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR
, this->result
);
301 EXPECT_EQ(0U, this->attachments
->size());
302 EXPECT_EQ(1U, this->failed_attachment_ids
->size());
303 EXPECT_EQ(attachment1
.GetId(), (*this->failed_attachment_ids
)[0]);
305 // Drop again, see that no error occurs.
307 ids
.push_back(attachment1
.GetId());
308 this->store
->Drop(ids
, this->drop_callback
);
309 this->ClearAndPumpLoop();
310 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
313 // Verify getting metadata for specific attachments.
314 TYPED_TEST_P(AttachmentStoreTest
, ReadMetadataById
) {
315 Attachment attachment1
= Attachment::Create(this->some_data1
);
316 Attachment attachment2
= Attachment::Create(this->some_data2
);
318 AttachmentList some_attachments
;
319 // Write attachment1 only.
320 some_attachments
.push_back(attachment1
);
321 this->store
->Write(some_attachments
, this->write_callback
);
322 this->ClearAndPumpLoop();
323 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
325 // Try to read metadata for both attachment1 and attachment2.
326 AttachmentIdList ids
;
327 ids
.push_back(attachment1
.GetId());
328 ids
.push_back(attachment2
.GetId());
329 this->store
->ReadMetadataById(ids
, this->read_metadata_callback
);
330 this->ClearAndPumpLoop();
332 // See that only one entry was read.
333 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR
, this->result
);
334 EXPECT_EQ(1U, this->attachment_metadata
->size());
336 // Now write attachment2.
337 some_attachments
[0] = attachment2
;
338 this->store
->Write(some_attachments
, this->write_callback
);
339 this->ClearAndPumpLoop();
340 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
342 // Try to read metadata for both attachment1 and attachment2 again.
343 this->store
->ReadMetadataById(ids
, this->read_metadata_callback
);
344 this->ClearAndPumpLoop();
345 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
346 EXPECT_EQ(2U, this->attachment_metadata
->size());
348 // Verify that we've got both entries back in the right order.
349 AttachmentMetadataList::const_iterator iter
=
350 this->attachment_metadata
->begin();
351 EXPECT_EQ(attachment1
.GetId(), iter
->GetId());
353 EXPECT_EQ(attachment2
.GetId(), iter
->GetId());
356 // Verify that ReadMetadata/ReadMetadataForSync returns metadata for correct
357 // set of attachments.
358 TYPED_TEST_P(AttachmentStoreTest
, ReadMetadata
) {
359 // Try to read all metadata from an empty store.
360 this->store
->ReadMetadata(this->read_metadata_callback
);
361 this->ClearAndPumpLoop();
362 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
363 EXPECT_EQ(0U, this->attachment_metadata
->size());
365 // Create and write attachments with different set of references.
366 Attachment attachment_mt
= Attachment::Create(this->some_data1
);
367 Attachment attachment_sync
= Attachment::Create(this->some_data1
);
368 Attachment attachment_both
= Attachment::Create(this->some_data1
);
370 AttachmentList attachments
;
371 attachments
.push_back(attachment_mt
);
372 attachments
.push_back(attachment_sync
);
373 attachments
.push_back(attachment_both
);
374 this->store
->Write(attachments
, this->write_callback
);
375 this->ClearAndPumpLoop();
376 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
378 AttachmentIdList ids
;
379 ids
.push_back(attachment_sync
.GetId());
380 ids
.push_back(attachment_both
.GetId());
381 this->store_for_sync
->SetSyncReference(ids
);
384 ids
.push_back(attachment_sync
.GetId());
385 this->store
->Drop(ids
, this->drop_callback
);
386 this->ClearAndPumpLoop();
388 // Calling ReadMetadataById for above three attachments should only return
389 // attachments with model type reference.
391 ids
.push_back(attachment_mt
.GetId());
392 ids
.push_back(attachment_sync
.GetId());
393 ids
.push_back(attachment_both
.GetId());
394 this->store
->ReadMetadataById(ids
, this->read_metadata_callback
);
395 this->ClearAndPumpLoop();
396 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR
, this->result
);
397 EXPECT_EQ(2U, this->attachment_metadata
->size());
398 AttachmentIdSet model_type_id_set
;
399 model_type_id_set
.insert(attachment_mt
.GetId());
400 model_type_id_set
.insert(attachment_both
.GetId());
401 EXPECT_THAT(model_type_id_set
,
402 testing::Contains((*this->attachment_metadata
)[0].GetId()));
403 EXPECT_THAT(model_type_id_set
,
404 testing::Contains((*this->attachment_metadata
)[1].GetId()));
406 // Call to ReadMetadata() should only return attachments with model type
408 this->store
->ReadMetadata(this->read_metadata_callback
);
409 this->ClearAndPumpLoop();
410 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
411 EXPECT_EQ(2U, this->attachment_metadata
->size());
413 // Verify that we get all attachments back (the order is undefined).
414 EXPECT_THAT(model_type_id_set
,
415 testing::Contains((*this->attachment_metadata
)[0].GetId()));
416 EXPECT_THAT(model_type_id_set
,
417 testing::Contains((*this->attachment_metadata
)[1].GetId()));
419 // Call to ReadMetadataForSync() should only return attachments with sync
421 this->store_for_sync
->ReadMetadataForSync(this->read_metadata_callback
);
422 this->ClearAndPumpLoop();
423 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
424 EXPECT_EQ(2U, this->attachment_metadata
->size());
426 AttachmentIdSet sync_id_set
;
427 sync_id_set
.insert(attachment_sync
.GetId());
428 sync_id_set
.insert(attachment_both
.GetId());
429 EXPECT_THAT(sync_id_set
,
430 testing::Contains((*this->attachment_metadata
)[0].GetId()));
431 EXPECT_THAT(sync_id_set
,
432 testing::Contains((*this->attachment_metadata
)[1].GetId()));
435 // Verify that setting/droping references gets reflected in ReadMetadata and
436 // that attachment is only deleted after last reference is droped.
437 TYPED_TEST_P(AttachmentStoreTest
, SetSyncReference_DropSyncReference
) {
438 Attachment attachment
= Attachment::Create(this->some_data1
);
439 AttachmentList attachments
;
440 attachments
.push_back(attachment
);
441 AttachmentIdList ids
;
442 ids
.push_back(attachment
.GetId());
444 // When writing attachment to store only model type reference should be set.
445 this->store
->Write(attachments
, this->write_callback
);
447 this->store
->ReadMetadata(this->read_metadata_callback
);
448 this->ClearAndPumpLoop();
449 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
450 EXPECT_EQ(1U, this->attachment_metadata
->size());
451 EXPECT_EQ(attachment
.GetId(), this->attachment_metadata
->begin()->GetId());
453 this->store_for_sync
->ReadMetadataForSync(this->read_metadata_callback
);
454 this->ClearAndPumpLoop();
455 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
456 EXPECT_EQ(0U, this->attachment_metadata
->size());
458 // After call to SetSyncReference() ReadMetadataForSync should start returning
460 this->store_for_sync
->SetSyncReference(ids
);
462 this->store_for_sync
->ReadMetadataForSync(this->read_metadata_callback
);
463 this->ClearAndPumpLoop();
464 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
465 EXPECT_EQ(1U, this->attachment_metadata
->size());
467 // Call SetSyncReference() to verify it is idempotent.
468 this->store_for_sync
->SetSyncReference(ids
);
469 this->ClearAndPumpLoop();
471 // Droping attachment should remove model type reference, but there is still
473 this->store
->Drop(ids
, this->drop_callback
);
474 this->ClearAndPumpLoop();
475 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
477 this->store
->ReadMetadata(this->read_metadata_callback
);
478 this->ClearAndPumpLoop();
479 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
480 EXPECT_EQ(0U, this->attachment_metadata
->size());
482 this->store_for_sync
->ReadMetadataForSync(this->read_metadata_callback
);
483 this->ClearAndPumpLoop();
484 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
485 EXPECT_EQ(1U, this->attachment_metadata
->size());
487 // ReadMetadataById should return UNSPECIFIED_ERROR, attachment doesn't have
488 // model type reference.
489 this->store
->ReadMetadataById(ids
, this->read_metadata_callback
);
490 this->ClearAndPumpLoop();
491 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR
, this->result
);
492 EXPECT_EQ(0U, this->attachment_metadata
->size());
494 // Call Drop() again to ensure it doesn't fail.
495 this->store
->Drop(ids
, this->drop_callback
);
496 this->ClearAndPumpLoop();
497 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
499 // After droping sync reference attachment should be deleted from store.
500 // ReadMetadataForSync should return empty result.
501 this->store_for_sync
->DropSyncReference(ids
);
503 this->store_for_sync
->ReadMetadataForSync(this->read_metadata_callback
);
504 this->ClearAndPumpLoop();
505 EXPECT_EQ(AttachmentStore::SUCCESS
, this->result
);
506 EXPECT_EQ(0U, this->attachment_metadata
->size());
509 REGISTER_TYPED_TEST_CASE_P(AttachmentStoreTest
,
510 CreateAttachmentStoreForSync
,
511 Write_NoOverwriteNoError
,
514 Drop_DropTwoButOnlyOneExists
,
518 SetSyncReference_DropSyncReference
);
520 } // namespace syncer
522 #endif // SYNC_INTERNAL_API_ATTACHMENTS_ATTACHMENT_STORE_TEST_TEMPLATE_H_