1 // Copyright 2018 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/internal/shared_proto_database_client.h"
6 
7 #include <set>
8 #include <string>
9 
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/strings/string_util.h"
14 #include "base/test/task_environment.h"
15 #include "components/leveldb_proto/internal/proto_leveldb_wrapper.h"
16 #include "components/leveldb_proto/internal/shared_proto_database.h"
17 #include "components/leveldb_proto/public/shared_proto_database_client_list.h"
18 #include "components/leveldb_proto/testing/proto/test_db.pb.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 
21 namespace leveldb_proto {
22 
23 class SharedProtoDatabaseClientTest : public testing::Test {
24  public:
SetUp()25   void SetUp() override {
26     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
27     db_ = base::WrapRefCounted(
28         new SharedProtoDatabase("client", temp_dir_.GetPath()));
29   }
30 
TearDown()31   void TearDown() override {
32     if (db_)
33       db_->Shutdown();
34   }
35 
36  protected:
db()37   scoped_refptr<SharedProtoDatabase> db() { return db_; }
temp_dir()38   base::ScopedTempDir* temp_dir() { return &temp_dir_; }
39 
GetLevelDB() const40   LevelDB* GetLevelDB() const { return db_->GetLevelDBForTesting(); }
41 
GetClient(ProtoDbType db_type,bool create_if_missing,Callbacks::InitStatusCallback callback,SharedDBMetadataProto::MigrationStatus expected_migration_status=SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED)42   std::unique_ptr<SharedProtoDatabaseClient> GetClient(
43       ProtoDbType db_type,
44       bool create_if_missing,
45       Callbacks::InitStatusCallback callback,
46       SharedDBMetadataProto::MigrationStatus expected_migration_status =
47           SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED) {
48     return db_->GetClientForTesting(
49         db_type, create_if_missing,
50         base::BindOnce(
51             [](SharedDBMetadataProto::MigrationStatus expected_migration_status,
52                Callbacks::InitStatusCallback callback, Enums::InitStatus status,
53                SharedDBMetadataProto::MigrationStatus migration_status) {
54               EXPECT_EQ(expected_migration_status, migration_status);
55               std::move(callback).Run(status);
56             },
57             expected_migration_status, std::move(callback)));
58   }
59 
GetClientAndWait(ProtoDbType db_type,bool create_if_missing,Enums::InitStatus * status,SharedDBMetadataProto::MigrationStatus expected_migration_status=SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED)60   std::unique_ptr<SharedProtoDatabaseClient> GetClientAndWait(
61       ProtoDbType db_type,
62       bool create_if_missing,
63       Enums::InitStatus* status,
64       SharedDBMetadataProto::MigrationStatus expected_migration_status =
65           SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED) {
66     base::RunLoop loop;
67     auto client =
68         GetClient(db_type, create_if_missing,
69                   base::BindOnce(
70                       [](Enums::InitStatus* status_out,
71                          base::OnceClosure closure, Enums::InitStatus status) {
72                         *status_out = status;
73                         std::move(closure).Run();
74                       },
75                       status, loop.QuitClosure()),
76                   expected_migration_status);
77     loop.Run();
78     return client;
79   }
80 
ContainsKeys(const leveldb_proto::KeyVector & db_keys,const leveldb_proto::KeyVector & keys,ProtoDbType db_type)81   bool ContainsKeys(const leveldb_proto::KeyVector& db_keys,
82                     const leveldb_proto::KeyVector& keys,
83                     ProtoDbType db_type) {
84     std::set<std::string> key_set(db_keys.begin(), db_keys.end());
85     for (auto& key : keys) {
86       auto full_key =
87           db_type == ProtoDbType::LAST
88               ? key
89               : SharedProtoDatabaseClient::PrefixForDatabase(db_type) + key;
90       if (key_set.find(full_key) == key_set.end())
91         return false;
92     }
93     return true;
94   }
95 
ContainsEntries(const leveldb_proto::KeyVector & db_keys,const ValueVector & entries,ProtoDbType db_type)96   bool ContainsEntries(const leveldb_proto::KeyVector& db_keys,
97                        const ValueVector& entries,
98                        ProtoDbType db_type) {
99     std::set<std::string> entry_id_set;
100     for (auto& entry : entries)
101       entry_id_set.insert(entry);
102 
103     // Entry IDs don't include the full prefix, so we don't look for that here.
104     auto prefix = SharedProtoDatabaseClient::PrefixForDatabase(db_type);
105     for (auto& key : db_keys) {
106       if (entry_id_set.find(prefix + key) == entry_id_set.end())
107         return false;
108     }
109     return true;
110   }
111 
UpdateEntries(SharedProtoDatabaseClient * client,const leveldb_proto::KeyVector & keys,const leveldb_proto::KeyVector & keys_to_remove,bool expect_success)112   void UpdateEntries(SharedProtoDatabaseClient* client,
113                      const leveldb_proto::KeyVector& keys,
114                      const leveldb_proto::KeyVector& keys_to_remove,
115                      bool expect_success) {
116     auto entries =
117         std::make_unique<ProtoDatabase<std::string>::KeyEntryVector>();
118     for (auto& key : keys) {
119       std::string value;
120       value = client->prefix_ + key;
121       entries->emplace_back(std::make_pair(key, std::move(value)));
122     }
123 
124     auto entries_to_remove = std::make_unique<leveldb_proto::KeyVector>();
125     for (auto& key : keys_to_remove)
126       entries_to_remove->push_back(key);
127 
128     base::RunLoop update_entries_loop;
129     client->UpdateEntries(
130         std::move(entries), std::move(entries_to_remove),
131         base::BindOnce(
132             [](base::OnceClosure signal, bool expect_success, bool success) {
133               ASSERT_EQ(success, expect_success);
134               std::move(signal).Run();
135             },
136             update_entries_loop.QuitClosure(), expect_success));
137     update_entries_loop.Run();
138   }
139 
UpdateEntriesWithRemoveFilter(SharedProtoDatabaseClient * client,const leveldb_proto::KeyVector & keys,const KeyFilter & delete_key_filter,bool expect_success)140   void UpdateEntriesWithRemoveFilter(SharedProtoDatabaseClient* client,
141                                      const leveldb_proto::KeyVector& keys,
142                                      const KeyFilter& delete_key_filter,
143                                      bool expect_success) {
144     auto entries =
145         std::make_unique<ProtoDatabase<std::string>::KeyEntryVector>();
146     for (auto& key : keys) {
147       std::string value;
148       value = client->prefix_ + key;
149       entries->emplace_back(std::make_pair(key, std::move(value)));
150     }
151 
152     base::RunLoop update_entries_loop;
153     client->UpdateEntriesWithRemoveFilter(
154         std::move(entries), delete_key_filter,
155         base::BindOnce(
156             [](base::OnceClosure signal, bool expect_success, bool success) {
157               ASSERT_EQ(success, expect_success);
158               std::move(signal).Run();
159             },
160             update_entries_loop.QuitClosure(), expect_success));
161     update_entries_loop.Run();
162   }
163 
164   // This fills in for all the LoadEntriesX functions because they all call this
165   // one.
LoadEntriesWithFilter(SharedProtoDatabaseClient * client,const KeyFilter & key_filter,const leveldb::ReadOptions & options,const std::string & target_prefix,bool expect_success,std::unique_ptr<ValueVector> * entries)166   void LoadEntriesWithFilter(SharedProtoDatabaseClient* client,
167                              const KeyFilter& key_filter,
168                              const leveldb::ReadOptions& options,
169                              const std::string& target_prefix,
170                              bool expect_success,
171                              std::unique_ptr<ValueVector>* entries) {
172     base::RunLoop load_entries_loop;
173     client->LoadEntriesWithFilter(
174         key_filter, options, target_prefix,
175         base::BindOnce(
176             [](std::unique_ptr<ValueVector>* entries_ptr,
177                base::OnceClosure signal, bool expect_success, bool success,
178                std::unique_ptr<ValueVector> entries) {
179               ASSERT_EQ(success, expect_success);
180               entries_ptr->reset(entries.release());
181               std::move(signal).Run();
182             },
183             entries, load_entries_loop.QuitClosure(), expect_success));
184     load_entries_loop.Run();
185   }
186 
LoadKeysAndEntriesInRange(SharedProtoDatabaseClient * client,const std::string & start,const std::string & end,bool expect_success,std::unique_ptr<KeyValueMap> * entries)187   void LoadKeysAndEntriesInRange(SharedProtoDatabaseClient* client,
188                                  const std::string& start,
189                                  const std::string& end,
190                                  bool expect_success,
191                                  std::unique_ptr<KeyValueMap>* entries) {
192     base::RunLoop load_entries_in_range_loop;
193     client->LoadKeysAndEntriesInRange(
194         start, end,
195         base::BindOnce(
196             [](std::unique_ptr<KeyValueMap>* entries_ptr,
197                base::OnceClosure signal, bool expect_success, bool success,
198                std::unique_ptr<KeyValueMap> entries) {
199               ASSERT_EQ(success, expect_success);
200               *entries_ptr = std::move(entries);
201               std::move(signal).Run();
202             },
203             entries, load_entries_in_range_loop.QuitClosure(), expect_success));
204     load_entries_in_range_loop.Run();
205   }
206 
LoadKeys(SharedProtoDatabaseClient * client,bool expect_success,std::unique_ptr<KeyVector> * keys)207   void LoadKeys(SharedProtoDatabaseClient* client,
208                 bool expect_success,
209                 std::unique_ptr<KeyVector>* keys) {
210     base::RunLoop load_keys_loop;
211     client->LoadKeys(base::BindOnce(
212         [](std::unique_ptr<KeyVector>* keys_ptr, base::OnceClosure signal,
213            bool expect_success, bool success, std::unique_ptr<KeyVector> keys) {
214           ASSERT_EQ(success, expect_success);
215           keys_ptr->reset(keys.release());
216           std::move(signal).Run();
217         },
218         keys, load_keys_loop.QuitClosure(), expect_success));
219     load_keys_loop.Run();
220   }
221 
GetEntry(SharedProtoDatabaseClient * client,const std::string & key,bool expect_success)222   std::unique_ptr<std::string> GetEntry(SharedProtoDatabaseClient* client,
223                                         const std::string& key,
224                                         bool expect_success) {
225     std::unique_ptr<std::string> entry;
226     base::RunLoop get_key_loop;
227     client->GetEntry(
228         key, base::BindOnce(
229                  [](std::unique_ptr<std::string>* entry_ptr,
230                     base::OnceClosure signal, bool expect_success, bool success,
231                     std::unique_ptr<std::string> entry) {
232                    ASSERT_EQ(success, expect_success);
233                    entry_ptr->reset(entry.release());
234                    std::move(signal).Run();
235                  },
236                  &entry, get_key_loop.QuitClosure(), expect_success));
237     get_key_loop.Run();
238     return entry;
239   }
240 
Destroy(SharedProtoDatabaseClient * client,bool expect_success)241   void Destroy(SharedProtoDatabaseClient* client, bool expect_success) {
242     base::RunLoop destroy_loop;
243     client->Destroy(base::BindOnce(
244         [](bool expect_success, base::OnceClosure signal, bool success) {
245           ASSERT_EQ(success, expect_success);
246           std::move(signal).Run();
247         },
248         expect_success, destroy_loop.QuitClosure()));
249     destroy_loop.Run();
250   }
251 
252   // Sets the obsolete client list to given list, runs clean up tasks and waits
253   // for them to complete.
DestroyObsoleteClientsAndWait(const ProtoDbType * client_list)254   void DestroyObsoleteClientsAndWait(const ProtoDbType* client_list) {
255     SharedProtoDatabaseClient::SetObsoleteClientListForTesting(client_list);
256     base::RunLoop wait_loop;
257     Callbacks::UpdateCallback wait_callback = base::BindOnce(
258         [](base::OnceClosure closure, bool success) {
259           EXPECT_TRUE(success);
260           std::move(closure).Run();
261         },
262         wait_loop.QuitClosure());
263 
264     SharedProtoDatabaseClient::DestroyObsoleteSharedProtoDatabaseClients(
265         std::make_unique<ProtoLevelDBWrapper>(
266             db_->database_task_runner_for_testing(), GetLevelDB()),
267         std::move(wait_callback));
268     wait_loop.Run();
269     SharedProtoDatabaseClient::SetObsoleteClientListForTesting(nullptr);
270   }
271 
UpdateMetadataAsync(SharedProtoDatabaseClient * client,SharedDBMetadataProto::MigrationStatus migration_status)272   void UpdateMetadataAsync(
273       SharedProtoDatabaseClient* client,
274       SharedDBMetadataProto::MigrationStatus migration_status) {
275     base::RunLoop wait_loop;
276     Callbacks::UpdateCallback wait_callback = base::BindOnce(
277         [](base::OnceClosure closure, bool success) {
278           EXPECT_TRUE(success);
279           std::move(closure).Run();
280         },
281         wait_loop.QuitClosure());
282     client->set_migration_status(migration_status);
283     SharedProtoDatabaseClient::UpdateClientMetadataAsync(
284         client->parent_db_, client->prefix_, migration_status,
285         std::move(wait_callback));
286     wait_loop.Run();
287   }
288 
289  private:
290   base::ScopedTempDir temp_dir_;
291   base::test::TaskEnvironment task_environment_;
292   scoped_refptr<SharedProtoDatabase> db_;
293 };
294 
TEST_F(SharedProtoDatabaseClientTest,InitSuccess)295 TEST_F(SharedProtoDatabaseClientTest, InitSuccess) {
296   auto status = Enums::InitStatus::kError;
297   auto client = GetClientAndWait(ProtoDbType::TEST_DATABASE0,
298                                  true /* create_if_missing */, &status);
299   ASSERT_EQ(status, Enums::InitStatus::kOK);
300 
301   client->Init("client", base::BindOnce([](Enums::InitStatus status) {
302                  ASSERT_EQ(status, Enums::InitStatus::kOK);
303                }));
304 }
305 
TEST_F(SharedProtoDatabaseClientTest,InitFail)306 TEST_F(SharedProtoDatabaseClientTest, InitFail) {
307   auto status = Enums::InitStatus::kError;
308   auto client = GetClientAndWait(ProtoDbType::TEST_DATABASE0,
309                                  false /* create_if_missing */, &status);
310   ASSERT_EQ(status, Enums::InitStatus::kInvalidOperation);
311 
312   client->Init("client", base::BindOnce([](Enums::InitStatus status) {
313                  ASSERT_EQ(status, Enums::InitStatus::kError);
314                }));
315 }
316 
317 // Ensure that our LevelDB contains the properly prefixed entries and also
318 // removes prefixed entries correctly.
TEST_F(SharedProtoDatabaseClientTest,UpdateEntriesAppropriatePrefix)319 TEST_F(SharedProtoDatabaseClientTest, UpdateEntriesAppropriatePrefix) {
320   auto status = Enums::InitStatus::kError;
321   auto client = GetClientAndWait(ProtoDbType::TEST_DATABASE0,
322                                  true /* create_if_missing */, &status);
323   ASSERT_EQ(status, Enums::InitStatus::kOK);
324 
325   KeyVector key_list = {"entry1", "entry2", "entry3"};
326   UpdateEntries(client.get(), key_list, leveldb_proto::KeyVector(), true);
327 
328   // Make sure those entries are in the LevelDB with the appropriate prefix.
329   KeyVector keys;
330   LevelDB* db = GetLevelDB();
331   db->LoadKeys(&keys);
332   ASSERT_EQ(keys.size(), 3U);
333   ASSERT_TRUE(ContainsKeys(keys, key_list, ProtoDbType::TEST_DATABASE0));
334 
335   auto remove_keys = {key_list[0]};
336   // Now try to delete one entry.
337   UpdateEntries(client.get(), leveldb_proto::KeyVector(), remove_keys, true);
338 
339   keys.clear();
340   db->LoadKeys(&keys);
341   ASSERT_EQ(keys.size(), 2U);
342   ASSERT_TRUE(ContainsKeys(keys, {key_list[1], key_list[2]},
343                            ProtoDbType::TEST_DATABASE0));
344 }
345 
TEST_F(SharedProtoDatabaseClientTest,UpdateEntries_DeletesCorrectClientEntries)346 TEST_F(SharedProtoDatabaseClientTest,
347        UpdateEntries_DeletesCorrectClientEntries) {
348   auto status = Enums::InitStatus::kError;
349   auto client_a = GetClientAndWait(ProtoDbType::TEST_DATABASE0,
350                                    true /* create_if_missing */, &status);
351   ASSERT_EQ(status, Enums::InitStatus::kOK);
352   auto client_b = GetClientAndWait(ProtoDbType::TEST_DATABASE2,
353                                    true /* create_if_missing */, &status);
354   ASSERT_EQ(status, Enums::InitStatus::kOK);
355 
356   KeyVector key_list = {"entry1", "entry2", "entry3"};
357   UpdateEntries(client_a.get(), key_list, leveldb_proto::KeyVector(), true);
358   UpdateEntries(client_b.get(), key_list, leveldb_proto::KeyVector(), true);
359 
360   KeyVector keys;
361   LevelDB* db = GetLevelDB();
362   db->LoadKeys(&keys);
363   ASSERT_EQ(keys.size(), 6U);
364   ASSERT_TRUE(ContainsKeys(keys, key_list, ProtoDbType::TEST_DATABASE0));
365   ASSERT_TRUE(ContainsKeys(keys, key_list, ProtoDbType::TEST_DATABASE2));
366 
367   // Now delete from client_b and ensure only client A's values exist.
368   UpdateEntries(client_b.get(), leveldb_proto::KeyVector(), key_list, true);
369   keys.clear();
370   db->LoadKeys(&keys);
371   ASSERT_EQ(keys.size(), 3U);
372   ASSERT_TRUE(ContainsKeys(keys, key_list, ProtoDbType::TEST_DATABASE0));
373 }
374 
TEST_F(SharedProtoDatabaseClientTest,UpdateEntriesWithRemoveFilter_DeletesCorrectEntries)375 TEST_F(SharedProtoDatabaseClientTest,
376        UpdateEntriesWithRemoveFilter_DeletesCorrectEntries) {
377   auto status = Enums::InitStatus::kError;
378   auto client = GetClientAndWait(ProtoDbType::TEST_DATABASE0,
379                                  true /* create_if_missing */, &status);
380   ASSERT_EQ(status, Enums::InitStatus::kOK);
381 
382   KeyVector key_list = {"entry1", "entry2", "testentry3"};
383   UpdateEntries(client.get(), key_list, leveldb_proto::KeyVector(), true);
384 
385   KeyVector keys;
386   LevelDB* db = GetLevelDB();
387   db->LoadKeys(&keys);
388   ASSERT_EQ(keys.size(), 3U);
389 
390   UpdateEntriesWithRemoveFilter(client.get(), leveldb_proto::KeyVector(),
391                                 base::BindRepeating([](const std::string& key) {
392                                   return base::StartsWith(
393                                       key, "test",
394                                       base::CompareCase::INSENSITIVE_ASCII);
395                                 }),
396                                 true);
397 
398   keys.clear();
399   db->LoadKeys(&keys);
400   ASSERT_EQ(keys.size(), 2U);
401   // Make sure "testentry3" was removed.
402   ASSERT_TRUE(
403       ContainsKeys(keys, {"entry1", "entry2"}, ProtoDbType::TEST_DATABASE0));
404 }
405 
TEST_F(SharedProtoDatabaseClientTest,LoadEntriesWithFilter)406 TEST_F(SharedProtoDatabaseClientTest, LoadEntriesWithFilter) {
407   auto status = Enums::InitStatus::kError;
408   auto client_a = GetClientAndWait(ProtoDbType::TEST_DATABASE0,
409                                    true /* create_if_missing */, &status);
410   ASSERT_EQ(status, Enums::InitStatus::kOK);
411   auto client_b = GetClientAndWait(ProtoDbType::TEST_DATABASE2,
412                                    true /* create_if_missing */, &status);
413   ASSERT_EQ(status, Enums::InitStatus::kOK);
414 
415   KeyVector key_list_a = {"entry123", "entry2123", "testentry3"};
416   UpdateEntries(client_a.get(), key_list_a, leveldb_proto::KeyVector(), true);
417   KeyVector key_list_b = {"testentry124", "entry2124", "testentry4"};
418   UpdateEntries(client_b.get(), key_list_b, leveldb_proto::KeyVector(), true);
419 
420   std::unique_ptr<ValueVector> entries;
421   LoadEntriesWithFilter(client_a.get(), leveldb_proto::KeyFilter(),
422                         leveldb::ReadOptions(), std::string(), true, &entries);
423   ASSERT_EQ(entries->size(), 3U);
424   ASSERT_TRUE(
425       ContainsEntries(key_list_a, *entries, ProtoDbType::TEST_DATABASE0));
426 
427   LoadEntriesWithFilter(client_b.get(), leveldb_proto::KeyFilter(),
428                         leveldb::ReadOptions(), std::string(), true, &entries);
429   ASSERT_EQ(entries->size(), 3U);
430   ASSERT_TRUE(
431       ContainsEntries(key_list_b, *entries, ProtoDbType::TEST_DATABASE2));
432 
433   // Now test the actual filtering functionality.
434   LoadEntriesWithFilter(
435       client_b.get(), base::BindRepeating([](const std::string& key) {
436         // Strips the entries that start with "test" from |key_list_b|.
437         return !base::StartsWith(key, "test",
438                                  base::CompareCase::INSENSITIVE_ASCII);
439       }),
440       leveldb::ReadOptions(), std::string(), true, &entries);
441   ASSERT_EQ(entries->size(), 1U);
442   ASSERT_TRUE(
443       ContainsEntries({"entry2124"}, *entries, ProtoDbType::TEST_DATABASE2));
444 }
445 
TEST_F(SharedProtoDatabaseClientTest,LoadKeysAndEntriesInRange)446 TEST_F(SharedProtoDatabaseClientTest, LoadKeysAndEntriesInRange) {
447   auto status = Enums::InitStatus::kError;
448   auto client_a = GetClientAndWait(ProtoDbType::TEST_DATABASE0,
449                                    true /* create_if_missing */, &status);
450   ASSERT_EQ(status, Enums::InitStatus::kOK);
451   auto client_b = GetClientAndWait(ProtoDbType::TEST_DATABASE2,
452                                    true /* create_if_missing */, &status);
453   ASSERT_EQ(status, Enums::InitStatus::kOK);
454 
455   KeyVector key_list_a = {"entry0",           "entry1", "entry2", "entry3",
456                           "entry3notinrange", "entry4", "entry5"};
457   UpdateEntries(client_a.get(), key_list_a, leveldb_proto::KeyVector(), true);
458   KeyVector key_list_b = {"entry2", "entry3", "entry4"};
459   UpdateEntries(client_b.get(), key_list_b, leveldb_proto::KeyVector(), true);
460 
461   std::unique_ptr<KeyValueMap> keys_and_entries_a;
462   LoadKeysAndEntriesInRange(client_a.get(), "entry1", "entry3", true,
463                             &keys_and_entries_a);
464 
465   std::unique_ptr<KeyValueMap> keys_and_entries_b;
466   LoadKeysAndEntriesInRange(client_b.get(), "entry0", "entry1", true,
467                             &keys_and_entries_b);
468 
469   ValueVector entries;
470 
471   for (auto& pair : *keys_and_entries_a) {
472     entries.push_back(pair.second);
473   }
474 
475   ASSERT_EQ(keys_and_entries_a->size(), 3U);
476   ASSERT_EQ(keys_and_entries_b->size(), 0U);
477   ASSERT_TRUE(ContainsEntries({"entry1", "entry2", "entry3"}, entries,
478                               ProtoDbType::TEST_DATABASE0));
479 }
480 
TEST_F(SharedProtoDatabaseClientTest,LoadKeys)481 TEST_F(SharedProtoDatabaseClientTest, LoadKeys) {
482   auto status = Enums::InitStatus::kError;
483   auto client_a = GetClientAndWait(ProtoDbType::TEST_DATABASE0,
484                                    true /* create_if_missing */, &status);
485   ASSERT_EQ(status, Enums::InitStatus::kOK);
486   auto client_b = GetClientAndWait(ProtoDbType::TEST_DATABASE2,
487                                    true /* create_if_missing */, &status);
488   ASSERT_EQ(status, Enums::InitStatus::kOK);
489 
490   KeyVector key_list_a = {"entry123", "entry2123", "testentry3", "testing"};
491   UpdateEntries(client_a.get(), key_list_a, leveldb_proto::KeyVector(), true);
492   KeyVector key_list_b = {"testentry124", "entry2124", "testentry4"};
493   UpdateEntries(client_b.get(), key_list_b, leveldb_proto::KeyVector(), true);
494 
495   std::unique_ptr<KeyVector> keys;
496   LoadKeys(client_a.get(), true, &keys);
497   ASSERT_EQ(keys->size(), 4U);
498   ASSERT_TRUE(ContainsKeys(*keys, key_list_a, ProtoDbType::LAST));
499   LoadKeys(client_b.get(), true, &keys);
500   ASSERT_EQ(keys->size(), 3U);
501   ASSERT_TRUE(ContainsKeys(*keys, key_list_b, ProtoDbType::LAST));
502 }
503 
TEST_F(SharedProtoDatabaseClientTest,GetEntry)504 TEST_F(SharedProtoDatabaseClientTest, GetEntry) {
505   auto status = Enums::InitStatus::kError;
506   auto client_a = GetClientAndWait(ProtoDbType::TEST_DATABASE0,
507                                    true /* create_if_missing */, &status);
508   ASSERT_EQ(status, Enums::InitStatus::kOK);
509   auto client_b = GetClientAndWait(ProtoDbType::TEST_DATABASE2,
510                                    true /* create_if_missing */, &status);
511   ASSERT_EQ(status, Enums::InitStatus::kOK);
512 
513   KeyVector key_list = {"a", "b", "c"};
514   // Add the same entries to both because we want to make sure we only get the
515   // entries from the proper client.
516   UpdateEntries(client_a.get(), key_list, leveldb_proto::KeyVector(), true);
517   UpdateEntries(client_b.get(), key_list, leveldb_proto::KeyVector(), true);
518 
519   auto a_prefix =
520       SharedProtoDatabaseClient::PrefixForDatabase(ProtoDbType::TEST_DATABASE0);
521   auto b_prefix =
522       SharedProtoDatabaseClient::PrefixForDatabase(ProtoDbType::TEST_DATABASE2);
523 
524   for (auto& key : key_list) {
525     auto entry = GetEntry(client_a.get(), key, true);
526     ASSERT_EQ(*entry, a_prefix + key);
527     entry = GetEntry(client_b.get(), key, true);
528     ASSERT_EQ(*entry, b_prefix + key);
529   }
530 }
531 
TEST_F(SharedProtoDatabaseClientTest,TestCleanupObsoleteClients)532 TEST_F(SharedProtoDatabaseClientTest, TestCleanupObsoleteClients) {
533   auto status = Enums::InitStatus::kError;
534   auto client_a = GetClientAndWait(ProtoDbType::TEST_DATABASE0,
535                                    true /* create_if_missing */, &status);
536   ASSERT_EQ(status, Enums::InitStatus::kOK);
537   auto client_b = GetClientAndWait(ProtoDbType::TEST_DATABASE1,
538                                    true /* create_if_missing */, &status);
539   ASSERT_EQ(status, Enums::InitStatus::kOK);
540   auto client_c = GetClientAndWait(ProtoDbType::TEST_DATABASE2,
541                                    true /* create_if_missing */, &status);
542   ASSERT_EQ(status, Enums::InitStatus::kOK);
543 
544   KeyVector test_keys = {"a", "b", "c"};
545   UpdateEntries(client_a.get(), test_keys, leveldb_proto::KeyVector(), true);
546   UpdateEntries(client_b.get(), test_keys, leveldb_proto::KeyVector(), true);
547   UpdateEntries(client_c.get(), test_keys, leveldb_proto::KeyVector(), true);
548 
549   // Check that the original list does not clear any data from test DBs.
550   DestroyObsoleteClientsAndWait(nullptr /* client_list */);
551 
552   KeyVector keys;
553   LevelDB* db = GetLevelDB();
554   db->LoadKeys(&keys);
555   EXPECT_EQ(keys.size(), test_keys.size() * 3);
556 
557   // Mark some DBs obsolete.
558   const ProtoDbType kObsoleteList1[] = {ProtoDbType::TEST_DATABASE0,
559                                         ProtoDbType::TEST_DATABASE1,
560                                         ProtoDbType::LAST};
561   DestroyObsoleteClientsAndWait(kObsoleteList1);
562 
563   keys.clear();
564   db->LoadKeys(&keys);
565 
566   EXPECT_EQ(keys.size(), test_keys.size());
567   EXPECT_FALSE(ContainsKeys(keys, test_keys, ProtoDbType::TEST_DATABASE0));
568   EXPECT_FALSE(ContainsKeys(keys, test_keys, ProtoDbType::TEST_DATABASE1));
569   EXPECT_TRUE(ContainsKeys(keys, test_keys, ProtoDbType::TEST_DATABASE2));
570 
571   // Make all the DBs obsolete.
572   const ProtoDbType kObsoleteList2[] = {
573       ProtoDbType::TEST_DATABASE0, ProtoDbType::TEST_DATABASE1,
574       ProtoDbType::TEST_DATABASE2, ProtoDbType::LAST};
575   DestroyObsoleteClientsAndWait(kObsoleteList2);
576 
577   // Nothing should remain.
578   keys.clear();
579   db->LoadKeys(&keys);
580   EXPECT_EQ(keys.size(), 0U);
581 }
582 
TEST_F(SharedProtoDatabaseClientTest,TestDestroy)583 TEST_F(SharedProtoDatabaseClientTest, TestDestroy) {
584   auto status = Enums::InitStatus::kError;
585   auto client_a = GetClientAndWait(ProtoDbType::TEST_DATABASE0,
586                                    true /* create_if_missing */, &status);
587   ASSERT_EQ(status, Enums::InitStatus::kOK);
588   auto client_b = GetClientAndWait(ProtoDbType::TEST_DATABASE2,
589                                    true /* create_if_missing */, &status);
590   ASSERT_EQ(status, Enums::InitStatus::kOK);
591 
592   KeyVector key_list = {"a", "b", "c"};
593   // Add the same entries to both because we want to make sure we only destroy
594   // the entries from the proper client.
595   UpdateEntries(client_a.get(), key_list, leveldb_proto::KeyVector(), true);
596   UpdateEntries(client_b.get(), key_list, leveldb_proto::KeyVector(), true);
597 
598   // Delete only client A.
599   KeyVector keys;
600   LevelDB* db = GetLevelDB();
601   db->LoadKeys(&keys);
602   ASSERT_EQ(keys.size(), key_list.size() * 2);
603   ASSERT_TRUE(ContainsKeys(keys, key_list, ProtoDbType::TEST_DATABASE0));
604   ASSERT_TRUE(ContainsKeys(keys, key_list, ProtoDbType::TEST_DATABASE2));
605   Destroy(client_a.get(), true);
606 
607   // Make sure only client B remains and delete client B.
608   keys.clear();
609   db->LoadKeys(&keys);
610   ASSERT_EQ(keys.size(), key_list.size());
611   ASSERT_TRUE(ContainsKeys(keys, key_list, ProtoDbType::TEST_DATABASE2));
612   Destroy(client_b.get(), true);
613 
614   // Nothing should remain.
615   keys.clear();
616   db->LoadKeys(&keys);
617   ASSERT_EQ(keys.size(), 0U);
618 }
619 
TEST_F(SharedProtoDatabaseClientTest,UpdateClientMetadataAsync)620 TEST_F(SharedProtoDatabaseClientTest, UpdateClientMetadataAsync) {
621   auto status = Enums::InitStatus::kError;
622   auto client_a = GetClientAndWait(ProtoDbType::TEST_DATABASE0,
623                                    true /* create_if_missing */, &status);
624   EXPECT_EQ(status, Enums::InitStatus::kOK);
625   EXPECT_EQ(SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
626             client_a->migration_status());
627 
628   auto client_b = GetClientAndWait(ProtoDbType::TEST_DATABASE1,
629                                    true /* create_if_missing */, &status);
630   EXPECT_EQ(status, Enums::InitStatus::kOK);
631   EXPECT_EQ(SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
632             client_b->migration_status());
633 
634   auto client_c = GetClientAndWait(ProtoDbType::TEST_DATABASE2,
635                                    true /* create_if_missing */, &status);
636   EXPECT_EQ(status, Enums::InitStatus::kOK);
637   EXPECT_EQ(SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
638             client_c->migration_status());
639 
640   UpdateMetadataAsync(client_a.get(),
641                       SharedDBMetadataProto::MIGRATE_TO_SHARED_SUCCESSFUL);
642   UpdateMetadataAsync(
643       client_b.get(),
644       SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED);
645 
646   client_a.reset();
647   client_b.reset();
648   client_c.reset();
649 
650   auto client_d = GetClientAndWait(
651       ProtoDbType::TEST_DATABASE0, true /* create_if_missing */, &status,
652       SharedDBMetadataProto::MIGRATE_TO_SHARED_SUCCESSFUL);
653   EXPECT_EQ(status, Enums::InitStatus::kOK);
654 
655   auto client_e = GetClientAndWait(
656       ProtoDbType::TEST_DATABASE1, true /* create_if_missing */, &status,
657       SharedDBMetadataProto::MIGRATE_TO_UNIQUE_SHARED_TO_BE_DELETED);
658   EXPECT_EQ(status, Enums::InitStatus::kOK);
659 
660   auto client_f = GetClientAndWait(ProtoDbType::TEST_DATABASE2,
661                                    true /* create_if_missing */, &status);
662   EXPECT_EQ(status, Enums::InitStatus::kOK);
663 
664   UpdateMetadataAsync(client_d.get(),
665                       SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED);
666   UpdateMetadataAsync(client_e.get(),
667                       SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED);
668 }
669 
670 }  // namespace leveldb_proto
671