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 make_scoped_ptr(mock_db
), 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 make_scoped_ptr(mock_db
), 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 (const auto& pair
: model
)
140 output
->push_back(pair
.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 make_scoped_ptr(mock_db
), 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 make_scoped_ptr(mock_db
), 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 (const auto& pair
: actual
) {
203 if (!entry
.ParseFromString(pair
.second
)) {
204 ADD_FAILURE() << "Unable to deserialize the protobuf";
208 extracted_entries
.push_back(entry
);
211 ExpectEntryPointersEquals(expected
, extracted_entries
);
215 // Test that ProtoDatabaseImpl calls Save on the underlying database with the
216 // correct entries to save and that the caller's SaveCallback is called with the
217 // correct success value.
218 TEST_F(ProtoDatabaseImplTest
, TestDBSaveSuccess
) {
219 base::FilePath
path(FILE_PATH_LITERAL("/fake/path"));
221 MockDB
* mock_db
= new MockDB();
222 MockDatabaseCaller caller
;
223 EntryMap model
= GetSmallModel();
225 EXPECT_CALL(*mock_db
, Init(_
));
226 EXPECT_CALL(caller
, InitCallback(_
));
227 db_
->InitWithDatabase(
228 make_scoped_ptr(mock_db
), path
,
229 base::Bind(&MockDatabaseCaller::InitCallback
, base::Unretained(&caller
)));
231 scoped_ptr
<ProtoDatabase
<TestProto
>::KeyEntryVector
> entries(
232 new ProtoDatabase
<TestProto
>::KeyEntryVector());
233 for (const auto& pair
: model
)
234 entries
->push_back(std::make_pair(pair
.second
.id(), pair
.second
));
236 scoped_ptr
<KeyVector
> keys_to_remove(new KeyVector());
238 EXPECT_CALL(*mock_db
, Save(_
, _
)).WillOnce(VerifyUpdateEntries(model
));
239 EXPECT_CALL(caller
, SaveCallback(true));
241 entries
.Pass(), keys_to_remove
.Pass(),
242 base::Bind(&MockDatabaseCaller::SaveCallback
, base::Unretained(&caller
)));
244 base::RunLoop().RunUntilIdle();
247 TEST_F(ProtoDatabaseImplTest
, TestDBSaveFailure
) {
248 base::FilePath
path(FILE_PATH_LITERAL("/fake/path"));
250 MockDB
* mock_db
= new MockDB();
251 MockDatabaseCaller caller
;
252 scoped_ptr
<ProtoDatabase
<TestProto
>::KeyEntryVector
> entries(
253 new ProtoDatabase
<TestProto
>::KeyEntryVector());
254 scoped_ptr
<KeyVector
> keys_to_remove(new KeyVector());
256 EXPECT_CALL(*mock_db
, Init(_
));
257 EXPECT_CALL(caller
, InitCallback(_
));
258 db_
->InitWithDatabase(
259 make_scoped_ptr(mock_db
), path
,
260 base::Bind(&MockDatabaseCaller::InitCallback
, base::Unretained(&caller
)));
262 EXPECT_CALL(*mock_db
, Save(_
, _
)).WillOnce(Return(false));
263 EXPECT_CALL(caller
, SaveCallback(false));
265 entries
.Pass(), keys_to_remove
.Pass(),
266 base::Bind(&MockDatabaseCaller::SaveCallback
, base::Unretained(&caller
)));
268 base::RunLoop().RunUntilIdle();
271 // Test that ProtoDatabaseImpl calls Save on the underlying database with the
272 // correct entries to delete and that the caller's SaveCallback is called with
273 // the correct success value.
274 TEST_F(ProtoDatabaseImplTest
, TestDBRemoveSuccess
) {
275 base::FilePath
path(FILE_PATH_LITERAL("/fake/path"));
277 MockDB
* mock_db
= new MockDB();
278 MockDatabaseCaller caller
;
279 EntryMap model
= GetSmallModel();
281 EXPECT_CALL(*mock_db
, Init(_
));
282 EXPECT_CALL(caller
, InitCallback(_
));
283 db_
->InitWithDatabase(
284 make_scoped_ptr(mock_db
), path
,
285 base::Bind(&MockDatabaseCaller::InitCallback
, base::Unretained(&caller
)));
287 scoped_ptr
<ProtoDatabase
<TestProto
>::KeyEntryVector
> entries(
288 new ProtoDatabase
<TestProto
>::KeyEntryVector());
289 scoped_ptr
<KeyVector
> keys_to_remove(new KeyVector());
290 for (const auto& pair
: model
)
291 keys_to_remove
->push_back(pair
.second
.id());
293 KeyVector
keys_copy(*keys_to_remove
.get());
294 EXPECT_CALL(*mock_db
, Save(_
, keys_copy
)).WillOnce(Return(true));
295 EXPECT_CALL(caller
, SaveCallback(true));
297 entries
.Pass(), keys_to_remove
.Pass(),
298 base::Bind(&MockDatabaseCaller::SaveCallback
, base::Unretained(&caller
)));
300 base::RunLoop().RunUntilIdle();
303 TEST_F(ProtoDatabaseImplTest
, TestDBRemoveFailure
) {
304 base::FilePath
path(FILE_PATH_LITERAL("/fake/path"));
306 MockDB
* mock_db
= new MockDB();
307 MockDatabaseCaller caller
;
308 scoped_ptr
<ProtoDatabase
<TestProto
>::KeyEntryVector
> entries(
309 new ProtoDatabase
<TestProto
>::KeyEntryVector());
310 scoped_ptr
<KeyVector
> keys_to_remove(new KeyVector());
312 EXPECT_CALL(*mock_db
, Init(_
));
313 EXPECT_CALL(caller
, InitCallback(_
));
314 db_
->InitWithDatabase(
315 make_scoped_ptr(mock_db
), path
,
316 base::Bind(&MockDatabaseCaller::InitCallback
, base::Unretained(&caller
)));
318 EXPECT_CALL(*mock_db
, Save(_
, _
)).WillOnce(Return(false));
319 EXPECT_CALL(caller
, SaveCallback(false));
321 entries
.Pass(), keys_to_remove
.Pass(),
322 base::Bind(&MockDatabaseCaller::SaveCallback
, base::Unretained(&caller
)));
324 base::RunLoop().RunUntilIdle();
327 // This tests that normal usage of the real database does not cause any
328 // threading violations.
329 TEST(ProtoDatabaseImplThreadingTest
, TestDBDestruction
) {
330 base::MessageLoop main_loop
;
332 ScopedTempDir temp_dir
;
333 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
335 base::Thread
db_thread("dbthread");
336 ASSERT_TRUE(db_thread
.Start());
338 scoped_ptr
<ProtoDatabaseImpl
<TestProto
>> db(
339 new ProtoDatabaseImpl
<TestProto
>(db_thread
.task_runner()));
341 MockDatabaseCaller caller
;
342 EXPECT_CALL(caller
, InitCallback(_
));
343 db
->Init(temp_dir
.path(), base::Bind(&MockDatabaseCaller::InitCallback
,
344 base::Unretained(&caller
)));
348 base::RunLoop run_loop
;
349 db_thread
.task_runner()->PostTaskAndReply(
350 FROM_HERE
, base::Bind(base::DoNothing
), run_loop
.QuitClosure());
354 // Test that the LevelDB properly saves entries and that load returns the saved
355 // entries. If |close_after_save| is true, the database will be closed after
356 // saving and then re-opened to ensure that the data is properly persisted.
357 void TestLevelDBSaveAndLoad(bool close_after_save
) {
358 ScopedTempDir temp_dir
;
359 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
361 EntryMap model
= GetSmallModel();
363 KeyValueVector save_entries
;
364 std::vector
<std::string
> load_entries
;
365 KeyVector remove_keys
;
367 for (const auto& pair
: model
) {
368 save_entries
.push_back(
369 std::make_pair(pair
.second
.id(), pair
.second
.SerializeAsString()));
372 scoped_ptr
<LevelDB
> db(new LevelDB());
373 EXPECT_TRUE(db
->Init(temp_dir
.path()));
374 EXPECT_TRUE(db
->Save(save_entries
, remove_keys
));
376 if (close_after_save
) {
377 db
.reset(new LevelDB());
378 EXPECT_TRUE(db
->Init(temp_dir
.path()));
381 EXPECT_TRUE(db
->Load(&load_entries
));
383 // Convert the strings back to TestProto.
384 std::vector
<TestProto
> loaded_protos
;
385 for (const auto& serialized_entry
: load_entries
) {
387 ASSERT_TRUE(entry
.ParseFromString(serialized_entry
));
388 loaded_protos
.push_back(entry
);
391 ExpectEntryPointersEquals(model
, loaded_protos
);
394 TEST(ProtoDatabaseImplLevelDBTest
, TestDBSaveAndLoad
) {
395 TestLevelDBSaveAndLoad(false);
398 TEST(ProtoDatabaseImplLevelDBTest
, TestDBCloseAndReopen
) {
399 TestLevelDBSaveAndLoad(true);
402 TEST(ProtoDatabaseImplLevelDBTest
, TestDBInitFail
) {
403 ScopedTempDir temp_dir
;
404 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
406 leveldb::Options options
;
407 options
.create_if_missing
= false;
408 scoped_ptr
<LevelDB
> db(new LevelDB());
410 KeyValueVector save_entries
;
411 std::vector
<std::string
> load_entries
;
412 KeyVector remove_keys
;
414 EXPECT_FALSE(db
->InitWithOptions(temp_dir
.path(), options
));
415 EXPECT_FALSE(db
->Load(&load_entries
));
416 EXPECT_FALSE(db
->Save(save_entries
, remove_keys
));
419 TEST(ProtoDatabaseImplLevelDBTest
, TestMemoryDatabase
) {
420 scoped_ptr
<LevelDB
> db(new LevelDB());
422 std::vector
<std::string
> load_entries
;
424 ASSERT_TRUE(db
->Init(base::FilePath()));
426 ASSERT_TRUE(db
->Load(&load_entries
));
427 EXPECT_EQ(0u, load_entries
.size());
429 KeyValueVector
save_entries(1, std::make_pair("foo", "bar"));
430 KeyVector remove_keys
;
432 ASSERT_TRUE(db
->Save(save_entries
, remove_keys
));
434 std::vector
<std::string
> second_load_entries
;
436 ASSERT_TRUE(db
->Load(&second_load_entries
));
437 EXPECT_EQ(1u, second_load_entries
.size());
440 } // namespace leveldb_proto