blob: a3933a812c9d01741c7530e19bb0ee0c91882ec0 [file] [log] [blame]
// 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 "garnet/bin/zxdb/client/setting_store.h"
#include <gtest/gtest.h>
#include "garnet/bin/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(
SettingSchema::Level level = SettingSchema::Level::kDefault) {
auto schema = fxl::MakeRefCounted<SettingSchema>(level);
SettingSchemaItem item("bool", "bool option", true);
schema->AddSetting("bool", item);
item = SettingSchemaItem("int", "int option", kDefaultInt);
schema->AddSetting("int", item);
item = SettingSchemaItem("string", "string option", kDefaultString);
schema->AddSetting("string", item);
item = SettingSchemaItem::StringWithOptions(
"string_options", "string with options", kDefaultString, DefaultList());
schema->AddSetting("string_options", item);
item = SettingSchemaItem("list", "list option", DefaultList());
schema->AddSetting("list", item);
return schema;
}
class SettingObserver : public SettingStoreObserver {
public:
// Keep track of who called.
struct SettingNotificationRecord {
const SettingStore* store;
std::string setting_name;
SettingValue new_value;
};
void OnSettingChanged(const SettingStore& store,
const std::string& setting_name) override {
SettingNotificationRecord record = {};
record.store = &store;
record.setting_name = setting_name;
record.new_value = store.GetSetting(setting_name, false).value;
notifications_.push_back(std::move(record));
}
const std::vector<SettingNotificationRecord>& notifications() const {
return notifications_;
}
private:
std::vector<SettingNotificationRecord> notifications_;
};
} // namespace
TEST(SettingStore, Defaults) {
SettingStore store(GetSchema(), nullptr);
auto setting = store.GetSetting("bool");
ASSERT_TRUE(setting.value.is_bool());
EXPECT_TRUE(setting.value.GetBool());
EXPECT_EQ(setting.level, SettingSchema::Level::kDefault);
setting = store.GetSetting("int");
ASSERT_TRUE(setting.value.is_int());
EXPECT_EQ(setting.value.GetInt(), kDefaultInt);
EXPECT_EQ(setting.level, SettingSchema::Level::kDefault);
setting = store.GetSetting("string");
ASSERT_TRUE(setting.value.is_string());
EXPECT_EQ(setting.value.GetString(), kDefaultString);
EXPECT_EQ(setting.level, SettingSchema::Level::kDefault);
setting = store.GetSetting("list");
ASSERT_TRUE(setting.value.is_list());
EXPECT_EQ(setting.value.GetList(), DefaultList());
EXPECT_EQ(setting.level, SettingSchema::Level::kDefault);
// Not found.
EXPECT_TRUE(store.GetSetting("unexistent").value.is_null());
}
TEST(SettingStore, Overrides) {
SettingStore 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);
// Valid options.
err = store.SetString("string_options", "list");
ASSERT_FALSE(err.has_error()) << err.msg();
ASSERT_TRUE(store.GetSetting("string_options").value.is_string());
EXPECT_EQ(store.GetString("string_options"), "list");
// Invalid option.
err = store.SetString("string_options", "invalid");
EXPECT_TRUE(err.has_error());
}
TEST(SettingStore, Fallback) {
SettingStore fallback2(GetSchema(SettingSchema::Level::kSystem), nullptr);
std::vector<std::string> new_list = {"new", "list"};
fallback2.SetList("list", new_list);
SettingStore fallback(GetSchema(SettingSchema::Level::kTarget), &fallback2);
std::string new_string = "new string";
fallback.SetString("string", new_string);
SettingStore store(GetSchema(SettingSchema::Level::kThread), &fallback);
store.SetBool("bool", false);
// Also test that the correct fallback level is communicated.
// Should get default for not overridden.
auto setting = store.GetSetting("int");
ASSERT_TRUE(setting.value.is_int());
EXPECT_EQ(setting.value.GetInt(), kDefaultInt);
EXPECT_EQ(setting.level, SettingSchema::Level::kDefault);
// Should get local level.
setting = store.GetSetting("bool");
ASSERT_TRUE(setting.value.is_bool());
EXPECT_FALSE(setting.value.GetBool());
EXPECT_EQ(setting.level, SettingSchema::Level::kThread);
// Should get one override hop.
setting = store.GetSetting("string");
ASSERT_TRUE(setting.value.is_string());
EXPECT_EQ(setting.value.GetString(), new_string);
EXPECT_EQ(setting.level, SettingSchema::Level::kTarget);
// Should fallback through the chain.
setting = store.GetSetting("list");
ASSERT_TRUE(setting.value.is_list());
EXPECT_EQ(setting.value.GetList(), new_list);
EXPECT_EQ(setting.level, SettingSchema::Level::kSystem);
}
TEST(SettingStore, Notifications) {
SettingStore 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.store, &store);
EXPECT_EQ(record.setting_name, "int");
ASSERT_TRUE(record.new_value.is_int());
EXPECT_EQ(record.new_value.GetInt(), 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.store, &store);
EXPECT_EQ(record.setting_name, "list");
ASSERT_TRUE(record.new_value.is_list());
EXPECT_EQ(record.new_value.GetList(), 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.store, &store);
EXPECT_EQ(record.setting_name, "list");
ASSERT_TRUE(record.new_value.is_list());
EXPECT_EQ(record.new_value.GetList(), 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.store, &store);
EXPECT_EQ(record.setting_name, "list");
ASSERT_TRUE(record.new_value.is_list());
EXPECT_EQ(record.new_value.GetList(), new_list);
ASSERT_EQ(observer2.notifications().size(), 1u);
record = observer2.notifications().back();
EXPECT_EQ(record.store, &store);
EXPECT_EQ(record.setting_name, "list");
ASSERT_TRUE(record.new_value.is_list());
EXPECT_EQ(record.new_value.GetList(), 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.store, &store);
EXPECT_EQ(record.setting_name, "list");
ASSERT_TRUE(record.new_value.is_list());
EXPECT_EQ(record.new_value.GetList(), 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