this doesn't do much, just wrap a few nlohmann headers in headers of our own (and delete includes we don't need because they're transitively seen by other includes). doing this now will make the next change much nicer. Change-Id: I166933102ea86bb5322ebbf9ba9411f96032a53b
329 lines
11 KiB
C++
329 lines
11 KiB
C++
#include "lix/libutil/config.hh"
|
|
#include "lix/libutil/args.hh"
|
|
#include "lix/libutil/file-system.hh"
|
|
#include "lix/libutil/environment-variables.hh"
|
|
#include "lix/libutil/json.hh"
|
|
#include "lix/libutil/logging.hh"
|
|
#include "tests/test-data.hh"
|
|
|
|
#include <sstream>
|
|
#include <gtest/gtest.h>
|
|
|
|
namespace nix {
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
* Config
|
|
* --------------------------------------------------------------------------*/
|
|
|
|
TEST(Config, setUndefinedSetting) {
|
|
Config config;
|
|
ASSERT_EQ(config.set("undefined-key", "value"), false);
|
|
}
|
|
|
|
TEST(Config, setDefinedSetting) {
|
|
Config config;
|
|
std::string value;
|
|
Setting<std::string> foo{&config, value, "name-of-the-setting", "description"};
|
|
ASSERT_EQ(config.set("name-of-the-setting", "value"), true);
|
|
}
|
|
|
|
TEST(Config, getDefinedSetting) {
|
|
Config config;
|
|
std::string value;
|
|
std::map<std::string, Config::SettingInfo> settings;
|
|
Setting<std::string> foo{&config, value, "name-of-the-setting", "description"};
|
|
|
|
config.getSettings(settings, /* overriddenOnly = */ false);
|
|
const auto iter = settings.find("name-of-the-setting");
|
|
ASSERT_NE(iter, settings.end());
|
|
ASSERT_EQ(iter->second.value, "");
|
|
ASSERT_EQ(iter->second.description, "description\n");
|
|
}
|
|
|
|
TEST(Config, getDefinedOverriddenSettingNotSet) {
|
|
Config config;
|
|
std::string value;
|
|
std::map<std::string, Config::SettingInfo> settings;
|
|
Setting<std::string> foo{&config, value, "name-of-the-setting", "description"};
|
|
|
|
config.getSettings(settings, /* overriddenOnly = */ true);
|
|
const auto e = settings.find("name-of-the-setting");
|
|
ASSERT_EQ(e, settings.end());
|
|
}
|
|
|
|
TEST(Config, getDefinedSettingSet1) {
|
|
Config config;
|
|
std::string value;
|
|
std::map<std::string, Config::SettingInfo> settings;
|
|
Setting<std::string> setting{&config, value, "name-of-the-setting", "description"};
|
|
|
|
setting.override("value");
|
|
|
|
config.getSettings(settings, /* overriddenOnly = */ false);
|
|
const auto iter = settings.find("name-of-the-setting");
|
|
ASSERT_NE(iter, settings.end());
|
|
ASSERT_EQ(iter->second.value, "value");
|
|
ASSERT_EQ(iter->second.description, "description\n");
|
|
}
|
|
|
|
TEST(Config, getDefinedSettingSet2) {
|
|
Config config;
|
|
std::map<std::string, Config::SettingInfo> settings;
|
|
Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
|
|
|
|
ASSERT_TRUE(config.set("name-of-the-setting", "value"));
|
|
|
|
config.getSettings(settings, /* overriddenOnly = */ false);
|
|
const auto e = settings.find("name-of-the-setting");
|
|
ASSERT_NE(e, settings.end());
|
|
ASSERT_EQ(e->second.value, "value");
|
|
ASSERT_EQ(e->second.description, "description\n");
|
|
}
|
|
|
|
TEST(Config, addSetting) {
|
|
class TestSetting : public AbstractSetting {
|
|
public:
|
|
TestSetting() : AbstractSetting("test", "test", {}) {}
|
|
void set(const std::string & value, bool append, const ApplyConfigOptions & options) override {}
|
|
std::string to_string() const override { return {}; }
|
|
bool isAppendable() override { return false; }
|
|
};
|
|
|
|
Config config;
|
|
TestSetting setting;
|
|
|
|
ASSERT_FALSE(config.set("test", "value"));
|
|
config.addSetting(&setting);
|
|
ASSERT_TRUE(config.set("test", "value"));
|
|
ASSERT_FALSE(config.set("extra-test", "value"));
|
|
}
|
|
|
|
TEST(Config, withInitialValue) {
|
|
const StringMap initials = {
|
|
{ "key", "value" },
|
|
};
|
|
Config config(initials);
|
|
|
|
{
|
|
std::map<std::string, Config::SettingInfo> settings;
|
|
config.getSettings(settings, /* overriddenOnly = */ false);
|
|
ASSERT_EQ(settings.find("key"), settings.end());
|
|
}
|
|
|
|
Setting<std::string> setting{&config, "default-value", "key", "description"};
|
|
|
|
{
|
|
std::map<std::string, Config::SettingInfo> settings;
|
|
config.getSettings(settings, /* overriddenOnly = */ false);
|
|
ASSERT_EQ(settings["key"].value, "value");
|
|
}
|
|
}
|
|
|
|
TEST(Config, resetOverridden) {
|
|
Config config;
|
|
config.resetOverridden();
|
|
}
|
|
|
|
TEST(Config, resetOverriddenWithSetting) {
|
|
Config config;
|
|
Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
|
|
|
|
{
|
|
std::map<std::string, Config::SettingInfo> settings;
|
|
|
|
setting.setDefault("foo");
|
|
ASSERT_EQ(setting.get(), "foo");
|
|
config.getSettings(settings, /* overriddenOnly = */ true);
|
|
ASSERT_TRUE(settings.empty());
|
|
}
|
|
|
|
{
|
|
std::map<std::string, Config::SettingInfo> settings;
|
|
|
|
setting.override("bar");
|
|
ASSERT_TRUE(setting.overridden);
|
|
ASSERT_EQ(setting.get(), "bar");
|
|
config.getSettings(settings, /* overriddenOnly = */ true);
|
|
ASSERT_FALSE(settings.empty());
|
|
}
|
|
|
|
{
|
|
std::map<std::string, Config::SettingInfo> settings;
|
|
|
|
config.resetOverridden();
|
|
ASSERT_FALSE(setting.overridden);
|
|
config.getSettings(settings, /* overriddenOnly = */ true);
|
|
ASSERT_TRUE(settings.empty());
|
|
}
|
|
}
|
|
|
|
TEST(Config, toJSONOnEmptyConfig) {
|
|
ASSERT_EQ(Config().toJSON().dump(), "{}");
|
|
}
|
|
|
|
TEST(Config, toJSONOnNonEmptyConfig) {
|
|
using nlohmann::literals::operator "" _json;
|
|
Config config;
|
|
Setting<std::string> setting{
|
|
&config,
|
|
"",
|
|
"name-of-the-setting",
|
|
"description",
|
|
};
|
|
setting.override("value");
|
|
|
|
ASSERT_EQ(config.toJSON(),
|
|
R"#({
|
|
"name-of-the-setting": {
|
|
"aliases": [],
|
|
"defaultValue": "",
|
|
"description": "description\n",
|
|
"documentDefault": true,
|
|
"value": "value",
|
|
"experimentalFeature": null
|
|
}
|
|
})#"_json);
|
|
}
|
|
|
|
TEST(Config, toJSONOnNonEmptyConfigWithExperimentalSetting) {
|
|
using nlohmann::literals::operator "" _json;
|
|
Config config;
|
|
Setting<std::string> setting{
|
|
&config,
|
|
"",
|
|
"name-of-the-setting",
|
|
"description",
|
|
{},
|
|
true,
|
|
Xp::Flakes,
|
|
};
|
|
setting.override("value");
|
|
|
|
ASSERT_EQ(config.toJSON(),
|
|
R"#({
|
|
"name-of-the-setting": {
|
|
"aliases": [],
|
|
"defaultValue": "",
|
|
"description": "description\n",
|
|
"documentDefault": true,
|
|
"value": "value",
|
|
"experimentalFeature": "flakes"
|
|
}
|
|
})#"_json);
|
|
}
|
|
|
|
TEST(Config, setSettingAlias) {
|
|
Config config;
|
|
Setting<std::string> setting{&config, "", "some-int", "best number", { "another-int" }};
|
|
ASSERT_TRUE(config.set("some-int", "1"));
|
|
ASSERT_EQ(setting.get(), "1");
|
|
ASSERT_TRUE(config.set("another-int", "2"));
|
|
ASSERT_EQ(setting.get(), "2");
|
|
ASSERT_TRUE(config.set("some-int", "3"));
|
|
ASSERT_EQ(setting.get(), "3");
|
|
}
|
|
|
|
/* FIXME: The reapplyUnknownSettings method doesn't seem to do anything
|
|
* useful (these days). Whenever we add a new setting to Config the
|
|
* unknown settings are always considered. In which case is this function
|
|
* actually useful? Is there some way to register a Setting without calling
|
|
* addSetting? */
|
|
TEST(Config, DISABLED_reapplyUnknownSettings) {
|
|
Config config;
|
|
ASSERT_FALSE(config.set("name-of-the-setting", "unknownvalue"));
|
|
Setting<std::string> setting{&config, "default", "name-of-the-setting", "description"};
|
|
ASSERT_EQ(setting.get(), "default");
|
|
config.reapplyUnknownSettings();
|
|
ASSERT_EQ(setting.get(), "unknownvalue");
|
|
}
|
|
|
|
TEST(Config, applyConfigEmpty) {
|
|
Config config;
|
|
std::map<std::string, Config::SettingInfo> settings;
|
|
config.applyConfig("");
|
|
config.getSettings(settings);
|
|
ASSERT_TRUE(settings.empty());
|
|
}
|
|
|
|
TEST(Config, applyConfigEmptyWithComment) {
|
|
Config config;
|
|
std::map<std::string, Config::SettingInfo> settings;
|
|
config.applyConfig("# just a comment");
|
|
config.getSettings(settings);
|
|
ASSERT_TRUE(settings.empty());
|
|
}
|
|
|
|
TEST(Config, applyConfigAssignment) {
|
|
Config config;
|
|
std::map<std::string, Config::SettingInfo> settings;
|
|
Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
|
|
config.applyConfig(
|
|
"name-of-the-setting = value-from-file #useful comment\n"
|
|
"# name-of-the-setting = foo\n"
|
|
);
|
|
config.getSettings(settings);
|
|
ASSERT_FALSE(settings.empty());
|
|
ASSERT_EQ(settings["name-of-the-setting"].value, "value-from-file");
|
|
}
|
|
|
|
TEST(Config, applyConfigWithReassignedSetting) {
|
|
Config config;
|
|
std::map<std::string, Config::SettingInfo> settings;
|
|
Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
|
|
config.applyConfig(
|
|
"name-of-the-setting = first-value\n"
|
|
"name-of-the-setting = second-value\n"
|
|
);
|
|
config.getSettings(settings);
|
|
ASSERT_FALSE(settings.empty());
|
|
ASSERT_EQ(settings["name-of-the-setting"].value, "second-value");
|
|
}
|
|
|
|
TEST(Config, applyConfigFailsOnMissingIncludes) {
|
|
Config config;
|
|
std::map<std::string, Config::SettingInfo> settings;
|
|
Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
|
|
|
|
ASSERT_THROW(config.applyConfig(
|
|
"name-of-the-setting = value-from-file\n"
|
|
"# name-of-the-setting = foo\n"
|
|
"include /nix/store/does/not/exist.nix"
|
|
), Error);
|
|
}
|
|
|
|
TEST(Config, includeRelativePath) {
|
|
Config config;
|
|
Setting<std::string> setting{&config, "", "puppy", "description"};
|
|
|
|
config.applyConfig("include puppy.conf", {
|
|
.path = getUnitTestDataPath("nix.conf")
|
|
});
|
|
|
|
std::map<std::string, Config::SettingInfo> settings;
|
|
config.getSettings(settings);
|
|
ASSERT_FALSE(settings.empty());
|
|
ASSERT_EQ(settings["puppy"].value, "doggy");
|
|
}
|
|
|
|
TEST(Config, includeTildePath) {
|
|
Config config;
|
|
Setting<std::string> setting{&config, "", "puppy", "description"};
|
|
|
|
config.applyConfig("include ~/puppy.conf", {
|
|
.path = "/doesnt-exist",
|
|
.home = getUnitTestData()
|
|
});
|
|
|
|
std::map<std::string, Config::SettingInfo> settings;
|
|
config.getSettings(settings);
|
|
ASSERT_FALSE(settings.empty());
|
|
ASSERT_EQ(settings["puppy"].value, "doggy");
|
|
}
|
|
|
|
TEST(Config, applyConfigInvalidThrows) {
|
|
Config config;
|
|
ASSERT_THROW(config.applyConfig("value == key"), UsageError);
|
|
ASSERT_THROW(config.applyConfig("value "), UsageError);
|
|
}
|
|
}
|