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