Support in-memory databases in leveldb_proto
[chromium-blink-merge.git] / components / leveldb_proto / proto_database_impl_unittest.cc
blob7ac19a5572a9fd327f63bcfffbbfcc869f0c8008
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 "components/leveldb_proto/proto_database_impl.h"
7 #include <map>
9 #include "base/bind.h"
10 #include "base/files/file_util.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/location.h"
13 #include "base/run_loop.h"
14 #include "base/threading/thread.h"
15 #include "components/leveldb_proto/leveldb_database.h"
16 #include "components/leveldb_proto/testing/proto/test.pb.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/leveldatabase/src/include/leveldb/options.h"
21 using base::MessageLoop;
22 using base::ScopedTempDir;
23 using testing::Invoke;
24 using testing::Return;
25 using testing::_;
27 namespace leveldb_proto {
29 namespace {
31 typedef std::map<std::string, TestProto> EntryMap;
33 class MockDB : public LevelDB {
34 public:
35 MOCK_METHOD1(Init, bool(const base::FilePath&));
36 MOCK_METHOD2(Save, bool(const KeyValueVector&, const KeyVector&));
37 MOCK_METHOD1(Load, bool(std::vector<std::string>*));
39 MockDB() {
40 ON_CALL(*this, Init(_)).WillByDefault(Return(true));
41 ON_CALL(*this, Save(_, _)).WillByDefault(Return(true));
42 ON_CALL(*this, Load(_)).WillByDefault(Return(true));
46 class MockDatabaseCaller {
47 public:
48 MOCK_METHOD1(InitCallback, void(bool));
49 MOCK_METHOD1(SaveCallback, void(bool));
50 void LoadCallback(bool success, scoped_ptr<std::vector<TestProto> > entries) {
51 LoadCallback1(success, entries.get());
53 MOCK_METHOD2(LoadCallback1, void(bool, std::vector<TestProto>*));
56 } // namespace
58 EntryMap GetSmallModel() {
59 EntryMap model;
61 model["0"].set_id("0");
62 model["0"].set_data("http://foo.com/1");
64 model["1"].set_id("1");
65 model["1"].set_data("http://bar.com/all");
67 model["2"].set_id("2");
68 model["2"].set_data("http://baz.com/1");
70 return model;
73 void ExpectEntryPointersEquals(EntryMap expected,
74 const std::vector<TestProto>& actual) {
75 EXPECT_EQ(expected.size(), actual.size());
76 for (size_t i = 0; i < actual.size(); i++) {
77 EntryMap::iterator expected_it = expected.find(actual[i].id());
78 EXPECT_TRUE(expected_it != expected.end());
79 std::string serialized_expected = expected_it->second.SerializeAsString();
80 std::string serialized_actual = actual[i].SerializeAsString();
81 EXPECT_EQ(serialized_expected, serialized_actual);
82 expected.erase(expected_it);
86 class ProtoDatabaseImplTest : public testing::Test {
87 public:
88 void SetUp() override {
89 main_loop_.reset(new MessageLoop());
90 db_.reset(new ProtoDatabaseImpl<TestProto>(main_loop_->task_runner()));
93 void TearDown() override {
94 db_.reset();
95 base::RunLoop().RunUntilIdle();
96 main_loop_.reset();
99 scoped_ptr<ProtoDatabaseImpl<TestProto> > db_;
100 scoped_ptr<MessageLoop> main_loop_;
103 // Test that ProtoDatabaseImpl calls Init on the underlying database and that
104 // the caller's InitCallback is called with the correct value.
105 TEST_F(ProtoDatabaseImplTest, TestDBInitSuccess) {
106 base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
108 MockDB* mock_db = new MockDB();
109 EXPECT_CALL(*mock_db, Init(path)).WillOnce(Return(true));
111 MockDatabaseCaller caller;
112 EXPECT_CALL(caller, InitCallback(true));
114 db_->InitWithDatabase(
115 scoped_ptr<LevelDB>(mock_db), base::FilePath(path),
116 base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
118 base::RunLoop().RunUntilIdle();
121 TEST_F(ProtoDatabaseImplTest, TestDBInitFailure) {
122 base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
124 MockDB* mock_db = new MockDB();
125 EXPECT_CALL(*mock_db, Init(path)).WillOnce(Return(false));
127 MockDatabaseCaller caller;
128 EXPECT_CALL(caller, InitCallback(false));
130 db_->InitWithDatabase(
131 scoped_ptr<LevelDB>(mock_db), base::FilePath(path),
132 base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
134 base::RunLoop().RunUntilIdle();
137 ACTION_P(AppendLoadEntries, model) {
138 std::vector<std::string>* output = arg0;
139 for (EntryMap::const_iterator it = model.begin(); it != model.end(); ++it) {
140 output->push_back(it->second.SerializeAsString());
142 return true;
145 ACTION_P(VerifyLoadEntries, expected) {
146 std::vector<TestProto>* actual = arg1;
147 ExpectEntryPointersEquals(expected, *actual);
150 // Test that ProtoDatabaseImpl calls Load on the underlying database and that
151 // the caller's LoadCallback is called with the correct success value. Also
152 // confirms that on success, the expected entries are passed to the caller's
153 // LoadCallback.
154 TEST_F(ProtoDatabaseImplTest, TestDBLoadSuccess) {
155 base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
157 MockDB* mock_db = new MockDB();
158 MockDatabaseCaller caller;
159 EntryMap model = GetSmallModel();
161 EXPECT_CALL(*mock_db, Init(_));
162 EXPECT_CALL(caller, InitCallback(_));
163 db_->InitWithDatabase(
164 scoped_ptr<LevelDB>(mock_db), base::FilePath(path),
165 base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
167 EXPECT_CALL(*mock_db, Load(_)).WillOnce(AppendLoadEntries(model));
168 EXPECT_CALL(caller, LoadCallback1(true, _))
169 .WillOnce(VerifyLoadEntries(testing::ByRef(model)));
170 db_->LoadEntries(
171 base::Bind(&MockDatabaseCaller::LoadCallback, base::Unretained(&caller)));
173 base::RunLoop().RunUntilIdle();
176 TEST_F(ProtoDatabaseImplTest, TestDBLoadFailure) {
177 base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
179 MockDB* mock_db = new MockDB();
180 MockDatabaseCaller caller;
182 EXPECT_CALL(*mock_db, Init(_));
183 EXPECT_CALL(caller, InitCallback(_));
184 db_->InitWithDatabase(
185 scoped_ptr<LevelDB>(mock_db), base::FilePath(path),
186 base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
188 EXPECT_CALL(*mock_db, Load(_)).WillOnce(Return(false));
189 EXPECT_CALL(caller, LoadCallback1(false, _));
190 db_->LoadEntries(
191 base::Bind(&MockDatabaseCaller::LoadCallback, base::Unretained(&caller)));
193 base::RunLoop().RunUntilIdle();
196 ACTION_P(VerifyUpdateEntries, expected) {
197 const KeyValueVector actual = arg0;
198 // Create a vector of TestProto from |actual| to reuse the comparison
199 // function.
200 std::vector<TestProto> extracted_entries;
201 for (KeyValueVector::const_iterator it = actual.begin(); it != actual.end();
202 ++it) {
203 TestProto entry;
204 entry.ParseFromString(it->second);
205 extracted_entries.push_back(entry);
207 ExpectEntryPointersEquals(expected, extracted_entries);
208 return true;
211 // Test that ProtoDatabaseImpl calls Save on the underlying database with the
212 // correct entries to save and that the caller's SaveCallback is called with the
213 // correct success value.
214 TEST_F(ProtoDatabaseImplTest, TestDBSaveSuccess) {
215 base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
217 MockDB* mock_db = new MockDB();
218 MockDatabaseCaller caller;
219 EntryMap model = GetSmallModel();
221 EXPECT_CALL(*mock_db, Init(_));
222 EXPECT_CALL(caller, InitCallback(_));
223 db_->InitWithDatabase(
224 scoped_ptr<LevelDB>(mock_db), base::FilePath(path),
225 base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
227 scoped_ptr<ProtoDatabase<TestProto>::KeyEntryVector> entries(
228 new ProtoDatabase<TestProto>::KeyEntryVector());
229 for (EntryMap::iterator it = model.begin(); it != model.end(); ++it) {
230 entries->push_back(std::make_pair(it->second.id(), it->second));
232 scoped_ptr<KeyVector> keys_to_remove(new KeyVector());
234 EXPECT_CALL(*mock_db, Save(_, _)).WillOnce(VerifyUpdateEntries(model));
235 EXPECT_CALL(caller, SaveCallback(true));
236 db_->UpdateEntries(
237 entries.Pass(), keys_to_remove.Pass(),
238 base::Bind(&MockDatabaseCaller::SaveCallback, base::Unretained(&caller)));
240 base::RunLoop().RunUntilIdle();
243 TEST_F(ProtoDatabaseImplTest, TestDBSaveFailure) {
244 base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
246 MockDB* mock_db = new MockDB();
247 MockDatabaseCaller caller;
248 scoped_ptr<ProtoDatabase<TestProto>::KeyEntryVector> entries(
249 new ProtoDatabase<TestProto>::KeyEntryVector());
250 scoped_ptr<KeyVector> keys_to_remove(new KeyVector());
252 EXPECT_CALL(*mock_db, Init(_));
253 EXPECT_CALL(caller, InitCallback(_));
254 db_->InitWithDatabase(
255 scoped_ptr<LevelDB>(mock_db), base::FilePath(path),
256 base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
258 EXPECT_CALL(*mock_db, Save(_, _)).WillOnce(Return(false));
259 EXPECT_CALL(caller, SaveCallback(false));
260 db_->UpdateEntries(
261 entries.Pass(), keys_to_remove.Pass(),
262 base::Bind(&MockDatabaseCaller::SaveCallback, base::Unretained(&caller)));
264 base::RunLoop().RunUntilIdle();
267 // Test that ProtoDatabaseImpl calls Save on the underlying database with the
268 // correct entries to delete and that the caller's SaveCallback is called with
269 // the correct success value.
270 TEST_F(ProtoDatabaseImplTest, TestDBRemoveSuccess) {
271 base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
273 MockDB* mock_db = new MockDB();
274 MockDatabaseCaller caller;
275 EntryMap model = GetSmallModel();
277 EXPECT_CALL(*mock_db, Init(_));
278 EXPECT_CALL(caller, InitCallback(_));
279 db_->InitWithDatabase(
280 scoped_ptr<LevelDB>(mock_db), base::FilePath(path),
281 base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
283 scoped_ptr<ProtoDatabase<TestProto>::KeyEntryVector> entries(
284 new ProtoDatabase<TestProto>::KeyEntryVector());
285 scoped_ptr<KeyVector> keys_to_remove(new KeyVector());
286 for (EntryMap::iterator it = model.begin(); it != model.end(); ++it) {
287 keys_to_remove->push_back(it->second.id());
290 KeyVector keys_copy(*keys_to_remove.get());
291 EXPECT_CALL(*mock_db, Save(_, keys_copy)).WillOnce(Return(true));
292 EXPECT_CALL(caller, SaveCallback(true));
293 db_->UpdateEntries(
294 entries.Pass(), keys_to_remove.Pass(),
295 base::Bind(&MockDatabaseCaller::SaveCallback, base::Unretained(&caller)));
297 base::RunLoop().RunUntilIdle();
300 TEST_F(ProtoDatabaseImplTest, TestDBRemoveFailure) {
301 base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
303 MockDB* mock_db = new MockDB();
304 MockDatabaseCaller caller;
305 scoped_ptr<ProtoDatabase<TestProto>::KeyEntryVector> entries(
306 new ProtoDatabase<TestProto>::KeyEntryVector());
307 scoped_ptr<KeyVector> keys_to_remove(new KeyVector());
309 EXPECT_CALL(*mock_db, Init(_));
310 EXPECT_CALL(caller, InitCallback(_));
311 db_->InitWithDatabase(
312 scoped_ptr<LevelDB>(mock_db), base::FilePath(path),
313 base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
315 EXPECT_CALL(*mock_db, Save(_, _)).WillOnce(Return(false));
316 EXPECT_CALL(caller, SaveCallback(false));
317 db_->UpdateEntries(
318 entries.Pass(), keys_to_remove.Pass(),
319 base::Bind(&MockDatabaseCaller::SaveCallback, base::Unretained(&caller)));
321 base::RunLoop().RunUntilIdle();
324 // This tests that normal usage of the real database does not cause any
325 // threading violations.
326 TEST(ProtoDatabaseImplThreadingTest, TestDBDestruction) {
327 base::MessageLoop main_loop;
329 ScopedTempDir temp_dir;
330 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
332 base::Thread db_thread("dbthread");
333 ASSERT_TRUE(db_thread.Start());
335 scoped_ptr<ProtoDatabaseImpl<TestProto>> db(
336 new ProtoDatabaseImpl<TestProto>(db_thread.task_runner()));
338 MockDatabaseCaller caller;
339 EXPECT_CALL(caller, InitCallback(_));
340 db->Init(temp_dir.path(), base::Bind(&MockDatabaseCaller::InitCallback,
341 base::Unretained(&caller)));
343 db.reset();
345 base::RunLoop run_loop;
346 db_thread.task_runner()->PostTaskAndReply(
347 FROM_HERE, base::Bind(base::DoNothing), run_loop.QuitClosure());
348 run_loop.Run();
351 // Test that the LevelDB properly saves entries and that load returns the saved
352 // entries. If |close_after_save| is true, the database will be closed after
353 // saving and then re-opened to ensure that the data is properly persisted.
354 void TestLevelDBSaveAndLoad(bool close_after_save) {
355 ScopedTempDir temp_dir;
356 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
358 EntryMap model = GetSmallModel();
360 KeyValueVector save_entries;
361 std::vector<std::string> load_entries;
362 KeyVector remove_keys;
364 for (EntryMap::iterator it = model.begin(); it != model.end(); ++it) {
365 save_entries.push_back(
366 std::make_pair(it->second.id(), it->second.SerializeAsString()));
369 scoped_ptr<LevelDB> db(new LevelDB());
370 EXPECT_TRUE(db->Init(temp_dir.path()));
371 EXPECT_TRUE(db->Save(save_entries, remove_keys));
373 if (close_after_save) {
374 db.reset(new LevelDB());
375 EXPECT_TRUE(db->Init(temp_dir.path()));
378 EXPECT_TRUE(db->Load(&load_entries));
379 // Convert the strings back to TestProto.
380 std::vector<TestProto> loaded_protos;
381 for (std::vector<std::string>::iterator it = load_entries.begin();
382 it != load_entries.end(); ++it) {
383 TestProto entry;
384 entry.ParseFromString(*it);
385 loaded_protos.push_back(entry);
387 ExpectEntryPointersEquals(model, loaded_protos);
390 TEST(ProtoDatabaseImplLevelDBTest, TestDBSaveAndLoad) {
391 TestLevelDBSaveAndLoad(false);
394 TEST(ProtoDatabaseImplLevelDBTest, TestDBCloseAndReopen) {
395 TestLevelDBSaveAndLoad(true);
398 TEST(ProtoDatabaseImplLevelDBTest, TestDBInitFail) {
399 ScopedTempDir temp_dir;
400 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
402 leveldb::Options options;
403 options.create_if_missing = false;
404 scoped_ptr<LevelDB> db(new LevelDB());
406 KeyValueVector save_entries;
407 std::vector<std::string> load_entries;
408 KeyVector remove_keys;
410 EXPECT_FALSE(db->InitWithOptions(temp_dir.path(), options));
411 EXPECT_FALSE(db->Load(&load_entries));
412 EXPECT_FALSE(db->Save(save_entries, remove_keys));
415 TEST(ProtoDatabaseImplLevelDBTest, TestMemoryDatabase) {
416 scoped_ptr<LevelDB> db(new LevelDB());
418 std::vector<std::string> load_entries;
420 ASSERT_TRUE(db->Init(base::FilePath()));
422 ASSERT_TRUE(db->Load(&load_entries));
423 EXPECT_EQ(0u, load_entries.size());
425 KeyValueVector save_entries(1, std::make_pair("foo", "bar"));
426 KeyVector remove_keys;
428 ASSERT_TRUE(db->Save(save_entries, remove_keys));
430 std::vector<std::string> second_load_entries;
432 ASSERT_TRUE(db->Load(&second_load_entries));
433 EXPECT_EQ(1u, second_load_entries.size());
436 } // namespace leveldb_proto