diff --git a/bindings/libdnf5/advisory.i b/bindings/libdnf5/advisory.i index 66a477442..2a5e4bc72 100644 --- a/bindings/libdnf5/advisory.i +++ b/bindings/libdnf5/advisory.i @@ -13,6 +13,18 @@ %import "common.i" +%exception { + try { + $action + } catch (const libdnf::UserAssertionError & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); + } catch (const libdnf::Error & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); + } catch (const std::runtime_error & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); + } +} + // TODO(jkolarik): advisory modules skipped for now %{ diff --git a/bindings/libdnf5/base.i b/bindings/libdnf5/base.i index 052496c02..6fe9b2e5e 100644 --- a/bindings/libdnf5/base.i +++ b/bindings/libdnf5/base.i @@ -27,6 +27,10 @@ $action } catch (const libdnf::UserAssertionError & e) { SWIG_exception(SWIG_RuntimeError, e.what()); + } catch (const libdnf::Error & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); + } catch (const std::runtime_error & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); } } diff --git a/bindings/libdnf5/comps.i b/bindings/libdnf5/comps.i index 89537611e..ba8df74d3 100644 --- a/bindings/libdnf5/comps.i +++ b/bindings/libdnf5/comps.i @@ -21,6 +21,10 @@ $action } catch (const libdnf::UserAssertionError & e) { SWIG_exception(SWIG_RuntimeError, e.what()); + } catch (const libdnf::Error & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); + } catch (const std::runtime_error & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); } } diff --git a/bindings/libdnf5/conf.i b/bindings/libdnf5/conf.i index fe7e4f239..c38c8ebb0 100644 --- a/bindings/libdnf5/conf.i +++ b/bindings/libdnf5/conf.i @@ -17,6 +17,10 @@ %exception { try { $action + } catch (const libdnf::UserAssertionError & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); + } catch (const libdnf::Error & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); } catch (const std::out_of_range & e) { SWIG_exception(SWIG_IndexError, e.what()); } catch (const std::runtime_error & e) { @@ -140,3 +144,5 @@ def create_config_option_attributes(cls): create_config_option_attributes(ConfigMain) %} #endif + +%exception; // beware this resets all exception handlers if you import this file after defining any diff --git a/bindings/libdnf5/logger.i b/bindings/libdnf5/logger.i index 9a2d51f75..52b14d3ba 100644 --- a/bindings/libdnf5/logger.i +++ b/bindings/libdnf5/logger.i @@ -13,6 +13,18 @@ %import "common.i" +%exception { + try { + $action + } catch (const libdnf::UserAssertionError & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); + } catch (const libdnf::Error & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); + } catch (const std::runtime_error & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); + } +} + typedef int64_t time_t; typedef int32_t pid_t; diff --git a/bindings/libdnf5/repo.i b/bindings/libdnf5/repo.i index 2e45faafb..0e1278231 100644 --- a/bindings/libdnf5/repo.i +++ b/bindings/libdnf5/repo.i @@ -18,6 +18,10 @@ %exception { try { $action + } catch (const libdnf::UserAssertionError & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); + } catch (const libdnf::Error & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); } catch (const std::runtime_error & e) { SWIG_exception(SWIG_RuntimeError, e.what()); } diff --git a/bindings/libdnf5/rpm.i b/bindings/libdnf5/rpm.i index 215415f91..3ed28752a 100644 --- a/bindings/libdnf5/rpm.i +++ b/bindings/libdnf5/rpm.i @@ -20,10 +20,12 @@ %exception { try { $action - } catch (const std::runtime_error & e) { - SWIG_exception(SWIG_RuntimeError, e.what()); } catch (const libdnf::UserAssertionError & e) { SWIG_exception(SWIG_RuntimeError, e.what()); + } catch (const libdnf::Error & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); + } catch (const std::runtime_error & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); } } diff --git a/bindings/libdnf5/transaction.i b/bindings/libdnf5/transaction.i index 61b12e771..19b90ff99 100644 --- a/bindings/libdnf5/transaction.i +++ b/bindings/libdnf5/transaction.i @@ -14,6 +14,18 @@ %import "common.i" +%exception { + try { + $action + } catch (const libdnf::UserAssertionError & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); + } catch (const libdnf::Error & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); + } catch (const std::runtime_error & e) { + SWIG_exception(SWIG_RuntimeError, e.what()); + } +} + %{ // enums #include "libdnf/transaction/transaction_item_action.hpp" diff --git a/include/libdnf/conf/option.hpp b/include/libdnf/conf/option.hpp index 81662cecb..b7efee202 100644 --- a/include/libdnf/conf/option.hpp +++ b/include/libdnf/conf/option.hpp @@ -118,7 +118,7 @@ class Option { /// @since 1.0 bool is_locked() const noexcept; - /// Asserts the option is not locked and throws a `libdnf::AssertionError` in case it is. + /// Asserts the option is not locked and throws a `libdnf::UserAssertionError` in case it is. /// /// @since 1.0 void assert_not_locked() const; @@ -159,7 +159,7 @@ inline bool Option::is_locked() const noexcept { } inline void Option::assert_not_locked() const { - libdnf_assert(!locked, "Attempting to write to a locked option: {}", get_lock_comment()); + libdnf_user_assert(!locked, "Attempting to write to a locked option: {}", get_lock_comment()); } inline const std::string & Option::get_lock_comment() const noexcept { diff --git a/libdnf/base/goal.cpp b/libdnf/base/goal.cpp index 44a4d9061..bac50f92d 100644 --- a/libdnf/base/goal.cpp +++ b/libdnf/base/goal.cpp @@ -285,7 +285,7 @@ void Goal::add_rpm_reason_change( const libdnf::transaction::TransactionItemReason reason, const std::string & group_id, const libdnf::GoalJobSettings & settings) { - libdnf_assert( + libdnf_user_assert( reason != libdnf::transaction::TransactionItemReason::GROUP || !group_id.empty(), "group_id is required for setting reason \"GROUP\""); p_impl->rpm_reason_change_specs.push_back(std::make_tuple(reason, spec, group_id, settings)); @@ -306,9 +306,7 @@ void Goal::Impl::add_spec(GoalAction action, const std::string & spec, const Goa const std::string_view ext(".rpm"); if (libdnf::utils::url::is_url(spec) || (spec.length() > ext.length() && spec.ends_with(ext))) { // spec is a remote rpm file or a local rpm file - if (action == GoalAction::REMOVE) { - libdnf_throw_assertion("Unsupported argument for REMOVE action: {}", spec); - } + libdnf_user_assert(action != GoalAction::REMOVE, "Unsupported argument for REMOVE action: {}", spec); rpm_filepaths.emplace_back(action, spec, settings); } else { // otherwise the spec is a repository package diff --git a/libdnf/logger/global_logger.cpp b/libdnf/logger/global_logger.cpp index 072fe14aa..395f51b12 100644 --- a/libdnf/logger/global_logger.cpp +++ b/libdnf/logger/global_logger.cpp @@ -30,7 +30,7 @@ static GlibLogHandler * librepo_logger{nullptr}; static GlibLogHandler * libmodulemd_logger{nullptr}; GlobalLogger::GlobalLogger() { - libdnf_assert(librepo_logger == nullptr, "Only one GlobalLogger can exist at a time"); + libdnf_user_assert(librepo_logger == nullptr, "Only one GlobalLogger can exist at a time"); } GlobalLogger::~GlobalLogger() { diff --git a/libdnf/repo/repo.cpp b/libdnf/repo/repo.cpp index 8627ca6b7..3d5962381 100644 --- a/libdnf/repo/repo.cpp +++ b/libdnf/repo/repo.cpp @@ -247,8 +247,8 @@ void Repo::load() { } void Repo::load_extra_system_repo(const std::string & rootdir) { - libdnf_assert(type == Type::SYSTEM, "repo type must be SYSTEM to load an extra system repo"); - libdnf_assert(solv_repo, "repo must be loaded to load an extra system repo"); + libdnf_user_assert(type == Type::SYSTEM, "repo type must be SYSTEM to load an extra system repo"); + libdnf_user_assert(solv_repo, "repo must be loaded to load an extra system repo"); solv_repo->load_system_repo(rootdir); } diff --git a/test/libdnf/conf/test_option.cpp b/test/libdnf/conf/test_option.cpp index 2ae03984d..7f191b111 100644 --- a/test/libdnf/conf/test_option.cpp +++ b/test/libdnf/conf/test_option.cpp @@ -112,10 +112,10 @@ void OptionTest::test_options_bool() { CPPUNIT_ASSERT_THROW(option2.set(Option::Priority::RUNTIME, std::string("invalid")), OptionInvalidValueError); option.lock("option locked by test_options_bool"); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, true), AssertionError); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, false), AssertionError); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, std::string("true")), AssertionError); - CPPUNIT_ASSERT_THROW(option.set(true), AssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, true), UserAssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, false), UserAssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, std::string("true")), UserAssertionError); + CPPUNIT_ASSERT_THROW(option.set(true), UserAssertionError); } @@ -148,9 +148,9 @@ void OptionTest::test_options_child() { ochild.set(Option::Priority::RUNTIME, true); ochild.lock("ochild_bool locked by test_option_child"); - CPPUNIT_ASSERT_THROW(ochild.set(Option::Priority::RUNTIME, true), AssertionError); - CPPUNIT_ASSERT_THROW(ochild.set(Option::Priority::RUNTIME, false), AssertionError); - CPPUNIT_ASSERT_THROW(ochild.set(Option::Priority::RUNTIME, std::string("true")), AssertionError); + CPPUNIT_ASSERT_THROW(ochild.set(Option::Priority::RUNTIME, true), UserAssertionError); + CPPUNIT_ASSERT_THROW(ochild.set(Option::Priority::RUNTIME, false), UserAssertionError); + CPPUNIT_ASSERT_THROW(ochild.set(Option::Priority::RUNTIME, std::string("true")), UserAssertionError); } void OptionTest::test_options_enum() { @@ -170,8 +170,8 @@ void OptionTest::test_options_enum() { option.set(Option::Priority::RUNTIME, "aa"); option.lock("option locked by test_option_enum"); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "aa"), AssertionError); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "dd"), AssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "aa"), UserAssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "dd"), UserAssertionError); } void OptionTest::test_options_number() { @@ -195,8 +195,8 @@ void OptionTest::test_options_number() { option.set(Option::Priority::RUNTIME, 1); option.lock("option locked by test_option_number"); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, 1), AssertionError); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, 2), AssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, 1), UserAssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, 2), UserAssertionError); } void OptionTest::test_options_path() { @@ -216,8 +216,8 @@ void OptionTest::test_options_path() { CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "not_absolute"), OptionValueNotAllowedError); option.lock("option locked by test_option_path"); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "/path2"), AssertionError); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "/next"), AssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "/path2"), UserAssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "/next"), UserAssertionError); } @@ -242,8 +242,8 @@ void OptionTest::test_options_seconds() { option.set(Option::Priority::RUNTIME, 12); option.lock("option locked by test_option_seconds"); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, 12), AssertionError); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, 10), AssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, 12), UserAssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, 10), UserAssertionError); OptionSeconds option2(DEFAULT); option2.set(Option::Priority::RUNTIME, "5s"); @@ -295,8 +295,8 @@ void OptionTest::test_options_string() { CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "drain"), OptionValueNotAllowedError); option.lock("option locked by test_option_string"); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "doXXnut"), AssertionError); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "invalid"), AssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "doXXnut"), UserAssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "invalid"), UserAssertionError); } { @@ -318,8 +318,8 @@ void OptionTest::test_options_string() { CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "drain"), OptionValueNotAllowedError); option.lock("option locked by test_option_string"); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "doXXnut"), AssertionError); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "invalid"), AssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "doXXnut"), UserAssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "invalid"), UserAssertionError); } } @@ -358,8 +358,8 @@ void OptionTest::test_options_string_list() { CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "donutX, drain"), OptionValueNotAllowedError); option.lock("option locked by test_option_string_list"); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "doXXnut"), AssertionError); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "invalid"), AssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "doXXnut"), UserAssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "invalid"), UserAssertionError); } { @@ -396,8 +396,8 @@ void OptionTest::test_options_string_list() { CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "donutX, drain"), OptionValueNotAllowedError); option.lock("option locked by test_option_string_list"); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "doXXnut"), AssertionError); - CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "invalid"), AssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "doXXnut"), UserAssertionError); + CPPUNIT_ASSERT_THROW(option.set(Option::Priority::RUNTIME, "invalid"), UserAssertionError); } } diff --git a/test/python3/libdnf5/base/test_base.py b/test/python3/libdnf5/base/test_base.py index f678c1ad5..d9f10673e 100644 --- a/test/python3/libdnf5/base/test_base.py +++ b/test/python3/libdnf5/base/test_base.py @@ -54,12 +54,9 @@ def test_weak_ptr(self): # with self.assertRaisesRegex(RuntimeError, 'Dereferencing an invalidated WeakPtr'): # vars2.get_value("test_variable") - def test_missing_setup_goal_resolve(self): - # Create a new Base object + def test_non_existing_config_load(self): + # Try to load configuration from non-existing path base = libdnf5.base.Base() + base.get_config().config_file_path = 'this-path-does-not-exist.conf' - # Create a new empty Goal - goal = libdnf5.base.Goal(base) - - # Try to resolve the goal without running base.setup() - self.assertRaises(RuntimeError, goal.resolve) + self.assertRaises(RuntimeError, base.load_config_from_file) diff --git a/test/python3/libdnf5/base/test_goal.py b/test/python3/libdnf5/base/test_goal.py new file mode 100644 index 000000000..5191d60c8 --- /dev/null +++ b/test/python3/libdnf5/base/test_goal.py @@ -0,0 +1,70 @@ +# Copyright Contributors to the libdnf project. +# +# This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ +# +# Libdnf is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# Libdnf is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with libdnf. If not, see . + +import unittest + +import libdnf5.base + + +class TestGoal(unittest.TestCase): + def test_missing_setup_goal_resolve(self): + # Create a new Base object + base = libdnf5.base.Base() + + # Create a new empty Goal + goal = libdnf5.base.Goal(base) + + # Try to resolve the goal without running base.setup() + self.assertRaises(RuntimeError, goal.resolve) + + def test_missing_rpm_goal_resolve(self): + # Try to resolve the goal using unknown RPM package + base = libdnf5.base.Base() + base.setup() + + goal = libdnf5.base.Goal(base) + goal.add_install('unknown_pkg.rpm') + + self.assertRaises(RuntimeError, goal.resolve) + + def test_invalid_url_goal_resolve(self): + # Try to resolve the goal using invalid URL + base = libdnf5.base.Base() + base.setup() + + goal = libdnf5.base.Goal(base) + goal.add_install('https://i-dont-exist.com') + + self.assertRaises(RuntimeError, goal.resolve) + + def test_unsupported_argument_add_remove(self): + # Try passing unsupported argument to add_remove() method + base = libdnf5.base.Base() + base.setup() + + goal = libdnf5.base.Goal(base) + + self.assertRaises(RuntimeError, goal.add_remove, 'pkg.rpm') + + def test_group_rpm_reason_change_without_id(self): + # Try changing group reason without group_id + base = libdnf5.base.Base() + base.setup() + + goal = libdnf5.base.Goal(base) + + self.assertRaises(RuntimeError, goal.add_rpm_reason_change, '@fake-group-spec', libdnf5.base.transaction.TransactionItemReason_GROUP, '') diff --git a/test/python3/libdnf5/conf/test_option.py b/test/python3/libdnf5/conf/test_option.py index edcbce809..57b29dddc 100644 --- a/test/python3/libdnf5/conf/test_option.py +++ b/test/python3/libdnf5/conf/test_option.py @@ -62,3 +62,11 @@ def test_set_get_by_attribute(self): config = self.base.get_config() config.comment = 'test' self.assertEqual(config.comment, 'test') + + def test_writing_to_locked_option(self): + config = self.base.get_config() + + option = config.get_cacheonly_option() + option.lock('') + + self.assertRaises(RuntimeError, option.set, False) diff --git a/test/python3/libdnf5/logger/test_global_logger.py b/test/python3/libdnf5/logger/test_global_logger.py new file mode 100644 index 000000000..67f95c3d0 --- /dev/null +++ b/test/python3/libdnf5/logger/test_global_logger.py @@ -0,0 +1,29 @@ +# Copyright Contributors to the libdnf project. +# +# This file is part of libdnf: https://github.com/rpm-software-management/libdnf/ +# +# Libdnf is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# Libdnf is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with libdnf. If not, see . + +import libdnf5 + +import base_test_case + + +class TestGlobalLogger(base_test_case.BaseTestCase): + def test_creating_global_logger_again(self): + # Try creating two instances of global logger + router = libdnf5.logger.LogRouter() + global_logger = libdnf5.logger.GlobalLogger() + global_logger.set(router, libdnf5.logger.Logger.Level_DEBUG) + self.assertRaises(RuntimeError, libdnf5.logger.GlobalLogger) diff --git a/test/python3/libdnf5/repo/test_repo.py b/test/python3/libdnf5/repo/test_repo.py index 31a9a82d6..cf00e8b23 100644 --- a/test/python3/libdnf5/repo/test_repo.py +++ b/test/python3/libdnf5/repo/test_repo.py @@ -85,3 +85,12 @@ def repokey_import(self, id, user_id, fingerprint, url, timestamp): self.assertEqual(cbs.fastest_mirror_cnt, 0) self.assertEqual(cbs.handle_mirror_failure_cnt, 0) self.assertEqual(cbs.repokey_import_cnt, 0) + + def test_load_extra_system_repo_incorrectly(self): + # Try to load extra system repo on non-system repo + repo = self.repo_sack.create_repo('test') + self.assertRaises(RuntimeError, repo.load_extra_system_repo, 'some-root-dir') + + # Try to load extra system repo on non-loaded repo + repo = self.repo_sack.get_system_repo() + self.assertRaises(RuntimeError, repo.load_extra_system_repo, 'dir')