aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorLibravatar Mubashshir <ahm@jadupc.com>2023-08-03 16:33:41 +0600
committerLibravatar Mubashshir <ahm@jadupc.com>2023-08-03 16:33:41 +0600
commit70faa8e9a0ff3cba74b4f753e257d56b768fcbd2 (patch)
tree3a49460715f7319d100e0cc9c1a278758500c7c8 /tests
downloadaptdaemon-70faa8e9a0ff3cba74b4f753e257d56b768fcbd2.tar.gz
aptdaemon-70faa8e9a0ff3cba74b4f753e257d56b768fcbd2.zip
Import Upstream version 1.1.1+bzr982
Diffstat (limited to 'tests')
-rw-r--r--tests/__init__.py0
-rw-r--r--tests/_test_py2_string_handling.py103
-rw-r--r--tests/_test_py3_string_handling.py46
l---------tests/aptdaemon1
-rw-r--r--tests/data/high-trust-repository-whitelist-broken.cfg13
-rw-r--r--tests/data/high-trust-repository-whitelist.cfg10
l---------tests/data/high-trust-repository-whitelist.d/high-trust-repository-whitelist-broken.cfg1
l---------tests/data/high-trust-repository-whitelist.d/high-trust-repository-whitelist.cfg1
-rw-r--r--tests/dbus.conf21
-rwxr-xr-xtests/debconf/aptdaemon.config10
-rw-r--r--tests/debconf/aptdaemon.templates5
-rwxr-xr-xtests/dpkg-wrapper.sh2
-rwxr-xr-xtests/fake-polkitd.py64
-rwxr-xr-xtests/fakeroot-apt-key2
l---------tests/regressions/aptdaemon1
-rw-r--r--tests/regressions/test_lp722228.py152
-rw-r--r--tests/regressions/test_lp768691.py66
-rw-r--r--tests/repo/Packages217
-rw-r--r--tests/repo/Release22
-rw-r--r--tests/repo/Release.gpg11
-rw-r--r--tests/repo/backports/Packages20
-rw-r--r--tests/repo/backports/Packages.gpg34
-rw-r--r--tests/repo/backports/Release16
-rw-r--r--tests/repo/backports/Release.gpg30
-rw-r--r--tests/repo/glatzor.gpg115
-rw-r--r--tests/repo/gstreamer0.10-silly_0.1-0_all.debbin0 -> 52016 bytes
-rw-r--r--tests/repo/security/Packages20
-rw-r--r--tests/repo/security/Packages.gpg34
-rw-r--r--tests/repo/security/Release16
-rw-r--r--tests/repo/security/Release.gpg30
-rw-r--r--tests/repo/silly-base_0.1-0_all.debbin0 -> 1824 bytes
-rw-r--r--tests/repo/silly-base_0.1-0update1_all.debbin0 -> 1934 bytes
-rw-r--r--tests/repo/silly-broken_0.1-0_all.debbin0 -> 1840 bytes
-rw-r--r--tests/repo/silly-bully_0.1-0_all.debbin0 -> 1808 bytes
-rw-r--r--tests/repo/silly-config_0.1-0_all.debbin0 -> 1928 bytes
-rw-r--r--tests/repo/silly-depend-base-lintian-broken_0.1-0_all.debbin0 -> 1758 bytes
-rw-r--r--tests/repo/silly-depend-base_0.1-0_all.debbin0 -> 1812 bytes
-rw-r--r--tests/repo/silly-essential_0.1-0_all.debbin0 -> 1800 bytes
-rw-r--r--tests/repo/silly-fail_0.1-0_all.debbin0 -> 1874 bytes
-rw-r--r--tests/repo/silly-important_0.1-0_all.debbin0 -> 1792 bytes
-rw-r--r--tests/repo/silly-postinst-input_0.1-0_all.debbin0 -> 1906 bytes
-rw-r--r--tests/repo/whitelisted/Packages34
-rw-r--r--tests/repo/whitelisted/Packages.gpg34
-rw-r--r--tests/repo/whitelisted/Release17
-rw-r--r--tests/repo/whitelisted/Release.gpg0
l---------tests/repo/whitelisted/silly-base_0.1-0update1_all.deb1
-rw-r--r--tests/test_cdrom.py99
-rw-r--r--tests/test_client.py174
-rw-r--r--tests/test_configfileprompt.py147
-rw-r--r--tests/test_configparser.py100
-rw-r--r--tests/test_dbus_type.py196
-rw-r--r--tests/test_debconf.py132
-rw-r--r--tests/test_gtk3widgets.py49
-rw-r--r--tests/test_high_trust_repository_whitelist.py200
-rw-r--r--tests/test_index.py48
-rw-r--r--tests/test_lock.py127
-rw-r--r--tests/test_lock_location.py53
-rw-r--r--tests/test_pep8.py42
-rw-r--r--tests/test_pk.py601
-rw-r--r--tests/test_progress.py55
-rw-r--r--tests/test_py2_string_handling.py44
-rw-r--r--tests/test_simulate.py74
-rw-r--r--tests/test_trans_chain.py59
-rw-r--r--tests/test_valid_package_names.py71
-rw-r--r--tests/test_worker.py588
65 files changed, 4008 insertions, 0 deletions
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/__init__.py
diff --git a/tests/_test_py2_string_handling.py b/tests/_test_py2_string_handling.py
new file mode 100644
index 0000000..86709fb
--- /dev/null
+++ b/tests/_test_py2_string_handling.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2011 Michael Vogt <mvo@ubuntu.com>
+#
+# Licensed under the GNU General Public License Version 2
+#
+# This program 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.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Licensed under the GNU General Public License Version 2
+
+"""Regression test for a unicode decoding error in the status_details,
+progress_download or error_properties attributes of the transaction,
+see LP #724735.
+"""
+
+__author__ = "Michael Vogt <mvo@glatzor.de>"
+
+import os
+import sys
+import unittest
+
+import dbus
+from dbus.lowlevel import ErrorMessage, Message
+
+from aptdaemon.core import Transaction
+from aptdaemon.errors import TransactionFailed
+from aptdaemon.test import AptDaemonTestCase, Chroot
+
+
+class TestUnicodeDecoding(AptDaemonTestCase):
+
+ """Test the workaround."""
+
+ def setUp(self):
+ self.chroot = Chroot()
+ self.chroot.setup()
+ self.addCleanup(self.chroot.remove)
+ self.start_dbus_daemon()
+ self.dbus = dbus.bus.BusConnection(self.dbus_address)
+ self.trans = Transaction(None, "role-test", None,
+ os.getpid(), os.getuid(), os.getgid(),
+ sys.argv[0],
+ "org.debian.apt.test", bus=self.dbus)
+
+ def test(self):
+ # ensure we don't crash regardless if str or unicode is passed here
+ self.trans.status_details = "äää"
+ self.trans.status_details = u"äää"
+
+ self.trans.progress_download = ("äö", "ß", "üöä", 1L, 2L, "ö")
+ self.trans.progress_download = (u"äö", u"ß", u"üöä", 1L, 2L, u"ö")
+
+ self.trans.error = TransactionFailed("ERROR_TEST", "Mixed ä %s", u"öä")
+ self.trans.error = TransactionFailed("ERROR_TEST", u"Mixed ü %s", "öä")
+ self.trans.error = TransactionFailed("ERROR_TEST", "Str ä %s", "öä")
+ self.trans.error = TransactionFailed("ERROR_TEST", u"Uni ä %s", u"öä")
+
+ def test_dbus_exception(self):
+ """This tests simulates the steps taken by the DBus bindings to
+ send an error reply, see LP# 761386"""
+ # The original error message that we get from python-apt
+ orig = "E:Die Paketliste oder die Statusdatei konnte nicht " \
+ "eingelesen oder ge\xc3\xb6ffnet werden."
+ for msg in [u"E:Die Paketliste oder die Statusdatei konnte nicht "
+ u"eingelesen oder geöffnet werden.",
+ "E:Die Paketliste oder die Statusdatei konnte nicht "
+ "eingelesen oder geöffnet werden.", orig]:
+ # Create a simple DBus exception
+ error = TransactionFailed("ERROR_TEST", msg)
+
+ # Taken from dbus.service._method_reply_error()
+ name = error.get_dbus_name()
+ content = error.get_dbus_message()
+ self.assertEqual(orig, content[12:])
+ # The constructor of the ErrorMessage fails since we only use
+ # a fake Message
+ self.assertRaises(MemoryError, ErrorMessage, Message(), name,
+ content)
+
+ def test_dbus_exception_lp846044(self):
+ e = TransactionFailed("foo", "bar")
+ e.details = u"ä"
+ foo = unicode(e)
+ self.assertEqual(foo, u"Transaction failed: None\nä")
+ foo = str(e)
+ self.assertEqual(foo, u"Transaction failed: None\nä".encode("utf-8"))
+
+
+if __name__ == "__main__":
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/_test_py3_string_handling.py b/tests/_test_py3_string_handling.py
new file mode 100644
index 0000000..9773412
--- /dev/null
+++ b/tests/_test_py3_string_handling.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2011 Michael Vogt <mvo@ubuntu.com>
+#
+# Licensed under the GNU General Public License Version 2
+#
+# This program 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.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Licensed under the GNU General Public License Version 2
+
+"""Regression test for a unicode decoding error in the status_details,
+progress_download or error_properties attributes of the transaction,
+see LP #724735.
+"""
+
+__author__ = "Michael Vogt <mvo@glatzor.de>"
+
+import unittest
+
+from aptdaemon.errors import TransactionFailed
+from aptdaemon.test import AptDaemonTestCase
+
+
+class TestUnicodeDecodingPy3(AptDaemonTestCase):
+
+ def test_dbus_exception_lp846044(self):
+ e = TransactionFailed("foo", "bar")
+ e.details = "ä"
+ self.assertEqual(str(e), "Transaction failed: None\nä")
+
+
+if __name__ == "__main__":
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/aptdaemon b/tests/aptdaemon
new file mode 120000
index 0000000..d2811fd
--- /dev/null
+++ b/tests/aptdaemon
@@ -0,0 +1 @@
+../aptdaemon \ No newline at end of file
diff --git a/tests/data/high-trust-repository-whitelist-broken.cfg b/tests/data/high-trust-repository-whitelist-broken.cfg
new file mode 100644
index 0000000..513e99c
--- /dev/null
+++ b/tests/data/high-trust-repository-whitelist-broken.cfg
@@ -0,0 +1,13 @@
+
+This line makes this no longer a valid file and that is used in the tests
+
+[repo name1]
+origin = Ubuntu
+component = main
+pkgnames = foo.*
+
+[repo name2]
+origin = Debian-Security
+component = non-free
+pkgnames = ^bar$
+
diff --git a/tests/data/high-trust-repository-whitelist.cfg b/tests/data/high-trust-repository-whitelist.cfg
new file mode 100644
index 0000000..68113ac
--- /dev/null
+++ b/tests/data/high-trust-repository-whitelist.cfg
@@ -0,0 +1,10 @@
+
+[repo name1]
+origin = Ubuntu
+component = main
+pkgnames = foo.*
+
+[repo whitelist2]
+origin = Debian-Security
+component = non-free
+pkgnames = ^bar$ \ No newline at end of file
diff --git a/tests/data/high-trust-repository-whitelist.d/high-trust-repository-whitelist-broken.cfg b/tests/data/high-trust-repository-whitelist.d/high-trust-repository-whitelist-broken.cfg
new file mode 120000
index 0000000..3dea1a9
--- /dev/null
+++ b/tests/data/high-trust-repository-whitelist.d/high-trust-repository-whitelist-broken.cfg
@@ -0,0 +1 @@
+../high-trust-repository-whitelist-broken.cfg \ No newline at end of file
diff --git a/tests/data/high-trust-repository-whitelist.d/high-trust-repository-whitelist.cfg b/tests/data/high-trust-repository-whitelist.d/high-trust-repository-whitelist.cfg
new file mode 120000
index 0000000..b5972cc
--- /dev/null
+++ b/tests/data/high-trust-repository-whitelist.d/high-trust-repository-whitelist.cfg
@@ -0,0 +1 @@
+../high-trust-repository-whitelist.cfg \ No newline at end of file
diff --git a/tests/dbus.conf b/tests/dbus.conf
new file mode 100644
index 0000000..6d7c2bc
--- /dev/null
+++ b/tests/dbus.conf
@@ -0,0 +1,21 @@
+<!-- This configuration file controls the per-user-login-session message bus.
+ Add a session-local.conf and edit that rather than changing this
+ file directly. -->
+
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <type>system</type>
+
+ <listen>unix:tmpdir=/tmp</listen>
+
+ <policy context="default">
+ <!-- Allow everything to be sent -->
+ <allow send_destination="*" eavesdrop="true"/>
+ <!-- Allow everything to be received -->
+ <allow eavesdrop="true"/>
+ <!-- Allow anyone to own anything -->
+ <allow own="*"/>
+ </policy>
+
+</busconfig>
diff --git a/tests/debconf/aptdaemon.config b/tests/debconf/aptdaemon.config
new file mode 100755
index 0000000..c7a8d43
--- /dev/null
+++ b/tests/debconf/aptdaemon.config
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+set -e
+
+. /usr/share/debconf/confmodule
+
+db_input high aptdaemon/test || true
+db_go || true
+
+exit 0
diff --git a/tests/debconf/aptdaemon.templates b/tests/debconf/aptdaemon.templates
new file mode 100644
index 0000000..e226272
--- /dev/null
+++ b/tests/debconf/aptdaemon.templates
@@ -0,0 +1,5 @@
+Template: aptdaemon/test
+Type: string
+Default: bli bla blub
+Description: This is a test
+ Huhu
diff --git a/tests/dpkg-wrapper.sh b/tests/dpkg-wrapper.sh
new file mode 100755
index 0000000..3880036
--- /dev/null
+++ b/tests/dpkg-wrapper.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec /usr/bin/fakeroot /usr/bin/dpkg $*
diff --git a/tests/fake-polkitd.py b/tests/fake-polkitd.py
new file mode 100755
index 0000000..e8dfa70
--- /dev/null
+++ b/tests/fake-polkitd.py
@@ -0,0 +1,64 @@
+#!/usr/bin/python3
+"""Fake a PolicyKit daemon."""
+
+from optparse import OptionParser
+import sys
+
+import dbus
+import dbus.mainloop.glib
+import dbus.service
+from gi.repository import GLib
+
+# Setup the DBus main loop
+dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+
+class FakePolicyKitDaemon(dbus.service.Object):
+
+ def __init__(self, allowed_actions):
+ self.allowed_actions = allowed_actions
+ bus_name = dbus.service.BusName("org.freedesktop.PolicyKit1",
+ dbus.SystemBus(),
+ do_not_queue=True)
+ dbus.service.Object.__init__(self, bus_name,
+ "/org/freedesktop/PolicyKit1/Authority")
+ self.loop = GLib.MainLoop()
+
+ def run(self):
+ self.loop.run()
+
+ @dbus.service.method("org.freedesktop.PolicyKit1.Authority",
+ in_signature='(sa{sv})sa{ss}us',
+ out_signature='(bba{ss})')
+ def CheckAuthorization(self, subject, action_id, details, flags,
+ cancellation_id):
+ if "all" in self.allowed_actions:
+ allowed = True
+ else:
+ allowed = action_id in self.allowed_actions
+ challenged = False
+ details = {"test": "test"}
+ return (allowed, challenged, details)
+
+ @dbus.service.method("org.freedesktop.PolicyKit1.Authority",
+ in_signature='', out_signature='')
+ def Quit(self):
+ GLib.idle_add(self._quit)
+
+ def _quit(self):
+ self.loop.quit()
+ sys.exit()
+
+
+def main():
+ parser = OptionParser()
+ parser.add_option("-a", "--allowed-actions",
+ default="", action="store", type="string",
+ dest="allowed_actions",
+ help="Comma separated list of allowed action ids")
+ options, args = parser.parse_args()
+ polkitd = FakePolicyKitDaemon(options.allowed_actions.split(","))
+ polkitd.run()
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/fakeroot-apt-key b/tests/fakeroot-apt-key
new file mode 100755
index 0000000..7be9971
--- /dev/null
+++ b/tests/fakeroot-apt-key
@@ -0,0 +1,2 @@
+#!/bin/sh
+fakeroot /usr/bin/apt-key $*
diff --git a/tests/regressions/aptdaemon b/tests/regressions/aptdaemon
new file mode 120000
index 0000000..9b3d444
--- /dev/null
+++ b/tests/regressions/aptdaemon
@@ -0,0 +1 @@
+../../aptdaemon \ No newline at end of file
diff --git a/tests/regressions/test_lp722228.py b/tests/regressions/test_lp722228.py
new file mode 100644
index 0000000..1f84376
--- /dev/null
+++ b/tests/regressions/test_lp722228.py
@@ -0,0 +1,152 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2010 Michael Vogt <mvo@ubuntu.com>
+#
+# Licensed under the GNU General Public License Version 2
+#
+# This program 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.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Licensed under the GNU General Public License Version 2
+
+"""Regression test for the security issue CVE-2011-0725 tracked
+in LP #722228.
+
+Thanks to Sergey Nizovtsev for spotting this issue.
+
+The UpdateCache method allows to specify an alternative sources.list
+snippet to only update the repositories specified in the corresponding
+configuration file.
+
+Aptdaemon did not restrict the path to the sources.list.d directory and
+allowed to inject packages from malicious sources specified in a custom
+sources.list and even to read every file on the system.
+"""
+
+__author__ = "Michael Vogt <mvo@glatzor.de>"
+
+import os
+import tempfile
+import time
+import unittest2
+
+import apt_pkg
+import dbus
+import mock
+
+import aptdaemon.client
+import aptdaemon.test
+from aptdaemon.worker import AptWorker
+from aptdaemon.errors import AptDaemonError
+
+
+class TestFix(unittest2.TestCase):
+
+ """Test the fix."""
+
+ def test_closed(self):
+ worker = AptWorker()
+ # We don't want to perform any cache changes
+ worker._cache = mock.Mock()
+ trans = mock.Mock()
+ # ensure normal operation keeps working
+ worker.update_cache(trans, None)
+ self.assertTrue(worker._cache.update.called)
+ worker._cache.reset_mock()
+ worker.update_cache(trans, "foobar.list")
+ self.assertTrue(worker._cache.update.called)
+ worker._cache.reset_mock()
+ worker.update_cache(trans, "/etc/apt/sources.list.d/foobar.list")
+ self.assertTrue(worker._cache.update.called)
+ worker._cache.reset_mock()
+ worker.update_cache(trans, "/etc/apt/sources.list")
+ self.assertTrue(worker._cache.update.called)
+
+ # ensure absolute path is no longer working
+ worker._cache.reset_mock()
+ self.assertRaises(AptDaemonError, worker.update_cache, trans,
+ "/etc/fstab")
+ self.assertFalse(worker._cache.update.called)
+ worker._cache.reset_mock()
+ self.assertRaises(AptDaemonError, worker.update_cache, trans,
+ "/tmp/etc/apt/sources.list.d")
+ self.assertFalse(worker._cache.update.called)
+ worker._cache.reset_mock()
+ self.assertRaises(AptDaemonError, worker.update_cache, trans,
+ "/etc/apt/sources.list.d/../../tmp/evil.list")
+ self.assertFalse(worker._cache.update.called)
+ worker._cache.reset_mock()
+ self.assertRaises(AptDaemonError, worker.update_cache, trans,
+ "../../../../../../../../../tmp/evil.list")
+ self.assertFalse(worker._cache.update.called)
+
+
+class TestExploit(aptdaemon.test.AptDaemonTestCase):
+
+ """Test if the a possible exploit still exists."""
+
+ def setUp(self):
+ """Setup a chroot, run the aptdaemon and a fake PolicyKit daemon."""
+ # Setup chroot
+ self.chroot = aptdaemon.test.Chroot()
+ self.chroot.setup()
+ self.addCleanup(self.chroot.remove)
+ # Start aptdaemon with the chroot on the session bus
+ self.start_dbus_daemon()
+ self.bus = dbus.bus.BusConnection(self.dbus_address)
+ self.start_session_aptd(self.chroot.path)
+ # Start the fake PolikcyKit daemon
+ self.start_fake_polkitd()
+ time.sleep(1)
+ # Create a file which containts a virtual secret
+ self.secrets_file = tempfile.NamedTemporaryFile(dir=self.chroot.path,
+ delete=False)
+ self.secrets_file.write("Oh oh!")
+ self.secrets_file.close()
+
+ @unittest2.expectedFailure
+ def test(self):
+ """A possible exploit of the security issue.
+ Originally provided by Sergey Nizovtsev.
+ """
+ repo_path = os.path.join(self.chroot.path, "repo")
+ lst_path = os.path.join(self.chroot.path, "malicious.list")
+
+ arch = apt_pkg.config["APT::Architecture"]
+
+ # Setup a pseudo repository and link the file which should be extracted
+ # to the Packages file
+ dir = os.path.join(repo_path, "dists/a/a/binary-%s" % arch)
+ os.makedirs(dir)
+ os.symlink(self.secrets_file.name, "%s/Packages" % dir)
+ # Create a malicious list file which injects the repo
+ with open(lst_path, "w") as lst_file:
+ lst_file.write("deb file://%s a a" % repo_path)
+
+ client = aptdaemon.client.AptClient(self.bus)
+ exit = client.update_cache(sources_list=lst_path, wait=True)
+ self.assertEqual(exit, aptdaemon.enums.EXIT_SUCCESS)
+
+ # Check if succeeded to leak the file content!
+ repo_path_encoded = apt_pkg.uri_to_filename("file://%s" % repo_path)
+ leaked_path = os.path.join(self.chroot.path, "var/lib/apt/lists/",
+ "%s_dists_a_a_binary-%s_"
+ "Packages" % (repo_path_encoded, arch))
+ with open(leaked_path, "r") as leaked_file:
+ self.assertEqual(leaked_file.read(), "Oh oh!")
+
+
+if __name__ == "__main__":
+ unittest2.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/regressions/test_lp768691.py b/tests/regressions/test_lp768691.py
new file mode 100644
index 0000000..14e5fe9
--- /dev/null
+++ b/tests/regressions/test_lp768691.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Tests if we can handle different encodings well."""
+
+import locale
+import logging
+import os
+import sys
+import unittest
+
+from aptdaemon import core, enums, test, errors, utils
+
+DEBUG = True
+
+if sys.version >= '3':
+ unicode = str
+
+
+class GettextTest(test.AptDaemonTestCase):
+
+ """Regression test for LP: #768691 and LP: #926340
+
+ The gettext.translation.gettext() method returns a string in Python 2.
+ If we try to perform a string format operation, Python wants to convert
+ string to unicode. If the daemon is running with a different
+ default encoding as the translated message this results in an error.
+ By defaulf aptdaemon runs as C if activated by D-Bus.
+ """
+
+ def setUp(self):
+ # Use the mo files from the build
+ local_mo_files = os.path.join(test.get_tests_dir(),
+ "../build/mo")
+ if not os.path.isdir(local_mo_files):
+ self.skipTest("Please run setup.py build before since local mo "
+ "files are required. Run python setup.py build_i18n")
+ core.gettext._default_localedir = local_mo_files
+
+ self.trans = core.Transaction(None, enums.ROLE_FIX_BROKEN_DEPENDS,
+ None, os.getpid(), os.getuid(),
+ sys.argv[0], "org.debian.apt.test",
+ connect=False)
+ self.codes = utils.IsoCodes("iso_639", tag="iso_639_1_code",
+ fallback_tag="iso_639_2T_code")
+
+ def test(self):
+ """Test if the installation of an unauthenticated packages fails
+ if simulate hasn't been called explicitly before.
+ """
+ self.trans._set_locale("de_DE.UTF-8")
+ ret = self.trans.gettext("CD/DVD '%s' is required")
+ self.assertTrue(isinstance(ret, unicode))
+ error = errors.TransactionFailed(enums.ERROR_NO_PACKAGE,
+ "CD/DVD '%s' is required", "lala")
+ self.trans.error = error
+
+ utils.gettext._default_localedir = "/usr/share/locale"
+ lang = self.codes.get_localised_name("en", "ru.UTF-8")
+ self.assertTrue(isinstance(lang, unicode))
+
+if __name__ == "__main__":
+ if DEBUG:
+ logging.basicConfig(level=logging.DEBUG)
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/repo/Packages b/tests/repo/Packages
new file mode 100644
index 0000000..68b585f
--- /dev/null
+++ b/tests/repo/Packages
@@ -0,0 +1,217 @@
+Package: silly-important
+Priority: important
+Section: admin
+Installed-Size: 32
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Source: silly-packages
+Version: 0.1-0
+Filename: ./silly-important_0.1-0_all.deb
+Size: 1792
+MD5sum: ef9a1eefd088a375ef58773831256205
+SHA1: 4075517d20fdd81d8642ab0febe7ad8c5fcde4f0
+SHA256: 3003723f0f2686a6ed700f364b0c13c966026c5e5f3b383969d87b5dba411d5e
+Description: an essential package
+ Silly packages is a set of packages which will break your package
+ management tool. They are created only for debugging purposes.
+ .
+ This package is an essential one.
+
+Package: silly-fail
+Priority: extra
+Section: admin
+Installed-Size: 36
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Source: silly-packages
+Version: 0.1-0
+Filename: ./silly-fail_0.1-0_all.deb
+Size: 1874
+MD5sum: 623a67e49bb7a1351a9bf1ae72fc6cfb
+SHA1: 76417770f2b788b680ef18be66b5a6c4ec51bc62
+SHA256: 45fea3eb64a427a288a90b12f42cc3a6abea8e8387efd4879b319db736890ebd
+Description: installation fails
+ Silly packages is a set of packages which will break your package
+ management tool. They are created only for debugging purposes.
+ .
+ This installation of this package will always fail.
+
+Package: silly-broken
+Priority: extra
+Section: admin
+Installed-Size: 32
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Source: silly-packages
+Version: 0.1-0
+Depends: silly-unavailable
+Filename: ./silly-broken_0.1-0_all.deb
+Size: 1840
+MD5sum: 78bbe306af637c18f5df9dc47bfbf41f
+SHA1: bb63cdbde699fe2100349b0418af095e6d9f80fe
+SHA256: a816e61369de433e261b5ad041e913ee3c140637825ce6613a5c99efbfd4c21b
+Description: package with broken dependencies
+ Silly packages is a set of packages which will break your package
+ management tool. They are created only for debugging purposes.
+ .
+ This package cannot be installed because of a missing dependency.
+
+Package: silly-depend-base-lintian-broken
+Priority: extra
+Section: admin
+Installed-Size: 32
+Architecture: all
+Source: silly-packages
+Version: 0.1-0
+Depends: silly-base
+Filename: ./silly-depend-base-lintian-broken_0.1-0_all.deb
+Size: 1758
+MD5sum: ba9e0b746b08fae8f8a8fbb4cf9ba41e
+SHA1: 8bcaf6034b09383e461f0e366762e6b6683e69e2
+SHA256: ff1302170d232c21ab66f332e38c51b2af9b6d06d8f377ae5944465be9467c22
+Description: package depending on silly-base (but lintian broken)
+ Silly packages is a set of packages which will break your package
+ management tool. They are created only for debugging purposes.
+ .
+ This package depends on silly-base and has no "Maintainer" set
+ and the file owner is totally wrong so lintian complains about it.
+
+Package: silly-essential
+Essential: yes
+Priority: extra
+Section: admin
+Installed-Size: 32
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Source: silly-packages
+Version: 0.1-0
+Filename: ./silly-essential_0.1-0_all.deb
+Size: 1800
+MD5sum: 492c977ce9b2f1b6a51d7c475aad058b
+SHA1: 06fb9a5efb07f72edbd297d2a21cffd1787a2388
+SHA256: e516159e99bd40d860515e4c9af251ab056eaabae58ea3f5ee11eecc4f3bee50
+Description: an essential package
+ Silly packages is a set of packages which will break your package
+ management tool. They are created only for debugging purposes.
+ .
+ This package is an essential one.
+
+Package: silly-postinst-input
+Priority: extra
+Section: admin
+Installed-Size: 36
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Source: silly-packages
+Version: 0.1-0
+Filename: ./silly-postinst-input_0.1-0_all.deb
+Size: 1906
+MD5sum: 99eecdacbcdf95b9e7b3b0c50f812a8d
+SHA1: c066b13414faa9931ff688c29f6dbcf4809861a9
+SHA256: ce6e4f7f59c3d1da45ff0b3e8e54ebdcf9a5b44edeccf9fc04c96daec5cfa020
+Description: breaks your non-interactive package manager
+ Silly packages is a set of packages which will break your package
+ management tool. They are created only for debugging purposes.
+ .
+ This package will wait for user input in the postinst script.
+
+Package: silly-config
+Priority: extra
+Section: admin
+Installed-Size: 44
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Source: silly-packages
+Version: 0.1-0
+Filename: ./silly-config_0.1-0_all.deb
+Size: 1928
+MD5sum: 565698b052640ed3d514f6b38ad457c7
+SHA1: e7169cc58f10e8ff9481cf31b2ffb947bc99f617
+SHA256: 255f025bae5b24a91158e7daa986f9bf59146ab1895f39452104eb97c412d190
+Description: wants to update a locally changed config file
+ Silly packages is a set of packages which will break your package
+ management tool. They are created only for debugging purposes.
+ .
+ This package will install a configuration file.
+
+Package: gstreamer0.10-silly
+Priority: extra
+Section: admin
+Installed-Size: 168
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Source: silly-packages
+Version: 0.1-0
+Depends: libc6 (>= 2.7-1), libglib2.0-0 (>= 2.16.0), libgstreamer-plugins-base0.10-0 (>= 0.10.0), libgstreamer0.10-0 (>= 0.10.14), libogg0 (>= 1.0rc3)
+Filename: ./gstreamer0.10-silly_0.1-0_all.deb
+Size: 52016
+MD5sum: bbaf259e0dfcb39050061181d2a13755
+SHA1: be59be2d82b7f57097f387a22f5f295d202d9c1d
+SHA256: 6b91b67de10ae547cd4c648ef343c8acd35e626e4a1ac20df1aaae51d6ec6328
+Description: gstreamer plugin test package
+ Silly packages is a set of packages which will break your package
+ management tool. They are created only for debugging purposes.
+ .
+ This package is a GStreamer test plugin package.
+Gstreamer-Version: 0.10
+Gstreamer-Decoders: audio/ac3; audio/mpeg, mpegversion=(int){ 1, 2, 4 };
+Gstreamer-Elements: ac3parse
+
+Package: silly-base
+Priority: extra
+Section: admin
+Installed-Size: 32
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Source: silly-packages
+Version: 0.1-0
+Filename: ./silly-base_0.1-0_all.deb
+Size: 1824
+MD5sum: 81ba1cf86142c466f35a36a11b5b7b52
+SHA1: f949c8e5962a43fbcd4f7d67df0ebe741052c497
+SHA256: a7c2a30abdb6488d5c3d2894db398f5dfafbc6be098da1135822be4e3ac94b32
+Description: working package
+ Silly packages is a set of packages which will break your package
+ management tool. They are created only for debugging purposes.
+ .
+ This package doesn't contain any files and should always be installable.
+
+Package: silly-depend-base
+Priority: extra
+Section: admin
+Installed-Size: 32
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Source: silly-packages
+Version: 0.1-0
+Depends: silly-base
+Filename: ./silly-depend-base_0.1-0_all.deb
+Size: 1812
+MD5sum: 3ee7181283e4926558d2afdf7b2657f1
+SHA1: 8294794b1727e22b123866f9124431a2c7b79380
+SHA256: 5fa74f7bdc0d207188790419130330becb0cf7484a972762ed512090885016f9
+Description: package depending on silly-base
+ Silly packages is a set of packages which will break your package
+ management tool. They are created only for debugging purposes.
+ .
+ This package depends on silly-base.
+
+Package: silly-base
+Priority: extra
+Section: admin
+Installed-Size: 44
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Source: silly-packages (0.1-0)
+Version: 0.1-0update1
+Filename: ./silly-base_0.1-0update1_all.deb
+Size: 1934
+MD5sum: 8e20af56a63a1e0cf40db3b0d07e7989
+SHA1: 7ce87423d9c7a734478c464021994944d07fbf1b
+SHA256: d3693c0e3e7a9519b2833fdf1301c7e03e0620edf15b95b4c7329d9eb0bee553
+Description: working package
+ Silly packages is a set of packages which will break your package
+ management tool. They are created only for debugging purposes.
+ .
+ This package doesn't contain any files and should always be installable.
+
diff --git a/tests/repo/Release b/tests/repo/Release
new file mode 100644
index 0000000..1a116a9
--- /dev/null
+++ b/tests/repo/Release
@@ -0,0 +1,22 @@
+Date: Wed, 09 May 2012 01:24:59 UTC
+MD5Sum:
+ da80d4a76b8c6119b697558524cdaf71 7325 Packages
+ d41d8cd98f00b204e9800998ecf8427e 0 Release
+ 0bd018b62f0031d1ee4993896f50ddd5 782 backports/Packages
+ cec65a680d0777c2f8f06bbaa503d345 899 backports/Release
+ 0bd018b62f0031d1ee4993896f50ddd5 782 security/Packages
+ 4fa3f7d1e32aa2bc06b63bf7519d9e64 895 security/Release
+SHA1:
+ c911274d83557d97f04bbab8d681efd11450b695 7325 Packages
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 Release
+ 42201c0ab580cb2d557ca22a7ed04717a68b2e6e 782 backports/Packages
+ 71e98c6dbe072ebbf7c959c4ebc496743fc4d3ae 899 backports/Release
+ 42201c0ab580cb2d557ca22a7ed04717a68b2e6e 782 security/Packages
+ 8e14bd91f977a2e12d3e30b3649c3f772e84126e 895 security/Release
+SHA256:
+ 9ee3b2e2d7cd1d6f567a7914d4a78e201b80b65d01d21a0865f1d5a9f5cceab2 7325 Packages
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 Release
+ 950233659ae7e86ac11ae886298963e57f4414691881af1357456c6a4038ef91 782 backports/Packages
+ 6e85dc9fc5506e5564bd9914abc165f0c50ac15d34ede44c31b363468ef641b5 899 backports/Release
+ 950233659ae7e86ac11ae886298963e57f4414691881af1357456c6a4038ef91 782 security/Packages
+ cfdf15ef290d6ab0c9e3afff718dec679d78a07b4364cbfc46be9d9ad4296ca1 895 security/Release
diff --git a/tests/repo/Release.gpg b/tests/repo/Release.gpg
new file mode 100644
index 0000000..868d5ef
--- /dev/null
+++ b/tests/repo/Release.gpg
@@ -0,0 +1,11 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.12 (GNU/Linux)
+
+iQEcBAABCAAGBQJPqculAAoJEB8w/lhZqRjZCeEH/jpt4El5MNKUR3Eu0nUR9CYn
+MNo0ZuFGQ+y11yL+FZ7nkwKIFdbFDwcR/5GFPZBPvltTmuBkbSa7sWNU9jyPwJHC
+7IcKVw40C87lMUDN/rRFdGMhYZzpu5n2F74P7frwlgu16yRq5towprwNdRt+6k9F
+LmVvo/os8/p/WwFCPX10ARdm+JTZa/G8OTfSSu6iWmoClOQ/JvYGnmzxyqVICFiF
+lR2YGLAW+RXOo/B9jhJWpz6f5/0MPVd8SpHaONV8RxZeOEkJVTeWYXg9HCIBCjUc
+azCiflzQpTC/cVFvjfEaEP23KHaSV2dTMp0fq91yMCiPq/wDqyB0M64Q8xAMN00=
+=FgqZ
+-----END PGP SIGNATURE-----
diff --git a/tests/repo/backports/Packages b/tests/repo/backports/Packages
new file mode 100644
index 0000000..b8492c6
--- /dev/null
+++ b/tests/repo/backports/Packages
@@ -0,0 +1,20 @@
+Package: silly-base
+Priority: extra
+Section: admin
+Installed-Size: 44
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Source: silly-packages (0.1-0)
+Version: 0.1-0update1
+Filename: ./silly-base_0.1-0update1_all.deb
+Size: 1934
+MD5sum: 8e20af56a63a1e0cf40db3b0d07e7989
+SHA1: 7ce87423d9c7a734478c464021994944d07fbf1b
+SHA256: d3693c0e3e7a9519b2833fdf1301c7e03e0620edf15b95b4c7329d9eb0bee553
+SHA512: e9eded74e2449a98b02828539c55a83de85a762d2361cd8c929292eb9c5a6e5a9b8eb9b64c26c45d7b73280e12a280cd799a9b831126e484bcf55b56456d559f
+Description: working package
+ Silly packages is a set of packages which will break your package
+ management tool. They are created only for debugging purposes.
+ .
+ This package doesn't contain any files and should always be installable.
+
diff --git a/tests/repo/backports/Packages.gpg b/tests/repo/backports/Packages.gpg
new file mode 100644
index 0000000..3b72d67
--- /dev/null
+++ b/tests/repo/backports/Packages.gpg
@@ -0,0 +1,34 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA256
+
+Package: silly-base
+Priority: extra
+Section: admin
+Installed-Size: 44
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Source: silly-packages (0.1-0)
+Version: 0.1-0update1
+Filename: ./silly-base_0.1-0update1_all.deb
+Size: 1934
+MD5sum: 8e20af56a63a1e0cf40db3b0d07e7989
+SHA1: 7ce87423d9c7a734478c464021994944d07fbf1b
+SHA256: d3693c0e3e7a9519b2833fdf1301c7e03e0620edf15b95b4c7329d9eb0bee553
+SHA512: e9eded74e2449a98b02828539c55a83de85a762d2361cd8c929292eb9c5a6e5a9b8eb9b64c26c45d7b73280e12a280cd799a9b831126e484bcf55b56456d559f
+Description: working package
+ Silly packages is a set of packages which will break your package
+ management tool. They are created only for debugging purposes.
+ .
+ This package doesn't contain any files and should always be installable.
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+iQEcBAEBCAAGBQJO3ZylAAoJEGg8U8fPmC0YooQH/inTQPInDCiN3pTDvzWfV16F
+Ea+UpwBN0vzuyS6f3xmhLRxLQpuz/yk8buc1H+f/XKn6eygydJRFwIEgtdWAN/Tk
+eG9I4c5zYiHzZnNWe4XNBhRdPVkIHPkbmbRs/RvDiM5Cq0LvXIe0X0RV+empJyrC
+EgKbt3PJxh8qpMfrf/OIF+GkSqAug4tq0i0n6QxLOi0raeb9PjfDwErmBpbLDSFg
+XyDnNvPET5BtWxjgupOwoFqs2QRkrLv10JBdGRz+7qG6WhH1BCAOfzYxxCtn++Ip
+kmwo8c/pmtOr1BzZyyNMWP8nvVtB728eb/M84WGYynIEyCObUQ+Q2HVsXcsPQLc=
+=j3Nx
+-----END PGP SIGNATURE-----
diff --git a/tests/repo/backports/Release b/tests/repo/backports/Release
new file mode 100644
index 0000000..0ed3dca
--- /dev/null
+++ b/tests/repo/backports/Release
@@ -0,0 +1,16 @@
+Origin: Debian
+Suite: sid-backports
+Codename: lalelu
+Date: Tue, 06 Dec 2011 04:40:53 UTC
+MD5Sum:
+ 0bd018b62f0031d1ee4993896f50ddd5 782 Packages
+ d41d8cd98f00b204e9800998ecf8427e 0 Release
+SHA1:
+ 42201c0ab580cb2d557ca22a7ed04717a68b2e6e 782 Packages
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 Release
+SHA256:
+ 950233659ae7e86ac11ae886298963e57f4414691881af1357456c6a4038ef91 782 Packages
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 Release
+SHA512:
+ 984c9203b8e534b9b9cc486b21da94cf807428dc1d2daa56427a870f4d53111c58ce749c35509a5fcf7112433d7a403830378a0f482354fb382c0aa7a25bdbc2 782 Packages
+ cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e 0 Release
diff --git a/tests/repo/backports/Release.gpg b/tests/repo/backports/Release.gpg
new file mode 100644
index 0000000..38f5901
--- /dev/null
+++ b/tests/repo/backports/Release.gpg
@@ -0,0 +1,30 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA256
+
+Origin: Debian
+Suite: sid-backports
+Codename: lalelu
+Date: Tue, 06 Dec 2011 04:40:53 UTC
+MD5Sum:
+ 0bd018b62f0031d1ee4993896f50ddd5 782 Packages
+ d41d8cd98f00b204e9800998ecf8427e 0 Release
+SHA1:
+ 42201c0ab580cb2d557ca22a7ed04717a68b2e6e 782 Packages
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 Release
+SHA256:
+ 950233659ae7e86ac11ae886298963e57f4414691881af1357456c6a4038ef91 782 Packages
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 Release
+SHA512:
+ 984c9203b8e534b9b9cc486b21da94cf807428dc1d2daa56427a870f4d53111c58ce749c35509a5fcf7112433d7a403830378a0f482354fb382c0aa7a25bdbc2 782 Packages
+ cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e 0 Release
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+iQEcBAEBCAAGBQJO3aBFAAoJEGg8U8fPmC0Y+esIAMigdsJQneBznIwfTsA6EMSB
+wKegB/IAl7VJDPMXoUAdu9kVxaJbScrrhHJ6lFdIobgIA+5zWfJjMnvujsM5PDAv
+t8xzh/sU3m3HzR8iBDi10BO+SleKps/fKHnnQZOP8msf03rzydYUTbQI2KVhy+C1
+MSx/KTAD4a/0NPKMw09kKNqfU0uoFLnJFZEO3kVYt3hIZyGjtvw2QoEPMphNMixk
+Qq7YVhKiXw/QB318dPb065bXQePvc1EWxQuR9RgtCCM+9FMyOyF1NwAzOc8XzjEY
+qHib6JzkbH+tBLqq5AmVLOTR07rndGQQuNtu70BkIGkuZC8KpDRMRTq5mi1cipg=
+=CUOV
+-----END PGP SIGNATURE-----
diff --git a/tests/repo/glatzor.gpg b/tests/repo/glatzor.gpg
new file mode 100644
index 0000000..8bb45d3
--- /dev/null
+++ b/tests/repo/glatzor.gpg
@@ -0,0 +1,115 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.12 (GNU/Linux)
+
+mQENBE0hzl4BCADSYPRyP0ZiI/FC+ds375PrW59JRf150kK3nPuEdH8hWQFzO0Sq
+5d3xa7GLGzD2/kk6T7GwSVdH9TVoNBr2aDXdbSIykd04gFvuetFpc/TrB94yHf9F
+1GNSJCVzX4ICzoazMk08Nj/8p+3swp8r4mG1rgIMiV4Vf1mvTL+tN8v68/DleHw2
+8RI0NFK1YdoOh5M46VVFzpBrzkf2aymoA3th8q5ufnOGnDzt867DRu4czLMICqFI
+jSurKIzP0B+ABGZQUpvptGnNOQyYG1rxR1GXXXfXAIInucvDUOrMqq1mJRySgzFC
+xXpJFgWqyaTt1+hzazgzCqCQgtp0CSte8FBBABEBAAG0J1NlYmFzdGlhbiBIZWlu
+bGVpbiA8Z2xhdHpvckB1YnVudHUuY29tPokBNwQTAQgAIQUCT2BikQIbAwULCQgH
+AwUVCgkICwUWAgMBAAIeAQIXgAAKCRBoPFPHz5gtGK6DCADOgqivyWrAXWRTH/9W
+Nl9Vt38QPuS+OOFJxzPcxqCGc8zfvmNDmCQksnZ7i3pkyrGagofUNLJVN8OgXrmJ
+51IXYAZxtqxY9WBjfWMEiOTkt7bszZ/6f5GBg5easEEjHGcOmjR21B8R6AvPV2y1
+hWAgt0KMve4uImd9DPuxUmzGy045BS30BPndWglPDFajecnKC0clQroeV7A31NwY
+WhFCEP2ZXyGv0GHDJ50Ts76nJQAHytJoGgwEkcjb8q5phJROIna8jXUyGV5+1p3E
+UB9bZfwejmAFoy4+PvscqBwE5k2lyJbZ2Z1d5FwsnzsPn+KcdZbrFYhHsABaE24v
+WYuItCVTZWJhc3RpYW4gSGVpbmxlaW4gPGdsYXR6b3JAZnNmZS5vcmc+iQE3BBMB
+CAAhBQJPWv41AhsDBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEGg8U8fPmC0Y
+sMgIAIYjmhICQ/Cwxk39MkgKWOoXQaqwZJA67Z5LFxPewolyRfhfYjzpPLSxUg0Y
+389WNtM6ufh6aHzsM9qnNkMpTL1K8Bq+V2HuAghLA1eVyonjs74yx3UVjOr97RqX
+Urkp4iv9udPK92pa43oIH4R6DLzt8sqEVDStmLSV5h0Qx1qKS14FFXEJfExf33Ds
+Kfo2AlSib0G/1qGshlde8+vahGE7BklWWMDaiZV9HSROd/Q66IL0qzSjz6C4qPse
+aVJTmjaP6pJ6lrI983eRdjmSxIL2Xsdg+ELT0S1EndvC1pEwmEIlR/3aACY+KVF4
+Gj/vsSlPABT6eq/S2c9nBUVPYJy0JFNlYmFzdGlhbiBIZWlubGVpbiA8c2ViaUBn
+bGF0em9yLmRlPokBNwQTAQgAIQUCTSHO0AIbAwULCQgHAwUVCgkICwUWAgMBAAIe
+AQIXgAAKCRBoPFPHz5gtGKypB/47mW4RP/c+YLv7b30Fvt2GSYMZqIFbSN4Y0dGo
+1Pkd+XWq/wo2h1UPoZb6K7DTfAXNTCHoqyP5qSe/DO5CrxJuiL5F5mQGTqe+QmSy
+5YYdK3cInddWNwE+ZMBCe2U5oRxBItACq4R0VJ3ZEP3ivYA+/p8FR6H0ya08KEku
+frMoHScG3CfmcWWqxL3ZK4+7XVAdrO3tiuRIEVjT7Luzfmxforc0N63izBhvGqmf
+1uKDRc9qMILbATJRHEQbYxR9d1g/gIY+KIBvrXVSdcRzLC3y1wXM6Zy7XHhx5Mvs
+gvhP1YlfM24Gx23KsJCE9v1120mqt5ZifMkqvPhoiAIlJjwBiEoEEBEIAAoFAk0h
+zyoDBQJ4AAoJEHro6KyTTCLVrAQAnRP5U517+leetoollvfkFGscY9eyAKCHNu7/
+2NJP532vH/h1TAFanzTq+rQyU2ViYXN0aWFuIEhlaW5sZWluIDxzZWJhc3RpYW4u
+aGVpbmxlaW5AZ2xhdHpvci5kZT6JATcEEwEIACEFAk0hzl4CGwMFCwkIBwMFFQoJ
+CAsFFgIDAQACHgECF4AACgkQaDxTx8+YLRiRbQgAg0BT+6RR4pRgORGML4QcBROJ
+Tb51GFe83Fx/cKBJiChSarbdbS5/dDzfq2fzbMjSNOpfPnusDpAvUe1gTcXBTxzz
+V0hcOZHTPLbp6hbLANVbFsjIV+HLDL2VD3RX5XmK/zt62WhB/iHQkSytfFPJb1x8
+kigh5z5pfdb78j+US0cAWF9RpWBjYs0xzIXaxYe0UI7UGu20PwvEbbmAheG/exS2
+ezL8cebnXT1ymlz55ba3Y052XFupNOd5H7KH8MS4Ex4Y0hNaP3zcK01eEe5IdZqq
+afb3XtkxojgzV43OMgWs2o1Yqp58DvuUNSPd5zL90NE57v/Y3Zw5026cQf1KM4hK
+BBARCAAKBQJNIc8qAwUCeAAKCRB66Oisk0wi1SD7AJ9W0wtzT9iL0ZN2Ke+WVyy5
+ndMX/wCfaTLHEIYtgZVsezi1yXKZnejFzIe0LVNlYmFzdGlhbiBIZWlubGVpbiAo
+ZGV2ZWwpIDxkZXZlbEBnbGF0em9yLmRlPokBNwQTAQgAIQUCTSHOogIbAwULCQgH
+AwUVCgkICwUWAgMBAAIeAQIXgAAKCRBoPFPHz5gtGCpWB/9r7Fv4lZgbhmdBAQPE
+rEwWu8Zmr7aLquNcvs2PcBqtQU9JiU8oOJBGskQ6gDT6DAzymobyvALkQB6csf9E
+Lo+JL2HjL0iuytXwEoRAFy3D8ddnEnAC/iVfFsWkMrIsfXkfbNpD7RsOkthsXl8J
+wnA8OBoEayG3mj+cUyddmuLLzbD9k0sF3S8NIiqyp8YkobbP4veAjyBBrOjQEXDC
+pDudYoP9zDE81wUZABU8uj1LcXlK75JpZKJnrMMNXX17VzHbYWPiOThRNLARNidh
+d/rVzSgJbcs5ETbODWH60lZS3LwHYjbqxTp4+FZOyQw+FvNYTCd0dkAVGanc2k/J
+bTaPiEoEEBEIAAoFAk0hzyoDBQJ4AAoJEHro6KyTTCLVQr4Amwdi5Y/Fs4uvA+O5
+xqZeNQMbSxFJAJ49mVdpeO2I1DXxsPqzC3XM6CB3FbQpU2ViYXN0aWFuIEhlaW5s
+ZWluIChkcG9vbCkgPHNoQGRwb29sLm5ldD6JATcEEwEIACEFAk0hzr0CGwMFCwkI
+BwMFFQoJCAsFFgIDAQACHgECF4AACgkQaDxTx8+YLRhB/gf+LsJo0C27+UpisfS+
+AG6ngR/LE9DY4+YMC5gTVT3u6Rnc8rQrvWb9sQgQ1BEl5FAp98EHqbpAq+AGhYMj
+56QedcXFl8ATgV24PkVarUrypo2r99H//f5fMB8mUI2I17lYxuvRNbeEnro9kzqS
+fiQD/P796endPmy+9JtwZ6IiAoov/9WvwQB8i6U3I1mj4eI665QpDaxqVu7XwXeR
+3JN2ezMoz+KJqgpL5IQrwHPP2VyLSOv1lPdrdbWk/rBWDcv+kHxDnIXWlsiLr/Ge
+dPwRhPFK4yOtTxDqrzZxQI6/ySMkVzXFv26lyXEqSfOH/B7hdkMj3x5FeRs4ZOFU
+RJgRi4hKBBARCAAKBQJNIc8qAwUCeAAKCRB66Oisk0wi1bklAJ9taZygn7ZKxw/L
+VEgyacvMeS53nwCghWZr/5o3x4NzPsb7OCKNYb7ftSu5AQ0ET1ot8gEIALYo/VgD
+I2nXjn7bOanslizePCpsYMHp9nRfTYRwKLTukV9cpd0MirqlrYKM8/TZxJqLzd1Q
+mrLQembUU0SW/kaFGYvRpTP/LQcZojyrgN8cJpc1idrgF+pSR7YCIFvUnowdP85u
+rj+My2rAmx5F5qxLXbeTXNcnO60GZs1MfRFN5yU4tKxGuq5FfZPtWtxG8sS2lqAH
+4/ciSI4wjHlVycCtCH0vT9x+S16JZnnIG52o4zGHI0T08ojI8FjvRiSnfwuKYQ4C
+nNbQbaOxHxpz+lIjYQ0QVkzdhFwHBsD744bXWR1ehCOEax2ehF89P62Dw9FvlZSU
+HAgDe0id/W5FpH0AEQEAAYkBHwQYAQgACQUCT1ot8gIbIAAKCRBoPFPHz5gtGPJI
+B/473IbrXxfjSEf85+cIj0JP8VRLoVsZMnOxzUyQkpVEAfLXLHkmRwMc9/aheX9O
+9796p//PSiMP/ig9e8oF4mv5wssN6oBphe9uLpg9eQ9PW/aIbakRlJhhwDCZON6Q
+RPPyQR6zQSjsouePGMstgH+w/OFyKRKcUCDUDJVOr63VsSSoOJN8OarHCi7m6QuW
+Lh0/TnoZBp1cAgd8py6r6bXELj9R2T4I/LtoL74dlbFPZ7Ct+k6j9TgaDl/pbL81
+462D7hMcARZAJLP1e+Q/6ha2w5BhUYoYfpF0wYriKlEl2jX98+lPBk610H8GhgkV
+9X4g615HwQkQWQa8cKIlEcE0uQENBE9bL7MBCADJJbyU4pbHK06wul9RAzHI5h/A
+EoUBo6K5j45HcAx3LHWW4ZRFN6JBmudgKcRTKpu5ij7U61ndi/FXnOctLBQ4BtAM
+nFU5y2I4HIKy7o5ZkaKFbrQJJCy7DuxoN4sj7THGBOoeE2JZQYKXGKfGb/c5qM5p
+ljeZkYt26nrRuPbKMcxg3Kbm0WNaKQli1WYuUkOEyKAlcQWYE9Kuj8kmTBN3+kSq
+PwPBWzdIEWPzQp2a5ZY3Svq4zDaWvaWCz/18J+CzISeqlF7sLoiv8yqJ/E7kxD18
+GZxLxt7Cqd/6/I/4nup1f2vhd77y8PTR9Psl2fhmBDd6AC9yaAHYvr5NGySnABEB
+AAGJAR8EGAEIAAkFAk9bL7MCGwwACgkQaDxTx8+YLRgqjwf/b6VPE/5gVOrAgJVv
+76Xa6N9lqU0Hs026nJ0u5zhcU7WIBBvJiL+irQ4pDTKQZdu3QYV8vIeYa9ssGoQd
+P8OHksq3ZOBvBEsjzblfb1uhDpBHgvsxk7c2lfuDawn0+lQFy94AcV0f/jyYKGKp
+5I9AEgSOhuhrtxqD1fGlRBIjdAqhTKC9cASsS3qGGMuLIjbD6gfRSeSPf+ncpeCb
+6Jw0xJDLIb3xXP2JTvRkgKDVWVZ0Cyvgmvx8ox1vKZABVH8TjGrH3IdRUgb643O4
+BHAnk99PwM7yoRJkP2O2fIICZdY0ZpUaqPCF0QgNXY4TJhI8i0xgnkwbCJA/8lZJ
+w3pqTbkBDQRPWzBBAQgAqyMOJPED8bxfksgtFHp20c/CloKR+dkCIV2Xz7jdlG6h
+U85PniHukuHY1i7BDQofVo8nAjWj0QPPZN6nrHUhAjyxWH41dyd4vI+w8Ryf1fru
+aEk6SpoSr5HimAthyXPPs+wZEXwrGI2AGzdP6HDX7WX+nlHT8J9nnFUa7OyIUnJU
+rd7yv3+z4yKaKKpFmqo3LnXx5ltLuz9X6bOcj07XGdTPijztS1cCHmZdcbPcVQbS
+2yugGtqw7x02dL6sZm8gtEhzaQIAptxwirq5/H7y7Vqy5TWe3Z859PHNcA5cXrg8
+Rhi/tDxSFqlo/1mOmHInaMbssRFgWJ5wHGpBKFBwkwARAQABiQI+BBgBCAAJBQJP
+WzBBAhsCASkJEGg8U8fPmC0YwF0gBBkBCAAGBQJPWzBBAAoJEB8w/lhZqRjZ5gkH
+/jdrZ+9eTHOlUkOT+nGdvG2xI9QnVnoxNeO+V5PO2BgjRcju8U3Cv320KjSJhzIF
+PLXwRSy8x24Uc36ARLj69YM8fwF9ZiCPrameBvECL5qDYFCmSe8WQAEi/ckrhTbG
+RFlpgMc1Z52LnhOcZNw5E7lK15QBxZybwJe8dWsZwqf0AJU/1XUSoxhhZF2kffWf
+5c2hjRrCdldxgT+do5Fr6v0rQOWyc8nv88fb3wawXXasBSD/a6tS6upwtrQJpoXt
+ZI8lukBjQYzWhGKpi0GVrqj6lre1YeZHuTHWEmesfCSYiTLAe4mdEQ3P3SYRNKX1
+rnZOCO0/EW92NQR/iSXWBW9fGAf/fihEurEYFdiquQVhAx0xyeBs1iDwa9LtRY6E
+5CPTRkmQK+Glcjm1NL7fvClYVc9iWLfDEXGl+7t7XpUwlwku9BKxQcstfMhV0uAe
+VnbJ9uDur78j6JYyXTuxh5RopBk3mmH0XE9rBoy5ytPYftoAs9p3FT1qI9Azb584
+1aeWnxbJghqi9a08Iwxr5i596aF6Ilxd6phfmEVufxJ/W5BwX46LpbO8kqlf5+qn
+h6hMcSDFexAJb/RKiGLGqg2OO9U7ghW7Ldk9vQPGLogowHlNpWn609uYO/C2QORH
+4zmRcHf2KEbctxLFj5bZKepgzX0+xPBmIP1rlj8wL7dkFGcbVLkBDQRNIc5eAQgA
+2lDY2Uq/1H1ZbiW9KgAfo4qgahCsOrY2PTq3wQUMaSucdSUn9vZPHhw8OwZh9cnM
+oofoZKW3ub5ZHDxcgZnB/hKBfdchTpNmwIu0PtXy9Bm2e8t7qGChbzVPiaSRanOo
+cpUWnOYXHIooSM84iCCKXCIidRemlq3X+/PjxAMmRyM9MNxPmENoJbFVgjaK1uEz
+7haQnpcvH3NytXbxhGceTInO0PJP1uMBmmCdkpacBddxYdcW9PAVCFv3cusa+GX0
+SSsVIHRB79pA2UM3ZKiErZkK/vSsM6PaLLGfB+PFwSOjKPdBZHlXvC4vp8/MLues
+dtv8pq4jHQz+lNmqmsnC5wARAQABiQEfBBgBCAAJBQJNIc5eAhsMAAoJEGg8U8fP
+mC0YnUEH/32IQgeswnN8JWzEmi5vOmzNI8NB0zri08ECo/+FUMg/flMzKnfV2+fB
+sg5ea0UMOi7XGzm60nvEuFp8C3N+VZrslUOAjc4RErzhy7UZfVZttNb1hGUqwUAm
+fU8jwEGAx69qwHxFqZ7KgcljHl+MHzZl82PkV/v/vnTTxTqvQZ1Xinmu4wPlfGCQ
+5fMj6n/foJpKq7C3xAnxcexD7FPAznFI51xqMCILe7TnsjT4NYUyVKdfPheBG1gh
+ukdmn7rMSUBeaswlSvl4qC7rMEOHovV0NgJR7z+edOBp6+mJiv9Q66zbeIbaZt7i
+8fwzmD4MW2kyoYtEl1VGBL9PjLSZ8Ns=
+=BAJh
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/repo/gstreamer0.10-silly_0.1-0_all.deb b/tests/repo/gstreamer0.10-silly_0.1-0_all.deb
new file mode 100644
index 0000000..7eee4ea
--- /dev/null
+++ b/tests/repo/gstreamer0.10-silly_0.1-0_all.deb
Binary files differ
diff --git a/tests/repo/security/Packages b/tests/repo/security/Packages
new file mode 100644
index 0000000..b8492c6
--- /dev/null
+++ b/tests/repo/security/Packages
@@ -0,0 +1,20 @@
+Package: silly-base
+Priority: extra
+Section: admin
+Installed-Size: 44
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Source: silly-packages (0.1-0)
+Version: 0.1-0update1
+Filename: ./silly-base_0.1-0update1_all.deb
+Size: 1934
+MD5sum: 8e20af56a63a1e0cf40db3b0d07e7989
+SHA1: 7ce87423d9c7a734478c464021994944d07fbf1b
+SHA256: d3693c0e3e7a9519b2833fdf1301c7e03e0620edf15b95b4c7329d9eb0bee553
+SHA512: e9eded74e2449a98b02828539c55a83de85a762d2361cd8c929292eb9c5a6e5a9b8eb9b64c26c45d7b73280e12a280cd799a9b831126e484bcf55b56456d559f
+Description: working package
+ Silly packages is a set of packages which will break your package
+ management tool. They are created only for debugging purposes.
+ .
+ This package doesn't contain any files and should always be installable.
+
diff --git a/tests/repo/security/Packages.gpg b/tests/repo/security/Packages.gpg
new file mode 100644
index 0000000..3b72d67
--- /dev/null
+++ b/tests/repo/security/Packages.gpg
@@ -0,0 +1,34 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA256
+
+Package: silly-base
+Priority: extra
+Section: admin
+Installed-Size: 44
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Source: silly-packages (0.1-0)
+Version: 0.1-0update1
+Filename: ./silly-base_0.1-0update1_all.deb
+Size: 1934
+MD5sum: 8e20af56a63a1e0cf40db3b0d07e7989
+SHA1: 7ce87423d9c7a734478c464021994944d07fbf1b
+SHA256: d3693c0e3e7a9519b2833fdf1301c7e03e0620edf15b95b4c7329d9eb0bee553
+SHA512: e9eded74e2449a98b02828539c55a83de85a762d2361cd8c929292eb9c5a6e5a9b8eb9b64c26c45d7b73280e12a280cd799a9b831126e484bcf55b56456d559f
+Description: working package
+ Silly packages is a set of packages which will break your package
+ management tool. They are created only for debugging purposes.
+ .
+ This package doesn't contain any files and should always be installable.
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+iQEcBAEBCAAGBQJO3ZylAAoJEGg8U8fPmC0YooQH/inTQPInDCiN3pTDvzWfV16F
+Ea+UpwBN0vzuyS6f3xmhLRxLQpuz/yk8buc1H+f/XKn6eygydJRFwIEgtdWAN/Tk
+eG9I4c5zYiHzZnNWe4XNBhRdPVkIHPkbmbRs/RvDiM5Cq0LvXIe0X0RV+empJyrC
+EgKbt3PJxh8qpMfrf/OIF+GkSqAug4tq0i0n6QxLOi0raeb9PjfDwErmBpbLDSFg
+XyDnNvPET5BtWxjgupOwoFqs2QRkrLv10JBdGRz+7qG6WhH1BCAOfzYxxCtn++Ip
+kmwo8c/pmtOr1BzZyyNMWP8nvVtB728eb/M84WGYynIEyCObUQ+Q2HVsXcsPQLc=
+=j3Nx
+-----END PGP SIGNATURE-----
diff --git a/tests/repo/security/Release b/tests/repo/security/Release
new file mode 100644
index 0000000..e8ebce5
--- /dev/null
+++ b/tests/repo/security/Release
@@ -0,0 +1,16 @@
+Origin: Debian
+Label: Debian-Security
+Suite: sid
+Date: Tue, 06 Dec 2011 04:40:53 UTC
+MD5Sum:
+ 0bd018b62f0031d1ee4993896f50ddd5 782 Packages
+ d41d8cd98f00b204e9800998ecf8427e 0 Release
+SHA1:
+ 42201c0ab580cb2d557ca22a7ed04717a68b2e6e 782 Packages
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 Release
+SHA256:
+ 950233659ae7e86ac11ae886298963e57f4414691881af1357456c6a4038ef91 782 Packages
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 Release
+SHA512:
+ 984c9203b8e534b9b9cc486b21da94cf807428dc1d2daa56427a870f4d53111c58ce749c35509a5fcf7112433d7a403830378a0f482354fb382c0aa7a25bdbc2 782 Packages
+ cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e 0 Release
diff --git a/tests/repo/security/Release.gpg b/tests/repo/security/Release.gpg
new file mode 100644
index 0000000..d012876
--- /dev/null
+++ b/tests/repo/security/Release.gpg
@@ -0,0 +1,30 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA256
+
+Origin: Debian
+Label: Debian-Security
+Suite: sid
+Date: Tue, 06 Dec 2011 04:40:53 UTC
+MD5Sum:
+ 0bd018b62f0031d1ee4993896f50ddd5 782 Packages
+ d41d8cd98f00b204e9800998ecf8427e 0 Release
+SHA1:
+ 42201c0ab580cb2d557ca22a7ed04717a68b2e6e 782 Packages
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 Release
+SHA256:
+ 950233659ae7e86ac11ae886298963e57f4414691881af1357456c6a4038ef91 782 Packages
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 Release
+SHA512:
+ 984c9203b8e534b9b9cc486b21da94cf807428dc1d2daa56427a870f4d53111c58ce749c35509a5fcf7112433d7a403830378a0f482354fb382c0aa7a25bdbc2 782 Packages
+ cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e 0 Release
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+iQEcBAEBCAAGBQJO3aA1AAoJEGg8U8fPmC0YigkIAK9CXZukop9g2byChtM8TRsN
+t5K3epgmAoxws7yFF0gZJdFk7p6EMVp+13o5R4WYZhTfVQO8AFcYrEFvb17CGY9g
+Y7JYAl79lkewZJuG5LYFaxUlyczft0p8yqRShpEMp+kKHUuHbpvABfVsLVh8dRWD
+ydm4xIxHD558QSctn0Do+yEuzEJvKuYfp2h5LedXcpylhXjgMchfDhZyvNhPhCEB
+uEb8/o458SILq4XWMSlkyOof/Rn/X7afbtJlG/oq0tSZ8SNYYkVj49TUPPGjD+zw
+cU7LY3G+oEsDMl9XvUTEjX8zfeqcvpfBDa/d5KyXV2ZRg0EJB7mgLVIdBNdhxqA=
+=8bi0
+-----END PGP SIGNATURE-----
diff --git a/tests/repo/silly-base_0.1-0_all.deb b/tests/repo/silly-base_0.1-0_all.deb
new file mode 100644
index 0000000..cb10c10
--- /dev/null
+++ b/tests/repo/silly-base_0.1-0_all.deb
Binary files differ
diff --git a/tests/repo/silly-base_0.1-0update1_all.deb b/tests/repo/silly-base_0.1-0update1_all.deb
new file mode 100644
index 0000000..858514f
--- /dev/null
+++ b/tests/repo/silly-base_0.1-0update1_all.deb
Binary files differ
diff --git a/tests/repo/silly-broken_0.1-0_all.deb b/tests/repo/silly-broken_0.1-0_all.deb
new file mode 100644
index 0000000..ced1e29
--- /dev/null
+++ b/tests/repo/silly-broken_0.1-0_all.deb
Binary files differ
diff --git a/tests/repo/silly-bully_0.1-0_all.deb b/tests/repo/silly-bully_0.1-0_all.deb
new file mode 100644
index 0000000..54c3b5d
--- /dev/null
+++ b/tests/repo/silly-bully_0.1-0_all.deb
Binary files differ
diff --git a/tests/repo/silly-config_0.1-0_all.deb b/tests/repo/silly-config_0.1-0_all.deb
new file mode 100644
index 0000000..c75ec3a
--- /dev/null
+++ b/tests/repo/silly-config_0.1-0_all.deb
Binary files differ
diff --git a/tests/repo/silly-depend-base-lintian-broken_0.1-0_all.deb b/tests/repo/silly-depend-base-lintian-broken_0.1-0_all.deb
new file mode 100644
index 0000000..34c09b8
--- /dev/null
+++ b/tests/repo/silly-depend-base-lintian-broken_0.1-0_all.deb
Binary files differ
diff --git a/tests/repo/silly-depend-base_0.1-0_all.deb b/tests/repo/silly-depend-base_0.1-0_all.deb
new file mode 100644
index 0000000..56cdc36
--- /dev/null
+++ b/tests/repo/silly-depend-base_0.1-0_all.deb
Binary files differ
diff --git a/tests/repo/silly-essential_0.1-0_all.deb b/tests/repo/silly-essential_0.1-0_all.deb
new file mode 100644
index 0000000..43c3079
--- /dev/null
+++ b/tests/repo/silly-essential_0.1-0_all.deb
Binary files differ
diff --git a/tests/repo/silly-fail_0.1-0_all.deb b/tests/repo/silly-fail_0.1-0_all.deb
new file mode 100644
index 0000000..f08cfc0
--- /dev/null
+++ b/tests/repo/silly-fail_0.1-0_all.deb
Binary files differ
diff --git a/tests/repo/silly-important_0.1-0_all.deb b/tests/repo/silly-important_0.1-0_all.deb
new file mode 100644
index 0000000..5d9ab51
--- /dev/null
+++ b/tests/repo/silly-important_0.1-0_all.deb
Binary files differ
diff --git a/tests/repo/silly-postinst-input_0.1-0_all.deb b/tests/repo/silly-postinst-input_0.1-0_all.deb
new file mode 100644
index 0000000..89e0c3d
--- /dev/null
+++ b/tests/repo/silly-postinst-input_0.1-0_all.deb
Binary files differ
diff --git a/tests/repo/whitelisted/Packages b/tests/repo/whitelisted/Packages
new file mode 100644
index 0000000..af8eaef
--- /dev/null
+++ b/tests/repo/whitelisted/Packages
@@ -0,0 +1,34 @@
+Package: silly-base
+Priority: extra
+Section: admin
+Installed-Size: 44
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Source: silly-packages (0.1-0)
+Version: 0.1-0update1
+Filename: ./silly-base_0.1-0update1_all.deb
+Size: 1934
+MD5sum: 8e20af56a63a1e0cf40db3b0d07e7989
+SHA1: 7ce87423d9c7a734478c464021994944d07fbf1b
+SHA256: d3693c0e3e7a9519b2833fdf1301c7e03e0620edf15b95b4c7329d9eb0bee553
+SHA512: e9eded74e2449a98b02828539c55a83de85a762d2361cd8c929292eb9c5a6e5a9b8eb9b64c26c45d7b73280e12a280cd799a9b831126e484bcf55b56456d559f
+Description: working package
+ Silly packages is a set of packages which will break your package
+ management tool. They are created only for debugging purposes.
+ .
+ This package doesn't contain any files and should always be installable.
+
+Package: other-pkg
+Priority: extra
+Section: admin
+Installed-Size: 44
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Version: 2.0
+Filename: ./other-pkg_2.0_all.deb
+Size: 1934
+MD5sum: 8e20af56a63a1e0cf40db3b0d07e7989
+SHA1: 7ce87423d9c7a734478c464021994944d07fbf1b
+SHA256: d3693c0e3e7a9519b2833fdf1301c7e03e0620edf15b95b4c7329d9eb0bee553
+SHA512: e9eded74e2449a98b02828539c55a83de85a762d2361cd8c929292eb9c5a6e5a9b8eb9b64c26c45d7b73280e12a280cd799a9b831126e484bcf55b56456d559f
+Description: another working package
diff --git a/tests/repo/whitelisted/Packages.gpg b/tests/repo/whitelisted/Packages.gpg
new file mode 100644
index 0000000..3b72d67
--- /dev/null
+++ b/tests/repo/whitelisted/Packages.gpg
@@ -0,0 +1,34 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA256
+
+Package: silly-base
+Priority: extra
+Section: admin
+Installed-Size: 44
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Source: silly-packages (0.1-0)
+Version: 0.1-0update1
+Filename: ./silly-base_0.1-0update1_all.deb
+Size: 1934
+MD5sum: 8e20af56a63a1e0cf40db3b0d07e7989
+SHA1: 7ce87423d9c7a734478c464021994944d07fbf1b
+SHA256: d3693c0e3e7a9519b2833fdf1301c7e03e0620edf15b95b4c7329d9eb0bee553
+SHA512: e9eded74e2449a98b02828539c55a83de85a762d2361cd8c929292eb9c5a6e5a9b8eb9b64c26c45d7b73280e12a280cd799a9b831126e484bcf55b56456d559f
+Description: working package
+ Silly packages is a set of packages which will break your package
+ management tool. They are created only for debugging purposes.
+ .
+ This package doesn't contain any files and should always be installable.
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+iQEcBAEBCAAGBQJO3ZylAAoJEGg8U8fPmC0YooQH/inTQPInDCiN3pTDvzWfV16F
+Ea+UpwBN0vzuyS6f3xmhLRxLQpuz/yk8buc1H+f/XKn6eygydJRFwIEgtdWAN/Tk
+eG9I4c5zYiHzZnNWe4XNBhRdPVkIHPkbmbRs/RvDiM5Cq0LvXIe0X0RV+empJyrC
+EgKbt3PJxh8qpMfrf/OIF+GkSqAug4tq0i0n6QxLOi0raeb9PjfDwErmBpbLDSFg
+XyDnNvPET5BtWxjgupOwoFqs2QRkrLv10JBdGRz+7qG6WhH1BCAOfzYxxCtn++Ip
+kmwo8c/pmtOr1BzZyyNMWP8nvVtB728eb/M84WGYynIEyCObUQ+Q2HVsXcsPQLc=
+=j3Nx
+-----END PGP SIGNATURE-----
diff --git a/tests/repo/whitelisted/Release b/tests/repo/whitelisted/Release
new file mode 100644
index 0000000..859c02c
--- /dev/null
+++ b/tests/repo/whitelisted/Release
@@ -0,0 +1,17 @@
+Origin: Ubuntu
+Label: Ubuntu-Whitelisted
+Components: main
+Suite: sid
+Date: Tue, 06 Dec 2011 04:40:53 UTC
+MD5Sum:
+ 0bd018b62f0031d1ee4993896f50ddd5 782 Packages
+ d41d8cd98f00b204e9800998ecf8427e 0 Release
+SHA1:
+ 42201c0ab580cb2d557ca22a7ed04717a68b2e6e 782 Packages
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 Release
+SHA256:
+ 950233659ae7e86ac11ae886298963e57f4414691881af1357456c6a4038ef91 782 Packages
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 Release
+SHA512:
+ 984c9203b8e534b9b9cc486b21da94cf807428dc1d2daa56427a870f4d53111c58ce749c35509a5fcf7112433d7a403830378a0f482354fb382c0aa7a25bdbc2 782 Packages
+ cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e 0 Release
diff --git a/tests/repo/whitelisted/Release.gpg b/tests/repo/whitelisted/Release.gpg
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/repo/whitelisted/Release.gpg
diff --git a/tests/repo/whitelisted/silly-base_0.1-0update1_all.deb b/tests/repo/whitelisted/silly-base_0.1-0update1_all.deb
new file mode 120000
index 0000000..a960865
--- /dev/null
+++ b/tests/repo/whitelisted/silly-base_0.1-0update1_all.deb
@@ -0,0 +1 @@
+../silly-base_0.1-0update1_all.deb \ No newline at end of file
diff --git a/tests/test_cdrom.py b/tests/test_cdrom.py
new file mode 100644
index 0000000..352bbbe
--- /dev/null
+++ b/tests/test_cdrom.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Tests the client module."""
+
+import logging
+import time
+import unittest
+
+import defer
+import dbus
+
+import aptdaemon.client
+import aptdaemon.loop
+import aptdaemon.enums
+
+import aptdaemon.test
+
+DEBUG = True
+
+
+class CDROMTestCase(aptdaemon.test.AptDaemonTestCase):
+
+ """Test the installation from removable media, e.g. CDROMs."""
+
+ def setUp(self):
+ """Setup a chroot, run the aptdaemon and a fake PolicyKit daemon."""
+ # Setup chroot
+ self.chroot = aptdaemon.test.Chroot()
+ self.chroot.setup()
+ self.addCleanup(self.chroot.remove)
+ self.chroot.add_trusted_key()
+ self.chroot.add_cdrom_repository()
+ # Start aptdaemon with the chroot on the session bus
+ self.start_dbus_daemon()
+ self.bus = dbus.bus.BusConnection(self.dbus_address)
+ self.start_session_aptd(self.chroot.path)
+ # Start the fake PolikcyKit daemon
+ self.start_fake_polkitd()
+ time.sleep(1)
+ self.called = False
+
+ @defer.inline_callbacks
+ def _on_medium_required_cancel(self, trans, medium, mount_point):
+ yield trans.cancel()
+
+ @defer.inline_callbacks
+ def _on_medium_required(self, trans, medium, mount_point):
+ if self.called:
+ # Abort if we get asked twice for the cdrom
+ yield trans.cancel()
+ self.chroot.mount_cdrom()
+ self.called = True
+ yield trans.provide_medium(medium)
+
+ def _on_finished(self, trans, exit):
+ """Callback to stop the mainloop after a transaction is done."""
+ aptdaemon.loop.mainloop.quit()
+
+ def test(self):
+ """Test changing media."""
+ @defer.inline_callbacks
+ def run():
+ self.client = aptdaemon.client.AptClient(self.bus)
+ trans = yield self.client.install_packages(["silly-depend-base"])
+ trans.connect("finished", self._on_finished)
+ trans.connect("medium-required", self._on_medium_required)
+ yield trans.run()
+ defer.return_value(trans)
+ deferred = run()
+ aptdaemon.loop.mainloop.run()
+ self.assertEqual(deferred.result.exit, aptdaemon.enums.EXIT_SUCCESS)
+
+ def test_cancel(self):
+ """Test cancelling a required medium request."""
+ @defer.inline_callbacks
+ def run():
+ self.client = aptdaemon.client.AptClient(self.bus)
+ trans = yield self.client.install_packages(["silly-depend-base"])
+ trans.connect("finished", self._on_finished)
+ trans.connect("medium-required", self._on_medium_required_cancel)
+ yield trans.run()
+ defer.return_value(trans)
+ deferred = run()
+ aptdaemon.loop.mainloop.run()
+ self.assertEqual(deferred.result.exit, aptdaemon.enums.EXIT_CANCELLED)
+
+ self.chroot.mount_cdrom()
+
+ deferred = run()
+ aptdaemon.loop.mainloop.run()
+ self.assertEqual(deferred.result.exit, aptdaemon.enums.EXIT_SUCCESS)
+
+
+if __name__ == "__main__":
+ if DEBUG:
+ logging.basicConfig(level=logging.DEBUG)
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/test_client.py b/tests/test_client.py
new file mode 100644
index 0000000..c7370c6
--- /dev/null
+++ b/tests/test_client.py
@@ -0,0 +1,174 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Tests the client module."""
+
+import logging
+import time
+import unittest
+
+import dbus
+import defer
+
+from gi.repository import GObject
+
+import aptdaemon.client
+import aptdaemon.loop
+import aptdaemon.enums
+import aptdaemon.errors
+
+import aptdaemon.test
+
+DEBUG = True
+
+
+class ClientTestNotAuthorized(aptdaemon.test.AptDaemonTestCase):
+
+ """Test the python client."""
+
+ def setUp(self):
+ """Setup a chroot, run the aptdaemon and a fake PolicyKit daemon."""
+ # Setup chroot
+ self.chroot = aptdaemon.test.Chroot()
+ self.chroot.setup()
+ self.addCleanup(self.chroot.remove)
+ self.chroot.add_test_repository()
+ # Start aptdaemon with the chroot on the session bus
+ self.start_dbus_daemon()
+ self.bus = dbus.bus.BusConnection(self.dbus_address)
+ self.start_session_aptd(self.chroot.path)
+ # Start the fake PolikcyKit daemon and disallow all actions
+ self.start_fake_polkitd("none")
+ time.sleep(1)
+
+ def _on_finished(self, trans, exit):
+ """Callback to stop the mainloop after a transaction is done."""
+ aptdaemon.loop.mainloop.quit()
+
+ def test_auth_failed(self):
+ """Test the failing of an unauthorized transaction"""
+ @defer.inline_callbacks
+ def run():
+ self.client = aptdaemon.client.AptClient(self.bus)
+ trans = yield self.client.update_cache()
+ trans.connect("finished", self._on_finished)
+ try:
+ yield trans.run()
+ except aptdaemon.errors.NotAuthorizedError as error:
+ print(error)
+ except Exception as error:
+ self.fail("Wrong exception: %s" % error)
+ else:
+ self.fail("Authorization passed (sic!)")
+ defer.return_value(trans)
+ deferred = run()
+ aptdaemon.loop.mainloop.run()
+ self.assertEqual(deferred.result.error.code,
+ aptdaemon.enums.ERROR_NOT_AUTHORIZED)
+
+
+class ClientTest(aptdaemon.test.AptDaemonTestCase):
+
+ """Test the python client."""
+
+ def setUp(self):
+ """Setup a chroot, run the aptdaemon and a fake PolicyKit daemon."""
+ # Setup chroot
+ self.chroot = aptdaemon.test.Chroot()
+ self.chroot.setup()
+ self.addCleanup(self.chroot.remove)
+ self.chroot.add_test_repository()
+ self.chroot.add_trusted_key()
+ # Start aptdaemon with the chroot on the session bus
+ self.start_dbus_daemon()
+ self.bus = dbus.bus.BusConnection(self.dbus_address)
+ self.start_session_aptd(self.chroot.path)
+ # Start the fake PolikcyKit daemon
+ self.start_fake_polkitd()
+ time.sleep(1)
+
+ def _on_finished(self, trans, exit):
+ """Callback to stop the mainloop after a transaction is done."""
+ aptdaemon.loop.mainloop.quit()
+
+ def test_sync(self):
+ """Test synchronous calls to the client."""
+ self.client = aptdaemon.client.AptClient(self.bus)
+ trans = self.client.update_cache()
+ trans.connect("finished", self._on_finished)
+ trans.run()
+ aptdaemon.loop.mainloop.run()
+ self.assertEqual(trans.exit, aptdaemon.enums.EXIT_SUCCESS)
+
+ def test_deferred(self):
+ """Test deferred calls to the client."""
+ @defer.inline_callbacks
+ def run():
+ self.client = aptdaemon.client.AptClient(self.bus)
+ trans = yield self.client.update_cache()
+ trans.connect("finished", self._on_finished)
+ yield trans.run()
+ defer.return_value(trans)
+ deferred = run()
+ aptdaemon.loop.mainloop.run()
+ self.assertEqual(deferred.result.exit, aptdaemon.enums.EXIT_SUCCESS)
+
+ def test_client_methods_sync(self):
+ """ Test most client methods (syncronous) """
+ test_methods = [
+ ("enable_distro_component", ("universe",)),
+ ("add_repository", ("deb", "http://archive.ubuntu.com/ubuntu",
+ "lucid", "restricted"))]
+ client = aptdaemon.client.AptClient(self.bus)
+ for (method, args) in test_methods:
+ f = getattr(client, method)
+ exit = f(*args, wait=True)
+ self.assertEqual(exit, aptdaemon.enums.EXIT_SUCCESS)
+
+ def test_simulation_error(self):
+ """Test if a simulation fails in a correct way."""
+ @defer.inline_callbacks
+ def run():
+ self.client = aptdaemon.client.AptClient(self.bus)
+ trans = yield self.client.install_packages(["silly-broken"])
+ trans.connect("finished", self._on_finished)
+ yield trans.simulate()
+ self.fail("We should never have been here")
+ yield trans.run()
+ defer.return_value(trans)
+ deferred = run()
+ aptdaemon.loop.mainloop.run()
+ self.assertEqual(deferred.result.value.code,
+ aptdaemon.enums.ERROR_DEP_RESOLUTION_FAILED)
+
+ def test_run_error(self):
+ """Test if a simulation during run fails in a correct way."""
+ @defer.inline_callbacks
+ def run():
+ self.client = aptdaemon.client.AptClient(self.bus)
+ trans = yield self.client.install_packages(["silly-broken"])
+ trans.connect("finished", self._on_finished)
+ yield trans.run()
+ self.fail("We should never have been here")
+ defer.return_value(trans)
+ deferred = run()
+ aptdaemon.loop.mainloop.run()
+ self.assertEqual(deferred.result.value.code,
+ aptdaemon.enums.ERROR_DEP_RESOLUTION_FAILED)
+
+ def test_tid_caching(self):
+ """Test if calling Client with identical TIDs uses caching."""
+
+ tid = "/meep"
+ trans = aptdaemon.client.AptTransaction(tid, bus=self.bus)
+ trans2 = aptdaemon.client.AptTransaction(tid, bus=self.bus)
+ trans3 = aptdaemon.client.AptTransaction("/meep2", bus=self.bus)
+ self.assertEqual(trans, trans2)
+ self.assertNotEqual(trans, trans3)
+
+
+if __name__ == "__main__":
+ if DEBUG:
+ logging.basicConfig(level=logging.DEBUG)
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/test_configfileprompt.py b/tests/test_configfileprompt.py
new file mode 100644
index 0000000..2f1ede4
--- /dev/null
+++ b/tests/test_configfileprompt.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Tests the config file handling."""
+
+import logging
+import os
+import time
+import unittest
+
+import defer
+import dbus
+
+import aptdaemon.client
+import aptdaemon.loop
+import aptdaemon.enums
+
+import aptdaemon.test
+
+DEBUG = True
+REPO_PATH = os.path.join(aptdaemon.test.get_tests_dir(), "repo")
+
+
+class ConfigFilePromptTestCase(aptdaemon.test.AptDaemonTestCase):
+
+ """Test the replacement of config files."""
+
+ def setUp(self):
+ """Setup a chroot, run the aptdaemon and a fake PolicyKit daemon."""
+ # Setup chroot
+ self.chroot = aptdaemon.test.Chroot()
+ self.chroot.setup()
+ self.addCleanup(self.chroot.remove)
+ self.chroot.add_test_repository()
+ # Start aptdaemon with the chroot on the session bus
+ self.start_dbus_daemon()
+ self.bus = dbus.bus.BusConnection(self.dbus_address)
+ self.start_session_aptd(self.chroot.path)
+ # Start the fake PolikcyKit daemon
+ self.start_fake_polkitd()
+ time.sleep(1)
+ self.called = False
+ # Create a fake config file which gets overwritten by silly-config
+ self.config_path = os.path.join(self.chroot.path,
+ "etc/silly-packages.cfg")
+ with open(self.config_path, "w") as config:
+ config.write("BliBlaBlub")
+
+ @defer.inline_callbacks
+ def _on_config_file_conflict(self, trans, config_old, config_new, answer):
+ self.assertEqual(trans.config_file_conflict, (config_old, config_new))
+ if answer == "urgs":
+ # Check if cancelling is forbidden
+ try:
+ yield trans.cancel()
+ except aptdaemon.errors.AptDaemonError as error:
+ self.assertTrue(str(error),
+ "org.debian.apt: Could not cancel transaction")
+ # Check if we fail correctly on wrong answers
+ try:
+ yield trans.resolve_config_file_conflict(config_old,
+ "a&&dasmk")
+ except aptdaemon.errors.AptDaemonError as error:
+ self.assertTrue(str(error).index("Invalid value"))
+ yield trans.resolve_config_file_conflict(config_old, "replace")
+ else:
+ self.fail("Failed to detect invalid answer")
+ else:
+ yield trans.resolve_config_file_conflict(config_old, answer)
+
+ def _on_finished(self, trans, exit):
+ """Callback to stop the mainloop after a transaction is done."""
+ aptdaemon.loop.mainloop.quit()
+
+ def test_keep(self):
+ """Test keeping the current configuration file."""
+ @defer.inline_callbacks
+ def run():
+ self.client = aptdaemon.client.AptClient(self.bus)
+ trans = yield self.client.install_packages(["silly-config"])
+ trans.connect("finished", self._on_finished)
+ trans.connect("config-file-conflict",
+ self._on_config_file_conflict,
+ "keep")
+ yield trans.run()
+ defer.return_value(trans)
+ deferred = run()
+ aptdaemon.loop.mainloop.run()
+ self.assertEqual(deferred.result.exit, aptdaemon.enums.EXIT_SUCCESS)
+ with open(self.config_path) as config:
+ self.assertEqual(config.read(),
+ "BliBlaBlub")
+ with open("%s.dpkg-dist" % self.config_path) as config_dist:
+ self.assertEqual(config_dist.read(),
+ "#Just another config file.\n")
+
+ def test_replace(self):
+ """Test replacing the current configuration file."""
+ @defer.inline_callbacks
+ def run():
+ self.client = aptdaemon.client.AptClient(self.bus)
+ trans = yield self.client.install_packages(["silly-config"])
+ trans.connect("finished", self._on_finished)
+ trans.connect("config-file-conflict",
+ self._on_config_file_conflict,
+ "replace")
+ yield trans.run()
+ defer.return_value(trans)
+ deferred = run()
+ aptdaemon.loop.mainloop.run()
+ self.assertEqual(deferred.result.exit, aptdaemon.enums.EXIT_SUCCESS)
+ with open(self.config_path) as config:
+ self.assertEqual(config.read(),
+ "#Just another config file.\n")
+ with open("%s.dpkg-old" % self.config_path) as config_old:
+ self.assertEqual(config_old.read(),
+ "BliBlaBlub")
+
+ def test_fail(self):
+ """Test failing correctly."""
+ @defer.inline_callbacks
+ def run():
+ self.client = aptdaemon.client.AptClient(self.bus)
+ trans = yield self.client.install_packages(["silly-config"])
+ yield trans.set_locale("C")
+ trans.connect("finished", self._on_finished)
+ trans.connect("config-file-conflict",
+ self._on_config_file_conflict,
+ "urgs")
+ yield trans.run()
+ defer.return_value(trans)
+ deferred = run()
+ aptdaemon.loop.mainloop.run()
+ self.assertEqual(deferred.result.exit, aptdaemon.enums.EXIT_SUCCESS)
+ with open(self.config_path) as config:
+ self.assertEqual(config.read(),
+ "#Just another config file.\n")
+ with open("%s.dpkg-old" % self.config_path) as config_old:
+ self.assertEqual(config_old.read(),
+ "BliBlaBlub")
+
+
+if __name__ == "__main__":
+ if DEBUG:
+ logging.basicConfig(level=logging.DEBUG)
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/test_configparser.py b/tests/test_configparser.py
new file mode 100644
index 0000000..006404b
--- /dev/null
+++ b/tests/test_configparser.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Provides unit tests for the APT configuration file parser"""
+# Copyright (C) 2010 Sebastian Heinlein <devel@glatzor.de>
+#
+# Licensed under the GNU General Public License Version 2
+#
+# This program 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.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Licensed under the GNU General Public License Version 2
+
+__author__ = "Sebastian Heinlein <devel@glatzor.de>"
+
+import os
+import sys
+import unittest
+
+import apt_pkg
+
+from aptdaemon.config import ConfigWriter
+
+
+class ConfigurationParserTestCase(unittest.TestCase):
+
+ """Test suite for the configuration parser."""
+
+ def setUp(self):
+ self.parser = ConfigWriter()
+
+ def test_comment_in_value(self):
+ """ ensure that comment strings in values are parsed correctly """
+ s = """// Server information for apt-changelog
+ APT {
+ Changelogs { # bar
+ Server "http://changelogs.ubuntu.com/changelogs"; // foo
+ }
+ }
+ """
+ cf = self.parser.parse(s.split("\n"))
+ self.assertEqual(cf["apt::changelogs::server"].string,
+ "http://changelogs.ubuntu.com/changelogs")
+
+ def test_multi_line_comments(self):
+ s = """/*
+ * APT configuration file for Zope Debian packages.
+ */
+
+DPkg {
+ Post-Invoke {"which dzhandle";};
+}
+ """
+ cf = self.parser.parse(s.split("\n"))
+ self.assertEqual(cf["dpkg::post-invoke"][0].string, "which dzhandle")
+
+ def test_(self):
+ config = {}
+ config_check = {}
+
+ for filename in os.listdir("/etc/apt/apt.conf.d"):
+ path = "/etc/apt/apt.conf.d/%s" % filename
+ config_apt = apt_pkg.Configuration()
+ with open(path, "r") as fd:
+ apt_pkg.read_config_file(config_apt, path)
+ config = self.parser.parse(fd.readlines())
+ for key in config_apt.keys():
+ if key.endswith("::"):
+ key = key[:-2]
+ value_list_apt = config_apt.value_list(key)
+ if value_list_apt:
+ value_list = [val.string for val in
+ config[key.lower()]]
+ self.assertTrue(value_list_apt == value_list,
+ "%s: %s != %s" % (key, value_list_apt,
+ value_list))
+ else:
+ value_apt = config_apt[key]
+ if value_apt:
+ self.assertTrue(
+ value_apt == config[key.lower()].string)
+
+
+@unittest.skipIf(sys.version_info.major < 3, "Only Python3")
+def setUp():
+ pass
+
+if __name__ == "__main__":
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/test_dbus_type.py b/tests/test_dbus_type.py
new file mode 100644
index 0000000..b7ffcac
--- /dev/null
+++ b/tests/test_dbus_type.py
@@ -0,0 +1,196 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Test index handling."""
+
+from collections import namedtuple
+import os
+import re
+import sys
+import unittest
+
+import dbus
+from gi.repository import GLib
+
+from aptdaemon.worker import aptworker
+from aptdaemon import core
+from aptdaemon import enums
+from aptdaemon import test
+
+REGEX_SIG = "([ibxsdt])|(a{[ibxsdt]+?})|(a[ixbsdt]+?)|(\([ibxsdt]+?\))"
+REGEX_IFACE = r"\n(org\.debian\.apt[a-z\.]*) --- "
+REGEX_ATTRIB = (r"\n\.\.\s+attribute::\s+(?P<name>[a-zA-Z]+)\s+:\s+"
+ "(?P<sig>[a-z\(\)\{\}]+)")
+
+# Setup the DBus main loop
+dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+DOC_PATH = os.path.join(test.get_tests_dir(), "../doc")
+
+
+class DBusTypeTest(test.AptDaemonTestCase):
+
+ """Make sure that the specified types are returned over D-Bus."""
+
+ def setUp(self):
+ # Extract the property type specification from the documentation
+ self.ifaces = {}
+ with open(os.path.join(DOC_PATH, "source/dbus.rst")) as rst_file:
+ docu = rst_file.read()
+ doc = ""
+ iface = ""
+ for match in re.split(REGEX_IFACE, docu, re.MULTILINE):
+ if match.startswith("org.debian.apt"):
+ iface = match
+ self.ifaces[iface] = {}
+ doc = ""
+ else:
+ doc = match
+ if doc and iface:
+ for match_attrib in re.finditer(REGEX_ATTRIB, doc):
+ name = match_attrib.group("name")
+ sig = match_attrib.group("sig")
+ self.ifaces[iface][name] = sig
+ self.start_dbus_daemon()
+ self.dbus = dbus.bus.BusConnection(self.dbus_address)
+ self.loop = GLib.MainLoop()
+ self.error = None
+
+ def _on_property_changed(self, name, value, iface):
+ if name == "Progress" and value == 100:
+ self.loop.quit()
+ try:
+ self._check_property_type(iface, name, value)
+ except:
+ self.loop.quit()
+ raise
+
+ def _check_property_type(self, iface, name, value, signature=None):
+ if signature is None:
+ signature = self.ifaces[iface][name]
+ if isinstance(value, dbus.String):
+ self.assertEqual(signature, "s",
+ "Property %s on %s doesnt' comply with the "
+ "spec: %s" % (name, iface, value))
+ elif isinstance(value, dbus.String):
+ self.assertEqual(signature, "s",
+ "Property %s on %s doesnt' comply with the "
+ "spec: %s" % (name, iface, value))
+ elif isinstance(value, dbus.Int32):
+ self.assertEqual(signature, "i",
+ "Property %s on %s doesnt' comply with the "
+ "spec: %s" % (name, iface, value))
+ elif isinstance(value, dbus.Int64):
+ self.assertEqual(signature, "x",
+ "Property %s on %s doesnt' comply with the "
+ "spec: %s" % (name, iface, value))
+ elif isinstance(value, dbus.UInt64):
+ self.assertEqual(signature, "t",
+ "Property %s on %s doesnt' comply with the "
+ "spec: %s" % (name, iface, value))
+ elif isinstance(value, dbus.Double):
+ self.assertEqual(signature, "d",
+ "Property %s on %s doesnt' comply with the "
+ "spec: %s" % (name, iface, value))
+ elif isinstance(value, dbus.Boolean):
+ self.assertEqual(signature, "b",
+ "Property %s on %s doesnt' comply with the "
+ "spec: %s" % (name, iface, value))
+ elif isinstance(value, dbus.Dictionary):
+ self.assertEqual(signature, "a{%s}" % value.signature,
+ "Property %s on %s doesnt' comply with the "
+ "spec: %s" % (name, iface, value))
+ elif isinstance(value, dbus.Struct):
+ if value.signature:
+ self.assertEqual(signature, "s(%s)" % value.signature,
+ "Property %s on %s doesnt' comply with the "
+ "spec: %s" % (name, iface, value))
+ else:
+ # The dbus proxy doesn't set the signature property
+ for val, sig in map(lambda x, y: (x, y), value,
+ ["".join(matches) for matches in
+ re.findall(REGEX_SIG, signature[1:-1])]):
+ self._check_property_type(iface, name, val, sig)
+ elif isinstance(value, dbus.Array):
+ self.assertEqual(signature, "a%s" % value.signature,
+ "Property %s on %s doesnt' comply with the "
+ "spec: %s" % (name, iface, value))
+ else:
+ raise Exception("Unkown type %s for property %s of %s" %
+ (type(value), name, iface))
+
+ def _error_cb(self, error):
+ """Errback of the GetAll call."""
+ self.loop.quit()
+ raise error
+
+ def _get_all_cb(self, iface, props):
+ """Callback of the GetAll call."""
+ try:
+ for name, value in props.items():
+ self._check_property_type(iface, name, value)
+ except Exception as error:
+ self.error = error
+ raise
+ finally:
+ self.loop.quit()
+
+ @unittest.skip("Requires to be convert to a C based test client")
+ def test_transaction_properties(self):
+ """Test object properties."""
+ trans = core.Transaction(None, enums.ROLE_REMOVE_PACKAGES, None,
+ os.getpid(), os.getuid(), sys.argv[0],
+ "org.debian.apt.test", bus=self.dbus)
+ proxy = self.dbus.get_object(core.APTDAEMON_DBUS_INTERFACE,
+ trans.tid)
+ iface = core.APTDAEMON_TRANSACTION_DBUS_INTERFACE
+ proxy.GetAll(iface,
+ reply_handler=lambda x: self._get_all_cb(iface, x),
+ error_handler=self._error_cb,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ self.loop.run()
+ self.assertEqual(self.error, None, self.error)
+
+ @unittest.skip("Requires to be convert to a C based test client")
+ def test_transaction_signals(self):
+ """Test signal emittion."""
+ trans = core.Transaction(None, enums.ROLE_COMMIT_PACKAGES, None,
+ os.getpid(), os.getuid(), sys.argv[0],
+ "org.debian.apt.test", bus=self.dbus,
+ packages=[["silly-base"], [], [], [], [], []])
+ proxy = self.dbus.get_object("org.debian.apt", trans.tid)
+ proxy.connect_to_signal("PropertyChanged",
+ self._on_property_changed,
+ dbus_interface="org.debian.apt.transaction",
+ interface_keyword="iface")
+ chroot = test.Chroot()
+ self.addCleanup(chroot.remove)
+ chroot.setup()
+ chroot.add_test_repository()
+ apt_worker = aptworker.AptWorker(load_plugins=False,
+ chroot=chroot.path)
+ apt_worker.run(trans)
+ self.loop.run()
+ self.assertEqual(self.error, None)
+
+ @unittest.skip("Requires to be convert to a C based test client")
+ def test_aptdaemon_properties(self):
+ """Test aptdaemon properties."""
+ Options = namedtuple("Options", "dummy")
+ opt = Options(True)
+ self.daemon = core.AptDaemon(opt, bus=self.dbus)
+
+ proxy = self.dbus.get_object(core.APTDAEMON_DBUS_SERVICE,
+ core.APTDAEMON_DBUS_PATH)
+ iface = core.APTDAEMON_DBUS_INTERFACE
+ proxy.GetAll(iface,
+ reply_handler=lambda x: self._get_all_cb(iface, x),
+ error_handler=self._error_cb,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ self.loop.run()
+ self.assertEqual(self.error, None)
+
+
+if __name__ == "__main__":
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/test_debconf.py b/tests/test_debconf.py
new file mode 100644
index 0000000..eca31cd
--- /dev/null
+++ b/tests/test_debconf.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Tests the debconf forwarding"""
+
+import logging
+import os
+import subprocess
+import sys
+import tempfile
+import unittest
+
+import apt_pkg
+from gi.repository import GLib
+
+from aptdaemon import test
+from aptdaemon.debconf import DebconfProxy
+
+DEBUG = False
+
+
+class DebconfTestBasic(unittest.TestCase):
+
+ def _stop(self):
+ self.proxy.stop()
+ self.loop.quit()
+
+ def setUp(self):
+ self.loop = GLib.MainLoop()
+ self.debconf_socket_path = tempfile.mktemp(prefix="debconf-socket-")
+ self._set_input_value()
+ self.proxy = DebconfProxy("editor", self.debconf_socket_path)
+ self.proxy.start()
+
+ def _set_input_value(self, template="aptdaemon/test", value="lalelu"):
+ os.environ["DEBIAN_PRIORITY"] = "high"
+ os.environ["EDITOR"] = "sed -ie 's/\\(%s=\\).*/\\1\\\"%s\\\"/i'" % \
+ (template.replace("/", "\\/"), value)
+
+ def _spawn_config_script(self, config_db_path, command=None):
+ if command is None:
+ command = [os.path.join(test.get_tests_dir(),
+ "debconf/aptdaemon.config")]
+ env = {}
+ env["DEBCONF_DB_REPLACE"] = "File{%s}" % config_db_path
+ env["DEBIAN_FRONTEND"] = "passthrough"
+ env["DEBCONF_PIPE"] = self.debconf_socket_path
+ if DEBUG:
+ env["DEBCONF_DEBUG"] = ".*"
+ env_str = ["%s=%s" % (key, val) for key, val in env.items()]
+
+ proc = subprocess.Popen(command, env=env)
+ return proc
+
+ def testBasic(self):
+ def config_done(pid, cond):
+ self.assertEqual(cond, 0,
+ "Config script failed: %s" % os.WEXITSTATUS(cond))
+ self._stop()
+ debconf_db_path = tempfile.mktemp(suffix=".dat",
+ prefix="config-basic-")
+ proc = self._spawn_config_script(debconf_db_path)
+ GLib.child_watch_add(GLib.PRIORITY_DEFAULT, proc.pid, config_done)
+ self.loop.run()
+ # Check the results
+ self._check_value(debconf_db_path)
+
+ @unittest.skipIf(sys.version_info.major < 3 and "nose" in sys.modules,
+ "For unknown reasons lets other tests fail "
+ "(test_simulate) if performed under Python2 and nose")
+ def testSerial(self):
+ """Run several config scripts in a row."""
+ def config_done(pid, cond):
+ self.assertEqual(cond, 0,
+ "Config script failed: %s" % os.WEXITSTATUS(cond))
+ self.config_scripts -= 1
+ if self.config_scripts <= 0:
+ self._stop()
+ else:
+ proc = self._spawn_config_script(debconf_db_path)
+ GLib.child_watch_add(GLib.PRIORITY_DEFAULT,
+ proc.pid, config_done)
+ debconf_db_path = tempfile.mktemp(suffix=".dat", prefix="config-row-")
+ self.config_scripts = 10
+ proc = self._spawn_config_script(debconf_db_path)
+ GLib.child_watch_add(GLib.PRIORITY_DEFAULT, proc.pid, config_done)
+ self.loop.run()
+ # Check the results
+ self._check_value(debconf_db_path)
+
+ def testRace(self):
+ def config_done(pid, cond):
+ self.assertEqual(cond, 0,
+ "Config script failed: %s" % os.WEXITSTATUS(cond))
+ self.workers -= 1
+ if self.workers <= 0:
+ self._stop()
+ debconf_dbs = []
+ self.workers = 0
+ for i in range(10):
+ debconf_db_path = tempfile.mktemp(suffix=".dat",
+ prefix="config-race-")
+ proc = self._spawn_config_script(debconf_db_path)
+ GLib.child_watch_add(GLib.PRIORITY_DEFAULT, proc.pid, config_done)
+ debconf_dbs.append(debconf_db_path)
+ self.workers += 1
+ self.loop.run()
+ # Check the results
+ for db_path in debconf_dbs:
+ self._check_value(db_path)
+
+ def _check_value(self, db_path, template=None, value="lalelu"):
+ with open(db_path) as db_file:
+ for section in apt_pkg.TagFile(db_file):
+ if template == section["Template"] or template is None:
+ self.assertEqual(section["Value"], value)
+ return
+ os.remove(db_path)
+ self.fail("Database doesn't contain any matching value or template")
+
+ def tearDown(self):
+ os.remove(self.debconf_socket_path)
+ self.proxy = None
+ self.loop.quit()
+ self.loop = None
+
+
+if __name__ == "__main__":
+ if DEBUG:
+ logging.basicConfig(level=logging.DEBUG)
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/test_gtk3widgets.py b/tests/test_gtk3widgets.py
new file mode 100644
index 0000000..ca3d73f
--- /dev/null
+++ b/tests/test_gtk3widgets.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+
+"""Test gtk3widgets.py."""
+
+import os
+import codecs
+import shutil
+import tempfile
+import unittest
+
+from aptdaemon.gtk3widgets import DiffView
+
+
+class TestLP1120322(unittest.TestCase):
+
+ def setUp(self):
+ tempdir = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, tempdir)
+ self.a = os.path.join(tempdir, 'a.txt')
+ self.b = os.path.join(tempdir, 'b.txt')
+ with codecs.open(self.a, 'w', encoding='utf-8') as f:
+ f.write('one\n')
+ with codecs.open(self.b, 'w', encoding='utf-8') as f:
+ f.write('onee\n')
+
+ def test_lp_1120322(self):
+ # UnboundLocalError when the diff is one line long.
+ dv = DiffView()
+ # This simply should not traceback.
+ dv.show_diff(self.a, self.b)
+
+
+class TestGoodPath(unittest.TestCase):
+
+ def setUp(self):
+ tempdir = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, tempdir)
+ self.a = os.path.join(tempdir, 'a.txt')
+ self.b = os.path.join(tempdir, 'b.txt')
+ with codecs.open(self.a, 'w', encoding='utf-8') as f:
+ f.write('one\ntwo\n')
+ with codecs.open(self.b, 'w', encoding='utf-8') as f:
+ f.write('one\ntoo\n')
+
+ def test_lp_1120322(self):
+ # UnboundLocalError when the diff is multiple lines long.
+ dv = DiffView()
+ # This simply should not traceback.
+ dv.show_diff(self.a, self.b)
diff --git a/tests/test_high_trust_repository_whitelist.py b/tests/test_high_trust_repository_whitelist.py
new file mode 100644
index 0000000..38da3c5
--- /dev/null
+++ b/tests/test_high_trust_repository_whitelist.py
@@ -0,0 +1,200 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Provides unit tests for the APTDAEMON high-trust-repo feature."""
+# Copyright (C) 2011 Sebastian Heinlein <devel@glatzor.de>
+#
+# Licensed under the GNU General Public License Version 2
+#
+# This program 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.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Licensed under the GNU General Public License Version 2
+
+__author__ = "Michael Vogt <michael.vogt@ubuntu.com>"
+
+import os
+import sys
+import time
+import unittest
+
+import dbus
+from gi.repository import GLib
+from mock import (
+ patch)
+
+import aptdaemon.client
+from aptdaemon.policykit1 import (
+ PK_ACTION_INSTALL_PACKAGES_FROM_HIGH_TRUST_REPO as PK_ACTION)
+import aptdaemon.test
+
+from aptdaemon.worker.aptworker import (
+ _read_high_trust_repository_whitelist_file,
+ read_high_trust_repository_dir,
+ trans_only_installs_pkgs_from_high_trust_repos,
+ AptWorker)
+from aptdaemon.core import Transaction
+from aptdaemon import enums
+
+
+REPO_PATH = os.path.join(aptdaemon.test.get_tests_dir(), "repo")
+
+PY3K = sys.version_info.major > 2
+
+
+class BaseHighTrustTestCase(aptdaemon.test.AptDaemonTestCase):
+
+ def setUp(self):
+ self.chroot = aptdaemon.test.Chroot()
+ self.chroot.setup()
+ self.addCleanup(self.chroot.remove)
+ self.loop = GLib.MainLoop()
+
+
+class HighTrustRepositoryTestCase(BaseHighTrustTestCase):
+
+ """ Test the worker low-level bits of the high-trust repo implementation"""
+
+ def setUp(self):
+ super(HighTrustRepositoryTestCase, self).setUp()
+ self.queue = aptdaemon.test.MockQueue()
+ self.worker = AptWorker(chroot=self.chroot.path, load_plugins=False)
+ self.worker.connect("transaction-done", lambda w, t: self.loop.quit())
+ self.worker.connect("transaction-simulated",
+ lambda w, t: self.loop.quit())
+
+ def test_read_high_trust_repository_whitelist_dir(self):
+ whitelist = read_high_trust_repository_dir(
+ os.path.join(aptdaemon.test.get_tests_dir(),
+ "data/high-trust-repository-whitelist.d"))
+ self.assertEqual(
+ whitelist, set([("Ubuntu", "main", "foo.*"),
+ ("Debian-Security", "non-free", "^bar$")]))
+
+ def test_read_high_trust_repository_whitelist(self):
+ whitelist = _read_high_trust_repository_whitelist_file(
+ os.path.join(aptdaemon.test.get_tests_dir(),
+ "data/high-trust-repository-whitelist.cfg"))
+ self.assertEqual(
+ whitelist, set([("Ubuntu", "main", "foo.*"),
+ ("Debian-Security", "non-free", "^bar$")]))
+
+ @patch("aptdaemon.worker.log")
+ def test_read_high_trust_repository_whitelist_broken(self, mock_log):
+ """ test that a broken repo file results in a empty whitelist """
+ whitelist = _read_high_trust_repository_whitelist_file(
+ os.path.join(aptdaemon.test.get_tests_dir(),
+ "data/high-trust-repository-whitelist-broken.cfg"))
+ self.assertEqual(whitelist, set())
+ # ensure we log a error if the config file is broken
+ # Skip due to LP: #1487087
+ #mock_log.error.assert_called()
+
+ @patch("aptdaemon.worker.log")
+ def test_read_high_trust_repository_whitelist_not_there(self, mock_log):
+ whitelist = _read_high_trust_repository_whitelist_file(
+ "lalalala-not-there-really.cfg")
+ self.assertEqual(whitelist, set())
+ # ensure we log no error if there is no config file
+ self.assertFalse(mock_log.called)
+
+ def test_high_trust_repository(self):
+ """Test if using a high_trust repo is working """
+ self.chroot.add_repository(os.path.join(aptdaemon.test.get_tests_dir(),
+ "repo/whitelisted"))
+ # setup a whitelist
+ self.worker._high_trust_repositories.add(
+ ("Ubuntu", "", "silly.*"))
+ # a high-trust whitelisted pkg and a non-whitelisted one
+ trans = Transaction(None, enums.ROLE_INSTALL_PACKAGES, self.queue,
+ os.getpid(), os.getuid(), os.getgid(), sys.argv[0],
+ "org.debian.apt.test", connect=False,
+ packages=[["silly-base", "other-pkg"], [], [], [],
+ [], []])
+ self.worker.simulate(trans)
+ self.loop.run()
+ self.assertEqual(trans.high_trust_packages, ["silly-base"])
+ self.assertFalse(
+ trans_only_installs_pkgs_from_high_trust_repos(
+ trans, self.worker._high_trust_repositories))
+ # whitelisted only
+ trans = Transaction(None, enums.ROLE_INSTALL_PACKAGES, self.queue,
+ os.getpid(), os.getuid(), os.getgid(), sys.argv[0],
+ "org.debian.apt.test", connect=False,
+ packages=[["silly-base"], [], [], [], [], []])
+ self.worker.simulate(trans)
+ self.loop.run()
+ self.assertTrue(
+ trans_only_installs_pkgs_from_high_trust_repos(
+ trans, self.worker._high_trust_repositories))
+
+
+class HighTrustRepositoryIntegrationTestCase(BaseHighTrustTestCase):
+ """ Test the whitelist feature inside the chroot """
+
+ def setUp(self):
+ super(HighTrustRepositoryIntegrationTestCase, self).setUp()
+ # Start aptdaemon with the chroot on the session bus
+ self.start_dbus_daemon()
+ self.bus = dbus.bus.BusConnection(self.dbus_address)
+ # setup the environment first including the high-trust whitelist
+ self.chroot.add_repository(os.path.join(aptdaemon.test.get_tests_dir(),
+ "repo/whitelisted"))
+ whitelist_file = os.path.join(
+ self.chroot.path, "etc", "aptdaemon",
+ "high-trust-repository-whitelist.d", "test.cfg")
+ os.makedirs(os.path.dirname(whitelist_file))
+
+ with open(whitelist_file, "w") as f:
+ f.write("""
+[test repo"]
+origin = Ubuntu
+component =
+pkgnames = silly.*
+""")
+ # *after* that start the aptdaemon
+ self.start_session_aptd(self.chroot.path)
+ time.sleep(1)
+ # start policykit and *only* allow from-whitelisted repo pk action
+ self.start_fake_polkitd(PK_ACTION)
+ time.sleep(1)
+
+ def test_high_trust_polkit_ok(self):
+ self.client = aptdaemon.client.AptClient(self.bus)
+ # test that the high trust whitelist works
+ trans = self.client.install_packages(["silly-base"])
+ trans.simulate()
+ trans.connect("finished", lambda a, b: self.loop.quit())
+ trans.run()
+ self.loop.run()
+ self.assertEqual(trans.exit, aptdaemon.enums.EXIT_SUCCESS)
+ # plus ensure removal will not work
+ trans = self.client.remove_packages(["silly-base"])
+ with self.assertRaises(aptdaemon.errors.NotAuthorizedError):
+ trans.run()
+
+ def test_high_trust_polkit_not_ok(self):
+ self.client = aptdaemon.client.AptClient(self.bus)
+ # ensure that non-whitelisted packages can not be installed
+ trans = self.client.install_packages(["other-pkg"])
+ trans.simulate()
+ trans.connect("finished", lambda a, b: self.loop.quit())
+ with self.assertRaises(aptdaemon.errors.NotAuthorizedError):
+ trans.run()
+
+
+if __name__ == "__main__":
+ # import logging
+ # logging.basicConfig(level=logging.DEBUG)
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/test_index.py b/tests/test_index.py
new file mode 100644
index 0000000..52c2717
--- /dev/null
+++ b/tests/test_index.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Test index handling."""
+
+import os.path
+
+import apt
+import apt_pkg
+import unittest
+
+import aptdaemon.test
+
+
+class IndexRaceTest(unittest.TestCase):
+
+ """If the indexes are deleted or manipulated at the time
+ apt.Cache.required_download was called we get the following error:
+ SystemError: I wasn't able to locate file for the XXX package.
+ This might mean you need to manually fix this package.
+
+ See lp:#659438
+ """
+
+ def setUp(self):
+ self.chroot = aptdaemon.test.Chroot()
+ self.chroot.setup()
+ self.chroot.add_test_repository()
+ # Check if installing an uninstalled package works
+ self.cache = apt.Cache(rootdir=self.chroot.path)
+ self.cache["silly-base"].mark_install()
+ self.assertEqual(self.cache.required_download, 0)
+ self.cache.clear()
+
+ def test(self):
+ lists_path = apt_pkg.config.find_dir("Dir::State::Lists")
+ for file_name in os.listdir(lists_path):
+ if file_name.endswith("Packages"):
+ os.remove(os.path.join(lists_path, file_name))
+ self.cache["silly-base"].mark_install()
+ self.assertRaises(SystemError,
+ lambda: self.cache.required_download)
+# self.cache.required_download
+
+
+if __name__ == "__main__":
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/test_lock.py b/tests/test_lock.py
new file mode 100644
index 0000000..d7aa000
--- /dev/null
+++ b/tests/test_lock.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Tests locking."""
+
+import logging
+import os
+import socket
+import subprocess
+import sys
+import unittest
+
+import aptdaemon.test
+
+import aptdaemon.worker.aptworker
+
+DEBUG = False
+
+import apt_pkg
+apt_pkg.init()
+
+
+class LockTest(unittest.TestCase):
+
+ REMOTE_REPO = "deb copy://%s/repo ./" % aptdaemon.test.get_tests_dir()
+
+ def setUp(self):
+ self.chroot = aptdaemon.test.Chroot("-lock-test")
+ self.chroot.setup()
+ self.addCleanup(self.chroot.remove)
+ # Required to change the lock pathes to the chroot
+ self.worker = aptdaemon.worker.aptworker.AptWorker(
+ chroot=self.chroot.path,
+ load_plugins=False)
+ pkg_path = os.path.join(aptdaemon.test.get_tests_dir(),
+ "repo/silly-base_0.1-0_all.deb")
+ self.dpkg_cmd = ["fakeroot", "dpkg", "--root", self.chroot.path,
+ "--log=%s/var/log/dpkg.log" % self.chroot.path,
+ "--install", pkg_path]
+ self.inst_cmd = ('apt-get install silly-base '
+ '-o "Dir"="%s" '
+ '-o "Dir::state::status"="%s/var/lib/dpkg/status" '
+ '-o "Dir::Bin::Dpkg"="%s/dpkg-wrapper.sh" '
+ '-o "DPkg::Options::"="--root=%s" -y --force-yes' %
+ (self.chroot.path, self.chroot.path,
+ aptdaemon.test.get_tests_dir(), self.chroot.path))
+ self.apt_cmd = ('apt-get update -o "Dir"="%s" -o "Dir::state::status="'
+ '"%s/var/lib/dpkg/status"' %
+ (self.chroot.path, self.chroot.path))
+ # ensure to kill /etc/apt/apt.conf.d, otherwise stuff like
+ # the (root only) Dpkg::Post-Invoke actions are run
+ with open("%s/etc/apt/apt.conf" % self.chroot.path, "w") as conf:
+ conf.write('Dir::Etc::parts "/directory-does-not-exist";')
+ self.env = {
+ # override the default apt conf to kill off apt.conf.d includes
+ "APT_CONFIG": "%s/etc/apt/apt.conf" % self.chroot.path,
+ # provide a path for dpkg
+ "PATH": "/sbin:/bin:/usr/bin:/usr/sbin"}
+
+ def test_global_lock(self):
+ """Check if the lock blocks dpkg and apt-get."""
+ # Lock!
+ aptdaemon.worker.aptworker.lock.acquire()
+ self.assertEqual(2, subprocess.call(self.dpkg_cmd, env=self.env))
+ self.assertEqual(100, subprocess.call(self.apt_cmd, env=self.env,
+ shell=True))
+ # Relase and all should work again!
+ aptdaemon.worker.aptworker.lock.release()
+ self.assertEqual(0, subprocess.call(self.dpkg_cmd, env=self.env))
+ self.assertEqual(0, subprocess.call(self.apt_cmd, env=self.env,
+ shell=True))
+
+ def test_status_lock(self):
+ """Test the lock on the status lock."""
+ # Lock!
+ aptdaemon.worker.aptworker.lock.status_lock.acquire()
+ self.assertEqual(2, subprocess.call(self.dpkg_cmd, env=self.env))
+ self.assertEqual(0, subprocess.call(self.apt_cmd, env=self.env,
+ shell=True))
+ # Relase and all should work again!
+ aptdaemon.worker.aptworker.lock.status_lock.release()
+ self.assertEqual(0, subprocess.call(self.dpkg_cmd, env=self.env))
+ self.assertEqual(0, subprocess.call(self.apt_cmd, env=self.env,
+ shell=True))
+
+ def test_lists_lock(self):
+ """Test the lock on the repository packages lists."""
+ # Lock!
+ aptdaemon.worker.aptworker.lock.lists_lock.acquire()
+ # Dpkg doesn't care about the lock
+ self.assertEqual(0, subprocess.call(self.dpkg_cmd, env=self.env))
+ self.assertEqual(100, subprocess.call(self.apt_cmd, env=self.env,
+ shell=True))
+ # Relase and all should work again!
+ aptdaemon.worker.aptworker.lock.lists_lock.release()
+ self.assertEqual(0, subprocess.call(self.apt_cmd, env=self.env,
+ shell=True))
+
+ def test_archives_lock(self):
+ """Test the lock on the download archives."""
+ # Skip the test if we don't have networking
+ aptdaemon.worker.aptworker.lock.archive_lock.acquire()
+ lst_path = os.path.join(self.chroot.path, "etc/apt/sources.list")
+ with open(lst_path, "w") as lst_file:
+ lst_file.write(self.REMOTE_REPO)
+ # Dpkg and apt-get doen't care about the lock as long as there aren't
+ # any downloads required
+ self.assertEqual(0, subprocess.call(self.dpkg_cmd, env=self.env))
+ self.assertEqual(100, subprocess.call(self.apt_cmd, env=self.env,
+ shell=True))
+ self.assertEqual(100, subprocess.call(self.inst_cmd, env=self.env,
+ shell=True))
+ # Relase and all should work again!
+ aptdaemon.worker.aptworker.lock.archive_lock.release()
+ self.assertEqual(0, subprocess.call(self.inst_cmd, env=self.env,
+ shell=True))
+
+
+@unittest.skipIf(sys.version_info.major < 3, "Python 3 only")
+def setUp():
+ pass
+
+if __name__ == "__main__":
+ if DEBUG:
+ logging.basicConfig(level=logging.DEBUG)
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/test_lock_location.py b/tests/test_lock_location.py
new file mode 100644
index 0000000..7f11b8a
--- /dev/null
+++ b/tests/test_lock_location.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Tests locking."""
+
+import imp
+import logging
+import os
+import subprocess
+import sys
+import unittest
+
+DEBUG = False
+
+
+class LockFileLocationTest(unittest.TestCase):
+
+ def test_lock_file_location(self):
+ """Make sure that the correct lock files are used."""
+ # Make sure that the lock module is reloaded if called from
+ # within a test suite (to ensure no stale apt_pkg.config values
+ # hanging around)
+ import aptdaemon.lock
+ imp.reload(aptdaemon.lock)
+ # the actual test
+ self.assertEqual(aptdaemon.lock.status_lock.path,
+ os.path.join(os.path.dirname(self.STATUS_PATH),
+ "lock"))
+ self.assertEqual(aptdaemon.lock.lists_lock.path,
+ os.path.join(self.LISTS_PATH, "lock"))
+ self.assertEqual(aptdaemon.lock.archive_lock.path,
+ os.path.join(self.ARCHIVES_PATH, "lock"))
+
+ def setUp(self):
+ """Extract the currently used pathes."""
+ for var, lock in [("self.STATUS_PATH", "'dir::state::status'/f"),
+ ("self.LISTS_PATH", "'dir::state::lists'/d"),
+ ("self.ARCHIVES_PATH", "'dir::cache::archives'/d")]:
+ cmd = subprocess.Popen("/usr/bin/apt-config shell %s %s" % (var,
+ lock),
+ shell=True, stdout=subprocess.PIPE)
+ exec(cmd.communicate()[0])
+
+
+@unittest.skipIf(sys.version_info.major < 3, "Only Python 3")
+def setUp():
+ pass
+
+if __name__ == "__main__":
+ if DEBUG:
+ logging.basicConfig(level=logging.DEBUG)
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/test_pep8.py b/tests/test_pep8.py
new file mode 100644
index 0000000..42faaaa
--- /dev/null
+++ b/tests/test_pep8.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Make sure that the code conforms the PEP8 conventions."""
+# Copyright (C) 2012 Sebastian Heinlein <devel@glatzor.de>
+#
+# Licensed under the GNU General Public License Version 2
+#
+# This program 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.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Licensed under the GNU General Public License Version 2
+
+__author__ = "Sebastian Heinlein <devel@glatzor.de>"
+
+import subprocess
+import unittest
+
+
+@unittest.skip("Does not work")
+class AptDaemonPep8TestCase(unittest.TestCase):
+
+ def test(self):
+ """Check if the source code matches the PEP8 style conventions."""
+ subprocess.check_call([
+ "pep8", "--statistics", "--show-source",
+ "--show-pep8", "--exclude",
+ "pkenums.py,aptdaemon,tests,debian,doc,.pc,gtk3-demo.py,setup.py",
+ "--ignore=E402"])
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_pk.py b/tests/test_pk.py
new file mode 100644
index 0000000..1c73632
--- /dev/null
+++ b/tests/test_pk.py
@@ -0,0 +1,601 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Tests the PackageKit compatibility layer.
+
+Since the PackageKit client doesn't support changing system D-Bus sockets,
+run the tests only by using nose (e.g. nosetests3 tests.test_pk.PackageKitTest)
+or by running the main routine (python3 test_pk.py).
+
+If a test fails all subsequent ones will fail, too. You will get the following
+error message:
+gi._glib.GError: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown:
+The name :1.16 was not provided by any .service files
+"""
+
+import atexit
+import logging
+import os
+import os.path
+import shutil
+import subprocess
+import tempfile
+import time
+import sys
+import unittest
+
+import apt_pkg
+import apt.auth
+from gi.repository import GLib
+from gi.repository import PackageKitGlib as pk
+
+import aptdaemon.test
+
+REPO_PATH = os.path.join(aptdaemon.test.get_tests_dir(), "repo")
+DEBUG = True
+
+
+@unittest.skip("Removed PackageKit compat")
+class PackageKitTest(aptdaemon.test.AptDaemonTestCase):
+
+ """Test the PackageKit compatibility layer."""
+
+ def setUp(self):
+ """Setup a chroot, run the aptdaemon and a fake PolicyKit daemon."""
+ # Setup chroot
+ self.chroot = aptdaemon.test.Chroot()
+ self.chroot.setup()
+ self.addCleanup(self.chroot.remove)
+ self.chroot.add_test_repository()
+ # set up scratch dir
+ self.workdir = tempfile.mkdtemp()
+ # allow tests to add plugins, etc.
+ self.orig_pythonpath = os.environ.get("PYTHONPATH")
+ os.environ["PYTHONPATH"] = "%s:%s" % (self.workdir,
+ os.environ.get("PYTHONPATH", ""))
+ # write apt config for calling apt-key
+ apt_conf = os.path.join(self.chroot.path, 'aptconfig')
+ with open(apt_conf, 'w') as f:
+ f.write('Dir "%s";\n' % self.chroot.path)
+ os.environ['APT_CONFIG'] = apt_conf
+
+ # if tests install keys, have aptd query a local fake server
+ os.environ['APTDAEMON_KEYSERVER'] = 'hkp://localhost:19191'
+
+ self.start_session_aptd(self.chroot.path)
+ # Start the fake PolikcyKit daemon
+ self.start_fake_polkitd()
+ time.sleep(2.0)
+
+ def tearDown(self):
+ shutil.rmtree(self.workdir)
+ if self.orig_pythonpath:
+ os.environ["PYTHONPATH"] = self.orig_pythonpath
+
+ def test_install(self):
+ """Test installing a package."""
+ pkg_name = "silly-depend-base"
+
+ client = pk.Client()
+
+ # Resolve the id of the package
+ res = client.resolve(pk.FilterEnum.NONE, [pkg_name], None,
+ lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ ids = []
+ for pkg in res.get_package_array():
+ self.assertEqual(pkg.get_name(), pkg_name)
+ ids.append(pkg.get_id())
+ break
+ else:
+ self.fail("Failed to resolve %s" % pkg_name)
+
+ # Simulate
+ res = client.install_packages(2 ** pk.TransactionFlagEnum.SIMULATE,
+ ids, None, lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ for pkg in res.get_package_array():
+ self.assertEqual(pkg.get_name(), "silly-base")
+ self.assertEqual(pkg.get_info(),
+ pk.info_enum_from_string("installing"))
+ break
+ else:
+ self.fail("Failed to get dependencies of %s" % pkg_name)
+
+ # Install
+ res = client.install_packages(pk.TransactionFlagEnum.NONE, ids, None,
+ lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+
+ # verify list of files
+ res = client.get_files(ids, None, lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ files = res.get_files_array()[0].get_property('files')
+ # ships two files, plus directories
+ self.assertGreaterEqual(len(files), 2,
+ 'expect two files in ' + str(files))
+ self.assertTrue('/usr/share/doc/silly-depend-base/copyright' in files,
+ files)
+
+ def test_install_files(self):
+ """Test installing local package files."""
+
+ path_pkg_config = os.path.join(
+ REPO_PATH,
+ "silly-config_.1-0_all.deb")
+ path_pkg = os.path.join(
+ REPO_PATH,
+ "silly-depend-base_0.1-0_all.deb")
+
+ client = pk.Client()
+
+ # Fail if more than package should be installed
+ try:
+ client.install_files(pk.TransactionFlagEnum.NONE,
+ [path_pkg_config, path_pkg], None,
+ lambda p, t, d: True, None)
+ except GLib.GError as error:
+ self.assertTrue("Only one package" in error.message)
+ else:
+ self.fail("Installing multiple package files didn't fail")
+
+ # Check simulating
+ res = client.install_files(2 ** pk.TransactionFlagEnum.SIMULATE,
+ [path_pkg], None,
+ lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ pkgs = res.get_package_array()
+ if len(pkgs) != 1:
+ self.fail("Failed to get dependencies")
+ self.assertEqual(pkgs[0].get_name(), "silly-base")
+ self.assertEqual(pkgs[0].get_version(), "0.1-0update1")
+
+ # Check the actual installtion
+ res = client.install_files(pk.TransactionFlagEnum.NONE,
+ [path_pkg], None,
+ lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+
+ # verify list of files
+ res = client.get_files(["silly-depend-base;0.1-0;all;"],
+ None, lambda p, t, d: True, None)
+ files = res.get_files_array()[0].get_property('files')
+ # ships two files, plus directories
+ self.assertGreaterEqual(len(files), 2,
+ 'expect two files in ' + str(files))
+ self.assertTrue('/usr/share/doc/silly-depend-base/copyright' in files,
+ files)
+
+ def test_download(self):
+ """Test downloading packages."""
+ pkg_filename = "silly-base_0.1-0update1_all.deb"
+ pkg_id = "silly-base;0.1-0update1;all;"
+ temp_dir = tempfile.mkdtemp(prefix="aptd-download-test-")
+
+ client = pk.Client()
+ res = client.download_packages([pkg_id], temp_dir,
+ None, lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ if not os.path.exists(os.path.join(temp_dir, pkg_filename)):
+ self.fail("Failed to download the package")
+
+ shutil.rmtree(temp_dir)
+
+ def test_filters(self):
+ """Test filters."""
+ pkg = "silly-base_0.1-0_all.deb"
+ self.chroot.install_debfile(os.path.join(REPO_PATH, pkg), True)
+
+ client = pk.Client()
+
+ # All version
+ res = client.resolve(pk.FilterEnum.NONE, ["silly-base"], None,
+ lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ pkgs = res.get_package_array()
+ if len(pkgs) != 2:
+ self.fail("Failed to get versions")
+ versions = ["0.1-0", "0.1-0update1"]
+ for pkg in pkgs:
+ self.assertEqual(pkg.get_name(), "silly-base")
+ versions.remove(pkg.get_version())
+
+ # Newest version
+ res = client.resolve(2 ** pk.FilterEnum.NEWEST, ["silly-base"], None,
+ lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ pkgs = res.get_package_array()
+ if len(pkgs) != 1:
+ self.fail("Failed to get version")
+ self.assertEqual(pkgs[0].get_name(), "silly-base")
+ self.assertEqual(pkgs[0].get_version(), "0.1-0update1")
+
+ # Installed version
+ res = client.resolve(2 ** pk.FilterEnum.INSTALLED, ["silly-base"],
+ None, lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ pkgs = res.get_package_array()
+ if len(pkgs) != 1:
+ self.fail("Failed to get version")
+ self.assertEqual(pkgs[0].get_name(), "silly-base")
+ self.assertEqual(pkgs[0].get_version(), "0.1-0")
+
+ # Available version
+ res = client.resolve(2 ** pk.FilterEnum.NOT_INSTALLED, ["silly-base"],
+ None, lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ pkgs = res.get_package_array()
+ if len(pkgs) != 1:
+ self.fail("Failed to get version")
+ self.assertEqual(pkgs[0].get_name(), "silly-base")
+ self.assertEqual(pkgs[0].get_version(), "0.1-0update1")
+
+ def test_get_updates(self):
+ """Test getting updates."""
+ pkg = "silly-base_0.1-0_all.deb"
+ self.chroot.install_debfile(os.path.join(REPO_PATH, pkg), True)
+
+ client = pk.Client()
+
+ res = client.get_updates(pk.FilterEnum.NONE, None,
+ lambda p, t, d: True, None)
+ for pkg in res.get_package_array():
+ self.assertEqual(pkg.get_name(), "silly-base")
+ self.assertEqual(pkg.get_version(), "0.1-0update1")
+ self.assertEqual(pkg.get_info(),
+ pk.info_enum_from_string("normal"))
+ break
+ else:
+ self.fail("Failed to detect upgrade")
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+
+ def test_get_updates_security(self):
+ """Test if security updates are detected correctly."""
+ self.chroot.add_repository(os.path.join(aptdaemon.test.get_tests_dir(),
+ "repo/security"))
+ pkg = "silly-base_0.1-0_all.deb"
+ self.chroot.install_debfile(os.path.join(REPO_PATH, pkg), True)
+
+ client = pk.Client()
+
+ res = client.get_updates(pk.FilterEnum.NONE, None,
+ lambda p, t, d: True, None)
+ for pkg in res.get_package_array():
+ self.assertEqual(pkg.get_name(), "silly-base")
+ self.assertEqual(pkg.get_version(), "0.1-0update1")
+ self.assertEqual(pkg.get_info(),
+ pk.info_enum_from_string("security"))
+ break
+ else:
+ self.fail("Failed to detect upgrade")
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+
+ def test_get_updates_backports(self):
+ """Test if backports are detected correctly."""
+ self.chroot.add_repository(os.path.join(aptdaemon.test.get_tests_dir(),
+ "repo/backports"))
+ pkg = "silly-base_0.1-0_all.deb"
+ self.chroot.install_debfile(os.path.join(REPO_PATH, pkg), True)
+
+ client = pk.Client()
+
+ res = client.get_updates(pk.FilterEnum.NONE, None,
+ lambda p, t, d: True, None)
+ for pkg in res.get_package_array():
+ self.assertEqual(pkg.get_name(), "silly-base")
+ self.assertEqual(pkg.get_version(), "0.1-0update1")
+ self.assertEqual(pkg.get_info(),
+ pk.info_enum_from_string("enhancement"))
+ break
+ else:
+ self.fail("Failed to detect upgrade")
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+
+ def test_require_restart(self):
+ """Test if the restart-required signal gets emitted."""
+ os.makedirs(os.path.join(self.chroot.path, "var/run"))
+ with open(os.path.join(self.chroot.path, "var/run/reboot-required"),
+ "w") as reboot_stamp:
+ reboot_stamp.write("")
+ client = pk.Client()
+
+ res = client.get_updates(pk.FilterEnum.NONE, None,
+ lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ self.assertEqual(res.get_require_restart_worst(),
+ pk.RestartEnum.SYSTEM)
+
+ def test_dependencies(self):
+ """Test getting dependencies and dependants."""
+ pkg_id_depend = "silly-depend-base;0.1-0;all;"
+ pkg_id = "silly-base;0.1-0update1;all;"
+
+ client = pk.Client()
+
+ # Get depends
+ res = client.get_depends(pk.FilterEnum.NONE, [pkg_id_depend], True,
+ None, lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ for pkg in res.get_package_array():
+ self.assertEqual(pkg.get_id(), pkg_id)
+ break
+ else:
+ self.fail("Failed to get dependencies of %s" % pkg_id_depend)
+
+ # Get requires
+ res = client.get_requires(pk.FilterEnum.NONE, [pkg_id], True,
+ None, lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ for pkg in res.get_package_array():
+ self.assertEqual(pkg.get_id(), pkg_id_depend)
+ break
+ else:
+ self.fail("Failed to get dependants of %s" % pkg_id)
+
+ def test_what_provides_unsupported(self):
+ """Test querying for provides for unsupported type."""
+
+ client = pk.Client()
+
+ try:
+ client.what_provides(pk.FilterEnum.NONE, pk.ProvidesEnum.CODEC,
+ ["gstreamer0.10(decoder-audio/ac3)"],
+ None, lambda p, t, d: True, None)
+ self.fail("expected GLib.Error failure")
+ except GLib.GError as e:
+ self.assertTrue("codec" in str(e), e)
+ self.assertTrue("not supported" in str(e), e)
+
+ def test_what_provides_plugin(self):
+ """Test querying for provides with plugins."""
+
+ # add plugin for extra codecs
+ f = open(os.path.join(self.workdir, "extra_codecs.py"), "w")
+ f.write("""import aptdaemon.pkenums as enums
+
+def fake_what_provides(cache, type, search):
+ if type in (enums.PROVIDES_CODEC, enums.PROVIDES_ANY):
+ if search.startswith('gstreamer'):
+ return [cache["gstreamer0.10-silly"]]
+ raise NotImplementedError('cannot handle type ' + str(type))
+""")
+ f.close()
+ os.mkdir(os.path.join(self.workdir, "extra_codecs-0.egg-info"))
+ f = open(os.path.join(self.workdir, "extra_codecs-0.egg-info",
+ 'entry_points.txt'), "w")
+ f.write("[packagekit.apt.plugins]\n"
+ "what_provides=extra_codecs:fake_what_provides\n")
+ f.close()
+
+ # invalid plugin, should not stop the valid ones
+ os.mkdir(os.path.join(self.workdir, "nonexisting-1.egg-info"))
+ f = open(os.path.join(self.workdir, "nonexisting-1.egg-info",
+ 'entry_points.txt'), "w")
+ f.write("[packagekit.apt.plugins]\n"
+ "what_provides=nonexisting:what_provides\n")
+ f.close()
+
+ # another plugin to test chaining and a new type
+ f = open(os.path.join(self.workdir, "more_stuff.py"), "w")
+ f.write("""import aptdaemon.pkenums as enums
+
+def my_what_provides(cache, type, search):
+ if type in (enums.PROVIDES_CODEC, enums.PROVIDES_ANY):
+ if search.startswith('gstreamer'):
+ return [cache["silly-base"]]
+ if type in (enums.PROVIDES_LANGUAGE_SUPPORT, enums.PROVIDES_ANY):
+ if search.startswith('locale('):
+ return [cache["silly-important"]]
+ raise NotImplementedError('cannot handle type ' + str(type))
+""")
+ f.close()
+ os.mkdir(os.path.join(self.workdir, "more_stuff-0.egg-info"))
+ f = open(os.path.join(self.workdir, "more_stuff-0.egg-info",
+ 'entry_points.txt'), "w")
+ f.write("[packagekit.apt.plugins]\n"
+ "what_provides=more_stuff:my_what_provides\n")
+ f.close()
+
+ client = pk.Client()
+
+ # search for CODEC
+ res = client.what_provides(pk.FilterEnum.NONE, pk.ProvidesEnum.CODEC,
+ ["gstreamer0.10(decoder-audio/vorbis)"],
+ None, lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ pkgs = [p.get_id().split(";")[0] for p in res.get_package_array()]
+ self.assertEqual(pkgs, ["gstreamer0.10-silly", "silly-base"])
+
+ # search for LANGUAGE_SUPPORT
+ res = client.what_provides(pk.FilterEnum.NONE,
+ pk.ProvidesEnum.LANGUAGE_SUPPORT,
+ ["locale(de_DE)"],
+ None, lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ pkgs = [p.get_id().split(";")[0] for p in res.get_package_array()]
+ self.assertEqual(pkgs, ["silly-important"])
+
+ # search ANY
+ res = client.what_provides(pk.FilterEnum.NONE, pk.ProvidesEnum.ANY,
+ ["gstreamer0.10(decoder-audio/vorbis)"],
+ None, lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ pkgs = [p.get_id().split(";")[0] for p in res.get_package_array()]
+ self.assertEqual(pkgs, ["gstreamer0.10-silly", "silly-base"])
+
+ res = client.what_provides(pk.FilterEnum.NONE, pk.ProvidesEnum.ANY,
+ ["locale(de_DE)"],
+ None, lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ pkgs = [p.get_id().split(";")[0] for p in res.get_package_array()]
+ self.assertEqual(pkgs, ["silly-important"])
+
+ res = client.what_provides(pk.FilterEnum.NONE, pk.ProvidesEnum.ANY,
+ ["modalias(pci:1)"],
+ None, lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ self.assertEqual(res.get_package_array(), [])
+
+ # unsupported type with plugins
+ try:
+ client.what_provides(pk.FilterEnum.NONE,
+ pk.ProvidesEnum.PLASMA_SERVICE,
+ ["plasma4(dataengine-weather)"],
+ None, lambda p, t, d: True, None)
+ self.fail("expected GLib.Error failure")
+ except GLib.GError as e:
+ self.assertTrue("plasma" in str(e), e)
+ self.assertTrue("not supported" in str(e), e)
+
+ def test_repo_enable(self):
+ """Test adding a repository."""
+ client = pk.Client()
+
+ # create test update repo
+ repo = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, repo)
+ with open(os.path.join(repo, 'Packages'), 'w') as f:
+ f.write('''Package: silly-new
+Priority: extra
+Section: admin
+Installed-Size: 44
+Maintainer: Sebastian Heinlein <devel@glatzor.de>
+Architecture: all
+Source: silly-new (0.1-0)
+Version: 1.2.3
+Filename: %s/silly-base_0.1-0update1_all.deb
+Size: 1934
+MD5sum: 8e20af56a63a1e0cf40db3b0d07e7989
+SHA1: 7ce87423d9c7a734478c464021994944d07fbf1b
+SHA256: d3693c0e3e7a9519b2833fdf1301c7e03e0620edf15b95b4c7329d9eb0bee553
+Description: new package from a third-party repo
+''' % self.chroot.path)
+
+ # without the new repo, we do not have it yet
+ self.assertRaises(GLib.GError, client.resolve, pk.FilterEnum.NONE,
+ ['silly-new'], None, lambda p, t, d: True, None)
+
+ # now add the new repo
+ apt_source = 'deb file://%s /' % repo
+ res = client.repo_enable(apt_source, True, None,
+ lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+
+ with open(os.path.join(self.chroot.path, 'etc', 'apt',
+ 'sources.list')) as f:
+ for line in f:
+ if line.strip() == apt_source:
+ break
+ else:
+ self.fail('did not find newly added repository in '
+ 'sources.list')
+
+ # should not actually download the indexes yet
+ self.assertRaises(GLib.GError, client.resolve, pk.FilterEnum.NONE,
+ ['silly-new'], None, lambda p, t, d: True, None)
+
+ res = client.refresh_cache(False, None, lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+
+ # we should now see the new package
+ res = client.resolve(pk.FilterEnum.NONE, ['silly-new'], None,
+ lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+ packages = res.get_package_array()
+ self.assertEqual(len(packages), 1)
+ self.assertEqual(packages[0].get_name(), "silly-new")
+ self.assertEqual(packages[0].get_version(), "1.2.3")
+ self.assertEqual(packages[0].get_info(),
+ pk.info_enum_from_string("available"))
+
+ def test_repo_enable_errors(self):
+ """Test errors when adding a repository."""
+ client = pk.Client()
+ client.set_locale("C")
+
+ try:
+ client.repo_enable('bogus', True, None, lambda p, t, d: True, None)
+ except GLib.GError as e:
+ self.assertTrue('format' in str(e))
+ self.assertTrue('bogus' in str(e))
+
+ try:
+ client.repo_enable('deb http://example.com', True, None,
+ lambda p, t, d: True, None)
+ except GLib.GError as e:
+ self.assertTrue('format' in str(e), e)
+ self.assertTrue('http://example.com' in str(e), e)
+
+ def test_install_signature(self):
+ """Test installing a new GPG key"""
+ # we do not have any key initially
+ self.assertEqual(len(apt.auth.list_keys()), 0)
+
+ # launch our keyserver
+ self.start_keyserver()
+
+ # now add one
+ client = pk.Client()
+ res = client.install_signature(
+ pk.SigTypeEnum.GPG,
+ 'D0BF65B7DBE28DB62BEDBF1B683C53C7CF982D18',
+ '', None, lambda p, t, d: True, None)
+ self.assertEqual(res.get_exit_code(), pk.ExitEnum.SUCCESS)
+
+ # key was imported correctly
+ key = apt.auth.list_keys()[0]
+ self.assertEqual('CF982D18', key.keyid)
+
+ def test_install_signature_error(self):
+ """Test installing a new GPG key with failing server"""
+
+ # do not start keyserver, so http://localhost.. will not exist
+ client = pk.Client()
+ client.set_locale("C")
+ try:
+ client.install_signature(
+ pk.SigTypeEnum.GPG,
+ '1111111111111111111111111111111111111111',
+ '', None, lambda p, t, d: True, None)
+ except GLib.GError as e:
+ self.assertTrue('Failed to download' in str(e), e)
+ self.assertTrue('11111111' in str(e), e)
+
+ def test_unimplemented(self):
+ """Test proper error message on unimplemented method."""
+ client = pk.Client()
+ client.set_locale("C")
+ try:
+ client.upgrade_system("sid", pk.UpgradeKindEnum.COMPLETE, None,
+ lambda p, t, d: True, None)
+ except GLib.GError as e:
+ self.assertTrue('implemented' in str(e))
+
+
+@unittest.skipIf(sys.version_info.major < 3, "Python3 only")
+def setUp():
+ """The PackageKit client cannot handle a changed system D-Bus address.
+ So we need to setup a static one for the whole test suite.
+
+ This requires to run nosetests to launch this test suite.
+ """
+ proc, address = aptdaemon.test.start_dbus_daemon()
+ os.environ["DBUS_SYSTEM_BUS_ADDRESS"] = address
+ # The pk.Client uses a DBus connection with exit-on-disconnect set to
+ # True which cannot be modified. Furthermore the registered signal
+ # handlers cannot be removed. Since GIO would kill the test suite if
+ # the daemon disappears we have to delay killing the daemon
+ atexit.register(os.kill, proc.pid, 9)
+
+
+def tearDown():
+ os.environ["DBUS_SYSTEM_BUS_ADDRESS"] = ""
+
+
+if __name__ == "__main__":
+ if DEBUG:
+ logging.basicConfig(level=logging.DEBUG)
+ setUp()
+ unittest.main()
+ tearDown()
+
+# vim: ts=4 et sts=4
diff --git a/tests/test_progress.py b/tests/test_progress.py
new file mode 100644
index 0000000..b691212
--- /dev/null
+++ b/tests/test_progress.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Tests the debconf forwarding"""
+
+import apt
+import logging
+import mock
+import sys
+import unittest
+
+import aptdaemon.test
+from aptdaemon.progress import DaemonOpenProgress
+
+
+class TestProgress(unittest.TestCase):
+
+ def setUp(self):
+ self.chroot = aptdaemon.test.Chroot()
+ self.chroot.setup()
+ self.addCleanup(self.chroot.remove)
+
+ def test_open_progress(self):
+ transaction = mock.Mock()
+ begin = 0
+ end = 5
+ d = DaemonOpenProgress(transaction, begin=begin, end=end)
+ # simulate cache open (c = apt.Cache(d)))
+ for j in range(4):
+ for i in range(0, 100, 10):
+ d.update(i)
+ self.assertTrue(d.progress >= begin)
+ self.assertTrue(d.progress <= end)
+ d.done()
+ # ensure we use the full range
+ self.assertEqual(d.progress, end)
+
+ def test_open_progress_real_cache(self):
+ transaction = mock.Mock()
+ begin = 0
+ end = 5
+ d = DaemonOpenProgress(transaction, begin=begin, end=end)
+ c = apt.Cache(d)
+ # ensure we use the full range
+ self.assertEqual(d.progress, end)
+
+
+@unittest.skipIf(sys.version_info.major < 3, "Python 3 only")
+def setUp():
+ pass
+
+if __name__ == "__main__":
+ logging.basicConfig(level=logging.DEBUG)
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/test_py2_string_handling.py b/tests/test_py2_string_handling.py
new file mode 100644
index 0000000..ab921f6
--- /dev/null
+++ b/tests/test_py2_string_handling.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2011 Michael Vogt <mvo@ubuntu.com>
+#
+# Licensed under the GNU General Public License Version 2
+#
+# This program 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.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Licensed under the GNU General Public License Version 2
+
+"""Regression test for a unicode decoding error in the status_details,
+progress_download or error_properties attributes of the transaction,
+see LP #724735.
+"""
+
+__author__ = "Michael Vogt <mvo@glatzor.de>"
+
+import sys
+import unittest
+
+if sys.version_info.major == 2:
+ from _test_py2_string_handling import *
+else:
+ try:
+ from _test_py3_string_handling import *
+ except ImportError:
+ from ._test_py3_string_handling import *
+
+
+if __name__ == "__main__":
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/test_simulate.py b/tests/test_simulate.py
new file mode 100644
index 0000000..15da129
--- /dev/null
+++ b/tests/test_simulate.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Tests if the daemon forces a simualte during run."""
+
+import logging
+import time
+import unittest
+
+from gi.repository import GObject
+import dbus
+
+import aptdaemon.client
+import aptdaemon.loop
+import aptdaemon.enums
+
+import aptdaemon.test
+
+DEBUG = True
+
+
+class DaemonTest(aptdaemon.test.AptDaemonTestCase):
+
+ """Test the python client."""
+
+ def setUp(self):
+ """Setup a chroot, run the aptdaemon and a fake PolicyKit daemon."""
+ # Setup chroot
+ self.chroot = aptdaemon.test.Chroot()
+ self.chroot.setup()
+ self.addCleanup(self.chroot.remove)
+ # Start aptdaemon with the chroot on the session bus
+ self.start_dbus_daemon()
+ self.bus = dbus.bus.BusConnection(self.dbus_address)
+ self.start_session_aptd(self.chroot.path)
+ # Start the fake PolikcyKit daemon
+ self.start_fake_polkitd()
+ time.sleep(1)
+
+ def _on_finished(self, trans, exit):
+ """Callback to stop the mainloop after a transaction is done."""
+ aptdaemon.loop.mainloop.quit()
+
+ def test_detect_unauthenticated(self):
+ """Test if the installation of an unauthenticated packages fails
+ if simulate hasn't been called explicitly before.
+ """
+ self.chroot.add_test_repository(copy_sig=False)
+ self.client = aptdaemon.client.AptClient(self.bus)
+ trans = self.client.install_packages(["silly-base"])
+ trans.connect("finished", self._on_finished)
+ trans.run()
+ aptdaemon.loop.mainloop.run()
+ self.assertEqual(trans.exit, aptdaemon.enums.EXIT_FAILED)
+ self.assertEqual(trans.error.code,
+ aptdaemon.enums.ERROR_PACKAGE_UNAUTHENTICATED)
+ self.assertEqual(trans.unauthenticated, ["silly-base"])
+
+ def test_environment(self):
+ """Ensure that the test environment works."""
+ self.chroot.add_test_repository()
+ self.client = aptdaemon.client.AptClient(self.bus)
+ trans = self.client.install_packages(["silly-base"])
+ trans.connect("finished", self._on_finished)
+ trans.run()
+ aptdaemon.loop.mainloop.run()
+ self.assertEqual(trans.exit, aptdaemon.enums.EXIT_SUCCESS)
+
+
+if __name__ == "__main__":
+ if DEBUG:
+ logging.basicConfig(level=logging.DEBUG)
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/test_trans_chain.py b/tests/test_trans_chain.py
new file mode 100644
index 0000000..209d7d7
--- /dev/null
+++ b/tests/test_trans_chain.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Tests the debconf forwarding"""
+import unittest
+from gi.repository import GLib
+
+from aptdaemon import enums, client
+
+DEBUG = False
+
+
+class TransChainTest(unittest.TestCase):
+
+ """These tests require an aptdaemon running with the dummy worker:
+ # sudo aptd -td --dummy
+ """
+
+ def setUp(self):
+ self.loop = GLib.MainLoop()
+ self.client = client.AptClient()
+
+ def _test_working(self):
+ def on_finished(trans, exit):
+ self.loop.quit()
+ trans1 = self.client.upgrade_packages(["huhu"])
+ trans2 = self.client.upgrade_packages(["lala"])
+ trans3 = self.client.upgrade_packages(["huhu"])
+ trans2.run_after(trans1)
+ trans3.run_after(trans2)
+ trans1.run()
+ trans3.connect("finished", on_finished)
+ self.loop.run()
+ self.assertTrue(trans1.exit == enums.EXIT_SUCCESS)
+ self.assertTrue(trans2.exit == enums.EXIT_SUCCESS)
+ self.assertTrue(trans3.exit == enums.EXIT_SUCCESS)
+
+ def _test_fail_after(self):
+ def on_finished(trans, exit):
+ self.loop.quit()
+ trans1 = self.client.update_cache()
+ trans2 = self.client.upgrade_packages(["huhululu"])
+ trans3 = self.client.upgrade_packages(["huhululu"])
+ trans2.run_after(trans1)
+ trans3.run_after(trans2)
+ trans1.run()
+ trans3.connect("finished", on_finished)
+ self.loop.run()
+ self.assertTrue(trans1.exit == enums.EXIT_FAILED)
+ self.assertTrue(trans2.exit == enums.EXIT_PREVIOUS_FAILED)
+ self.assertTrue(trans3.exit == enums.EXIT_PREVIOUS_FAILED)
+
+
+if __name__ == "__main__":
+ import logging
+ if DEBUG:
+ logging.basicConfig(level=logging.DEBUG)
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/test_valid_package_names.py b/tests/test_valid_package_names.py
new file mode 100644
index 0000000..0e81ab9
--- /dev/null
+++ b/tests/test_valid_package_names.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Test index handling."""
+
+import sys
+import unittest
+
+import apt
+import mock
+
+import aptdaemon.core
+import aptdaemon.errors
+
+
+class TestValidPacakgeNames(unittest.TestCase):
+
+ """Test the code that verifies for valid package names
+ """
+
+ def setUp(self):
+ self.cache = apt.Cache()
+ opt = mock.Mock()
+ opt.dummy = True
+ self.daemon = aptdaemon.core.AptDaemon(opt, connect=False)
+
+ def test_valid_package_names(self):
+ # ensure that the code raises on invalid ones, note that we
+ # test each item individually instead of the list because each
+ # needs to raise
+ for invalid in [
+ "foo_bar", "äää", "i space", "a", "+invalidstart", "noUpper",
+ "foo=", "foo=", "foo=a", "foo=0 space"
+ "foo/", "foo/ space"]:
+ with self.assertRaises(aptdaemon.errors.AptDaemonError):
+ self.daemon._check_package_names([invalid])
+
+ # check some simple good cases
+ for pkgname in ["apt", "apt/unstable", "apt=0.3.2", "apt:i386",
+ "apt+:i386/unstable", "apt+:amd64=0.3.2"]:
+ self.daemon._check_package_names([pkgname])
+
+ # ensure the code does not wrongly label valid packages as
+ # invalid, _check_package_names will raise on error
+ for pkg in self.cache:
+ self.daemon._check_package_names([pkg.name])
+
+ # test again, this time with version number
+ for pkg in self.cache:
+ if not pkg.candidate:
+ continue
+ self.daemon._check_package_names(
+ ["%s=%s" % (pkg.name, pkg.candidate.version)])
+
+ # test again, this time with release origin
+ for pkg in self.cache:
+ if not pkg.candidate:
+ continue
+ archive = pkg.candidate.origins[0].archive
+ if archive:
+ self.daemon._check_package_names(["%s/%s" % (pkg.name,
+ archive)])
+
+
+@unittest.skipIf(sys.version_info.major < 3, "Python 3 only")
+def setUp():
+ pass
+
+if __name__ == "__main__":
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/test_worker.py b/tests/test_worker.py
new file mode 100644
index 0000000..e82ace0
--- /dev/null
+++ b/tests/test_worker.py
@@ -0,0 +1,588 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Provides unit tests for the APT worker."""
+# Copyright (C) 2011 Sebastian Heinlein <devel@glatzor.de>
+#
+# Licensed under the GNU General Public License Version 2
+#
+# This program 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.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Licensed under the GNU General Public License Version 2
+
+__author__ = "Sebastian Heinlein <devel@glatzor.de>"
+
+import glob
+import netrc
+import os
+import shutil
+import stat
+import sys
+import tempfile
+import unittest
+
+import apt_pkg
+from gi.repository import GLib
+from mock import (
+ Mock,
+ patch)
+
+import aptdaemon.test
+from aptdaemon.worker.aptworker import (
+ AptWorker)
+from aptdaemon.core import Transaction
+from aptdaemon import enums, errors
+
+
+REPO_PATH = os.path.join(aptdaemon.test.get_tests_dir(), "repo")
+
+PY3K = sys.version_info.major > 2
+
+
+class WorkerTestCase(aptdaemon.test.AptDaemonTestCase):
+
+ """Test suite for the worker which performs the actual package
+ installation and removal."""
+
+ def setUp(self):
+ self.chroot = aptdaemon.test.Chroot()
+ self.chroot.setup()
+ self.addCleanup(self.chroot.remove)
+ self.loop = GLib.MainLoop()
+ self.queue = aptdaemon.test.MockQueue()
+ self.worker = AptWorker(chroot=self.chroot.path, load_plugins=False)
+ self.worker.connect("transaction-done", lambda w, t: self.loop.quit())
+ self.worker.connect("transaction-simulated",
+ lambda w, t: self.loop.quit())
+
+ @unittest.skipIf("nose" in sys.modules, "Fails under nosetests3")
+ def test_update_cache(self):
+ """Test updating the cache using a local repository."""
+ # Add a working and a non-working repository
+ self.chroot.add_trusted_key()
+ path = os.path.join(self.chroot.path,
+ "etc/apt/sources.list.d/test.list")
+ with open(path, "w") as part_file:
+ part_file.write("deb file://%s ./" % REPO_PATH)
+ self.chroot.add_repository("/does/not/exist", copy_list=False)
+ # Only update the repository from the working snippet
+ trans = Transaction(None, enums.ROLE_UPDATE_CACHE,
+ self.queue, os.getpid(), os.getuid(),
+ os.getgid(), sys.argv[0],
+ "org.debian.apt.test", connect=False,
+ kwargs={"sources_list": "test.list"})
+ self.worker.simulate(trans)
+ self.loop.run()
+ self.worker.run(trans)
+ self.loop.run()
+ self.assertEqual(trans.exit, enums.EXIT_SUCCESS,
+ "%s: %s" % (trans._error_property[0],
+ trans._error_property[1]))
+ self.worker._cache.open()
+ self.assertEqual(len(self.worker._cache), 10)
+ pkg = self.worker._cache["silly-base"]
+ self.assertTrue(pkg.candidate.origins[0].trusted)
+
+ def test_upgrade_system(self):
+ """Test upgrading the system."""
+ self.chroot.add_test_repository()
+ self.chroot.install_debfile(os.path.join(REPO_PATH,
+ "silly-base_0.1-0_all.deb"))
+ # Install the package
+ trans = Transaction(None, enums.ROLE_UPGRADE_SYSTEM,
+ self.queue, os.getpid(), os.getgid(),
+ os.getuid(), sys.argv[0],
+ "org.debian.apt.test", connect=False,
+ kwargs={"safe_mode": False})
+ self.worker.simulate(trans)
+ self.loop.run()
+ self.assertEqual(trans.depends[enums.PKGS_UPGRADE],
+ ["silly-base=0.1-0update1"])
+ self.assertTrue(trans.space > 0)
+ self.assertTrue(trans.download == 0)
+ self.worker.run(trans)
+ self.loop.run()
+ self.assertEqual(trans.exit, enums.EXIT_SUCCESS,
+ "%s: %s" % (trans._error_property[0],
+ trans._error_property[1]))
+ self.worker._cache.open()
+ # Test apt history log
+ with open(os.path.join(self.chroot.path, "var/log/apt/history.log")) \
+ as history_file:
+ history = history_file.read()
+ self.assertTrue("Commandline: aptdaemon role='%s'" % trans.role in
+ history)
+
+ self.assertEqual(self.worker._cache["silly-base"].installed.version,
+ "0.1-0update1")
+
+ def test_check_unauth(self):
+ """Test if packages from an unauthenticated repo are detected."""
+ self.chroot.add_test_repository(copy_sig=False)
+ # Install the package
+ trans = Transaction(None, enums.ROLE_INSTALL_PACKAGES, self.queue,
+ os.getpid(), os.getuid(), os.getgid(), sys.argv[0],
+ "org.debian.apt.test", connect=False,
+ packages=[["silly-base"], [], [], [], [], []])
+ self.worker.simulate(trans)
+ self.loop.run()
+ trans.allow_unauthenticated = False
+ self.assertEqual(trans.unauthenticated, ["silly-base"])
+ self.worker.run(trans)
+ self.loop.run()
+ self.assertEqual(trans.exit, enums.EXIT_FAILED)
+ self.assertEqual(trans.error.code, enums.ERROR_PACKAGE_UNAUTHENTICATED)
+
+ # Allow installation of unauthenticated packages
+ trans = Transaction(None, enums.ROLE_INSTALL_PACKAGES, self.queue,
+ os.getpid(), os.getuid(), os.getgid(), sys.argv[0],
+ "org.debian.apt.test", connect=False,
+ packages=[["silly-base"], [], [], [], [], []])
+ trans.allow_unauthenticated = True
+ self.worker.simulate(trans)
+ self.loop.run()
+ self.assertEqual(trans.unauthenticated, ["silly-base"])
+ self.worker.run(trans)
+ self.loop.run()
+ self.assertEqual(trans.exit, enums.EXIT_SUCCESS,
+ "%s: %s" % (trans._error_property[0],
+ trans._error_property[1]))
+ self.worker._cache.open()
+ self.assertTrue(self.worker._cache["silly-base"].is_installed)
+
+ def test_install(self):
+ """Test installation of a package from a repository."""
+ self.chroot.add_test_repository()
+ # Install the package
+ trans = Transaction(None, enums.ROLE_INSTALL_PACKAGES, self.queue,
+ os.getpid(), os.getuid(), os.getgid(), sys.argv[0],
+ "org.debian.apt.test", connect=False,
+ packages=[["silly-depend-base"], [], [], [],
+ [], []])
+ self.worker.simulate(trans)
+ self.loop.run()
+ self.assertEqual(trans.depends[enums.PKGS_INSTALL],
+ ["silly-base=0.1-0update1"])
+ self.assertTrue(trans.space > 0)
+ self.assertTrue(trans.download == 0)
+ self.worker.run(trans)
+ self.loop.run()
+ self.assertEqual(trans.exit, enums.EXIT_SUCCESS,
+ "%s: %s" % (trans._error_property[0],
+ trans._error_property[1]))
+ self.worker._cache.open()
+ self.assertTrue(self.worker._cache["silly-depend-base"].is_installed)
+
+ def test_remove_obsolete(self):
+ """Test the removal of obsoleted packages."""
+ for pkg in ["silly-base_0.1-0_all.deb",
+ "silly-depend-base_0.1-0_all.deb"]:
+ self.chroot.install_debfile(os.path.join(REPO_PATH, pkg))
+ ext_states = apt_pkg.config.find_file("Dir::State::extended_states")
+ with open(ext_states, "w") as ext_states_file:
+ ext_states_file.write("""Package: silly-base
+Architecture: all
+Auto-Installed: 1""")
+ trans = Transaction(None, enums.ROLE_REMOVE_PACKAGES, self.queue,
+ os.getpid(), os.getuid(), os.getgid(), sys.argv[0],
+ "org.debian.apt.test", connect=False,
+ packages=[[], [], ["silly-depend-base"], [],
+ [], []])
+ trans.remove_obsoleted_depends = True
+ self.worker.simulate(trans)
+ self.loop.run()
+ self.assertEqual(trans.depends[enums.PKGS_REMOVE],
+ ["silly-base=0.1-0"])
+ self.assertTrue(trans.space < 0)
+ self.worker.run(trans)
+ self.loop.run()
+ self.assertEqual(trans.exit, enums.EXIT_SUCCESS,
+ "%s: %s" % (trans._error_property[0],
+ trans._error_property[1]))
+ self.worker._cache.open()
+ self.assertFalse("silly-base" in self.worker._cache)
+ self.assertFalse("silly-depend-base" in self.worker._cache)
+
+ def test_remove(self):
+ """Test the removal of packages."""
+ for pkg in ["silly-base_0.1-0_all.deb",
+ "silly-essential_0.1-0_all.deb",
+ "silly-depend-base_0.1-0_all.deb"]:
+ self.chroot.install_debfile(os.path.join(REPO_PATH, pkg))
+ trans = Transaction(None, enums.ROLE_REMOVE_PACKAGES, self.queue,
+ os.getpid(), os.getuid(), os.getgid(), sys.argv[0],
+ "org.debian.apt.test", connect=False,
+ packages=[[], [], ["silly-base"], [], [], []])
+ self.worker.simulate(trans)
+ self.loop.run()
+ self.assertEqual(trans.depends[enums.PKGS_REMOVE],
+ ["silly-depend-base=0.1-0"])
+ self.assertTrue(trans.space < 0)
+ self.worker.run(trans)
+ self.loop.run()
+ self.assertEqual(trans.exit, enums.EXIT_SUCCESS,
+ "%s: %s" % (trans._error_property[0],
+ trans._error_property[1]))
+ self.worker._cache.open()
+ try:
+ installed = self.worker._cache["silly-depend-base"].is_installed
+ self.assertFalse(installed)
+ except KeyError:
+ pass
+ # Don't allow to remove essential packages
+ trans = Transaction(None, enums.ROLE_REMOVE_PACKAGES, self.queue,
+ os.getpid(), os.getuid(), os.getgid(), sys.argv[0],
+ "org.debian.apt.test", connect=False,
+ packages=[[], [], ["silly-essential"], [], [], []])
+ self.worker.run(trans)
+ self.loop.run()
+ self.assertEqual(trans.exit, enums.EXIT_FAILED,
+ "Allowed to remove an essential package")
+ self.assertEqual(trans.error.code,
+ enums.ERROR_NOT_REMOVE_ESSENTIAL_PACKAGE,
+ "Allowed to remove an essential package")
+
+ def test_upgrade(self):
+ """Test upgrading of packages."""
+ self.chroot.add_test_repository()
+ for pkg in ["silly-base_0.1-0_all.deb",
+ "silly-depend-base_0.1-0_all.deb"]:
+ self.chroot.install_debfile(os.path.join(REPO_PATH, pkg))
+ ext_states = apt_pkg.config.find_file("Dir::State::extended_states")
+ with open(ext_states, "w") as ext_states_file:
+ ext_states_file.write("""Package: silly-base
+Architecture: all
+Auto-Installed: 1""")
+ trans = Transaction(None, enums.ROLE_COMMIT_PACKAGES, self.queue,
+ os.getpid(), os.getuid(), os.getgid(), sys.argv[0],
+ "org.debian.apt.test", connect=False,
+ packages=[[], [], [], [],
+ ["silly-base=0.1-0update1"], []])
+ self.worker.run(trans)
+ self.loop.run()
+ self.assertEqual(trans.exit, enums.EXIT_SUCCESS,
+ "%s: %s" % (trans._error_property[0],
+ trans._error_property[1]))
+ self.worker._cache.open()
+ self.assertEqual(self.worker._cache["silly-base"].installed.version,
+ "0.1-0update1", "Failed to upgrade.")
+ self.assertTrue(self.worker._cache["silly-base"].is_auto_installed)
+
+ def test_downgrade(self):
+ """Test downgrading of packages."""
+ self.chroot.add_test_repository()
+ pkg = os.path.join(REPO_PATH, "silly-base_0.1-0update1_all.deb")
+ self.chroot.install_debfile(pkg)
+ trans = Transaction(None, enums.ROLE_COMMIT_PACKAGES, self.queue,
+ os.getpid(), os.getuid(), os.getgid(), sys.argv[0],
+ "org.debian.apt.test", connect=False,
+ packages=[[], [], [], [], [],
+ ["silly-base=0.1-0"]])
+ self.worker.run(trans)
+ self.loop.run()
+ self.assertEqual(trans.exit, enums.EXIT_SUCCESS,
+ "%s: %s" % (trans._error_property[0],
+ trans._error_property[1]))
+ self.worker._cache.open()
+ self.assertEqual(self.worker._cache["silly-base"].installed.version,
+ "0.1-0", "Failed to downgrade.")
+
+ def test_purge(self):
+ """Test the purging of packages."""
+ for pkg in ["silly-base_0.1-0_all.deb", "silly-config_0.1-0_all.deb"]:
+ self.chroot.install_debfile(os.path.join(REPO_PATH, pkg))
+ trans = Transaction(None, enums.ROLE_REMOVE_PACKAGES, self.queue,
+ os.getpid(), os.getuid(), os.getgid(), sys.argv[0],
+ "org.debian.apt.test", connect=False,
+ packages=[[], [], [], ["silly-config"], [], []])
+ self.worker.run(trans)
+ self.loop.run()
+ self.assertEqual(trans.exit, enums.EXIT_SUCCESS,
+ "%s: %s" % (trans._error_property[0],
+ trans._error_property[1]))
+ self.assertFalse(
+ os.path.exists(os.path.join(self.chroot.path,
+ "etc/silly-packages.cfg")),
+ "Configuration file wasn't removed.")
+
+ def test_install_file(self):
+ """Test the installation of a local package file."""
+ # test
+ self.chroot.add_test_repository()
+ pkg = os.path.join(REPO_PATH,
+ "silly-depend-base_0.1-0_all.deb")
+ trans = Transaction(None, enums.ROLE_INSTALL_FILE, self.queue,
+ os.getpid(), os.getuid(), os.getgid(), sys.argv[0],
+ "org.debian.apt.test", connect=False,
+ kwargs={"path": os.path.join(REPO_PATH, pkg),
+ "force": False})
+ self.worker.simulate(trans)
+ self.loop.run()
+ self.assertEqual(trans.depends[enums.PKGS_INSTALL],
+ ["silly-base=0.1-0update1"])
+ self.assertTrue(trans.space > 0)
+ self.worker.run(trans)
+ self.loop.run()
+ self.assertEqual(trans.exit, enums.EXIT_SUCCESS,
+ "%s: %s" % (trans._error_property[0],
+ trans._error_property[1]))
+ self.worker._cache.open()
+ pkg = self.worker._cache["silly-depend-base"]
+ self.assertTrue(pkg.is_installed)
+
+ def test_install_conflicting_file(self):
+ """Test installing of a local package file that conflicts with
+ installed packages.
+
+ Regression test for LP: #750958
+ """
+ pkg_base = "silly-base_0.1-0_all.deb"
+ self.chroot.install_debfile(os.path.join(REPO_PATH, pkg_base))
+ pkg = os.path.join(REPO_PATH, "silly-bully_0.1-0_all.deb")
+ trans = Transaction(None, enums.ROLE_INSTALL_FILE, self.queue,
+ os.getpid(), os.getuid(), os.getgid(), sys.argv[0],
+ "org.debian.apt.test", connect=False,
+ kwargs={"path": os.path.join(REPO_PATH, pkg),
+ "force": True})
+ self.worker.simulate(trans)
+ self.loop.run()
+ self.assertEqual(trans.exit, enums.EXIT_FAILED,
+ "%s: %s" % (trans._error_property[0],
+ trans._error_property[1]))
+ self.assertEqual(trans.error.code, enums.ERROR_DEP_RESOLUTION_FAILED)
+ self.worker._cache.open()
+
+ def test_install_unknown_file(self):
+ """Test the installation of a local package file which is not known
+ to the cache.
+
+ Regression test for LP #702217
+ """
+ pkg = os.path.join(REPO_PATH, "silly-base_0.1-0_all.deb")
+ trans = Transaction(None, enums.ROLE_INSTALL_FILE, self.queue,
+ os.getpid(), os.getuid(), os.getgid(), sys.argv[0],
+ "org.debian.apt.test", connect=False,
+ kwargs={"path": os.path.join(REPO_PATH, pkg),
+ "force": True})
+ self.worker.simulate(trans)
+ self.loop.run()
+ self.assertEqual(trans.packages, (["silly-base"], [], [], [], [], []))
+ self.assertTrue(trans.space > 0)
+ self.worker.run(trans)
+ self.loop.run()
+ self.assertEqual(trans.exit, enums.EXIT_SUCCESS,
+ "%s: %s" % (trans._error_property[0],
+ trans._error_property[1]))
+ self.worker._cache.open()
+ self.assertTrue(self.worker._cache["silly-base"].is_installed)
+
+ def test_fix_broken_depends(self):
+ """Test the fixing of broken dependencies."""
+ for pkg in ["silly-base_0.1-0_all.deb", "silly-broken_0.1-0_all.deb"]:
+ self.chroot.install_debfile(os.path.join(REPO_PATH, pkg), True)
+ trans = Transaction(None, enums.ROLE_FIX_BROKEN_DEPENDS, self.queue,
+ os.getpid(), os.getuid(), os.getgid(), sys.argv[0],
+ "org.debian.apt.test", connect=False)
+ self.worker.simulate(trans)
+ self.loop.run()
+ self.assertEqual(trans.depends[enums.PKGS_REMOVE],
+ ["silly-broken=0.1-0"])
+ self.worker.run(trans)
+ self.loop.run()
+ self.assertEqual(trans.exit, enums.EXIT_SUCCESS,
+ "%s: %s" % (trans._error_property[0],
+ trans._error_property[1]))
+ self.worker._cache.open()
+ self.assertEqual(self.worker._cache.broken_count, 0)
+
+ def test_install_broken_depends(self):
+ """Test the that installing a package with broken dependencies
+ fails in a correct way.
+ """
+ self.chroot.add_test_repository()
+ trans = Transaction(None, enums.ROLE_COMMIT_PACKAGES, self.queue,
+ os.getpid(), os.getuid(), os.getgid(), sys.argv[0],
+ "org.debian.apt.test",
+ packages=[["silly-broken"], [], [], [], [], []],
+ connect=False)
+ self.worker.simulate(trans)
+ self.loop.run()
+ self.assertEqual(trans.error.code, enums.ERROR_DEP_RESOLUTION_FAILED)
+
+ def test_add_license_key_unsecure(self):
+ """Test if we refuse to install license key files to an unsecure
+ location or binaries."""
+ self.chroot.add_test_repository(copy_sig=False)
+ # Should fail because of an untrusted source
+ license_key = "NASTY_BLOB"
+ license_key_path = "/opt/silly-license/NASTY.KEY"
+ pkg_name = "silly-license"
+ self.assertRaises(errors.TransactionFailed,
+ self.worker._add_license_key_to_system,
+ pkg_name, license_key, license_key_path)
+ # Check if we don't allow to install executables
+ with open("/bin/ls", "rb") as sample_exec:
+ license_key = sample_exec.read()
+ if PY3K:
+ license_key = license_key.decode("UTF-8", "ignore")
+ pkg_name = "silly-license"
+ self.assertRaises(errors.TransactionFailed,
+ self.worker._add_license_key_to_system,
+ pkg_name, license_key, license_key_path)
+
+ def test_add_license_key(self):
+ """Test the installation of license key files."""
+ license_key = "Bli bla blub, I am a nasty BLOB!"
+ license_path = "/opt/silly-license/NASTY.KEY"
+
+ def get_license_key_mock(uid, pkg, oauth, server):
+ return license_key, license_path
+
+ self.chroot.add_test_repository()
+ trans = Transaction(None, enums.ROLE_ADD_LICENSE_KEY, self.queue,
+ os.getpid(), os.getuid(), os.getgid(), sys.argv[0],
+ "org.debian.apt.test",
+ kwargs={"pkg_name": "silly-license",
+ "json_token": "lalelu",
+ "server_name": "mock"},
+ connect=False)
+ os.makedirs(os.path.join(
+ aptdaemon.worker.aptworker.apt_pkg.config["Dir"],
+ "opt/silly-license/"))
+ self.worker.plugins["get_license_key"] = [get_license_key_mock]
+ self.worker.run(trans)
+ self.loop.run()
+ self.assertEqual(trans.exit, enums.EXIT_SUCCESS,
+ "%s: %s" % (trans._error_property[0],
+ trans._error_property[1]))
+ # Check the content of the installed key
+ verify_path = os.path.join(
+ aptdaemon.worker.aptworker.apt_pkg.config["Dir"], license_path[1:])
+ with open(verify_path) as verify_file:
+ self.assertEqual(license_key, verify_file.read(),
+ "Content of license key doesn't match")
+
+ def test_use_apt_auth_conf(self):
+ """Test if credentials of repositories are store securely in a
+ separate file.
+ """
+ source_file_name = "private_source.list"
+ self.worker.add_repository(Mock(), "deb",
+ "https://user:pass@host.example.com/path",
+ "natty", ["main"], "comment",
+ source_file_name)
+ # check if password was stripped (source file)
+ source_parts = apt_pkg.config.find_dir("Dir::Etc::sourceparts")
+ source_file_path = os.path.join(source_parts,
+ source_file_name)
+ with open(source_file_path) as source_file:
+ source_file_content = source_file.read()
+ self.assertFalse("user:pass" in source_file_content)
+ # check if password was stored correctly (auth.conf)
+ auth_file_path = apt_pkg.config.find_file("Dir::Etc::netrc")
+ with open(auth_file_path) as auth_file:
+ auth_file_content = auth_file.read()
+ for token in ["login user",
+ "password pass",
+ "machine host.example.com/path"]:
+ self.assertTrue(token in auth_file_content)
+ buf = os.stat(auth_file_path)
+ self.assertEqual(stat.S_IMODE(buf.st_mode), 0o640)
+ # now add the repo again with updated auth credentials and ensure
+ # that the info is not duplicated
+ self.worker.add_repository(Mock(), "deb",
+ "https://xuser:xpass@host.example.com/path",
+ "natty", ["main"], "comment",
+ source_file_name)
+ # check if password was stored correctly (auth.conf)
+ auth_file_path = apt_pkg.config.find_file("Dir::Etc::netrc")
+ netrc_file = netrc.netrc(auth_file_path)
+ self.assertEqual(len(netrc_file.hosts), 1)
+ with open(auth_file_path) as auth_file:
+ auth_file_content = auth_file.read()
+ self.assertFalse("login user" in auth_file_content)
+ for token in ["login xuser",
+ "password xpass",
+ "machine host.example.com/path"]:
+ self.assertTrue(token in auth_file_content)
+ netrc_file = netrc.netrc(auth_file_path)
+ self.assertEqual(len(netrc_file.hosts), 1)
+ # add another one
+ self.worker.add_repository(
+ Mock(), "deb", "https://user2:pass2@host.example.com/path2",
+ "natty", ["main"], "comment", source_file_name)
+ netrc_file = netrc.netrc(auth_file_path)
+ self.assertEqual(len(netrc_file.hosts), 2)
+ # change mode and add another repo
+ os.chmod(auth_file_path, 0o740)
+ self.worker.add_repository(Mock(), "deb",
+ "https://user:pass@host.example.com/path3",
+ "natty", ["main"], "comment",
+ source_file_name)
+ # and ensure auth.conf mode is kept and *not* reset
+ buf = os.stat(auth_file_path)
+ self.assertEqual(stat.S_IMODE(buf.st_mode), 0o740)
+ self.assertEqual(len(netrc.netrc(auth_file_path).hosts), 3)
+
+ def test_modify_apt_auth_fallback(self):
+ netrc_tempfile = tempfile.NamedTemporaryFile()
+ # note that the order of password/login is different than the
+ # standard order
+ netrc_tempfile.write("""
+machine private-ppa.launchpad.net/project-foo
+password baz
+login foo
+""".encode("utf8"))
+ netrc_tempfile.flush()
+ # now pretend we have a different user/pass
+ uri = "http://foo2:baz2@private-ppa.launchpad.net/project-foo"
+ self.worker._store_and_strip_password_from_uri(
+ uri, netrc_tempfile.name)
+ # ensure that it used the fallback prepend
+ with open(netrc_tempfile.name) as f:
+ self.assertEqual(f.read(), """
+machine private-ppa.launchpad.net/project-foo login foo2 password baz2
+
+machine private-ppa.launchpad.net/project-foo
+password baz
+login foo
+""")
+ # and another one with a new entry that just goes to the end
+ # and special chars, note that the python netrc parser will fail
+ # for non-ascii (yes, it does)
+ uri = "http://m%20oo:bär@private-ppa.launchpad.net/project-moobar"
+ self.worker._store_and_strip_password_from_uri(
+ uri, netrc_tempfile.name)
+ with open(netrc_tempfile.name, 'rb') as f:
+ self.assertEqual(f.read().decode("utf-8"), """
+machine private-ppa.launchpad.net/project-foo login foo2 password baz2
+
+machine private-ppa.launchpad.net/project-foo
+password baz
+login foo
+
+machine private-ppa.launchpad.net/project-moobar login m%20oo password bär
+""")
+
+
+@unittest.skipIf(not PY3K, "Only test the backend for Python3")
+def setUp():
+ pass
+
+
+if __name__ == "__main__":
+ # import logging
+ # logging.basicConfig(level=logging.DEBUG)
+ unittest.main()
+
+# vim: ts=4 et sts=4