| // 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 "components/sqlite_proto/proto_table_manager.h" |
| |
| #include <vector> |
| |
| #include "base/memory/scoped_refptr.h" |
| #include "base/strings/strcat.h" |
| #include "base/task/post_task.h" |
| #include "base/test/task_environment.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "components/sqlite_proto/key_value_data.h" |
| #include "components/sqlite_proto/key_value_table.h" |
| #include "components/sqlite_proto/test_proto.pb.h" |
| #include "sql/database.h" |
| #include "sql/meta_table.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace sqlite_proto { |
| |
| namespace { |
| |
| MATCHER_P(EqualsProto, |
| message, |
| "Match a proto Message equal to the matcher's argument.") { |
| std::string expected_serialized, actual_serialized; |
| message.SerializeToString(&expected_serialized); |
| arg.SerializeToString(&actual_serialized); |
| return expected_serialized == actual_serialized; |
| } |
| |
| constexpr char kTableName[] = "my_table"; |
| } // namespace |
| |
| TEST(ProtoTableTest, PutReinitializeAndGet) { |
| // In order to test ProtoTableManager is correctly |
| // initializing the underlying database's tables: |
| // - create a database and a ProtoTableManager on it; |
| // - store some data; and |
| // - construct a new ProtoTableManager to read from the |
| // existing database state. |
| |
| base::test::TaskEnvironment env; |
| sql::Database db; |
| CHECK(db.OpenInMemory()); |
| |
| auto manager = base::MakeRefCounted<ProtoTableManager>( |
| base::ThreadTaskRunnerHandle::Get()); |
| manager->InitializeOnDbSequence(&db, std::vector<std::string>{kTableName}, |
| /*schema_version=*/1); |
| |
| KeyValueTable<TestProto> table(kTableName); |
| |
| TestProto first_entry, second_entry; |
| first_entry.set_value(1); |
| second_entry.set_value(1); |
| |
| { |
| KeyValueData<TestProto> data(manager, &table, |
| /*max_num_entries=*/absl::nullopt, |
| /*flush_delay=*/base::TimeDelta()); |
| |
| // In these tests, we're using the current thread as the DB sequence. |
| data.InitializeOnDBSequence(); |
| |
| data.UpdateData("a", first_entry); |
| data.UpdateData("b", second_entry); |
| env.RunUntilIdle(); |
| } |
| |
| manager = base::MakeRefCounted<ProtoTableManager>( |
| base::ThreadTaskRunnerHandle::Get()); |
| manager->InitializeOnDbSequence(&db, std::vector<std::string>{kTableName}, |
| /*schema_version=*/1); |
| |
| { |
| KeyValueData<TestProto> data(manager, &table, |
| /*max_num_entries=*/absl::nullopt, |
| /*flush_delay=*/base::TimeDelta()); |
| |
| data.InitializeOnDBSequence(); |
| |
| TestProto result; |
| ASSERT_TRUE(data.TryGetData("a", &result)); |
| EXPECT_THAT(result, EqualsProto(first_entry)); |
| |
| ASSERT_TRUE(data.TryGetData("b", &result)); |
| EXPECT_THAT(result, EqualsProto(second_entry)); |
| } |
| } |
| |
| TEST(ProtoTableTest, ReinitializingWithDifferentVersionClearsTables) { |
| // In order to test ProtoTableManager is correctly |
| // initializing the underlying database's tables: |
| // - create a database and a ProtoTableManager on it; |
| // - store some data; and |
| // - construct a new ProtoTableManager to read from the |
| // existing database state. |
| |
| base::test::TaskEnvironment env; |
| sql::Database db; |
| CHECK(db.OpenInMemory()); |
| |
| constexpr int kInitialVersion = 1; |
| |
| auto manager = base::MakeRefCounted<ProtoTableManager>( |
| base::ThreadTaskRunnerHandle::Get()); |
| manager->InitializeOnDbSequence(&db, std::vector<std::string>{kTableName}, |
| /*schema_version=*/kInitialVersion); |
| |
| KeyValueTable<TestProto> table(kTableName); |
| |
| TestProto first_entry, second_entry; |
| first_entry.set_value(1); |
| second_entry.set_value(1); |
| |
| { |
| KeyValueData<TestProto> data(manager, &table, |
| /*max_num_entries=*/absl::nullopt, |
| /*flush_delay=*/base::TimeDelta()); |
| |
| // In these tests, we're using the current thread as the DB sequence. |
| data.InitializeOnDBSequence(); |
| |
| data.UpdateData("a", first_entry); |
| data.UpdateData("b", second_entry); |
| env.RunUntilIdle(); |
| } |
| |
| manager = base::MakeRefCounted<ProtoTableManager>( |
| base::ThreadTaskRunnerHandle::Get()); |
| manager->InitializeOnDbSequence(&db, std::vector<std::string>{kTableName}, |
| /*schema_version=*/kInitialVersion + 1); |
| |
| { |
| KeyValueData<TestProto> data(manager, &table, |
| /*max_num_entries=*/absl::nullopt, |
| /*flush_delay=*/base::TimeDelta()); |
| |
| data.InitializeOnDBSequence(); |
| |
| TestProto result; |
| EXPECT_FALSE(data.TryGetData("a", &result)); |
| EXPECT_FALSE(data.TryGetData("b", &result)); |
| } |
| } |
| |
| TEST(ProtoTableTest, InitializingWithoutWrittenVersionClearsTables) { |
| // Check that, when reinitializing the database when the most recent write |
| // occurred before the database started keeping track of versions, |
| // ProtoTableManager correctly clears the database. |
| |
| base::test::TaskEnvironment env; |
| sql::Database db; |
| CHECK(db.OpenInMemory()); |
| |
| constexpr int kInitialVersion = 1; |
| |
| auto manager = base::MakeRefCounted<ProtoTableManager>( |
| base::ThreadTaskRunnerHandle::Get()); |
| manager->InitializeOnDbSequence(&db, std::vector<std::string>{kTableName}, |
| /*schema_version=*/kInitialVersion); |
| |
| KeyValueTable<TestProto> table(kTableName); |
| |
| TestProto first_entry, second_entry; |
| first_entry.set_value(1); |
| second_entry.set_value(1); |
| |
| { |
| KeyValueData<TestProto> data(manager, &table, |
| /*max_num_entries=*/absl::nullopt, |
| /*flush_delay=*/base::TimeDelta()); |
| |
| // In these tests, we're using the current thread as the DB sequence. |
| data.InitializeOnDBSequence(); |
| |
| data.UpdateData("a", first_entry); |
| data.UpdateData("b", second_entry); |
| env.RunUntilIdle(); |
| |
| ASSERT_TRUE(sql::MetaTable::DeleteTableForTesting(&db)); |
| env.RunUntilIdle(); |
| } |
| |
| manager = base::MakeRefCounted<ProtoTableManager>( |
| base::ThreadTaskRunnerHandle::Get()); |
| manager->InitializeOnDbSequence(&db, std::vector<std::string>{kTableName}, |
| /*schema_version=*/kInitialVersion); |
| |
| { |
| KeyValueData<TestProto> data(manager, &table, |
| /*max_num_entries=*/absl::nullopt, |
| /*flush_delay=*/base::TimeDelta()); |
| |
| data.InitializeOnDBSequence(); |
| |
| TestProto result; |
| EXPECT_FALSE(data.TryGetData("a", &result)); |
| EXPECT_FALSE(data.TryGetData("b", &result)); |
| } |
| } |
| |
| TEST(ProtoTableTest, LoadingUnexpectedlyLargeVersionClearsTables) { |
| // Check that, when reinitializing the database and the most recent write |
| // occurred against a greater version of the database, ProtoTableManager |
| // correctly clears the database. |
| |
| base::test::TaskEnvironment env; |
| sql::Database db; |
| CHECK(db.OpenInMemory()); |
| |
| constexpr int kInitialVersion = 1; |
| |
| auto manager = base::MakeRefCounted<ProtoTableManager>( |
| base::ThreadTaskRunnerHandle::Get()); |
| manager->InitializeOnDbSequence(&db, std::vector<std::string>{kTableName}, |
| /*schema_version=*/kInitialVersion); |
| |
| KeyValueTable<TestProto> table(kTableName); |
| |
| TestProto first_entry, second_entry; |
| first_entry.set_value(1); |
| second_entry.set_value(1); |
| |
| { |
| KeyValueData<TestProto> data(manager, &table, |
| /*max_num_entries=*/absl::nullopt, |
| /*flush_delay=*/base::TimeDelta()); |
| |
| // In these tests, we're using the current thread as the DB sequence. |
| data.InitializeOnDBSequence(); |
| |
| data.UpdateData("a", first_entry); |
| data.UpdateData("b", second_entry); |
| env.RunUntilIdle(); |
| |
| // Overwrite the stored version. It's safe to use an instance of |
| // sql::MetaTable here because all sql::MetaTable instances use the same |
| // database name ("meta") to manipulate versions. |
| // |
| // MetaTable::Init only writes a version if there was no version previously |
| // written, so it doesn't matter what values the final two arguments have. |
| // The SetVersionNumber is what actually overwrites the version. |
| sql::MetaTable meta_helper; |
| ASSERT_TRUE(meta_helper.Init(&db, 1, 1)); |
| meta_helper.SetVersionNumber(kInitialVersion + 1); |
| meta_helper.SetCompatibleVersionNumber(kInitialVersion + 1); |
| env.RunUntilIdle(); |
| } |
| |
| manager = base::MakeRefCounted<ProtoTableManager>( |
| base::ThreadTaskRunnerHandle::Get()); |
| manager->InitializeOnDbSequence(&db, std::vector<std::string>{kTableName}, |
| /*schema_version=*/kInitialVersion); |
| |
| { |
| KeyValueData<TestProto> data(manager, &table, |
| /*max_num_entries=*/absl::nullopt, |
| /*flush_delay=*/base::TimeDelta()); |
| |
| data.InitializeOnDBSequence(); |
| |
| TestProto result; |
| EXPECT_FALSE(data.TryGetData("a", &result)); |
| EXPECT_FALSE(data.TryGetData("b", &result)); |
| } |
| } |
| |
| } // namespace sqlite_proto |