// 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/key_value_table.h" #include "base/memory/scoped_refptr.h" #include "components/sqlite_proto/test_proto.pb.h" #include "sql/database.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::ElementsAre; using ::testing::Pair; using ::testing::UnorderedElementsAre; 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; } bool CreateProtoTable(sql::Database* db) { const char kCreateProtoTableStatementTemplate[] = "CREATE TABLE my_table ( " "key TEXT, " "proto BLOB, " "PRIMARY KEY(key))"; return db->Execute(kCreateProtoTableStatementTemplate); } } // namespace class KeyValueTableTest : public ::testing::Test { public: KeyValueTableTest() { CHECK(db_.OpenInMemory()); CHECK(CreateProtoTable(&db_)); } ~KeyValueTableTest() override = default; protected: sql::Database db_; KeyValueTable table_{"my_table"}; }; TEST_F(KeyValueTableTest, Empty) { std::map my_data; table_.GetAllData(&my_data, &db_); EXPECT_TRUE(my_data.empty()); } TEST_F(KeyValueTableTest, PutAndGet) { TestProto element; element.set_value(1); table_.UpdateData("a", element, &db_); TestProto second_element; second_element.set_value(2); table_.UpdateData("b", second_element, &db_); std::map my_data; table_.GetAllData(&my_data, &db_); EXPECT_THAT(my_data, UnorderedElementsAre(Pair("a", EqualsProto(element)), Pair("b", EqualsProto(second_element)))); } TEST_F(KeyValueTableTest, Update) { TestProto element; element.set_value(1); TestProto superseding_element; superseding_element.set_value(2); table_.UpdateData("a", element, &db_); table_.UpdateData("a", superseding_element, &db_); std::map my_data; table_.GetAllData(&my_data, &db_); EXPECT_THAT(my_data, ElementsAre(Pair("a", EqualsProto(superseding_element)))); } TEST_F(KeyValueTableTest, DeleteNonexistent) { TestProto element; element.set_value(1); table_.UpdateData("a", element, &db_); // Deleting a nonexistent key should no-op. table_.DeleteData(std::vector{"b"}, &db_); std::map my_data; table_.GetAllData(&my_data, &db_); EXPECT_THAT(my_data, ElementsAre(Pair("a", EqualsProto(element)))); } TEST_F(KeyValueTableTest, Delete) { TestProto element; element.set_value(1); table_.UpdateData("a", element, &db_); table_.DeleteData(std::vector{"a"}, &db_); std::map my_data; table_.GetAllData(&my_data, &db_); EXPECT_TRUE(my_data.empty()); } TEST_F(KeyValueTableTest, DeleteAll) { TestProto element; element.set_value(1); table_.UpdateData("a", element, &db_); table_.UpdateData("b", element, &db_); table_.DeleteAllData(&db_); std::map my_data; table_.GetAllData(&my_data, &db_); EXPECT_TRUE(my_data.empty()); } // Storing an element with a default proto value (zero byte size) should work; // this can be useful since a default value can have different semantics from a // missing value. TEST_F(KeyValueTableTest, PutGetDefaultValue) { TestProto element; table_.UpdateData("a", element, &db_); ASSERT_EQ(element.ByteSize(), 0); std::map my_data; table_.GetAllData(&my_data, &db_); EXPECT_THAT(my_data, ElementsAre(Pair("a", EqualsProto(element)))); } } // namespace sqlite_proto