text: // Copyright 2020 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "chrome/browser/persisted_state_db/profile_proto_db.h"
  
  #include <map>
  
  #include "base/bind.h"
  #include "base/memory/ptr_util.h"
  #include "base/memory/raw_ptr.h"
  #include "base/run_loop.h"
  #include "base/task/thread_pool.h"
  #include "base/test/task_environment.h"
  #include "components/leveldb_proto/testing/fake_db.h"
  #include "testing/gmock/include/gmock/gmock.h"
  #include "testing/gtest/include/gtest/gtest.h"
  
  #include "chrome/browser/persisted_state_db/profile_proto_db_test_proto.pb.h"
  
  using testing::_;
  
  namespace {
  persisted_state_db::PersistedStateContentProto BuildProto(
      const char* protoKey,
      const std::vector<uint8_t> byteArray) {
    persisted_state_db::PersistedStateContentProto proto;
    proto.set_key(protoKey);
    proto.set_content_data(byteArray.data(), byteArray.size());
    return proto;
  }
  
  profile_proto_db::ProfileProtoDBTestProto BuildTestProto(const char* key,
                                                           const int32_t value) {
    profile_proto_db::ProfileProtoDBTestProto proto;
    proto.set_key(key);
    proto.set_b(value);
    return proto;
  }
  
  const char kMockKeyA[] = "A_key";
  const char kMockKeyPrefixA[] = "A";
  const std::vector<uint8_t> kMockValueArrayA = {0xfa, 0x5b, 0x4c, 0x12};
  const persisted_state_db::PersistedStateContentProto kMockValueA =
      BuildProto(kMockKeyA, kMockValueArrayA);
  const std::vector<
      ProfileProtoDB<persisted_state_db::PersistedStateContentProto>::KeyAndValue>
      kExpectedA = {{kMockKeyA, kMockValueA}};
  const char kMockKeyB[] = "B_key";
  const std::vector<uint8_t> kMockValueArrayB = {0x3c, 0x9f, 0x5e, 0x69};
  const persisted_state_db::PersistedStateContentProto kMockValueB =
      BuildProto(kMockKeyB, kMockValueArrayB);
  const std::vector<
      ProfileProtoDB<persisted_state_db::PersistedStateContentProto>::KeyAndValue>
      kExpectedB = {{kMockKeyB, kMockValueB}};
  const std::vector<
      ProfileProtoDB<persisted_state_db::PersistedStateContentProto>::KeyAndValue>
      kExpectedAB = {{kMockKeyA, kMockValueA}, {kMockKeyB, kMockValueB}};
  const std::vector<
      ProfileProtoDB<persisted_state_db::PersistedStateContentProto>::KeyAndValue>
      kEmptyExpected = {};
  const profile_proto_db::ProfileProtoDBTestProto kTestProto =
      BuildTestProto(kMockKeyA, 42);
  const std::vector<
      ProfileProtoDB<profile_proto_db::ProfileProtoDBTestProto>::KeyAndValue>
      kTestProtoExpected = {{kMockKeyA, kTestProto}};
  
  const char kSPTDTab1Key[] = "1_SPTD";
  const persisted_state_db::PersistedStateContentProto kSPTDTab1Value =
      BuildProto(kSPTDTab1Key, {0x5b, 0x2c});
  
  const char kSPTDTab2Key[] = "2_SPTD";
  const persisted_state_db::PersistedStateContentProto kSPTDTab2Value =
      BuildProto(kSPTDTab2Key, {0xfe, 0xab});
  
  const char kSPTDTab3Key[] = "3_SPTD";
  const persisted_state_db::PersistedStateContentProto kSPTDTab3Value =
      BuildProto(kSPTDTab3Key, {0x0a, 0x1b});
  
  const char kMPTDTab1Key[] = "1_MPTD";
  const persisted_state_db::PersistedStateContentProto kMPTDTab1Value =
      BuildProto(kMPTDTab1Key, {0xaa, 0x3b});
  
  const char kMatchingDataIdForMaintenance[] = "SPTD";
  const char kNonMatchingDataIdForMaintenance[] = "asdf";
  
  }  // namespace
  
  class ProfileProtoDBTest : public testing::Test {
   public:
    ProfileProtoDBTest() = default;
    ProfileProtoDBTest(const ProfileProtoDBTest&) = delete;
    ProfileProtoDBTest& operator=(const ProfileProtoDBTest&) = delete;
  
    // The following methods are specific to the database containing
    // persisted_state_db::PersistedStateContentProto. There is one test which
    // contains profile_proto_db::ProfileProtoDBTestProto to test the use case
    // of multiple ProfileProtoDB databases running at the same time.
  
    // Initialize the test database
    void InitPersistedStateDB() {
      InitPersistedStateDBWithoutCallback();
      MockInitCallbackPersistedStateDB(content_db_,
                                       leveldb_proto::Enums::InitStatus::kOK);
    }
  
    void InitPersistedStateDBWithoutCallback() {
      auto storage_db = std::make_unique<leveldb_proto::test::FakeDB<
          persisted_state_db::PersistedStateContentProto>>(&content_db_storage_);
      content_db_ = storage_db.get();
      persisted_state_db_ = base::WrapUnique(
          new ProfileProtoDB<persisted_state_db::PersistedStateContentProto>(
              std::move(storage_db),
              base::ThreadPool::CreateSequencedTaskRunner(
                  {base::MayBlock(), base::TaskPriority::USER_VISIBLE})));
    }
  
    void MockInitCallbackPersistedStateDB(
        leveldb_proto::test::FakeDB<
            persisted_state_db::PersistedStateContentProto>* storage_db,
        leveldb_proto::Enums::InitStatus status) {
      storage_db->InitStatusCallback(status);
      RunUntilIdle();
    }
  
    void MockInsertCallbackPersistedStateDB(
        leveldb_proto::test::FakeDB<
            persisted_state_db::PersistedStateContentProto>* storage_db,
        bool result) {
      storage_db->UpdateCallback(result);
      RunUntilIdle();
    }
  
    void MockLoadCallbackPersistedStateDB(
        leveldb_proto::test::FakeDB<
            persisted_state_db::PersistedStateContentProto>* storage_db,
        bool res) {
      storage_db->LoadCallback(res);
      RunUntilIdle();
    }
  
    void MockDeleteCallbackPersistedStateDB(
        leveldb_proto::test::FakeDB<
            persisted_state_db::PersistedStateContentProto>* storage_db,
        bool res) {
      storage_db->UpdateCallback(res);
      RunUntilIdle();
    }
  
    void GetEvaluationPersistedStateDB(
        base::OnceClosure closure,
        std::vector<ProfileProtoDB<
            persisted_state_db::PersistedStateContentProto>::KeyAndValue>
            expected,
        bool result,
        std::vector<ProfileProtoDB<
            persisted_state_db::PersistedStateContentProto>::KeyAndValue> found) {
      for (size_t i = 0; i < expected.size(); i++) {
        EXPECT_EQ(found[i].first, expected[i].first);
        EXPECT_EQ(found[i].second.content_data(),
                  expected[i].second.content_data());
      }
      std::move(closure).Run();
    }
  
    void GetEmptyEvaluationPersistedStateDB(
        base::OnceClosure closure,
        bool result,
        std::vector<ProfileProtoDB<
            persisted_state_db::PersistedStateContentProto>::KeyAndValue> found) {
      EXPECT_TRUE(result);
      EXPECT_EQ(found.size(), 0U);
      std::move(closure).Run();
    }
  
    // Common to both databases
    void OperationEvaluation(base::OnceClosure closure,
                             bool expected_success,
                             bool actual_success) {
      EXPECT_EQ(expected_success, actual_success);
      std::move(closure).Run();
    }
  
    // Wait for all tasks to be cleared off the queue
    void RunUntilIdle() { task_environment_.RunUntilIdle(); }
  
    // Specific to profile_proto_db::ProfileProtoDBTestProto database
    void InitTestProtoDB() {
      auto storage_db = std::make_unique<
          leveldb_proto::test::FakeDB<profile_proto_db::ProfileProtoDBTestProto>>(
          &test_content_db_storage_);
      test_content_db_ = storage_db.get();
      test_proto_db_ = base::WrapUnique(
          new ProfileProtoDB<profile_proto_db::ProfileProtoDBTestProto>(
              std::move(storage_db),
              base::ThreadPool::CreateSequencedTaskRunner(
                  {base::MayBlock(), base::TaskPriority::USER_VISIBLE})));
    }
  
    void GetTestEvaluationTestProtoDB(
        base::OnceClosure closure,
        std::vector<ProfileProtoDB<
            profile_proto_db::ProfileProtoDBTestProto>::KeyAndValue> expected,
        bool result,
        std::vector<ProfileProtoDB<
            profile_proto_db::ProfileProtoDBTestProto>::KeyAndValue> found) {
      for (size_t i = 0; i < expected.size(); i++) {
        EXPECT_EQ(found[i].first, expected[i].first);
        EXPECT_EQ(found[i].second.b(), expected[i].second.b());
      }
      std::move(closure).Run();
    }
  
    void InsertContentAndVerify(
        const std::string& key,
        const persisted_state_db::PersistedStateContentProto& value) {
      InsertContent(key, value);
      VerifyContent(key, value);
    }
  
    void InsertContent(
        const std::string& key,
        const persisted_state_db::PersistedStateContentProto& value) {
      base::RunLoop wait_for_insert;
      persisted_state_db()->InsertContent(
          key, value,
          base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                         base::Unretained(this), wait_for_insert.QuitClosure(),
                         true));
      MockInsertCallbackPersistedStateDB(content_db(), true);
      wait_for_insert.Run();
    }
  
    void VerifyContent(
        const std::string& key,
        const persisted_state_db::PersistedStateContentProto& value) {
      base::RunLoop wait_for_load;
      persisted_state_db()->LoadContentWithPrefix(
          key,
          base::BindOnce(
              &ProfileProtoDBTest::GetEvaluationPersistedStateDB,
              base::Unretained(this), wait_for_load.QuitClosure(),
              std::vector<ProfileProtoDB<
                  persisted_state_db::PersistedStateContentProto>::KeyAndValue>(
                  {{key, value}})));
      MockLoadCallbackPersistedStateDB(content_db(), true);
      wait_for_load.Run();
    }
  
    void VerifyContentEmpty(const std::string& key) {
      base::RunLoop wait_for_load;
      persisted_state_db()->LoadContentWithPrefix(
          key,
          base::BindOnce(&ProfileProtoDBTest::GetEmptyEvaluationPersistedStateDB,
                         base::Unretained(this), wait_for_load.QuitClosure()));
      MockLoadCallbackPersistedStateDB(content_db(), true);
      wait_for_load.Run();
    }
  
    void PerformMaintenance(const std::vector<std::string>& keys_to_keep,
                            const std::string& data_id) {
      base::RunLoop wait_for_maintenance;
      persisted_state_db()->PerformMaintenance(
          keys_to_keep, data_id,
          base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                         base::Unretained(this),
                         wait_for_maintenance.QuitClosure(), true));
      MockLoadCallbackPersistedStateDB(content_db(), true);
      MockInsertCallbackPersistedStateDB(content_db(), true);
      wait_for_maintenance.Run();
    }
  
    // For persisted_state_db::PersistedStateContentProto database
    ProfileProtoDB<persisted_state_db::PersistedStateContentProto>*
    persisted_state_db() {
      return persisted_state_db_.get();
    }
    leveldb_proto::test::FakeDB<persisted_state_db::PersistedStateContentProto>*
    content_db() {
      return content_db_;
    }
  
    std::vector<base::OnceClosure>& deferred_operations() {
      return persisted_state_db()->deferred_operations_;
    }
  
    bool InitStatusUnknown() { return persisted_state_db()->InitStatusUnknown(); }
    bool FailedToInit() { return persisted_state_db()->FailedToInit(); }
  
    std::map<std::string, persisted_state_db::PersistedStateContentProto>
        content_db_storage_;
  
    // For profile_proto_db::ProfileProtoDBTestProto database
    ProfileProtoDB<profile_proto_db::ProfileProtoDBTestProto>* test_proto_db() {
      return test_proto_db_.get();
    }
    leveldb_proto::test::FakeDB<profile_proto_db::ProfileProtoDBTestProto>*
    test_content_db() {
      return test_content_db_;
    }
  
    std::map<std::string, profile_proto_db::ProfileProtoDBTestProto>
        test_content_db_storage_;
  
   protected:
    raw_ptr<
        leveldb_proto::test::FakeDB<profile_proto_db::ProfileProtoDBTestProto>>
        test_content_db_;
  
   private:
    base::test::TaskEnvironment task_environment_;
  
    // For persisted_state_db::PersistedStateContentProto database
    raw_ptr<leveldb_proto::test::FakeDB<
        persisted_state_db::PersistedStateContentProto>>
        content_db_;
    std::unique_ptr<
        ProfileProtoDB<persisted_state_db::PersistedStateContentProto>>
        persisted_state_db_;
  
    // For profile_proto_db::ProfileProtoDBTestProto database
    std::unique_ptr<ProfileProtoDB<profile_proto_db::ProfileProtoDBTestProto>>
        test_proto_db_;
  };
  
  // Test an arbitrary proto - this ensures we can have two ProfilProtoDB
  // databases running simultaneously.
  TEST_F(ProfileProtoDBTest, TestArbitraryProto) {
    InitTestProtoDB();
    test_content_db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
    RunUntilIdle();
    base::RunLoop run_loop[2];
    test_proto_db()->InsertContent(
        kMockKeyA, kTestProto,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[0].QuitClosure(), true));
    test_content_db()->UpdateCallback(true);
    RunUntilIdle();
    run_loop[0].Run();
    test_proto_db()->LoadContentWithPrefix(
        kMockKeyA,
        base::BindOnce(&ProfileProtoDBTest::GetTestEvaluationTestProtoDB,
                       base::Unretained(this), run_loop[1].QuitClosure(),
                       kTestProtoExpected));
    test_content_db()->LoadCallback(true);
    RunUntilIdle();
    run_loop[1].Run();
  }
  
  TEST_F(ProfileProtoDBTest, TestInit) {
    InitPersistedStateDB();
    EXPECT_EQ(false, FailedToInit());
  }
  
  TEST_F(ProfileProtoDBTest, TestKeyInsertionSucceeded) {
    InitPersistedStateDB();
    base::RunLoop run_loop[2];
    persisted_state_db()->InsertContent(
        kMockKeyA, kMockValueA,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[0].QuitClosure(), true));
    MockInsertCallbackPersistedStateDB(content_db(), true);
    run_loop[0].Run();
    persisted_state_db()->LoadContentWithPrefix(
        kMockKeyA,
        base::BindOnce(&ProfileProtoDBTest::GetEvaluationPersistedStateDB,
                       base::Unretained(this), run_loop[1].QuitClosure(),
                       kExpectedA));
    MockLoadCallbackPersistedStateDB(content_db(), true);
    run_loop[1].Run();
  }
  
  TEST_F(ProfileProtoDBTest, TestKeyInsertionFailed) {
    InitPersistedStateDB();
    base::RunLoop run_loop[2];
    persisted_state_db()->InsertContent(
        kMockKeyA, kMockValueA,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[0].QuitClosure(), false));
    MockInsertCallbackPersistedStateDB(content_db(), false);
    run_loop[0].Run();
    persisted_state_db()->LoadContentWithPrefix(
        kMockKeyA,
        base::BindOnce(&ProfileProtoDBTest::GetEvaluationPersistedStateDB,
                       base::Unretained(this), run_loop[1].QuitClosure(),
                       kEmptyExpected));
    MockLoadCallbackPersistedStateDB(content_db(), true);
    run_loop[1].Run();
  }
  
  TEST_F(ProfileProtoDBTest, TestKeyInsertionPrefix) {
    InitPersistedStateDB();
    base::RunLoop run_loop[2];
    persisted_state_db()->InsertContent(
        kMockKeyA, kMockValueA,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[0].QuitClosure(), true));
    MockInsertCallbackPersistedStateDB(content_db(), true);
    run_loop[0].Run();
    persisted_state_db()->LoadContentWithPrefix(
        kMockKeyPrefixA,
        base::BindOnce(&ProfileProtoDBTest::GetEvaluationPersistedStateDB,
                       base::Unretained(this), run_loop[1].QuitClosure(),
                       kExpectedA));
    MockLoadCallbackPersistedStateDB(content_db(), true);
    run_loop[1].Run();
  }
  
  TEST_F(ProfileProtoDBTest, TestLoadOneEntry) {
    InitPersistedStateDB();
    base::RunLoop run_loop[4];
    persisted_state_db()->InsertContent(
        kMockKeyA, kMockValueA,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[0].QuitClosure(), true));
    MockInsertCallbackPersistedStateDB(content_db(), true);
    run_loop[0].Run();
    persisted_state_db()->InsertContent(
        kMockKeyB, kMockValueB,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[1].QuitClosure(), true));
    MockInsertCallbackPersistedStateDB(content_db(), true);
    run_loop[1].Run();
    persisted_state_db()->LoadOneEntry(
        kMockKeyA,
        base::BindOnce(&ProfileProtoDBTest::GetEvaluationPersistedStateDB,
                       base::Unretained(this), run_loop[2].QuitClosure(),
                       kExpectedA));
    content_db()->GetCallback(true);
    run_loop[2].Run();
    persisted_state_db()->LoadOneEntry(
        kMockKeyB,
        base::BindOnce(&ProfileProtoDBTest::GetEvaluationPersistedStateDB,
                       base::Unretained(this), run_loop[3].QuitClosure(),
                       kExpectedB));
    content_db()->GetCallback(true);
    run_loop[3].Run();
  }
  
  TEST_F(ProfileProtoDBTest, TestLoadAllEntries) {
    InitPersistedStateDB();
    base::RunLoop run_loop[3];
    persisted_state_db()->InsertContent(
        kMockKeyA, kMockValueA,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[0].QuitClosure(), true));
    MockInsertCallbackPersistedStateDB(content_db(), true);
    run_loop[0].Run();
    persisted_state_db()->InsertContent(
        kMockKeyB, kMockValueB,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[1].QuitClosure(), true));
    MockInsertCallbackPersistedStateDB(content_db(), true);
    run_loop[1].Run();
    persisted_state_db()->LoadAllEntries(base::BindOnce(
        &ProfileProtoDBTest::GetEvaluationPersistedStateDB,
        base::Unretained(this), run_loop[2].QuitClosure(), kExpectedAB));
    MockLoadCallbackPersistedStateDB(content_db(), true);
    run_loop[2].Run();
  }
  
  TEST_F(ProfileProtoDBTest, TestDeleteWithPrefix) {
    InitPersistedStateDB();
    base::RunLoop run_loop[4];
    persisted_state_db()->InsertContent(
        kMockKeyA, kMockValueA,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[0].QuitClosure(), true));
    MockInsertCallbackPersistedStateDB(content_db(), true);
    run_loop[0].Run();
    persisted_state_db()->LoadContentWithPrefix(
        kMockKeyA,
        base::BindOnce(&ProfileProtoDBTest::GetEvaluationPersistedStateDB,
                       base::Unretained(this), run_loop[1].QuitClosure(),
                       kExpectedA));
    MockLoadCallbackPersistedStateDB(content_db(), true);
    run_loop[1].Run();
  
    persisted_state_db()->DeleteContentWithPrefix(
        kMockKeyPrefixA,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[2].QuitClosure(), true));
    MockDeleteCallbackPersistedStateDB(content_db(), true);
    run_loop[2].Run();
  
    persisted_state_db()->LoadContentWithPrefix(
        kMockKeyA,
        base::BindOnce(&ProfileProtoDBTest::GetEvaluationPersistedStateDB,
                       base::Unretained(this), run_loop[3].QuitClosure(),
                       kEmptyExpected));
    MockLoadCallbackPersistedStateDB(content_db(), true);
    run_loop[3].Run();
  }
  
  TEST_F(ProfileProtoDBTest, TestDeleteOneEntry) {
    InitPersistedStateDB();
    base::RunLoop run_loop[6];
    persisted_state_db()->InsertContent(
        kMockKeyA, kMockValueA,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[0].QuitClosure(), true));
    MockInsertCallbackPersistedStateDB(content_db(), true);
    run_loop[0].Run();
    persisted_state_db()->LoadContentWithPrefix(
        kMockKeyA,
        base::BindOnce(&ProfileProtoDBTest::GetEvaluationPersistedStateDB,
                       base::Unretained(this), run_loop[1].QuitClosure(),
                       kExpectedA));
    MockLoadCallbackPersistedStateDB(content_db(), true);
    run_loop[1].Run();
    persisted_state_db()->DeleteOneEntry(
        kMockKeyPrefixA,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[2].QuitClosure(), false));
    MockDeleteCallbackPersistedStateDB(content_db(), false);
    run_loop[2].Run();
    persisted_state_db()->LoadContentWithPrefix(
        kMockKeyA,
        base::BindOnce(&ProfileProtoDBTest::GetEvaluationPersistedStateDB,
                       base::Unretained(this), run_loop[3].QuitClosure(),
                       kExpectedA));
    MockLoadCallbackPersistedStateDB(content_db(), true);
    run_loop[3].Run();
    persisted_state_db()->DeleteOneEntry(
        kMockKeyA,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[4].QuitClosure(), true));
    MockDeleteCallbackPersistedStateDB(content_db(), true);
    run_loop[4].Run();
    persisted_state_db()->LoadContentWithPrefix(
        kMockKeyPrefixA,
        base::BindOnce(&ProfileProtoDBTest::GetEvaluationPersistedStateDB,
                       base::Unretained(this), run_loop[5].QuitClosure(),
                       kEmptyExpected));
    MockLoadCallbackPersistedStateDB(content_db(), true);
    run_loop[5].Run();
  }
  
  TEST_F(ProfileProtoDBTest, TestDeferredOperations) {
    InitPersistedStateDBWithoutCallback();
    RunUntilIdle();
    EXPECT_EQ(true, InitStatusUnknown());
    base::RunLoop run_loop[4];
  
    persisted_state_db()->InsertContent(
        kMockKeyA, kMockValueA,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[0].QuitClosure(), true));
    persisted_state_db()->LoadContentWithPrefix(
        kMockKeyA,
        base::BindOnce(&ProfileProtoDBTest::GetEvaluationPersistedStateDB,
                       base::Unretained(this), run_loop[1].QuitClosure(),
                       kExpectedA));
    EXPECT_EQ(2u, deferred_operations().size());
  
    content_db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
    EXPECT_EQ(false, FailedToInit());
  
    MockInsertCallbackPersistedStateDB(content_db(), true);
    MockLoadCallbackPersistedStateDB(content_db(), true);
    run_loop[0].Run();
    run_loop[1].Run();
    EXPECT_EQ(0u, deferred_operations().size());
  
    persisted_state_db()->DeleteContentWithPrefix(
        kMockKeyA,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[2].QuitClosure(), true));
    EXPECT_EQ(0u, deferred_operations().size());
    MockDeleteCallbackPersistedStateDB(content_db(), true);
  
    persisted_state_db()->LoadContentWithPrefix(
        kMockKeyA,
        base::BindOnce(&ProfileProtoDBTest::GetEvaluationPersistedStateDB,
                       base::Unretained(this), run_loop[3].QuitClosure(),
                       kEmptyExpected));
    EXPECT_EQ(0u, deferred_operations().size());
    MockLoadCallbackPersistedStateDB(content_db(), true);
    run_loop[3].Run();
  }
  
  TEST_F(ProfileProtoDBTest, TestInitializationFailure) {
    InitPersistedStateDBWithoutCallback();
    RunUntilIdle();
    EXPECT_EQ(true, InitStatusUnknown());
    base::RunLoop run_loop[6];
  
    // Do some operations before database status is known
    persisted_state_db()->InsertContent(
        kMockKeyA, kMockValueA,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[0].QuitClosure(), false));
    persisted_state_db()->LoadContentWithPrefix(
        kMockKeyA,
        base::BindOnce(&ProfileProtoDBTest::GetEvaluationPersistedStateDB,
                       base::Unretained(this), run_loop[1].QuitClosure(),
                       kEmptyExpected));
    persisted_state_db()->DeleteContentWithPrefix(
        kMockKeyA,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[2].QuitClosure(), false));
    EXPECT_EQ(3u, deferred_operations().size());
  
    // Error initializing database
    content_db()->InitStatusCallback(leveldb_proto::Enums::InitStatus::kError);
    EXPECT_EQ(true, FailedToInit());
    for (int i = 0; i < 3; i++) {
      run_loop[i].Run();
    }
  
    // Check deferred_operations is flushed
    EXPECT_EQ(0u, deferred_operations().size());
  
    // More operations should just return false/null as the database
    // failed to initialize
    persisted_state_db()->InsertContent(
        kMockKeyA, kMockValueA,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[3].QuitClosure(), false));
    persisted_state_db()->LoadContentWithPrefix(
        kMockKeyA,
        base::BindOnce(&ProfileProtoDBTest::GetEvaluationPersistedStateDB,
                       base::Unretained(this), run_loop[4].QuitClosure(),
                       kEmptyExpected));
    persisted_state_db()->DeleteContentWithPrefix(
        kMockKeyA,
        base::BindOnce(&ProfileProtoDBTest::OperationEvaluation,
                       base::Unretained(this), run_loop[5].QuitClosure(), false));
  
    // Operations should have returned immediately as database was initialization
    // resulted in an error
    EXPECT_EQ(0u, deferred_operations().size());
    for (int i = 3; i < 6; i++) {
      run_loop[i].Run();
    }
  }
  
  TEST_F(ProfileProtoDBTest, TestMaintenanceKeepSomeKeys) {
    InitPersistedStateDB();
    InsertContentAndVerify(kSPTDTab1Key, kSPTDTab1Value);
    InsertContentAndVerify(kSPTDTab2Key, kSPTDTab2Value);
    InsertContentAndVerify(kSPTDTab3Key, kSPTDTab3Value);
    InsertContentAndVerify(kMPTDTab1Key, kMPTDTab1Value);
    PerformMaintenance(std::vector<std::string>{kSPTDTab1Key, kSPTDTab3Key},
                       kMatchingDataIdForMaintenance);
    VerifyContent(kSPTDTab1Key, kSPTDTab1Value);
    VerifyContent(kSPTDTab3Key, kSPTDTab3Value);
    VerifyContent(kMPTDTab1Key, kMPTDTab1Value);
    VerifyContentEmpty(kSPTDTab2Key);
  }
  
  TEST_F(ProfileProtoDBTest, TestMaintenanceKeepNoKeys) {
    InitPersistedStateDB();
    InsertContentAndVerify(kSPTDTab1Key, kSPTDTab1Value);
    InsertContentAndVerify(kSPTDTab2Key, kSPTDTab2Value);
    InsertContentAndVerify(kSPTDTab3Key, kSPTDTab3Value);
    InsertContentAndVerify(kMPTDTab1Key, kMPTDTab1Value);
    PerformMaintenance(std::vector<std::string>{}, kMatchingDataIdForMaintenance);
    VerifyContent(kMPTDTab1Key, kMPTDTab1Value);
    VerifyContentEmpty(kSPTDTab1Key);
    VerifyContentEmpty(kSPTDTab2Key);
    VerifyContentEmpty(kSPTDTab3Key);
  }
  
  TEST_F(ProfileProtoDBTest, TestMaintenanceNonMatchingDataId) {
    InitPersistedStateDB();
    InsertContentAndVerify(kSPTDTab1Key, kSPTDTab1Value);
    InsertContentAndVerify(kSPTDTab2Key, kSPTDTab2Value);
    InsertContentAndVerify(kSPTDTab3Key, kSPTDTab3Value);
    InsertContentAndVerify(kMPTDTab1Key, kMPTDTab1Value);
    PerformMaintenance(std::vector<std::string>{kSPTDTab1Key, kSPTDTab3Key},
                       kNonMatchingDataIdForMaintenance);
    VerifyContent(kSPTDTab1Key, kSPTDTab1Value);
    VerifyContent(kSPTDTab2Key, kSPTDTab2Value);
    VerifyContent(kSPTDTab3Key, kSPTDTab3Value);
    VerifyContent(kMPTDTab1Key, kMPTDTab1Value);
  }
  