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 #include "sync/internal_api/public/attachments/on_disk_attachment_store.h"
7 #include "base/files/file_path.h"
8 #include "base/files/file_util.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/run_loop.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/time/time.h"
14 #include "sync/internal_api/attachments/attachment_store_test_template.h"
15 #include "sync/internal_api/attachments/proto/attachment_store.pb.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "third_party/leveldatabase/src/include/leveldb/db.h"
19 #include "third_party/leveldatabase/src/include/leveldb/options.h"
20 #include "third_party/leveldatabase/src/include/leveldb/slice.h"
21 #include "third_party/leveldatabase/src/include/leveldb/status.h"
27 void AttachmentStoreCreated(AttachmentStore::Result
* result_dest
,
28 const AttachmentStore::Result
& result
) {
29 *result_dest
= result
;
34 // Instantiation of common attachment store tests.
35 class OnDiskAttachmentStoreFactory
{
37 OnDiskAttachmentStoreFactory() {}
38 ~OnDiskAttachmentStoreFactory() {}
40 scoped_ptr
<AttachmentStore
> CreateAttachmentStore() {
41 EXPECT_TRUE(temp_dir_
.CreateUniqueTempDir());
42 scoped_ptr
<AttachmentStore
> store
;
43 AttachmentStore::Result result
= AttachmentStore::UNSPECIFIED_ERROR
;
44 store
= AttachmentStore::CreateOnDiskStore(
45 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get(),
46 base::Bind(&AttachmentStoreCreated
, &result
));
47 base::RunLoop run_loop
;
48 run_loop
.RunUntilIdle();
49 EXPECT_EQ(AttachmentStore::SUCCESS
, result
);
54 base::ScopedTempDir temp_dir_
;
57 INSTANTIATE_TYPED_TEST_CASE_P(OnDisk
,
59 OnDiskAttachmentStoreFactory
);
61 // Tests specific to OnDiskAttachmentStore.
62 class OnDiskAttachmentStoreSpecificTest
: public testing::Test
{
64 base::ScopedTempDir temp_dir_
;
65 base::FilePath db_path_
;
66 base::MessageLoop message_loop_
;
67 scoped_ptr
<AttachmentStore
> store_
;
69 OnDiskAttachmentStoreSpecificTest() {}
71 void SetUp() override
{
72 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
73 db_path_
= temp_dir_
.path().Append(FILE_PATH_LITERAL("leveldb"));
74 base::CreateDirectory(db_path_
);
77 void CopyResult(AttachmentStore::Result
* destination_result
,
78 const AttachmentStore::Result
& source_result
) {
79 *destination_result
= source_result
;
82 void CopyResultAttachments(
83 AttachmentStore::Result
* destination_result
,
84 AttachmentIdList
* destination_failed_attachment_ids
,
85 const AttachmentStore::Result
& source_result
,
86 scoped_ptr
<AttachmentMap
> source_attachments
,
87 scoped_ptr
<AttachmentIdList
> source_failed_attachment_ids
) {
88 CopyResult(destination_result
, source_result
);
89 *destination_failed_attachment_ids
= *source_failed_attachment_ids
;
92 void CopyResultMetadata(
93 AttachmentStore::Result
* destination_result
,
94 scoped_ptr
<AttachmentMetadataList
>* destination_metadata
,
95 const AttachmentStore::Result
& source_result
,
96 scoped_ptr
<AttachmentMetadataList
> source_metadata
) {
97 CopyResult(destination_result
, source_result
);
98 *destination_metadata
= source_metadata
.Pass();
101 scoped_ptr
<leveldb::DB
> OpenLevelDB() {
103 leveldb::Options options
;
104 options
.create_if_missing
= true;
106 leveldb::DB::Open(options
, db_path_
.AsUTF8Unsafe(), &db
);
108 return make_scoped_ptr(db
);
111 void UpdateRecord(const std::string
& key
, const std::string
& content
) {
112 scoped_ptr
<leveldb::DB
> db
= OpenLevelDB();
113 leveldb::Status s
= db
->Put(leveldb::WriteOptions(), key
, content
);
117 void UpdateStoreMetadataRecord(const std::string
& content
) {
118 UpdateRecord("database-metadata", content
);
121 void UpdateAttachmentMetadataRecord(const AttachmentId
& attachment_id
,
122 const std::string
& content
) {
123 std::string metadata_key
=
124 OnDiskAttachmentStore::MakeMetadataKeyFromAttachmentId(attachment_id
);
125 UpdateRecord(metadata_key
, content
);
128 std::string
ReadStoreMetadataRecord() {
129 scoped_ptr
<leveldb::DB
> db
= OpenLevelDB();
132 db
->Get(leveldb::ReadOptions(), "database-metadata", &content
);
137 void VerifyAttachmentRecordsPresent(const AttachmentId
& attachment_id
,
138 bool expect_records_present
) {
139 scoped_ptr
<leveldb::DB
> db
= OpenLevelDB();
141 std::string metadata_key
=
142 OnDiskAttachmentStore::MakeMetadataKeyFromAttachmentId(attachment_id
);
143 std::string data_key
=
144 OnDiskAttachmentStore::MakeDataKeyFromAttachmentId(attachment_id
);
146 leveldb::Status s
= db
->Get(leveldb::ReadOptions(), data_key
, &data
);
147 if (expect_records_present
)
150 EXPECT_TRUE(s
.IsNotFound());
151 s
= db
->Get(leveldb::ReadOptions(), metadata_key
, &data
);
152 if (expect_records_present
)
155 EXPECT_TRUE(s
.IsNotFound());
159 base::RunLoop run_loop
;
160 run_loop
.RunUntilIdle();
164 // Ensure that store can be closed and reopen while retaining stored
166 TEST_F(OnDiskAttachmentStoreSpecificTest
, CloseAndReopen
) {
167 AttachmentStore::Result result
;
169 result
= AttachmentStore::UNSPECIFIED_ERROR
;
170 store_
= AttachmentStore::CreateOnDiskStore(
171 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get(),
172 base::Bind(&AttachmentStoreCreated
, &result
));
174 EXPECT_EQ(AttachmentStore::SUCCESS
, result
);
176 result
= AttachmentStore::UNSPECIFIED_ERROR
;
177 std::string some_data
= "data";
178 Attachment attachment
=
179 Attachment::Create(base::RefCountedString::TakeString(&some_data
));
180 AttachmentList attachments
;
181 attachments
.push_back(attachment
);
182 store_
->Write(attachments
,
183 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResult
,
184 base::Unretained(this),
187 EXPECT_EQ(AttachmentStore::SUCCESS
, result
);
189 // Close and reopen attachment store.
191 result
= AttachmentStore::UNSPECIFIED_ERROR
;
192 store_
= AttachmentStore::CreateOnDiskStore(
193 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get(),
194 base::Bind(&AttachmentStoreCreated
, &result
));
196 EXPECT_EQ(AttachmentStore::SUCCESS
, result
);
198 result
= AttachmentStore::UNSPECIFIED_ERROR
;
199 AttachmentIdList attachment_ids
;
200 attachment_ids
.push_back(attachment
.GetId());
201 AttachmentIdList failed_attachment_ids
;
204 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultAttachments
,
205 base::Unretained(this), &result
, &failed_attachment_ids
));
207 EXPECT_EQ(AttachmentStore::SUCCESS
, result
);
208 EXPECT_TRUE(failed_attachment_ids
.empty());
211 // Ensure loading corrupt attachment store fails.
212 TEST_F(OnDiskAttachmentStoreSpecificTest
, FailToOpen
) {
213 // To simulate corrupt database write empty CURRENT file.
214 std::string current_file_content
= "";
215 base::WriteFile(db_path_
.Append(FILE_PATH_LITERAL("CURRENT")),
216 current_file_content
.c_str(), current_file_content
.size());
218 AttachmentStore::Result result
= AttachmentStore::SUCCESS
;
219 store_
= AttachmentStore::CreateOnDiskStore(
220 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get(),
221 base::Bind(&AttachmentStoreCreated
, &result
));
223 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR
, result
);
226 // Ensure that attachment store works correctly when store metadata is missing,
227 // corrupt or has unknown schema version.
228 TEST_F(OnDiskAttachmentStoreSpecificTest
, StoreMetadata
) {
229 // Create and close empty database.
231 // Open database with AttachmentStore.
232 AttachmentStore::Result result
= AttachmentStore::UNSPECIFIED_ERROR
;
233 store_
= AttachmentStore::CreateOnDiskStore(
234 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get(),
235 base::Bind(&AttachmentStoreCreated
, &result
));
237 EXPECT_EQ(AttachmentStore::SUCCESS
, result
);
238 // Close AttachmentStore so that test can check content.
242 // AttachmentStore should create metadata record.
243 std::string data
= ReadStoreMetadataRecord();
244 attachment_store_pb::StoreMetadata metadata
;
245 EXPECT_TRUE(metadata
.ParseFromString(data
));
246 EXPECT_EQ(1, metadata
.schema_version());
248 // Set unknown future schema version.
249 metadata
.set_schema_version(2);
250 data
= metadata
.SerializeAsString();
251 UpdateStoreMetadataRecord(data
);
253 // AttachmentStore should fail to load.
254 result
= AttachmentStore::SUCCESS
;
255 store_
= AttachmentStore::CreateOnDiskStore(
256 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get(),
257 base::Bind(&AttachmentStoreCreated
, &result
));
259 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR
, result
);
261 // Write garbage into metadata record.
262 UpdateStoreMetadataRecord("abra.cadabra");
264 // AttachmentStore should fail to load.
265 result
= AttachmentStore::SUCCESS
;
266 store_
= AttachmentStore::CreateOnDiskStore(
267 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get(),
268 base::Bind(&AttachmentStoreCreated
, &result
));
270 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR
, result
);
273 // Ensure that attachment store correctly maintains metadata records for
275 TEST_F(OnDiskAttachmentStoreSpecificTest
, RecordMetadata
) {
276 // Create attachment store.
277 AttachmentStore::Result create_result
= AttachmentStore::UNSPECIFIED_ERROR
;
278 store_
= AttachmentStore::CreateOnDiskStore(
279 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get(),
280 base::Bind(&AttachmentStoreCreated
, &create_result
));
282 // Write two attachments.
283 AttachmentStore::Result write_result
= AttachmentStore::UNSPECIFIED_ERROR
;
284 std::string some_data
;
285 AttachmentList attachments
;
287 attachments
.push_back(
288 Attachment::Create(base::RefCountedString::TakeString(&some_data
)));
290 attachments
.push_back(
291 Attachment::Create(base::RefCountedString::TakeString(&some_data
)));
292 store_
->Write(attachments
,
293 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResult
,
294 base::Unretained(this), &write_result
));
296 // Delete one of written attachments.
297 AttachmentStore::Result drop_result
= AttachmentStore::UNSPECIFIED_ERROR
;
298 AttachmentIdList attachment_ids
;
299 attachment_ids
.push_back(attachments
[0].GetId());
300 store_
->Drop(attachment_ids
,
301 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResult
,
302 base::Unretained(this), &drop_result
));
305 EXPECT_EQ(AttachmentStore::SUCCESS
, create_result
);
306 EXPECT_EQ(AttachmentStore::SUCCESS
, write_result
);
307 EXPECT_EQ(AttachmentStore::SUCCESS
, drop_result
);
309 // Verify that attachment store contains only records for second attachment.
310 VerifyAttachmentRecordsPresent(attachments
[0].GetId(), false);
311 VerifyAttachmentRecordsPresent(attachments
[1].GetId(), true);
314 // Ensure that attachment store fails to load attachment if the crc in the store
315 // does not match the data.
316 TEST_F(OnDiskAttachmentStoreSpecificTest
, MismatchedCrcInStore
) {
317 // Create attachment store.
318 AttachmentStore::Result create_result
= AttachmentStore::UNSPECIFIED_ERROR
;
319 store_
= AttachmentStore::CreateOnDiskStore(
320 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get(),
321 base::Bind(&AttachmentStoreCreated
, &create_result
));
323 // Write attachment with incorrect crc32c.
324 AttachmentStore::Result write_result
= AttachmentStore::UNSPECIFIED_ERROR
;
325 const uint32_t intentionally_wrong_crc32c
= 0;
327 scoped_refptr
<base::RefCountedString
> some_data(new base::RefCountedString());
328 some_data
->data() = "data1";
329 Attachment attachment
= Attachment::CreateFromParts(
330 AttachmentId::Create(some_data
->size(), intentionally_wrong_crc32c
),
332 AttachmentList attachments
;
333 attachments
.push_back(attachment
);
334 store_
->Write(attachments
,
335 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResult
,
336 base::Unretained(this), &write_result
));
339 AttachmentStore::Result read_result
= AttachmentStore::UNSPECIFIED_ERROR
;
340 AttachmentIdList attachment_ids
;
341 attachment_ids
.push_back(attachment
.GetId());
342 AttachmentIdList failed_attachment_ids
;
345 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultAttachments
,
346 base::Unretained(this), &read_result
, &failed_attachment_ids
));
348 EXPECT_EQ(AttachmentStore::SUCCESS
, create_result
);
349 EXPECT_EQ(AttachmentStore::SUCCESS
, write_result
);
350 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR
, read_result
);
351 EXPECT_THAT(failed_attachment_ids
, testing::ElementsAre(attachment
.GetId()));
354 // Ensure that attachment store fails to load attachment if the crc in the id
355 // does not match the data.
356 TEST_F(OnDiskAttachmentStoreSpecificTest
, MismatchedCrcInId
) {
357 // Create attachment store.
358 AttachmentStore::Result create_result
= AttachmentStore::UNSPECIFIED_ERROR
;
359 store_
= AttachmentStore::CreateOnDiskStore(
360 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get(),
361 base::Bind(&AttachmentStoreCreated
, &create_result
));
363 AttachmentStore::Result write_result
= AttachmentStore::UNSPECIFIED_ERROR
;
364 scoped_refptr
<base::RefCountedString
> some_data(new base::RefCountedString());
365 some_data
->data() = "data1";
366 Attachment attachment
= Attachment::Create(some_data
);
367 AttachmentList attachments
;
368 attachments
.push_back(attachment
);
369 store_
->Write(attachments
,
370 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResult
,
371 base::Unretained(this), &write_result
));
373 // Read, but with the wrong crc32c in the id.
374 AttachmentStore::Result read_result
= AttachmentStore::SUCCESS
;
376 AttachmentId id_with_bad_crc32c
=
377 AttachmentId::Create(attachment
.GetId().GetSize(), 12345);
378 AttachmentIdList attachment_ids
;
379 attachment_ids
.push_back(id_with_bad_crc32c
);
380 AttachmentIdList failed_attachment_ids
;
383 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultAttachments
,
384 base::Unretained(this), &read_result
, &failed_attachment_ids
));
386 EXPECT_EQ(AttachmentStore::SUCCESS
, create_result
);
387 EXPECT_EQ(AttachmentStore::SUCCESS
, write_result
);
388 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR
, read_result
);
389 EXPECT_THAT(failed_attachment_ids
, testing::ElementsAre(id_with_bad_crc32c
));
392 // Ensure that after store initialization failure ReadWrite/Drop operations fail
393 // with correct error.
394 TEST_F(OnDiskAttachmentStoreSpecificTest
, OpsAfterInitializationFailed
) {
395 // To simulate corrupt database write empty CURRENT file.
396 std::string current_file_content
= "";
397 base::WriteFile(db_path_
.Append(FILE_PATH_LITERAL("CURRENT")),
398 current_file_content
.c_str(), current_file_content
.size());
400 AttachmentStore::Result create_result
= AttachmentStore::SUCCESS
;
401 store_
= AttachmentStore::CreateOnDiskStore(
402 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get(),
403 base::Bind(&AttachmentStoreCreated
, &create_result
));
405 // Reading from uninitialized store should result in
406 // STORE_INITIALIZATION_FAILED.
407 AttachmentStore::Result read_result
= AttachmentStore::SUCCESS
;
408 AttachmentIdList attachment_ids
;
409 std::string
some_data("data1");
410 Attachment attachment
=
411 Attachment::Create(base::RefCountedString::TakeString(&some_data
));
412 attachment_ids
.push_back(attachment
.GetId());
413 AttachmentIdList failed_attachment_ids
;
416 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultAttachments
,
417 base::Unretained(this), &read_result
, &failed_attachment_ids
));
419 // Dropping from uninitialized store should result in
420 // STORE_INITIALIZATION_FAILED.
421 AttachmentStore::Result drop_result
= AttachmentStore::SUCCESS
;
422 store_
->Drop(attachment_ids
,
423 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResult
,
424 base::Unretained(this), &drop_result
));
426 // Writing to uninitialized store should result in
427 // STORE_INITIALIZATION_FAILED.
428 AttachmentStore::Result write_result
= AttachmentStore::SUCCESS
;
429 AttachmentList attachments
;
430 attachments
.push_back(attachment
);
431 store_
->Write(attachments
,
432 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResult
,
433 base::Unretained(this), &write_result
));
436 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR
, create_result
);
437 EXPECT_EQ(AttachmentStore::STORE_INITIALIZATION_FAILED
, read_result
);
438 EXPECT_THAT(failed_attachment_ids
, testing::ElementsAre(attachment_ids
[0]));
439 EXPECT_EQ(AttachmentStore::STORE_INITIALIZATION_FAILED
, drop_result
);
440 EXPECT_EQ(AttachmentStore::STORE_INITIALIZATION_FAILED
, write_result
);
443 // Ensure that attachment store handles the case of having an unexpected
444 // record at the end without crashing.
445 TEST_F(OnDiskAttachmentStoreSpecificTest
, ReadMetadataWithUnexpectedRecord
) {
446 // Write a bogus entry at the end of the database.
447 UpdateRecord("zzz", "foobar");
449 // Create attachment store.
450 AttachmentStore::Result create_result
= AttachmentStore::UNSPECIFIED_ERROR
;
451 store_
= AttachmentStore::CreateOnDiskStore(
452 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get(),
453 base::Bind(&AttachmentStoreCreated
, &create_result
));
455 // Read all metadata. Should be getting no error and zero entries.
456 AttachmentStore::Result metadata_result
= AttachmentStore::UNSPECIFIED_ERROR
;
457 scoped_ptr
<AttachmentMetadataList
> metadata_list
;
458 store_
->ReadMetadata(
459 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultMetadata
,
460 base::Unretained(this), &metadata_result
, &metadata_list
));
462 EXPECT_EQ(AttachmentStore::SUCCESS
, create_result
);
463 EXPECT_EQ(AttachmentStore::SUCCESS
, metadata_result
);
464 EXPECT_EQ(0U, metadata_list
->size());
465 metadata_list
.reset();
467 // Write 3 attachments to the store
468 AttachmentList attachments
;
470 for (int i
= 0; i
< 3; i
++) {
471 std::string some_data
= "data";
472 Attachment attachment
=
473 Attachment::Create(base::RefCountedString::TakeString(&some_data
));
474 attachments
.push_back(attachment
);
476 AttachmentStore::Result write_result
= AttachmentStore::UNSPECIFIED_ERROR
;
477 store_
->Write(attachments
,
478 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResult
,
479 base::Unretained(this), &write_result
));
481 // Read all metadata back. We should be getting 3 entries.
482 store_
->ReadMetadata(
483 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultMetadata
,
484 base::Unretained(this), &metadata_result
, &metadata_list
));
486 EXPECT_EQ(AttachmentStore::SUCCESS
, write_result
);
487 EXPECT_EQ(AttachmentStore::SUCCESS
, metadata_result
);
488 EXPECT_EQ(3U, metadata_list
->size());
489 // Get Id of the attachment in the middle.
490 AttachmentId id
= (*metadata_list
.get())[1].GetId();
491 metadata_list
.reset();
497 // Modify the middle attachment metadata entry so that it isn't valid anymore.
498 UpdateAttachmentMetadataRecord(id
, "foobar");
501 create_result
= AttachmentStore::UNSPECIFIED_ERROR
;
502 metadata_result
= AttachmentStore::UNSPECIFIED_ERROR
;
503 store_
= AttachmentStore::CreateOnDiskStore(
504 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get(),
505 base::Bind(&AttachmentStoreCreated
, &create_result
));
507 // Read all metadata back. We should be getting a failure and
508 // only 2 entries now.
509 store_
->ReadMetadata(
510 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultMetadata
,
511 base::Unretained(this), &metadata_result
, &metadata_list
));
513 EXPECT_EQ(AttachmentStore::SUCCESS
, create_result
);
514 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR
, metadata_result
);
515 EXPECT_EQ(2U, metadata_list
->size());
518 } // namespace syncer