blob: 7397464e07cdf1f7202500faa5593f2ca07defb6 [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 "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