| // Copyright 2018 The Fuchsia 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 "src/developer/debug/zxdb/client/map_setting_store.h" |
| |
| #include <gtest/gtest.h> |
| |
| #include "src/developer/debug/zxdb/client/setting_schema.h" |
| |
| namespace zxdb { |
| |
| namespace { |
| |
| constexpr int kDefaultInt = 10; |
| const char kDefaultString[] = "string default"; |
| |
| std::vector<std::string> DefaultList() { return {kDefaultString, "list"}; } |
| |
| fxl::RefPtr<SettingSchema> GetSchema() { |
| auto schema = fxl::MakeRefCounted<SettingSchema>(); |
| |
| schema->AddBool("bool", "bool_option", true); |
| schema->AddInt("int", "int_option", kDefaultInt); |
| schema->AddString("string", "string_option", kDefaultString); |
| if (!schema->AddList("list", "list_option", DefaultList())) { |
| FX_NOTREACHED() << "Schema should be valid!"; |
| return nullptr; |
| } |
| if (!schema->AddList("list_with_options", "list_with_options", {}, DefaultList())) { |
| FX_NOTREACHED() << "Schema should be valid!"; |
| return nullptr; |
| } |
| return schema; |
| } |
| |
| class SettingObserver : public SettingStoreObserver { |
| public: |
| // Keep track of who called. |
| struct SettingNotificationRecord { |
| std::string name; |
| SettingValue value; |
| }; |
| |
| void OnSettingChanged(const SettingStore& store, const std::string& setting_name) override { |
| SettingNotificationRecord record = {}; |
| record.name = setting_name; |
| record.value = store.GetValue(setting_name); |
| notifications_.push_back(std::move(record)); |
| } |
| |
| const std::vector<SettingNotificationRecord>& notifications() const { return notifications_; } |
| |
| private: |
| std::vector<SettingNotificationRecord> notifications_; |
| }; |
| |
| } // namespace |
| |
| TEST(MapSettingStore, Defaults) { |
| MapSettingStore store(GetSchema(), nullptr); |
| |
| auto value = store.GetValue("bool"); |
| ASSERT_TRUE(value.is_bool()); |
| EXPECT_TRUE(value.get_bool()); |
| |
| value = store.GetValue("int"); |
| ASSERT_TRUE(value.is_int()); |
| EXPECT_EQ(value.get_int(), kDefaultInt); |
| |
| value = store.GetValue("string"); |
| ASSERT_TRUE(value.is_string()); |
| EXPECT_EQ(value.get_string(), kDefaultString); |
| |
| value = store.GetValue("list"); |
| ASSERT_TRUE(value.is_list()); |
| EXPECT_EQ(value.get_list(), DefaultList()); |
| |
| // Not found. |
| EXPECT_TRUE(store.GetValue("unexistent").is_null()); |
| } |
| |
| TEST(MapSettingStore, Overrides) { |
| MapSettingStore store(GetSchema(), nullptr); |
| |
| Err err; |
| |
| // Wrong key. |
| err = store.SetInt("wrong", 10); |
| EXPECT_TRUE(err.has_error()); |
| |
| // Wrong type. |
| err = store.SetInt("bool", false); |
| EXPECT_TRUE(err.has_error()); |
| |
| constexpr int kNewInt = 15; |
| err = store.SetInt("int", kNewInt); |
| ASSERT_FALSE(err.has_error()); |
| EXPECT_EQ(store.GetInt("int"), kNewInt); |
| |
| // Clear value |
| err = store.ClearValue("int"); |
| ASSERT_FALSE(err.has_error()); |
| EXPECT_EQ(store.GetInt("int"), kDefaultInt); |
| } |
| |
| TEST(MapSettingStore, ListOptions) { |
| Err err; |
| MapSettingStore store(GetSchema(), nullptr); |
| |
| // Attemp to add a valid item to the list with options. |
| err = store.SetList("list_with_options", {kDefaultString}); |
| EXPECT_FALSE(err.has_error()) << err.msg(); |
| |
| // Add an option that doesn't exist. |
| err = store.SetList("list_with_options", {"some_weird_option"}); |
| EXPECT_TRUE(err.has_error()); |
| } |
| |
| TEST(MapSettingStore, Fallback) { |
| MapSettingStore fallback2(GetSchema(), nullptr); |
| std::vector<std::string> new_list = {"new", "list"}; |
| fallback2.SetList("list", new_list); |
| |
| MapSettingStore fallback(GetSchema(), &fallback2); |
| std::string new_string = "new string"; |
| fallback.SetString("string", new_string); |
| |
| MapSettingStore store(GetSchema(), &fallback); |
| store.SetBool("bool", false); |
| |
| // Should get default for not overridden. |
| auto value = store.GetValue("int"); |
| ASSERT_TRUE(value.is_int()); |
| EXPECT_EQ(value.get_int(), kDefaultInt); |
| |
| // Should get local level. |
| value = store.GetValue("bool"); |
| ASSERT_TRUE(value.is_bool()); |
| EXPECT_FALSE(value.get_bool()); |
| |
| // Should get one override hop. |
| value = store.GetValue("string"); |
| ASSERT_TRUE(value.is_string()); |
| EXPECT_EQ(value.get_string(), new_string); |
| |
| // Should fallback through the chain. |
| value = store.GetValue("list"); |
| ASSERT_TRUE(value.is_list()); |
| EXPECT_EQ(value.get_list(), new_list); |
| } |
| |
| TEST(MapSettingStore, Notifications) { |
| MapSettingStore store(GetSchema(), nullptr); |
| |
| SettingObserver observer; |
| store.AddObserver("int", &observer); |
| store.AddObserver("list", &observer); |
| |
| // Getting values should not notify. |
| store.GetBool("bool"); |
| store.GetInt("int"); |
| store.GetString("string"); |
| store.GetList("list"); |
| EXPECT_TRUE(observer.notifications().empty()); |
| |
| Err err; |
| |
| // Setting another value should not notify. |
| err = store.SetBool("bool", false); |
| EXPECT_FALSE(err.has_error()) << err.msg(); |
| EXPECT_TRUE(observer.notifications().empty()); |
| |
| // Setting the int should call. |
| constexpr int kNewInt = 15; |
| err = store.SetInt("int", kNewInt); |
| EXPECT_FALSE(err.has_error()) << err.msg(); |
| |
| ASSERT_EQ(observer.notifications().size(), 1u); |
| auto record = observer.notifications().back(); |
| EXPECT_EQ(record.name, "int"); |
| ASSERT_TRUE(record.value.is_int()); |
| EXPECT_EQ(record.value.get_int(), kNewInt); |
| |
| // List should also call. |
| std::vector<std::string> new_list = {"new", "list"}; |
| err = store.SetList("list", new_list); |
| EXPECT_FALSE(err.has_error()) << err.msg(); |
| |
| ASSERT_EQ(observer.notifications().size(), 2u); |
| record = observer.notifications().back(); |
| EXPECT_EQ(record.name, "list"); |
| ASSERT_TRUE(record.value.is_list()); |
| EXPECT_EQ(record.value.get_list(), new_list); |
| |
| // Removing an observer should not make to stop notifying. |
| store.RemoveObserver("int", &observer); |
| err = store.SetInt("int", 55); |
| EXPECT_FALSE(err.has_error()) << err.msg(); |
| EXPECT_EQ(observer.notifications().size(), 2u); |
| |
| // But not for the other one. |
| new_list.push_back("another value"); |
| err = store.SetList("list", new_list); |
| EXPECT_FALSE(err.has_error()) << err.msg(); |
| |
| ASSERT_EQ(observer.notifications().size(), 3u); |
| record = observer.notifications().back(); |
| EXPECT_EQ(record.name, "list"); |
| ASSERT_TRUE(record.value.is_list()); |
| EXPECT_EQ(record.value.get_list(), new_list); |
| |
| // Adding another observer should notify twice. |
| SettingObserver observer2; |
| store.AddObserver("list", &observer2); |
| new_list.push_back("yet another value"); |
| err = store.SetList("list", new_list); |
| EXPECT_FALSE(err.has_error()) << err.msg(); |
| |
| ASSERT_EQ(observer.notifications().size(), 4u); |
| record = observer.notifications().back(); |
| EXPECT_EQ(record.name, "list"); |
| ASSERT_TRUE(record.value.is_list()); |
| EXPECT_EQ(record.value.get_list(), new_list); |
| |
| ASSERT_EQ(observer2.notifications().size(), 1u); |
| record = observer2.notifications().back(); |
| EXPECT_EQ(record.name, "list"); |
| ASSERT_TRUE(record.value.is_list()); |
| EXPECT_EQ(record.value.get_list(), new_list); |
| |
| // Removing the first one should still notify the second. |
| store.RemoveObserver("list", &observer); |
| new_list.push_back("even another value?"); |
| err = store.SetList("list", new_list); |
| EXPECT_FALSE(err.has_error()) << err.msg(); |
| |
| EXPECT_EQ(observer.notifications().size(), 4u); |
| |
| ASSERT_EQ(observer2.notifications().size(), 2u); |
| record = observer2.notifications().back(); |
| EXPECT_EQ(record.name, "list"); |
| ASSERT_TRUE(record.value.is_list()); |
| EXPECT_EQ(record.value.get_list(), new_list); |
| |
| // Removing all observers should not notify. |
| store.RemoveObserver("list", &observer2); |
| |
| err = store.SetBool("bool", true); |
| EXPECT_FALSE(err.has_error()) << err.msg(); |
| err = store.SetInt("int", 22); |
| EXPECT_FALSE(err.has_error()) << err.msg(); |
| err = store.SetString("string", "blah"); |
| EXPECT_FALSE(err.has_error()) << err.msg(); |
| err = store.SetList("list", {"meh"}); |
| EXPECT_FALSE(err.has_error()) << err.msg(); |
| |
| EXPECT_EQ(observer.notifications().size(), 4u); |
| EXPECT_EQ(observer2.notifications().size(), 2u); |
| } |
| |
| } // namespace zxdb |