Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / sync / internal_api / attachments / on_disk_attachment_store_unittest.cc
blobe145172dc34e0d1653062fd61d1ba40bcf26381f
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"
23 namespace syncer {
25 namespace {
27 void AttachmentStoreCreated(AttachmentStore::Result* result_dest,
28 const AttachmentStore::Result& result) {
29 *result_dest = result;
32 } // namespace
34 // Instantiation of common attachment store tests.
35 class OnDiskAttachmentStoreFactory {
36 public:
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);
50 return store;
53 private:
54 base::ScopedTempDir temp_dir_;
57 INSTANTIATE_TYPED_TEST_CASE_P(OnDisk,
58 AttachmentStoreTest,
59 OnDiskAttachmentStoreFactory);
61 // Tests specific to OnDiskAttachmentStore.
62 class OnDiskAttachmentStoreSpecificTest : public testing::Test {
63 public:
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() {
102 leveldb::DB* db;
103 leveldb::Options options;
104 options.create_if_missing = true;
105 leveldb::Status s =
106 leveldb::DB::Open(options, db_path_.AsUTF8Unsafe(), &db);
107 EXPECT_TRUE(s.ok());
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);
114 EXPECT_TRUE(s.ok());
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();
130 std::string content;
131 leveldb::Status s =
132 db->Get(leveldb::ReadOptions(), "database-metadata", &content);
133 EXPECT_TRUE(s.ok());
134 return 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);
145 std::string data;
146 leveldb::Status s = db->Get(leveldb::ReadOptions(), data_key, &data);
147 if (expect_records_present)
148 EXPECT_TRUE(s.ok());
149 else
150 EXPECT_TRUE(s.IsNotFound());
151 s = db->Get(leveldb::ReadOptions(), metadata_key, &data);
152 if (expect_records_present)
153 EXPECT_TRUE(s.ok());
154 else
155 EXPECT_TRUE(s.IsNotFound());
158 void RunLoop() {
159 base::RunLoop run_loop;
160 run_loop.RunUntilIdle();
164 // Ensure that store can be closed and reopen while retaining stored
165 // attachments.
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));
173 RunLoop();
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),
185 &result));
186 RunLoop();
187 EXPECT_EQ(AttachmentStore::SUCCESS, result);
189 // Close and reopen attachment store.
190 store_ = nullptr;
191 result = AttachmentStore::UNSPECIFIED_ERROR;
192 store_ = AttachmentStore::CreateOnDiskStore(
193 temp_dir_.path(), base::ThreadTaskRunnerHandle::Get(),
194 base::Bind(&AttachmentStoreCreated, &result));
195 RunLoop();
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;
202 store_->Read(
203 attachment_ids,
204 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultAttachments,
205 base::Unretained(this), &result, &failed_attachment_ids));
206 RunLoop();
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));
222 RunLoop();
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.
230 OpenLevelDB();
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));
236 RunLoop();
237 EXPECT_EQ(AttachmentStore::SUCCESS, result);
238 // Close AttachmentStore so that test can check content.
239 store_ = nullptr;
240 RunLoop();
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));
258 RunLoop();
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));
269 RunLoop();
270 EXPECT_EQ(AttachmentStore::UNSPECIFIED_ERROR, result);
273 // Ensure that attachment store correctly maintains metadata records for
274 // attachments.
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;
286 some_data = "data1";
287 attachments.push_back(
288 Attachment::Create(base::RefCountedString::TakeString(&some_data)));
289 some_data = "data2";
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));
303 store_ = nullptr;
304 RunLoop();
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),
331 some_data);
332 AttachmentList attachments;
333 attachments.push_back(attachment);
334 store_->Write(attachments,
335 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResult,
336 base::Unretained(this), &write_result));
338 // Read attachment.
339 AttachmentStore::Result read_result = AttachmentStore::UNSPECIFIED_ERROR;
340 AttachmentIdList attachment_ids;
341 attachment_ids.push_back(attachment.GetId());
342 AttachmentIdList failed_attachment_ids;
343 store_->Read(
344 attachment_ids,
345 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultAttachments,
346 base::Unretained(this), &read_result, &failed_attachment_ids));
347 RunLoop();
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;
381 store_->Read(
382 attachment_ids,
383 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultAttachments,
384 base::Unretained(this), &read_result, &failed_attachment_ids));
385 RunLoop();
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;
414 store_->Read(
415 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));
435 RunLoop();
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));
461 RunLoop();
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));
485 RunLoop();
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();
493 // Close the store.
494 store_ = nullptr;
495 RunLoop();
497 // Modify the middle attachment metadata entry so that it isn't valid anymore.
498 UpdateAttachmentMetadataRecord(id, "foobar");
500 // Reopen the store.
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));
512 RunLoop();
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