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"
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
;
27 namespace leveldb_proto
{
31 typedef std::map
<std::string
, TestProto
> EntryMap
;
33 class MockDB
: public LevelDB
{
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
>*));
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
{
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
>*));
58 EntryMap
GetSmallModel() {
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");
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
{
88 void SetUp() override
{
89 main_loop_
.reset(new MessageLoop());
90 db_
.reset(new ProtoDatabaseImpl
<TestProto
>(main_loop_
->task_runner()));
93 void TearDown() override
{
95 base::RunLoop().RunUntilIdle();
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());
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
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
)));
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, _
));
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
200 std::vector
<TestProto
> extracted_entries
;
201 for (KeyValueVector::const_iterator it
= actual
.begin(); it
!= actual
.end();
204 entry
.ParseFromString(it
->second
);
205 extracted_entries
.push_back(entry
);
207 ExpectEntryPointersEquals(expected
, extracted_entries
);
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));
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));
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));
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));
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
)));
345 base::RunLoop run_loop
;
346 db_thread
.task_runner()->PostTaskAndReply(
347 FROM_HERE
, base::Bind(base::DoNothing
), run_loop
.QuitClosure());
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
) {
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