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_refptr
<AttachmentStore
> CreateAttachmentStore() {
41 EXPECT_TRUE(temp_dir_
.CreateUniqueTempDir());
42 scoped_refptr
<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_refptr
<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 with mismatched crc.
315 TEST_F(OnDiskAttachmentStoreSpecificTest
, MismatchedCrc
) {
316 // Create attachment store.
317 AttachmentStore::Result create_result
= AttachmentStore::UNSPECIFIED_ERROR
;
318 store_
= AttachmentStore::CreateOnDiskStore(
319 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get(),
320 base::Bind(&AttachmentStoreCreated
, &create_result
));
322 // Write attachment with incorrect crc32c.
323 AttachmentStore::Result write_result
= AttachmentStore::UNSPECIFIED_ERROR
;
324 const uint32_t intentionally_wrong_crc32c
= 0;
325 std::string
some_data("data1");
326 Attachment attachment
= Attachment::CreateFromParts(
327 AttachmentId::Create(), base::RefCountedString::TakeString(&some_data
),
328 intentionally_wrong_crc32c
);
329 AttachmentList attachments
;
330 attachments
.push_back(attachment
);
331 store_
->Write(attachments
,
332 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResult
,
333 base::Unretained(this), &write_result
));
336 AttachmentStore::Result read_result
= AttachmentStore::UNSPECIFIED_ERROR
;
337 AttachmentIdList attachment_ids
;
338 attachment_ids
.push_back(attachment
.GetId());
339 AttachmentIdList failed_attachment_ids
;
342 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultAttachments
,
343 base::Unretained(this), &read_result
, &failed_attachment_ids
));
345 EXPECT_EQ(AttachmentStore::SUCCESS
, create_result
);
346 EXPECT_EQ(AttachmentStore::SUCCESS
, write_result
);
347 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR
, read_result
);
348 EXPECT_THAT(failed_attachment_ids
, testing::ElementsAre(attachment
.GetId()));
351 // Ensure that after store initialization failure ReadWrite/Drop operations fail
352 // with correct error.
353 TEST_F(OnDiskAttachmentStoreSpecificTest
, OpsAfterInitializationFailed
) {
354 // To simulate corrupt database write empty CURRENT file.
355 std::string current_file_content
= "";
356 base::WriteFile(db_path_
.Append(FILE_PATH_LITERAL("CURRENT")),
357 current_file_content
.c_str(), current_file_content
.size());
359 AttachmentStore::Result create_result
= AttachmentStore::SUCCESS
;
360 store_
= AttachmentStore::CreateOnDiskStore(
361 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get(),
362 base::Bind(&AttachmentStoreCreated
, &create_result
));
364 // Reading from uninitialized store should result in
365 // STORE_INITIALIZATION_FAILED.
366 AttachmentStore::Result read_result
= AttachmentStore::SUCCESS
;
367 AttachmentIdList attachment_ids
;
368 attachment_ids
.push_back(AttachmentId::Create());
369 AttachmentIdList failed_attachment_ids
;
372 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultAttachments
,
373 base::Unretained(this), &read_result
, &failed_attachment_ids
));
375 // Dropping from uninitialized store should result in
376 // STORE_INITIALIZATION_FAILED.
377 AttachmentStore::Result drop_result
= AttachmentStore::SUCCESS
;
378 store_
->Drop(attachment_ids
,
379 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResult
,
380 base::Unretained(this), &drop_result
));
382 // Writing to uninitialized store should result in
383 // STORE_INITIALIZATION_FAILED.
384 AttachmentStore::Result write_result
= AttachmentStore::SUCCESS
;
385 std::string some_data
;
386 AttachmentList attachments
;
388 attachments
.push_back(
389 Attachment::Create(base::RefCountedString::TakeString(&some_data
)));
390 store_
->Write(attachments
,
391 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResult
,
392 base::Unretained(this), &write_result
));
395 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR
, create_result
);
396 EXPECT_EQ(AttachmentStore::STORE_INITIALIZATION_FAILED
, read_result
);
397 EXPECT_THAT(failed_attachment_ids
, testing::ElementsAre(attachment_ids
[0]));
398 EXPECT_EQ(AttachmentStore::STORE_INITIALIZATION_FAILED
, drop_result
);
399 EXPECT_EQ(AttachmentStore::STORE_INITIALIZATION_FAILED
, write_result
);
402 // Ensure that attachment store handles the case of having an unexpected
403 // record at the end without crashing.
404 TEST_F(OnDiskAttachmentStoreSpecificTest
, ReadAllMetadataWithUnexpectedRecord
) {
405 // Write a bogus entry at the end of the database.
406 UpdateRecord("zzz", "foobar");
408 // Create attachment store.
409 AttachmentStore::Result create_result
= AttachmentStore::UNSPECIFIED_ERROR
;
410 store_
= AttachmentStore::CreateOnDiskStore(
411 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get(),
412 base::Bind(&AttachmentStoreCreated
, &create_result
));
414 // Read all metadata. Should be getting no error and zero entries.
415 AttachmentStore::Result metadata_result
= AttachmentStore::UNSPECIFIED_ERROR
;
416 scoped_ptr
<AttachmentMetadataList
> metadata_list
;
417 store_
->ReadAllMetadata(
418 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultMetadata
,
419 base::Unretained(this), &metadata_result
, &metadata_list
));
421 EXPECT_EQ(AttachmentStore::SUCCESS
, create_result
);
422 EXPECT_EQ(AttachmentStore::SUCCESS
, metadata_result
);
423 EXPECT_EQ(0U, metadata_list
->size());
424 metadata_list
.reset();
426 // Write 3 attachments to the store
427 AttachmentList attachments
;
429 for (int i
= 0; i
< 3; i
++) {
430 std::string some_data
= "data";
431 Attachment attachment
=
432 Attachment::Create(base::RefCountedString::TakeString(&some_data
));
433 attachments
.push_back(attachment
);
435 AttachmentStore::Result write_result
= AttachmentStore::UNSPECIFIED_ERROR
;
436 store_
->Write(attachments
,
437 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResult
,
438 base::Unretained(this), &write_result
));
440 // Read all metadata back. We should be getting 3 entries.
441 store_
->ReadAllMetadata(
442 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultMetadata
,
443 base::Unretained(this), &metadata_result
, &metadata_list
));
445 EXPECT_EQ(AttachmentStore::SUCCESS
, write_result
);
446 EXPECT_EQ(AttachmentStore::SUCCESS
, metadata_result
);
447 EXPECT_EQ(3U, metadata_list
->size());
448 // Get Id of the attachment in the middle.
449 AttachmentId id
= (*metadata_list
.get())[1].GetId();
450 metadata_list
.reset();
456 // Modify the middle attachment metadata entry so that it isn't valid anymore.
457 UpdateAttachmentMetadataRecord(id
, "foobar");
460 create_result
= AttachmentStore::UNSPECIFIED_ERROR
;
461 metadata_result
= AttachmentStore::UNSPECIFIED_ERROR
;
462 store_
= AttachmentStore::CreateOnDiskStore(
463 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get(),
464 base::Bind(&AttachmentStoreCreated
, &create_result
));
466 // Read all metadata back. We should be getting a failure and
467 // only 2 entries now.
468 store_
->ReadAllMetadata(
469 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultMetadata
,
470 base::Unretained(this), &metadata_result
, &metadata_list
));
472 EXPECT_EQ(AttachmentStore::SUCCESS
, create_result
);
473 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR
, metadata_result
);
474 EXPECT_EQ(2U, metadata_list
->size());
477 } // namespace syncer