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/gtest/include/gtest/gtest.h"
17 #include "third_party/leveldatabase/src/include/leveldb/db.h"
18 #include "third_party/leveldatabase/src/include/leveldb/options.h"
19 #include "third_party/leveldatabase/src/include/leveldb/slice.h"
20 #include "third_party/leveldatabase/src/include/leveldb/status.h"
26 void AttachmentStoreCreated(scoped_refptr<AttachmentStore>* store_dest,
27 AttachmentStore::Result* result_dest,
28 const AttachmentStore::Result& result,
29 const scoped_refptr<AttachmentStore>& store) {
30 *result_dest = result;
36 // Instantiation of common attachment store tests.
37 class OnDiskAttachmentStoreFactory {
39 OnDiskAttachmentStoreFactory() {}
40 ~OnDiskAttachmentStoreFactory() {}
42 scoped_refptr<AttachmentStore> CreateAttachmentStore() {
43 EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
44 scoped_refptr<AttachmentStore> store;
45 AttachmentStore::Result result = AttachmentStore::UNSPECIFIED_ERROR;
46 AttachmentStore::CreateOnDiskStore(
48 base::ThreadTaskRunnerHandle::Get(),
49 base::Bind(&AttachmentStoreCreated, &store, &result));
50 base::RunLoop run_loop;
51 run_loop.RunUntilIdle();
52 EXPECT_EQ(result, AttachmentStore::SUCCESS);
57 base::ScopedTempDir temp_dir_;
60 INSTANTIATE_TYPED_TEST_CASE_P(OnDisk,
62 OnDiskAttachmentStoreFactory);
64 // Tests specific to OnDiskAttachmentStore.
65 class OnDiskAttachmentStoreSpecificTest : public testing::Test {
67 base::ScopedTempDir temp_dir_;
68 base::MessageLoop message_loop_;
69 scoped_refptr<AttachmentStore> store_;
71 OnDiskAttachmentStoreSpecificTest() {}
73 void CopyResult(AttachmentStore::Result* destination_result,
74 const AttachmentStore::Result& source_result) {
75 *destination_result = source_result;
78 void CopyResultAttachments(
79 AttachmentStore::Result* destination_result,
80 const AttachmentStore::Result& source_result,
81 scoped_ptr<AttachmentMap> source_attachments,
82 scoped_ptr<AttachmentIdList> source_failed_attachment_ids) {
83 CopyResult(destination_result, source_result);
86 scoped_ptr<leveldb::DB> OpenLevelDB(const base::FilePath& path) {
88 leveldb::Options options;
89 options.create_if_missing = true;
90 leveldb::Status s = leveldb::DB::Open(options, path.AsUTF8Unsafe(), &db);
92 return make_scoped_ptr(db);
95 void UpdateStoreMetadataRecord(const base::FilePath& path,
96 const std::string& content) {
97 scoped_ptr<leveldb::DB> db = OpenLevelDB(path);
99 db->Put(leveldb::WriteOptions(), "database-metadata", content);
103 std::string ReadStoreMetadataRecord(const base::FilePath& path) {
104 scoped_ptr<leveldb::DB> db = OpenLevelDB(path);
107 db->Get(leveldb::ReadOptions(), "database-metadata", &content);
112 void VerifyAttachmentRecordsPresent(const base::FilePath& path,
113 const AttachmentId& attachment_id,
114 bool expect_records_present) {
115 scoped_ptr<leveldb::DB> db = OpenLevelDB(path);
117 std::string metadata_key =
118 OnDiskAttachmentStore::MakeMetadataKeyFromAttachmentId(attachment_id);
119 std::string data_key =
120 OnDiskAttachmentStore::MakeDataKeyFromAttachmentId(attachment_id);
122 leveldb::Status s = db->Get(leveldb::ReadOptions(), data_key, &data);
123 if (expect_records_present)
126 EXPECT_TRUE(s.IsNotFound());
127 s = db->Get(leveldb::ReadOptions(), metadata_key, &data);
128 if (expect_records_present)
131 EXPECT_TRUE(s.IsNotFound());
135 base::RunLoop run_loop;
136 run_loop.RunUntilIdle();
140 // Ensure that store can be closed and reopen while retaining stored
142 TEST_F(OnDiskAttachmentStoreSpecificTest, CloseAndReopen) {
143 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
144 AttachmentStore::Result result;
146 result = AttachmentStore::UNSPECIFIED_ERROR;
147 AttachmentStore::CreateOnDiskStore(
149 base::ThreadTaskRunnerHandle::Get(),
150 base::Bind(&AttachmentStoreCreated, &store_, &result));
152 EXPECT_EQ(result, AttachmentStore::SUCCESS);
154 result = AttachmentStore::UNSPECIFIED_ERROR;
155 std::string some_data = "data";
156 Attachment attachment =
157 Attachment::Create(base::RefCountedString::TakeString(&some_data));
158 AttachmentList attachments;
159 attachments.push_back(attachment);
160 store_->Write(attachments,
161 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResult,
162 base::Unretained(this),
165 EXPECT_EQ(result, AttachmentStore::SUCCESS);
167 // Close attachment store.
169 result = AttachmentStore::UNSPECIFIED_ERROR;
170 AttachmentStore::CreateOnDiskStore(
172 base::ThreadTaskRunnerHandle::Get(),
173 base::Bind(&AttachmentStoreCreated, &store_, &result));
175 EXPECT_EQ(result, AttachmentStore::SUCCESS);
177 result = AttachmentStore::UNSPECIFIED_ERROR;
178 AttachmentIdList attachment_ids;
179 attachment_ids.push_back(attachment.GetId());
182 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResultAttachments,
183 base::Unretained(this),
186 EXPECT_EQ(result, AttachmentStore::SUCCESS);
189 // Ensure loading corrupt attachment store fails.
190 TEST_F(OnDiskAttachmentStoreSpecificTest, FailToOpen) {
191 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
192 base::FilePath db_path =
193 temp_dir_.path().Append(FILE_PATH_LITERAL("leveldb"));
194 base::CreateDirectory(db_path);
196 // To simulate corrupt database write empty CURRENT file.
197 std::string current_file_content = "";
198 base::WriteFile(db_path.Append(FILE_PATH_LITERAL("CURRENT")),
199 current_file_content.c_str(),
200 current_file_content.size());
202 AttachmentStore::Result result = AttachmentStore::SUCCESS;
203 AttachmentStore::CreateOnDiskStore(
205 base::ThreadTaskRunnerHandle::Get(),
206 base::Bind(&AttachmentStoreCreated, &store_, &result));
208 EXPECT_EQ(result, AttachmentStore::UNSPECIFIED_ERROR);
209 EXPECT_EQ(store_.get(), nullptr);
212 // Ensure that attachment store works correctly when store metadata is missing,
213 // corrupt or has unknown schema version.
214 TEST_F(OnDiskAttachmentStoreSpecificTest, StoreMetadata) {
215 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
216 base::FilePath db_path =
217 temp_dir_.path().Append(FILE_PATH_LITERAL("leveldb"));
218 base::CreateDirectory(db_path);
220 // Create and close empty database.
221 OpenLevelDB(db_path);
222 // Open database with AttachmentStore.
223 AttachmentStore::Result result = AttachmentStore::UNSPECIFIED_ERROR;
224 AttachmentStore::CreateOnDiskStore(
226 base::ThreadTaskRunnerHandle::Get(),
227 base::Bind(&AttachmentStoreCreated, &store_, &result));
229 EXPECT_EQ(result, AttachmentStore::SUCCESS);
230 // Close AttachmentStore so that test can check content.
234 // AttachmentStore should create metadata record.
235 std::string data = ReadStoreMetadataRecord(db_path);
236 attachment_store_pb::StoreMetadata metadata;
237 EXPECT_TRUE(metadata.ParseFromString(data));
238 EXPECT_EQ(metadata.schema_version(), 1);
240 // Set unknown future schema version.
241 metadata.set_schema_version(2);
242 data = metadata.SerializeAsString();
243 UpdateStoreMetadataRecord(db_path, data);
245 // AttachmentStore should fail to load.
246 result = AttachmentStore::SUCCESS;
247 AttachmentStore::CreateOnDiskStore(
249 base::ThreadTaskRunnerHandle::Get(),
250 base::Bind(&AttachmentStoreCreated, &store_, &result));
252 EXPECT_EQ(result, AttachmentStore::UNSPECIFIED_ERROR);
253 EXPECT_EQ(store_.get(), nullptr);
255 // Write garbage into metadata record.
256 UpdateStoreMetadataRecord(db_path, "abra.cadabra");
258 // AttachmentStore should fail to load.
259 result = AttachmentStore::SUCCESS;
260 AttachmentStore::CreateOnDiskStore(
262 base::ThreadTaskRunnerHandle::Get(),
263 base::Bind(&AttachmentStoreCreated, &store_, &result));
265 EXPECT_EQ(result, AttachmentStore::UNSPECIFIED_ERROR);
266 EXPECT_EQ(store_.get(), nullptr);
269 TEST_F(OnDiskAttachmentStoreSpecificTest, RecordMetadata) {
270 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
271 base::FilePath db_path =
272 temp_dir_.path().Append(FILE_PATH_LITERAL("leveldb"));
274 // Create attachment store.
275 AttachmentStore::Result result = AttachmentStore::UNSPECIFIED_ERROR;
276 AttachmentStore::CreateOnDiskStore(
278 base::ThreadTaskRunnerHandle::Get(),
279 base::Bind(&AttachmentStoreCreated, &store_, &result));
281 EXPECT_EQ(result, AttachmentStore::SUCCESS);
283 // Write two attachments.
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),
297 EXPECT_EQ(result, AttachmentStore::SUCCESS);
299 // Delete one of written attachments.
300 AttachmentIdList attachment_ids;
301 attachment_ids.push_back(attachments[0].GetId());
302 store_->Drop(attachment_ids,
303 base::Bind(&OnDiskAttachmentStoreSpecificTest::CopyResult,
304 base::Unretained(this),
307 EXPECT_EQ(result, AttachmentStore::SUCCESS);
311 // Verify that attachment store contains only records for second attachment.
312 VerifyAttachmentRecordsPresent(db_path, attachments[0].GetId(), false);
313 VerifyAttachmentRecordsPresent(db_path, attachments[1].GetId(), true);
316 } // namespace syncer