diff --git a/include/libdnf5/conf/vars.hpp b/include/libdnf5/conf/vars.hpp index a17a0c886..045966512 100644 --- a/include/libdnf5/conf/vars.hpp +++ b/include/libdnf5/conf/vars.hpp @@ -79,6 +79,14 @@ struct Vars { /// @throw ReadOnlyVariableError if the variable is read-only void set(const std::string & name, const std::string & value, Priority prio = Priority::RUNTIME); + /// @brief Unset particular variable + /// + /// @param name Name of the variable + /// @param prio Source/Priority of the request + /// @throw ReadOnlyVariableError if the variable is read-only + /// @return false if the variable exists after the function returns (insufficient request priority) + bool unset(const std::string & name, Priority prio = Priority::RUNTIME); + /// @brief Checks whether a variable is read-only /// /// @param name Name of the variable diff --git a/libdnf5/conf/vars.cpp b/libdnf5/conf/vars.cpp index 4e58e9763..e1ebdf785 100644 --- a/libdnf5/conf/vars.cpp +++ b/libdnf5/conf/vars.cpp @@ -386,6 +386,22 @@ void Vars::set(const std::string & name, const std::string & value, Priority pri set_unsafe(name, value, prio); } +bool Vars::unset(const std::string & name, Priority prio) { + auto it = variables.find(name); + if (it == variables.end()) { + return true; + } + if (is_read_only(name)) { + throw ReadOnlyVariableError(M_("Variable \"{}\" is read-only"), name); + } + // Do nothing if the var is already set with a higher priority + if (prio < it->second.priority) { + return false; + } + variables.erase(it); + return true; +} + void Vars::set_lazy( const std::string & name, const std::function()> & get_value, diff --git a/test/libdnf5/conf/test_vars.cpp b/test/libdnf5/conf/test_vars.cpp index 643ad90d6..6029bf52c 100644 --- a/test/libdnf5/conf/test_vars.cpp +++ b/test/libdnf5/conf/test_vars.cpp @@ -23,6 +23,8 @@ along with libdnf. If not, see . CPPUNIT_TEST_SUITE_REGISTRATION(VarsTest); +using namespace std::literals::string_literals; + // TODO possibly test the automatically detected vars (they depend on the host) void VarsTest::setUp() { @@ -30,21 +32,22 @@ void VarsTest::setUp() { base = get_preconfigured_base(); } + void VarsTest::test_vars() { base->get_config().get_varsdir_option().set( std::vector{PROJECT_SOURCE_DIR "/test/libdnf5/conf/data/vars"}); // Load all variables. base->setup(); - CPPUNIT_ASSERT_EQUAL(std::string("foovalue123-bar"), base->get_vars()->substitute("foo$var1-bar")); + CPPUNIT_ASSERT_EQUAL("foovalue123-bar"s, base->get_vars()->substitute("foo$var1-bar")); + CPPUNIT_ASSERT_EQUAL("$$$value123456-$nn-${nnn}"s, base->get_vars()->substitute("$$$${var1}$var2-$nn-${nnn}")); CPPUNIT_ASSERT_EQUAL( - std::string("$$$value123456-$nn-${nnn}"), base->get_vars()->substitute("$$$${var1}$var2-$nn-${nnn}")); - CPPUNIT_ASSERT_EQUAL( - std::string("alternate-default-${nn:+n${nn:-${nnn:}"), + "alternate-default-${nn:+n${nn:-${nnn:}"s, base->get_vars()->substitute("${var1:+alternate}-${unset:-default}-${nn:+n${nn:-${nnn:}")); - CPPUNIT_ASSERT_EQUAL(std::string("456"), base->get_vars()->substitute("${unset:-${var1:+${var2:+$var2}}}")); + CPPUNIT_ASSERT_EQUAL("456"s, base->get_vars()->substitute("${unset:-${var1:+${var2:+$var2}}}")); } + void VarsTest::test_vars_multiple_dirs() { base->get_config().get_varsdir_option().set(std::vector{ PROJECT_SOURCE_DIR "/test/libdnf5/conf/data/vars", @@ -53,9 +56,10 @@ void VarsTest::test_vars_multiple_dirs() { // Load all variables. base->setup(); - CPPUNIT_ASSERT_EQUAL(std::string("av333bthe answer is here"), base->get_vars()->substitute("a${var1}b${var42}")); + CPPUNIT_ASSERT_EQUAL("av333bthe answer is here"s, base->get_vars()->substitute("a${var1}b${var42}")); } + void VarsTest::test_vars_env() { base->get_config().get_varsdir_option().set( std::vector{PROJECT_SOURCE_DIR "/test/libdnf5/conf/data/vars"}); @@ -73,6 +77,175 @@ void VarsTest::test_vars_env() { // The variables var1 and var2 are defined in the files. // However, var1 was also an environment variable. The environment has a higher priority. CPPUNIT_ASSERT_EQUAL( - std::string("foo0-foo1-foo9-testvar1-testvar2-456"), + "foo0-foo1-foo9-testvar1-testvar2-456"s, base->get_vars()->substitute("${DNF0}-${DNF1}-${DNF9}-${var1}-${var41}-${var2}")); } + + +void VarsTest::test_vars_api() { + base->setup(); + auto vars = base->get_vars(); + + CPPUNIT_ASSERT(!vars->contains("test_var1")); + + vars->set("test_var1", "123", libdnf5::Vars::Priority::PLUGIN); + CPPUNIT_ASSERT(vars->contains("test_var1")); + + { + auto & [value, prioriry] = vars->get("test_var1"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("value returned by vars->get()", "123"s, value); + CPPUNIT_ASSERT_EQUAL_MESSAGE("priority returned by vars->get()", libdnf5::Vars::Priority::PLUGIN, prioriry); + } + + CPPUNIT_ASSERT_EQUAL_MESSAGE("value returned by vars->get_value()", "123"s, vars->get_value("test_var1")); + + CPPUNIT_ASSERT(vars->unset("test_var1")); + CPPUNIT_ASSERT_MESSAGE("after vars->unset(\"test_var1\")", !vars->contains("test_var1")); +} + + +void VarsTest::test_vars_api_set_prio() { + base->setup(); + auto vars = base->get_vars(); + + CPPUNIT_ASSERT(!vars->contains("test_var1")); + + // set a new variable + vars->set("test_var1", "123", libdnf5::Vars::Priority::PLUGIN); + { + auto & [value, prioriry] = vars->get("test_var1"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("value returned by vars->get()", "123"s, value); + CPPUNIT_ASSERT_EQUAL_MESSAGE("priority returned by vars->get()", libdnf5::Vars::Priority::PLUGIN, prioriry); + } + + // change the value using the same priority + vars->set("test_var1", "345", libdnf5::Vars::Priority::PLUGIN); + { + auto & [value, prioriry] = vars->get("test_var1"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("value returned by vars->get()", "345"s, value); + CPPUNIT_ASSERT_EQUAL_MESSAGE("priority returned by vars->get()", libdnf5::Vars::Priority::PLUGIN, prioriry); + } + + // change the value using a higher priority + vars->set("test_var1", "678", libdnf5::Vars::Priority::COMMANDLINE); + { + auto & [value, prioriry] = vars->get("test_var1"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("value returned by vars->get()", "678"s, value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "priority returned by vars->get()", libdnf5::Vars::Priority::COMMANDLINE, prioriry); + } + + // changing the value using a lower priority is ignored + vars->set("test_var1", "ignored_value", libdnf5::Vars::Priority::PLUGIN); + { + auto & [value, prioriry] = vars->get("test_var1"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("value returned by vars->get()", "678"s, value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "priority returned by vars->get()", libdnf5::Vars::Priority::COMMANDLINE, prioriry); + } + + // change the value using default priority - default is RUNTIME, the highest priority + vars->set("test_var1", "999"); + { + auto & [value, prioriry] = vars->get("test_var1"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("value returned by vars->get()", "999"s, value); + CPPUNIT_ASSERT_EQUAL_MESSAGE("priority returned by vars->get()", libdnf5::Vars::Priority::RUNTIME, prioriry); + } +} + + +void VarsTest::test_vars_api_unset_prio() { + base->setup(); + auto vars = base->get_vars(); + + // set a new variable + vars->set("test_var1", "123", libdnf5::Vars::Priority::COMMANDLINE); + { + auto & [value, prioriry] = vars->get("test_var1"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("value returned by vars->get()", "123"s, value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "priority returned by vars->get()", libdnf5::Vars::Priority::COMMANDLINE, prioriry); + } + + // removing a variable using a lower priority is ignored + CPPUNIT_ASSERT(!vars->unset("test_var1", libdnf5::Vars::Priority::PLUGIN)); + CPPUNIT_ASSERT_MESSAGE( + "after vars->unset(\"test_var1\", libdnf5::Vars::Priority::PLUGIN)", vars->contains("test_var1")); + { + auto & [value, prioriry] = vars->get("test_var1"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("value returned by vars->get()", "123"s, value); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "priority returned by vars->get()", libdnf5::Vars::Priority::COMMANDLINE, prioriry); + } + + // removing a variable using the same priority + CPPUNIT_ASSERT(vars->unset("test_var1", libdnf5::Vars::Priority::COMMANDLINE)); + CPPUNIT_ASSERT_MESSAGE( + "after vars->unset(\"test_var1\", libdnf5::Vars::Priority::COMMANDLINE)", !vars->contains("test_var1")); + + + // set a new variable + vars->set("test_var2", "345", libdnf5::Vars::Priority::PLUGIN); + CPPUNIT_ASSERT(vars->contains("test_var2")); + + // removing a variable using a higher priority + CPPUNIT_ASSERT(vars->unset("test_var2", libdnf5::Vars::Priority::COMMANDLINE)); + CPPUNIT_ASSERT_MESSAGE( + "after vars->unset(\"test_var2\", libdnf5::Vars::Priority::COMMANDLINE)", !vars->contains("test_var2")); + + + // set a new variable + vars->set("test_var3", "678", libdnf5::Vars::Priority::PLUGIN); + CPPUNIT_ASSERT(vars->contains("test_var3")); + + // removing a variable using default priority - default is RUNTIME, the highest priority + CPPUNIT_ASSERT(vars->unset("test_var3")); + CPPUNIT_ASSERT_MESSAGE("after vars->unset(\"test_var3\")", !vars->contains("test_var3")); +} + + +void VarsTest::test_vars_api_releasever() { + base->setup(); + auto vars = base->get_vars(); + + // set the "releasever" variable + vars->set("releasever", "40.12", libdnf5::Vars::Priority::PLUGIN); + + // check the value the of "releasever" variable + { + auto & [value, prioriry] = vars->get("releasever"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("value returned by vars->get()", "40.12"s, value); + CPPUNIT_ASSERT_EQUAL_MESSAGE("priority returned by vars->get()", libdnf5::Vars::Priority::PLUGIN, prioriry); + } + + // check the value the of auto-created read-only variables + { + auto & [value, prioriry] = vars->get("releasever_major"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("value returned by vars->get()", "40"s, value); + CPPUNIT_ASSERT_EQUAL_MESSAGE("priority returned by vars->get()", libdnf5::Vars::Priority::PLUGIN, prioriry); + } + { + auto & [value, prioriry] = vars->get("releasever_minor"); + CPPUNIT_ASSERT_EQUAL_MESSAGE("value returned by vars->get()", "12"s, value); + CPPUNIT_ASSERT_EQUAL_MESSAGE("priority returned by vars->get()", libdnf5::Vars::Priority::PLUGIN, prioriry); + } + + // the "releasever" variable is read-write + CPPUNIT_ASSERT(!vars->is_read_only("releasever")); + + // auto-created variables "releasever_major" and "releasever_minor" are read-only + CPPUNIT_ASSERT(vars->is_read_only("releasever_major")); + CPPUNIT_ASSERT(vars->is_read_only("releasever_minor")); + + // setting the value of a read-only variable throws exception + CPPUNIT_ASSERT_THROW( + vars->set("releasever_major", "40", libdnf5::Vars::Priority::PLUGIN), libdnf5::ReadOnlyVariableError); + + // removing read-only variable throws exception + CPPUNIT_ASSERT_THROW( + vars->unset("releasever_major", libdnf5::Vars::Priority::PLUGIN), libdnf5::ReadOnlyVariableError); + + // because the variable "releaver" is read-write, it can be removed + CPPUNIT_ASSERT(vars->unset("releasever", libdnf5::Vars::Priority::PLUGIN)); + CPPUNIT_ASSERT_MESSAGE("after vars->unset(\"test_var3\")", !vars->contains("releasever")); +} diff --git a/test/libdnf5/conf/test_vars.hpp b/test/libdnf5/conf/test_vars.hpp index b165798df..d1b6bd755 100644 --- a/test/libdnf5/conf/test_vars.hpp +++ b/test/libdnf5/conf/test_vars.hpp @@ -32,6 +32,10 @@ class VarsTest : public TestCaseFixture { CPPUNIT_TEST(test_vars); CPPUNIT_TEST(test_vars_multiple_dirs); CPPUNIT_TEST(test_vars_env); + CPPUNIT_TEST(test_vars_api); + CPPUNIT_TEST(test_vars_api_set_prio); + CPPUNIT_TEST(test_vars_api_unset_prio); + CPPUNIT_TEST(test_vars_api_releasever); CPPUNIT_TEST_SUITE_END(); public: @@ -40,6 +44,10 @@ class VarsTest : public TestCaseFixture { void test_vars(); void test_vars_multiple_dirs(); void test_vars_env(); + void test_vars_api(); + void test_vars_api_set_prio(); + void test_vars_api_unset_prio(); + void test_vars_api_releasever(); std::unique_ptr base; };