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/run_loop.h"
13 #include "base/threading/thread.h"
14 #include "components/leveldb_proto/leveldb_database.h"
15 #include "components/leveldb_proto/testing/proto/test.pb.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
19 using base::MessageLoop
;
20 using base::ScopedTempDir
;
21 using testing::Invoke
;
22 using testing::Return
;
25 namespace leveldb_proto
{
29 typedef std::map
<std::string
, TestProto
> EntryMap
;
31 class MockDB
: public LevelDB
{
33 MOCK_METHOD1(Init
, bool(const base::FilePath
&));
34 MOCK_METHOD2(Save
, bool(const KeyValueVector
&, const KeyVector
&));
35 MOCK_METHOD1(Load
, bool(std::vector
<std::string
>*));
38 ON_CALL(*this, Init(_
)).WillByDefault(Return(true));
39 ON_CALL(*this, Save(_
, _
)).WillByDefault(Return(true));
40 ON_CALL(*this, Load(_
)).WillByDefault(Return(true));
44 class MockDatabaseCaller
{
46 MOCK_METHOD1(InitCallback
, void(bool));
47 MOCK_METHOD1(SaveCallback
, void(bool));
48 void LoadCallback(bool success
, scoped_ptr
<std::vector
<TestProto
> > entries
) {
49 LoadCallback1(success
, entries
.get());
51 MOCK_METHOD2(LoadCallback1
, void(bool, std::vector
<TestProto
>*));
56 EntryMap
GetSmallModel() {
59 model
["0"].set_id("0");
60 model
["0"].set_data("http://foo.com/1");
62 model
["1"].set_id("1");
63 model
["1"].set_data("http://bar.com/all");
65 model
["2"].set_id("2");
66 model
["2"].set_data("http://baz.com/1");
71 void ExpectEntryPointersEquals(EntryMap expected
,
72 const std::vector
<TestProto
>& actual
) {
73 EXPECT_EQ(expected
.size(), actual
.size());
74 for (size_t i
= 0; i
< actual
.size(); i
++) {
75 EntryMap::iterator expected_it
= expected
.find(actual
[i
].id());
76 EXPECT_TRUE(expected_it
!= expected
.end());
77 std::string serialized_expected
= expected_it
->second
.SerializeAsString();
78 std::string serialized_actual
= actual
[i
].SerializeAsString();
79 EXPECT_EQ(serialized_expected
, serialized_actual
);
80 expected
.erase(expected_it
);
84 class ProtoDatabaseImplTest
: public testing::Test
{
86 virtual void SetUp() {
87 main_loop_
.reset(new MessageLoop());
89 new ProtoDatabaseImpl
<TestProto
>(main_loop_
->message_loop_proxy()));
92 virtual void TearDown() {
94 base::RunLoop().RunUntilIdle();
98 scoped_ptr
<ProtoDatabaseImpl
<TestProto
> > db_
;
99 scoped_ptr
<MessageLoop
> main_loop_
;
102 // Test that ProtoDatabaseImpl calls Init on the underlying database and that
103 // the caller's InitCallback is called with the correct value.
104 TEST_F(ProtoDatabaseImplTest
, TestDBInitSuccess
) {
105 base::FilePath
path(FILE_PATH_LITERAL("/fake/path"));
107 MockDB
* mock_db
= new MockDB();
108 EXPECT_CALL(*mock_db
, Init(path
)).WillOnce(Return(true));
110 MockDatabaseCaller caller
;
111 EXPECT_CALL(caller
, InitCallback(true));
113 db_
->InitWithDatabase(
114 scoped_ptr
<LevelDB
>(mock_db
), base::FilePath(path
),
115 base::Bind(&MockDatabaseCaller::InitCallback
, base::Unretained(&caller
)));
117 base::RunLoop().RunUntilIdle();
120 TEST_F(ProtoDatabaseImplTest
, TestDBInitFailure
) {
121 base::FilePath
path(FILE_PATH_LITERAL("/fake/path"));
123 MockDB
* mock_db
= new MockDB();
124 EXPECT_CALL(*mock_db
, Init(path
)).WillOnce(Return(false));
126 MockDatabaseCaller caller
;
127 EXPECT_CALL(caller
, InitCallback(false));
129 db_
->InitWithDatabase(
130 scoped_ptr
<LevelDB
>(mock_db
), base::FilePath(path
),
131 base::Bind(&MockDatabaseCaller::InitCallback
, base::Unretained(&caller
)));
133 base::RunLoop().RunUntilIdle();
136 ACTION_P(AppendLoadEntries
, model
) {
137 std::vector
<std::string
>* output
= arg0
;
138 for (EntryMap::const_iterator it
= model
.begin(); it
!= model
.end(); ++it
) {
139 output
->push_back(it
->second
.SerializeAsString());
144 ACTION_P(VerifyLoadEntries
, expected
) {
145 std::vector
<TestProto
>* actual
= arg1
;
146 ExpectEntryPointersEquals(expected
, *actual
);
149 // Test that ProtoDatabaseImpl calls Load on the underlying database and that
150 // the caller's LoadCallback is called with the correct success value. Also
151 // confirms that on success, the expected entries are passed to the caller's
153 TEST_F(ProtoDatabaseImplTest
, TestDBLoadSuccess
) {
154 base::FilePath
path(FILE_PATH_LITERAL("/fake/path"));
156 MockDB
* mock_db
= new MockDB();
157 MockDatabaseCaller caller
;
158 EntryMap model
= GetSmallModel();
160 EXPECT_CALL(*mock_db
, Init(_
));
161 EXPECT_CALL(caller
, InitCallback(_
));
162 db_
->InitWithDatabase(
163 scoped_ptr
<LevelDB
>(mock_db
), base::FilePath(path
),
164 base::Bind(&MockDatabaseCaller::InitCallback
, base::Unretained(&caller
)));
166 EXPECT_CALL(*mock_db
, Load(_
)).WillOnce(AppendLoadEntries(model
));
167 EXPECT_CALL(caller
, LoadCallback1(true, _
))
168 .WillOnce(VerifyLoadEntries(testing::ByRef(model
)));
170 base::Bind(&MockDatabaseCaller::LoadCallback
, base::Unretained(&caller
)));
172 base::RunLoop().RunUntilIdle();
175 TEST_F(ProtoDatabaseImplTest
, TestDBLoadFailure
) {
176 base::FilePath
path(FILE_PATH_LITERAL("/fake/path"));
178 MockDB
* mock_db
= new MockDB();
179 MockDatabaseCaller caller
;
181 EXPECT_CALL(*mock_db
, Init(_
));
182 EXPECT_CALL(caller
, InitCallback(_
));
183 db_
->InitWithDatabase(
184 scoped_ptr
<LevelDB
>(mock_db
), base::FilePath(path
),
185 base::Bind(&MockDatabaseCaller::InitCallback
, base::Unretained(&caller
)));
187 EXPECT_CALL(*mock_db
, Load(_
)).WillOnce(Return(false));
188 EXPECT_CALL(caller
, LoadCallback1(false, _
));
190 base::Bind(&MockDatabaseCaller::LoadCallback
, base::Unretained(&caller
)));
192 base::RunLoop().RunUntilIdle();
195 ACTION_P(VerifyUpdateEntries
, expected
) {
196 const KeyValueVector actual
= arg0
;
197 // Create a vector of TestProto from |actual| to reuse the comparison
199 std::vector
<TestProto
> extracted_entries
;
200 for (KeyValueVector::const_iterator it
= actual
.begin(); it
!= actual
.end();
203 entry
.ParseFromString(it
->second
);
204 extracted_entries
.push_back(entry
);
206 ExpectEntryPointersEquals(expected
, extracted_entries
);
210 // Test that ProtoDatabaseImpl calls Save on the underlying database with the
211 // correct entries to save and that the caller's SaveCallback is called with the
212 // correct success value.
213 TEST_F(ProtoDatabaseImplTest
, TestDBSaveSuccess
) {
214 base::FilePath
path(FILE_PATH_LITERAL("/fake/path"));
216 MockDB
* mock_db
= new MockDB();
217 MockDatabaseCaller caller
;
218 EntryMap model
= GetSmallModel();
220 EXPECT_CALL(*mock_db
, Init(_
));
221 EXPECT_CALL(caller
, InitCallback(_
));
222 db_
->InitWithDatabase(
223 scoped_ptr
<LevelDB
>(mock_db
), base::FilePath(path
),
224 base::Bind(&MockDatabaseCaller::InitCallback
, base::Unretained(&caller
)));
226 scoped_ptr
<ProtoDatabase
<TestProto
>::KeyEntryVector
> entries(
227 new ProtoDatabase
<TestProto
>::KeyEntryVector());
228 for (EntryMap::iterator it
= model
.begin(); it
!= model
.end(); ++it
) {
229 entries
->push_back(std::make_pair(it
->second
.id(), it
->second
));
231 scoped_ptr
<KeyVector
> keys_to_remove(new KeyVector());
233 EXPECT_CALL(*mock_db
, Save(_
, _
)).WillOnce(VerifyUpdateEntries(model
));
234 EXPECT_CALL(caller
, SaveCallback(true));
236 entries
.Pass(), keys_to_remove
.Pass(),
237 base::Bind(&MockDatabaseCaller::SaveCallback
, base::Unretained(&caller
)));
239 base::RunLoop().RunUntilIdle();
242 TEST_F(ProtoDatabaseImplTest
, TestDBSaveFailure
) {
243 base::FilePath
path(FILE_PATH_LITERAL("/fake/path"));
245 MockDB
* mock_db
= new MockDB();
246 MockDatabaseCaller caller
;
247 scoped_ptr
<ProtoDatabase
<TestProto
>::KeyEntryVector
> entries(
248 new ProtoDatabase
<TestProto
>::KeyEntryVector());
249 scoped_ptr
<KeyVector
> keys_to_remove(new KeyVector());
251 EXPECT_CALL(*mock_db
, Init(_
));
252 EXPECT_CALL(caller
, InitCallback(_
));
253 db_
->InitWithDatabase(
254 scoped_ptr
<LevelDB
>(mock_db
), base::FilePath(path
),
255 base::Bind(&MockDatabaseCaller::InitCallback
, base::Unretained(&caller
)));
257 EXPECT_CALL(*mock_db
, Save(_
, _
)).WillOnce(Return(false));
258 EXPECT_CALL(caller
, SaveCallback(false));
260 entries
.Pass(), keys_to_remove
.Pass(),
261 base::Bind(&MockDatabaseCaller::SaveCallback
, base::Unretained(&caller
)));
263 base::RunLoop().RunUntilIdle();
266 // Test that ProtoDatabaseImpl calls Save on the underlying database with the
267 // correct entries to delete and that the caller's SaveCallback is called with
268 // the correct success value.
269 TEST_F(ProtoDatabaseImplTest
, TestDBRemoveSuccess
) {
270 base::FilePath
path(FILE_PATH_LITERAL("/fake/path"));
272 MockDB
* mock_db
= new MockDB();
273 MockDatabaseCaller caller
;
274 EntryMap model
= GetSmallModel();
276 EXPECT_CALL(*mock_db
, Init(_
));
277 EXPECT_CALL(caller
, InitCallback(_
));
278 db_
->InitWithDatabase(
279 scoped_ptr
<LevelDB
>(mock_db
), base::FilePath(path
),
280 base::Bind(&MockDatabaseCaller::InitCallback
, base::Unretained(&caller
)));
282 scoped_ptr
<ProtoDatabase
<TestProto
>::KeyEntryVector
> entries(
283 new ProtoDatabase
<TestProto
>::KeyEntryVector());
284 scoped_ptr
<KeyVector
> keys_to_remove(new KeyVector());
285 for (EntryMap::iterator it
= model
.begin(); it
!= model
.end(); ++it
) {
286 keys_to_remove
->push_back(it
->second
.id());
289 KeyVector
keys_copy(*keys_to_remove
.get());
290 EXPECT_CALL(*mock_db
, Save(_
, keys_copy
)).WillOnce(Return(true));
291 EXPECT_CALL(caller
, SaveCallback(true));
293 entries
.Pass(), keys_to_remove
.Pass(),
294 base::Bind(&MockDatabaseCaller::SaveCallback
, base::Unretained(&caller
)));
296 base::RunLoop().RunUntilIdle();
299 TEST_F(ProtoDatabaseImplTest
, TestDBRemoveFailure
) {
300 base::FilePath
path(FILE_PATH_LITERAL("/fake/path"));
302 MockDB
* mock_db
= new MockDB();
303 MockDatabaseCaller caller
;
304 scoped_ptr
<ProtoDatabase
<TestProto
>::KeyEntryVector
> entries(
305 new ProtoDatabase
<TestProto
>::KeyEntryVector());
306 scoped_ptr
<KeyVector
> keys_to_remove(new KeyVector());
308 EXPECT_CALL(*mock_db
, Init(_
));
309 EXPECT_CALL(caller
, InitCallback(_
));
310 db_
->InitWithDatabase(
311 scoped_ptr
<LevelDB
>(mock_db
), base::FilePath(path
),
312 base::Bind(&MockDatabaseCaller::InitCallback
, base::Unretained(&caller
)));
314 EXPECT_CALL(*mock_db
, Save(_
, _
)).WillOnce(Return(false));
315 EXPECT_CALL(caller
, SaveCallback(false));
317 entries
.Pass(), keys_to_remove
.Pass(),
318 base::Bind(&MockDatabaseCaller::SaveCallback
, base::Unretained(&caller
)));
320 base::RunLoop().RunUntilIdle();
323 // This tests that normal usage of the real database does not cause any
324 // threading violations.
325 TEST(ProtoDatabaseImplThreadingTest
, TestDBDestruction
) {
326 base::MessageLoop main_loop
;
328 ScopedTempDir temp_dir
;
329 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
331 base::Thread
db_thread("dbthread");
332 ASSERT_TRUE(db_thread
.Start());
334 scoped_ptr
<ProtoDatabaseImpl
<TestProto
> > db(
335 new ProtoDatabaseImpl
<TestProto
>(db_thread
.message_loop_proxy()));
337 MockDatabaseCaller caller
;
338 EXPECT_CALL(caller
, InitCallback(_
));
339 db
->Init(temp_dir
.path(), base::Bind(&MockDatabaseCaller::InitCallback
,
340 base::Unretained(&caller
)));
344 base::RunLoop run_loop
;
345 db_thread
.message_loop_proxy()->PostTaskAndReply(
346 FROM_HERE
, base::Bind(base::DoNothing
), run_loop
.QuitClosure());
350 // Test that the LevelDB properly saves entries and that load returns the saved
351 // entries. If |close_after_save| is true, the database will be closed after
352 // saving and then re-opened to ensure that the data is properly persisted.
353 void TestLevelDBSaveAndLoad(bool close_after_save
) {
354 ScopedTempDir temp_dir
;
355 ASSERT_TRUE(temp_dir
.CreateUniqueTempDir());
357 EntryMap model
= GetSmallModel();
359 KeyValueVector save_entries
;
360 std::vector
<std::string
> load_entries
;
361 KeyVector remove_keys
;
363 for (EntryMap::iterator it
= model
.begin(); it
!= model
.end(); ++it
) {
364 save_entries
.push_back(
365 std::make_pair(it
->second
.id(), it
->second
.SerializeAsString()));
368 scoped_ptr
<LevelDB
> db(new LevelDB());
369 EXPECT_TRUE(db
->Init(temp_dir
.path()));
370 EXPECT_TRUE(db
->Save(save_entries
, remove_keys
));
372 if (close_after_save
) {
373 db
.reset(new LevelDB());
374 EXPECT_TRUE(db
->Init(temp_dir
.path()));
377 EXPECT_TRUE(db
->Load(&load_entries
));
378 // Convert the strings back to TestProto.
379 std::vector
<TestProto
> loaded_protos
;
380 for (std::vector
<std::string
>::iterator it
= load_entries
.begin();
381 it
!= load_entries
.end(); ++it
) {
383 entry
.ParseFromString(*it
);
384 loaded_protos
.push_back(entry
);
386 ExpectEntryPointersEquals(model
, loaded_protos
);
389 TEST(ProtoDatabaseImplLevelDBTest
, TestDBSaveAndLoad
) {
390 TestLevelDBSaveAndLoad(false);
393 TEST(ProtoDatabaseImplLevelDBTest
, TestDBCloseAndReopen
) {
394 TestLevelDBSaveAndLoad(true);
397 } // namespace leveldb_proto