#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/logging.hh" #include "tests/test-data.hh" #include #include #include 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 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 settings; Setting 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 settings; Setting 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 settings; Setting 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 settings; Setting 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 settings; config.getSettings(settings, /* overriddenOnly = */ false); ASSERT_EQ(settings.find("key"), settings.end()); } Setting setting{&config, "default-value", "key", "description"}; { std::map 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 setting{&config, "", "name-of-the-setting", "description"}; { std::map settings; setting.setDefault("foo"); ASSERT_EQ(setting.get(), "foo"); config.getSettings(settings, /* overriddenOnly = */ true); ASSERT_TRUE(settings.empty()); } { std::map settings; setting.override("bar"); ASSERT_TRUE(setting.overridden); ASSERT_EQ(setting.get(), "bar"); config.getSettings(settings, /* overriddenOnly = */ true); ASSERT_FALSE(settings.empty()); } { std::map 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 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 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 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 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 settings; config.applyConfig(""); config.getSettings(settings); ASSERT_TRUE(settings.empty()); } TEST(Config, applyConfigEmptyWithComment) { Config config; std::map settings; config.applyConfig("# just a comment"); config.getSettings(settings); ASSERT_TRUE(settings.empty()); } TEST(Config, applyConfigAssignment) { Config config; std::map settings; Setting 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 settings; Setting 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 settings; Setting 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 setting{&config, "", "puppy", "description"}; config.applyConfig("include puppy.conf", { .path = getUnitTestDataPath("nix.conf") }); std::map settings; config.getSettings(settings); ASSERT_FALSE(settings.empty()); ASSERT_EQ(settings["puppy"].value, "doggy"); } TEST(Config, includeTildePath) { Config config; Setting setting{&config, "", "puppy", "description"}; config.applyConfig("include ~/puppy.conf", { .path = "/doesnt-exist", .home = getUnitTestData() }); std::map 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); } }