• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "components/leveldb_proto/proto_database_impl.h"
6 
7 #include <map>
8 
9 #include "base/bind.h"
10 #include "base/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"
18 
19 using base::MessageLoop;
20 using base::ScopedTempDir;
21 using testing::Invoke;
22 using testing::Return;
23 using testing::_;
24 
25 namespace leveldb_proto {
26 
27 namespace {
28 
29 typedef std::map<std::string, TestProto> EntryMap;
30 
31 class MockDB : public LevelDB {
32  public:
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>*));
36 
MockDB()37   MockDB() {
38     ON_CALL(*this, Init(_)).WillByDefault(Return(true));
39     ON_CALL(*this, Save(_, _)).WillByDefault(Return(true));
40     ON_CALL(*this, Load(_)).WillByDefault(Return(true));
41   }
42 };
43 
44 class MockDatabaseCaller {
45  public:
46   MOCK_METHOD1(InitCallback, void(bool));
47   MOCK_METHOD1(SaveCallback, void(bool));
LoadCallback(bool success,scoped_ptr<std::vector<TestProto>> entries)48   void LoadCallback(bool success, scoped_ptr<std::vector<TestProto> > entries) {
49     LoadCallback1(success, entries.get());
50   }
51   MOCK_METHOD2(LoadCallback1, void(bool, std::vector<TestProto>*));
52 };
53 
54 }  // namespace
55 
GetSmallModel()56 EntryMap GetSmallModel() {
57   EntryMap model;
58 
59   model["0"].set_id("0");
60   model["0"].set_data("http://foo.com/1");
61 
62   model["1"].set_id("1");
63   model["1"].set_data("http://bar.com/all");
64 
65   model["2"].set_id("2");
66   model["2"].set_data("http://baz.com/1");
67 
68   return model;
69 }
70 
ExpectEntryPointersEquals(EntryMap expected,const std::vector<TestProto> & actual)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);
81   }
82 }
83 
84 class ProtoDatabaseImplTest : public testing::Test {
85  public:
SetUp()86   virtual void SetUp() {
87     main_loop_.reset(new MessageLoop());
88     db_.reset(
89         new ProtoDatabaseImpl<TestProto>(main_loop_->message_loop_proxy()));
90   }
91 
TearDown()92   virtual void TearDown() {
93     db_.reset();
94     base::RunLoop().RunUntilIdle();
95     main_loop_.reset();
96   }
97 
98   scoped_ptr<ProtoDatabaseImpl<TestProto> > db_;
99   scoped_ptr<MessageLoop> main_loop_;
100 };
101 
102 // Test that ProtoDatabaseImpl calls Init on the underlying database and that
103 // the caller's InitCallback is called with the correct value.
TEST_F(ProtoDatabaseImplTest,TestDBInitSuccess)104 TEST_F(ProtoDatabaseImplTest, TestDBInitSuccess) {
105   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
106 
107   MockDB* mock_db = new MockDB();
108   EXPECT_CALL(*mock_db, Init(path)).WillOnce(Return(true));
109 
110   MockDatabaseCaller caller;
111   EXPECT_CALL(caller, InitCallback(true));
112 
113   db_->InitWithDatabase(
114       scoped_ptr<LevelDB>(mock_db), base::FilePath(path),
115       base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
116 
117   base::RunLoop().RunUntilIdle();
118 }
119 
TEST_F(ProtoDatabaseImplTest,TestDBInitFailure)120 TEST_F(ProtoDatabaseImplTest, TestDBInitFailure) {
121   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
122 
123   MockDB* mock_db = new MockDB();
124   EXPECT_CALL(*mock_db, Init(path)).WillOnce(Return(false));
125 
126   MockDatabaseCaller caller;
127   EXPECT_CALL(caller, InitCallback(false));
128 
129   db_->InitWithDatabase(
130       scoped_ptr<LevelDB>(mock_db), base::FilePath(path),
131       base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
132 
133   base::RunLoop().RunUntilIdle();
134 }
135 
ACTION_P(AppendLoadEntries,model)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());
140   }
141   return true;
142 }
143 
ACTION_P(VerifyLoadEntries,expected)144 ACTION_P(VerifyLoadEntries, expected) {
145   std::vector<TestProto>* actual = arg1;
146   ExpectEntryPointersEquals(expected, *actual);
147 }
148 
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
152 // LoadCallback.
TEST_F(ProtoDatabaseImplTest,TestDBLoadSuccess)153 TEST_F(ProtoDatabaseImplTest, TestDBLoadSuccess) {
154   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
155 
156   MockDB* mock_db = new MockDB();
157   MockDatabaseCaller caller;
158   EntryMap model = GetSmallModel();
159 
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)));
165 
166   EXPECT_CALL(*mock_db, Load(_)).WillOnce(AppendLoadEntries(model));
167   EXPECT_CALL(caller, LoadCallback1(true, _))
168       .WillOnce(VerifyLoadEntries(testing::ByRef(model)));
169   db_->LoadEntries(
170       base::Bind(&MockDatabaseCaller::LoadCallback, base::Unretained(&caller)));
171 
172   base::RunLoop().RunUntilIdle();
173 }
174 
TEST_F(ProtoDatabaseImplTest,TestDBLoadFailure)175 TEST_F(ProtoDatabaseImplTest, TestDBLoadFailure) {
176   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
177 
178   MockDB* mock_db = new MockDB();
179   MockDatabaseCaller caller;
180 
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)));
186 
187   EXPECT_CALL(*mock_db, Load(_)).WillOnce(Return(false));
188   EXPECT_CALL(caller, LoadCallback1(false, _));
189   db_->LoadEntries(
190       base::Bind(&MockDatabaseCaller::LoadCallback, base::Unretained(&caller)));
191 
192   base::RunLoop().RunUntilIdle();
193 }
194 
ACTION_P(VerifyUpdateEntries,expected)195 ACTION_P(VerifyUpdateEntries, expected) {
196   const KeyValueVector actual = arg0;
197   // Create a vector of TestProto from |actual| to reuse the comparison
198   // function.
199   std::vector<TestProto> extracted_entries;
200   for (KeyValueVector::const_iterator it = actual.begin(); it != actual.end();
201        ++it) {
202     TestProto entry;
203     entry.ParseFromString(it->second);
204     extracted_entries.push_back(entry);
205   }
206   ExpectEntryPointersEquals(expected, extracted_entries);
207   return true;
208 }
209 
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.
TEST_F(ProtoDatabaseImplTest,TestDBSaveSuccess)213 TEST_F(ProtoDatabaseImplTest, TestDBSaveSuccess) {
214   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
215 
216   MockDB* mock_db = new MockDB();
217   MockDatabaseCaller caller;
218   EntryMap model = GetSmallModel();
219 
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)));
225 
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));
230   }
231   scoped_ptr<KeyVector> keys_to_remove(new KeyVector());
232 
233   EXPECT_CALL(*mock_db, Save(_, _)).WillOnce(VerifyUpdateEntries(model));
234   EXPECT_CALL(caller, SaveCallback(true));
235   db_->UpdateEntries(
236       entries.Pass(), keys_to_remove.Pass(),
237       base::Bind(&MockDatabaseCaller::SaveCallback, base::Unretained(&caller)));
238 
239   base::RunLoop().RunUntilIdle();
240 }
241 
TEST_F(ProtoDatabaseImplTest,TestDBSaveFailure)242 TEST_F(ProtoDatabaseImplTest, TestDBSaveFailure) {
243   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
244 
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());
250 
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)));
256 
257   EXPECT_CALL(*mock_db, Save(_, _)).WillOnce(Return(false));
258   EXPECT_CALL(caller, SaveCallback(false));
259   db_->UpdateEntries(
260       entries.Pass(), keys_to_remove.Pass(),
261       base::Bind(&MockDatabaseCaller::SaveCallback, base::Unretained(&caller)));
262 
263   base::RunLoop().RunUntilIdle();
264 }
265 
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.
TEST_F(ProtoDatabaseImplTest,TestDBRemoveSuccess)269 TEST_F(ProtoDatabaseImplTest, TestDBRemoveSuccess) {
270   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
271 
272   MockDB* mock_db = new MockDB();
273   MockDatabaseCaller caller;
274   EntryMap model = GetSmallModel();
275 
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)));
281 
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());
287   }
288 
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));
292   db_->UpdateEntries(
293       entries.Pass(), keys_to_remove.Pass(),
294       base::Bind(&MockDatabaseCaller::SaveCallback, base::Unretained(&caller)));
295 
296   base::RunLoop().RunUntilIdle();
297 }
298 
TEST_F(ProtoDatabaseImplTest,TestDBRemoveFailure)299 TEST_F(ProtoDatabaseImplTest, TestDBRemoveFailure) {
300   base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
301 
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());
307 
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)));
313 
314   EXPECT_CALL(*mock_db, Save(_, _)).WillOnce(Return(false));
315   EXPECT_CALL(caller, SaveCallback(false));
316   db_->UpdateEntries(
317       entries.Pass(), keys_to_remove.Pass(),
318       base::Bind(&MockDatabaseCaller::SaveCallback, base::Unretained(&caller)));
319 
320   base::RunLoop().RunUntilIdle();
321 }
322 
323 // This tests that normal usage of the real database does not cause any
324 // threading violations.
TEST(ProtoDatabaseImplThreadingTest,TestDBDestruction)325 TEST(ProtoDatabaseImplThreadingTest, TestDBDestruction) {
326   base::MessageLoop main_loop;
327 
328   ScopedTempDir temp_dir;
329   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
330 
331   base::Thread db_thread("dbthread");
332   ASSERT_TRUE(db_thread.Start());
333 
334   scoped_ptr<ProtoDatabaseImpl<TestProto> > db(
335       new ProtoDatabaseImpl<TestProto>(db_thread.message_loop_proxy()));
336 
337   MockDatabaseCaller caller;
338   EXPECT_CALL(caller, InitCallback(_));
339   db->Init(temp_dir.path(), base::Bind(&MockDatabaseCaller::InitCallback,
340                                        base::Unretained(&caller)));
341 
342   db.reset();
343 
344   base::RunLoop run_loop;
345   db_thread.message_loop_proxy()->PostTaskAndReply(
346       FROM_HERE, base::Bind(base::DoNothing), run_loop.QuitClosure());
347   run_loop.Run();
348 }
349 
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.
TestLevelDBSaveAndLoad(bool close_after_save)353 void TestLevelDBSaveAndLoad(bool close_after_save) {
354   ScopedTempDir temp_dir;
355   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
356 
357   EntryMap model = GetSmallModel();
358 
359   KeyValueVector save_entries;
360   std::vector<std::string> load_entries;
361   KeyVector remove_keys;
362 
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()));
366   }
367 
368   scoped_ptr<LevelDB> db(new LevelDB());
369   EXPECT_TRUE(db->Init(temp_dir.path()));
370   EXPECT_TRUE(db->Save(save_entries, remove_keys));
371 
372   if (close_after_save) {
373     db.reset(new LevelDB());
374     EXPECT_TRUE(db->Init(temp_dir.path()));
375   }
376 
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) {
382     TestProto entry;
383     entry.ParseFromString(*it);
384     loaded_protos.push_back(entry);
385   }
386   ExpectEntryPointersEquals(model, loaded_protos);
387 }
388 
TEST(ProtoDatabaseImplLevelDBTest,TestDBSaveAndLoad)389 TEST(ProtoDatabaseImplLevelDBTest, TestDBSaveAndLoad) {
390   TestLevelDBSaveAndLoad(false);
391 }
392 
TEST(ProtoDatabaseImplLevelDBTest,TestDBCloseAndReopen)393 TEST(ProtoDatabaseImplLevelDBTest, TestDBCloseAndReopen) {
394   TestLevelDBSaveAndLoad(true);
395 }
396 
397 }  // namespace leveldb_proto
398