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')