summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 18:07:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 18:07:41 +0000
commit76926159194e180003aa78de97e5f287bf4325a5 (patch)
tree2cea7245cdc3f66355900c820c145eba90598766 /tests
parentInitial commit. (diff)
downloadpython-apt-76926159194e180003aa78de97e5f287bf4325a5.tar.xz
python-apt-76926159194e180003aa78de97e5f287bf4325a5.zip
Adding upstream version 2.7.6.upstream/2.7.6
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests')
-rw-r--r--tests/__init__.py1
-rw-r--r--tests/data/aptsources/lsb-release4
-rw-r--r--tests/data/aptsources/os-release10
-rw-r--r--tests/data/aptsources/sources.list10
-rw-r--r--tests/data/aptsources/sources.list.d.testDistribution/main.sources74
-rw-r--r--tests/data/aptsources/sources.list.d.testDuplication/main.sources10
-rw-r--r--tests/data/aptsources/sources.list.d/main.sources32
-rw-r--r--tests/data/aptsources/sources.list.testDistribution14
-rw-r--r--tests/data/aptsources/sources.list.testDuplication2
-rw-r--r--tests/data/aptsources_ports/sources.list52
-rw-r--r--tests/data/fake-packages/Packages21
-rw-r--r--tests/data/fake-packages/Packages.gzbin0 -> 555 bytes
-rw-r--r--tests/data/hashsums/hashsum_test.data1
-rw-r--r--tests/data/hashsums/hashsum_test_with_zero.databin0 -> 7 bytes
-rw-r--r--tests/data/misc/foo_Release492
-rw-r--r--tests/data/tagfile/history.1.log.gzbin0 -> 270 bytes
-rw-r--r--tests/data/tagfile/history.log15
-rw-r--r--tests/data/test-provides/etc/apt/sources.list1
-rw-r--r--tests/data/test-provides/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_oneiric_main_binary-i386_Packages75
-rw-r--r--tests/data/test-provides/var/lib/dpkg/status0
-rw-r--r--tests/data/test-repo/Packages.gzbin0 -> 675 bytes
-rw-r--r--tests/data/test-repo2/Packages.gzbin0 -> 675 bytes
-rw-r--r--tests/data/test-signed-usable-repo/key.gpg.base6462
-rw-r--r--tests/data/test-signed-usable-repo/seckey.asc158
-rw-r--r--tests/data/test-signed-usable-repo/signed/Packages26
-rw-r--r--tests/data/test-signed-usable-repo/signed/Release17
-rw-r--r--tests/data/test-signed-usable-repo/signed/Release.gpg14
-rw-r--r--tests/data/test-signed-usable-repo/signed/Sources21
-rw-r--r--tests/data/test-signed-usable-repo/signed/signed-not-usable_1.0.dsc11
-rw-r--r--tests/data/test-signed-usable-repo/signed/signed-not-usable_1.0_all.deb1
-rw-r--r--tests/data/test-signed-usable-repo/signed/signed-usable_1.0.dsc11
-rw-r--r--tests/data/test-signed-usable-repo/signed/signed-usable_1.0_all.deb1
-rw-r--r--tests/data/test-signed-usable-repo/unsigned/Packages26
-rw-r--r--tests/data/test-signed-usable-repo/unsigned/Release17
-rw-r--r--tests/data/test-signed-usable-repo/unsigned/Sources22
-rw-r--r--tests/data/test-signed-usable-repo/unsigned/unsigned-not-usable_1.0.dsc11
-rw-r--r--tests/data/test-signed-usable-repo/unsigned/unsigned-unusable_1.0_all.deb1
-rw-r--r--tests/data/test-signed-usable-repo/unsigned/unsigned-usable_1.0.dsc11
-rw-r--r--tests/data/test-signed-usable-repo/unsigned/unsigned-usable_1.0_all.deb1
-rw-r--r--tests/data/test-source-repo/Sources25
-rw-r--r--tests/data/test_debs/data-tar-broken.debbin0 -> 626 bytes
-rw-r--r--tests/data/test_debs/data-tar-xz.debbin0 -> 680 bytes
-rw-r--r--tests/data/test_debs/data-tar.debbin0 -> 20672 bytes
-rw-r--r--tests/data/test_debs/etc/apt/sources.list1
-rw-r--r--tests/data/test_debs/gdebi-test1.debbin0 -> 934 bytes
-rw-r--r--tests/data/test_debs/gdebi-test10.debbin0 -> 614 bytes
-rw-r--r--tests/data/test_debs/gdebi-test11.debbin0 -> 634 bytes
-rw-r--r--tests/data/test_debs/gdebi-test12.debbin0 -> 966 bytes
-rw-r--r--tests/data/test_debs/gdebi-test13.debbin0 -> 720 bytes
-rw-r--r--tests/data/test_debs/gdebi-test2.debbin0 -> 554 bytes
-rw-r--r--tests/data/test_debs/gdebi-test3.debbin0 -> 570 bytes
-rw-r--r--tests/data/test_debs/gdebi-test4.debbin0 -> 2306 bytes
-rw-r--r--tests/data/test_debs/gdebi-test5.debbin0 -> 2306 bytes
-rw-r--r--tests/data/test_debs/gdebi-test6.debbin0 -> 600 bytes
-rw-r--r--tests/data/test_debs/gdebi-test7.debbin0 -> 578 bytes
-rw-r--r--tests/data/test_debs/gdebi-test8.debbin0 -> 598 bytes
-rw-r--r--tests/data/test_debs/gdebi-test9.debbin0 -> 586 bytes
-rw-r--r--tests/data/test_debs/hello_2.5-1.dsc34
-rw-r--r--tests/data/test_debs/impossible-build-depends_2.5-1.dsc33
-rw-r--r--tests/data/test_debs/large-package-content_1.0_all.debbin0 -> 781546 bytes
-rw-r--r--tests/data/test_debs/multiarch-test1_i386.debbin0 -> 978 bytes
-rw-r--r--tests/data/test_debs/testdep-allowed-any_1.0-1_i386.debbin0 -> 1128 bytes
-rw-r--r--tests/data/test_debs/testdep-same-arch_1.0-1_i386.debbin0 -> 1236 bytes
-rw-r--r--tests/data/test_debs/utf8-package_1.0-1_all.debbin0 -> 1150 bytes
-rw-r--r--tests/data/test_debs/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_maverick_main_binary-i386_Packages31
-rw-r--r--tests/data/test_debs/var/lib/dpkg/status92
-rwxr-xr-xtests/fakeroot-apt-key2
-rwxr-xr-xtests/helper_install_progress_run.py15
-rw-r--r--tests/old/__init__.py11
-rw-r--r--tests/old/apt-test.py27
-rw-r--r--tests/old/cache.py51
-rw-r--r--tests/old/depcache.py57
-rw-r--r--tests/old/lock.py47
-rw-r--r--tests/old/memleak.py45
-rw-r--r--tests/old/pkgproblemresolver.py71
-rw-r--r--tests/old/pkgrecords.py41
-rw-r--r--tests/old/pkgsrcrecords.py31
-rwxr-xr-xtests/old/refcount.py54
-rw-r--r--tests/old/test_enhances.py16
-rw-r--r--tests/test_all.py63
-rw-r--r--tests/test_apt_cache.py400
-rw-r--r--tests/test_aptsources.py1573
-rwxr-xr-xtests/test_aptsources_deb822.py63
-rw-r--r--tests/test_aptsources_ports.py41
-rw-r--r--tests/test_auth.py314
-rw-r--r--tests/test_cache_invocation.py31
-rw-r--r--tests/test_configuration.py31
-rw-r--r--tests/test_cve_2020_27351.py149
-rw-r--r--tests/test_debfile.py192
-rw-r--r--tests/test_debfile_multiarch.py63
-rw-r--r--tests/test_deps.py142
-rw-r--r--tests/test_group.py31
-rw-r--r--tests/test_hashes.py206
-rw-r--r--tests/test_hashsums.py106
-rw-r--r--tests/test_install_progress_exec.py43
-rw-r--r--tests/test_large_file.py25
-rw-r--r--tests/test_lfs.py19
-rw-r--r--tests/test_lp659438.py80
-rw-r--r--tests/test_paths.py158
-rw-r--r--tests/test_pep484.py28
-rw-r--r--tests/test_policy.py70
-rw-r--r--tests/test_progress.py56
-rw-r--r--tests/test_pyflakes.py43
-rw-r--r--tests/test_signed_usable.py403
-rw-r--r--tests/test_size_to_str.py104
-rw-r--r--tests/test_sourcerecords.py129
-rw-r--r--tests/test_tagfile.py186
-rw-r--r--tests/test_utils.py74
-rw-r--r--tests/testcommon.py30
109 files changed, 6762 insertions, 0 deletions
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..792d600
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1 @@
+#
diff --git a/tests/data/aptsources/lsb-release b/tests/data/aptsources/lsb-release
new file mode 100644
index 0000000..c4b5944
--- /dev/null
+++ b/tests/data/aptsources/lsb-release
@@ -0,0 +1,4 @@
+DISTRIB_ID=Ubuntu
+DISTRIB_RELEASE=16.04
+DISTRIB_CODENAME=xenial
+DISTRIB_DESCRIPTION="Ubuntu 16.04.1 LTS"
diff --git a/tests/data/aptsources/os-release b/tests/data/aptsources/os-release
new file mode 100644
index 0000000..95a5bcd
--- /dev/null
+++ b/tests/data/aptsources/os-release
@@ -0,0 +1,10 @@
+NAME="KDE neon"
+VERSION="16.04 LTS"
+ID=neon
+ID_LIKE="ubuntu debian"
+PRETTY_NAME="KDE neon User Edition on 16.04 LTS"
+VERSION_ID="16.04"
+HOME_URL="http://neon.kde.org/"
+SUPPORT_URL="http://neon.kde.org/"
+BUG_REPORT_URL="http://bugs.kde.org/"
+UBUNTU_CODENAME=xenial
diff --git a/tests/data/aptsources/sources.list b/tests/data/aptsources/sources.list
new file mode 100644
index 0000000..8ca6807
--- /dev/null
+++ b/tests/data/aptsources/sources.list
@@ -0,0 +1,10 @@
+# comment 1
+deb http://de.archive.ubuntu.com/ubuntu/ edgy main
+# comment 2
+deb http://de.archive.ubuntu.com/ubuntu/ edgy restricted
+# comment 3
+deb http://de.archive.ubuntu.com/ubuntu/ edgy universe
+
+# multi-arch
+deb [arch=amd64,i386] http://de.archive.ubuntu.com/ubuntu/ natty main
+deb [arch=amd64,i386 trusted=yes] http://de.archive.ubuntu.com/ubuntu/ natty main
diff --git a/tests/data/aptsources/sources.list.d.testDistribution/main.sources b/tests/data/aptsources/sources.list.d.testDistribution/main.sources
new file mode 100644
index 0000000..ccdda20
--- /dev/null
+++ b/tests/data/aptsources/sources.list.d.testDistribution/main.sources
@@ -0,0 +1,74 @@
+Types: deb
+URIs: http://de.archive.ubuntu.com/ubuntu/
+Suites: hardy
+Components: main
+
+Types: deb
+URIs: http://de.archive.ubuntu.com/ubuntu/
+Suites: hardy
+Components: restricted
+
+Types: deb
+URIs: http://de.archive.ubuntu.com/ubuntu/
+Suites: hardy
+Components: universe
+
+Types: deb
+URIs: http://de.archive.ubuntu.com/ubuntu/
+Suites: hardy-updates
+Components: universe multiverse
+
+Types: deb
+URIs: http://de.archive.ubuntu.com/ubuntu/
+Suites: hardy-updates
+Components: restricted
+
+Types: deb
+URIs: http://de.archive.ubuntu.com/ubuntu/
+Suites: hardy-security
+Components: main
+
+Types: deb
+URIs: http://de.archive.ubuntu.com/ubuntu/
+Suites: hardy-security
+Components: multiverse
+
+Types: deb
+URIs: http://de.archive.ubuntu.com/ubuntu/
+Suites: hardy-security
+Components: multiverse
+
+Types: deb
+URIs: http://ftp.debian.org/debian
+Suites: sid
+Components: main
+
+Types: deb
+URIs: http://archive.tanglu.org/tanglu
+Suites: aequorea
+Components: main
+
+Types: deb
+URIs: http://ftp.hosteurope.de/mirror/archive.ubuntu.com/
+Suites: hardy
+Components: main
+
+Types: deb
+URIs: http://ftp.hosteurope.de/mirror/archive.ubuntu.com/
+Suites: hardy-backports
+Components: main
+
+Types: deb
+URIs: http://archive.ubuntu.com/ubuntu/
+Suites: intrepid
+Components: main
+
+Types: deb
+URIs: cdrom:[Ubuntu 8.10 _Intrepid Ibex_ - Alpha]/
+Suites: intrepid
+Components: main
+
+Types: deb
+URIs: cdrom:[Ubuntu 8.04 _Hardy Heron_]
+Suites: hardy
+Components: main
diff --git a/tests/data/aptsources/sources.list.d.testDuplication/main.sources b/tests/data/aptsources/sources.list.d.testDuplication/main.sources
new file mode 100644
index 0000000..5b7765b
--- /dev/null
+++ b/tests/data/aptsources/sources.list.d.testDuplication/main.sources
@@ -0,0 +1,10 @@
+Types: deb
+URIs: http://ppa.launchpad.net/me/myproject/ubuntu
+Suites: xenial
+Components: main
+
+Types: deb-src
+URIs: http://ppa.launchpad.net/me/myproject/ubuntu
+Suites: xenial
+Components: main
+Enabled: no
diff --git a/tests/data/aptsources/sources.list.d/main.sources b/tests/data/aptsources/sources.list.d/main.sources
new file mode 100644
index 0000000..2495f06
--- /dev/null
+++ b/tests/data/aptsources/sources.list.d/main.sources
@@ -0,0 +1,32 @@
+# comment 1
+# note that we have deliberate case typos in each paragraph to test case-insensitivity
+TyPes: deb
+URIs: http://de.archive.ubuntu.com/ubuntu/
+Suites: edgy
+Components: main
+
+# comment 2
+Types: deb
+uRIs: http://de.archive.ubuntu.com/ubuntu/
+Suites: edgy
+Components: restricted
+
+# comment 3
+Types: deb
+URIs: http://de.archive.ubuntu.com/ubuntu/
+SuiTes: edgy
+Components: universe
+
+# multi-arch
+Types: deb
+URIs: http://de.archive.ubuntu.com/ubuntu/
+Suites: natty
+ComPonents: main
+Architectures: amd64 i386
+
+Types: deb
+URIs: http://de.archive.ubuntu.com/ubuntu/
+Suites: natty
+Components: main
+ArchiTectures: amd64 i386
+Trusted: yes
diff --git a/tests/data/aptsources/sources.list.testDistribution b/tests/data/aptsources/sources.list.testDistribution
new file mode 100644
index 0000000..021ee87
--- /dev/null
+++ b/tests/data/aptsources/sources.list.testDistribution
@@ -0,0 +1,14 @@
+deb http://de.archive.ubuntu.com/ubuntu/ hardy main
+deb http://de.archive.ubuntu.com/ubuntu/ hardy restricted
+deb http://de.archive.ubuntu.com/ubuntu/ hardy universe
+deb http://de.archive.ubuntu.com/ubuntu/ hardy-updates universe multiverse
+deb http://de.archive.ubuntu.com/ubuntu/ hardy-updates restricted
+deb http://de.archive.ubuntu.com/ubuntu/ hardy-security main
+deb http://de.archive.ubuntu.com/ubuntu/ hardy-security multiverse
+deb http://ftp.debian.org/debian sid main
+deb http://archive.tanglu.org/tanglu aequorea main
+deb http://ftp.hosteurope.de/mirror/archive.ubuntu.com/ hardy main
+deb http://ftp.hosteurope.de/mirror/archive.ubuntu.com/ hardy-backports main
+deb http://archive.ubuntu.com/ubuntu/ intrepid main
+deb cdrom:[Ubuntu 8.10 _Intrepid Ibex_ - Alpha]/ intrepid main
+deb cdrom:[Ubuntu 8.04 _Hardy Heron_] hardy main
diff --git a/tests/data/aptsources/sources.list.testDuplication b/tests/data/aptsources/sources.list.testDuplication
new file mode 100644
index 0000000..28a4db2
--- /dev/null
+++ b/tests/data/aptsources/sources.list.testDuplication
@@ -0,0 +1,2 @@
+deb http://ppa.launchpad.net/me/myproject/ubuntu xenial main
+# deb-src http://ppa.launchpad.net/me/myproject/ubuntu xenial main
diff --git a/tests/data/aptsources_ports/sources.list b/tests/data/aptsources_ports/sources.list
new file mode 100644
index 0000000..0efa282
--- /dev/null
+++ b/tests/data/aptsources_ports/sources.list
@@ -0,0 +1,52 @@
+#
+# deb cdrom:[Ubuntu-Server 8.04.1 _Hardy Heron_ - Release powerpc (20080703)]/ hardy main restricted
+
+# deb cdrom:[Ubuntu-Server 8.04.1 _Hardy Heron_ - Release powerpc (20080703)]/ hardy main restricted
+# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to
+# newer versions of the distribution.
+
+deb http://ports.ubuntu.com/ubuntu-ports/ hardy main restricted
+deb-src http://ports.ubuntu.com/ubuntu-ports/ hardy main restricted
+
+## Major bug fix updates produced after the final release of the
+## distribution.
+deb http://ports.ubuntu.com/ubuntu-ports/ hardy-updates main restricted
+deb-src http://ports.ubuntu.com/ubuntu-ports/ hardy-updates main restricted
+
+## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
+## team, and may not be under a free licence. Please satisfy yourself as to
+## your rights to use the software. Also, please note that software in
+## universe WILL NOT receive any review or updates from the Ubuntu security
+## team.
+deb http://ports.ubuntu.com/ubuntu-ports/ hardy universe
+deb-src http://ports.ubuntu.com/ubuntu-ports/ hardy universe
+deb http://ports.ubuntu.com/ubuntu-ports/ hardy-updates universe
+deb-src http://ports.ubuntu.com/ubuntu-ports/ hardy-updates universe
+
+## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
+## team, and may not be under a free licence. Please satisfy yourself as to
+## your rights to use the software. Also, please note that software in
+## multiverse WILL NOT receive any review or updates from the Ubuntu
+## security team.
+deb http://ports.ubuntu.com/ubuntu-ports/ hardy multiverse
+deb-src http://ports.ubuntu.com/ubuntu-ports/ hardy multiverse
+deb http://ports.ubuntu.com/ubuntu-ports/ hardy-updates multiverse
+deb-src http://ports.ubuntu.com/ubuntu-ports/ hardy-updates multiverse
+
+## Uncomment the following two lines to add software from the 'backports'
+## repository.
+## N.B. software from this repository may not have been tested as
+## extensively as that contained in the main release, although it includes
+## newer versions of some applications which may provide useful features.
+## Also, please note that software in backports WILL NOT receive any review
+## or updates from the Ubuntu security team.
+# deb http://ports.ubuntu.com/ubuntu-ports/ hardy-backports main restricted universe multiverse
+# deb-src http://ports.ubuntu.com/ubuntu-ports/ hardy-backports main restricted universe multiverse
+
+deb http://ports.ubuntu.com/ubuntu-ports/ hardy-security main restricted
+deb-src http://ports.ubuntu.com/ubuntu-ports/ hardy-security main restricted
+deb http://ports.ubuntu.com/ubuntu-ports/ hardy-security universe
+deb-src http://ports.ubuntu.com/ubuntu-ports/ hardy-security universe
+deb http://ports.ubuntu.com/ubuntu-ports/ hardy-security multiverse
+deb-src http://ports.ubuntu.com/ubuntu-ports/ hardy-security multiverse
+deb-src http://ports.ubuntu.com/ubuntu-ports/ hardy-proposed restricted main multiverse universe
diff --git a/tests/data/fake-packages/Packages b/tests/data/fake-packages/Packages
new file mode 100644
index 0000000..9fbab95
--- /dev/null
+++ b/tests/data/fake-packages/Packages
@@ -0,0 +1,21 @@
+Package: 2vcard
+Priority: optional
+Section: universe/utils
+Installed-Size: 108
+Maintainer: Arvind Autar <Autar022@planet.nl>
+Architecture: amd64
+Version: 0.5-1ubuntu1
+Filename: pool/universe/2/2vcard/2vcard_0.5-1ubuntu1_amd64.deb
+Size: 14164
+MD5sum: 105ea91f0a75417d0f9e8e9624513b2a
+SHA1: d55beee01c08efc33cd131e106330dca72ee14be
+SHA256: 4a72edaf87cdb826e5508b85311fcf0bec9b7e019a55740ded7feb1b9e197f11
+Description: A little perl script to convert an adressbook to VCARD file format
+ 2vcard is a little perl script that you can use to convert the popular vcard
+ file format. Currently 2vcard can only convert adressbooks and alias files from
+ the following formats: abook,eudora,juno,ldif,mutt,mh and pine.
+ .
+ The VCARD format is used by gnomecard, for example, which is turn is used by
+ the balsa email client.
+Bugs: mailto:ubuntu-users@lists.ubuntu.com
+Origin: Ubuntu
diff --git a/tests/data/fake-packages/Packages.gz b/tests/data/fake-packages/Packages.gz
new file mode 100644
index 0000000..506bc8d
--- /dev/null
+++ b/tests/data/fake-packages/Packages.gz
Binary files differ
diff --git a/tests/data/hashsums/hashsum_test.data b/tests/data/hashsums/hashsum_test.data
new file mode 100644
index 0000000..1910281
--- /dev/null
+++ b/tests/data/hashsums/hashsum_test.data
@@ -0,0 +1 @@
+foo \ No newline at end of file
diff --git a/tests/data/hashsums/hashsum_test_with_zero.data b/tests/data/hashsums/hashsum_test_with_zero.data
new file mode 100644
index 0000000..2ec9a6d
--- /dev/null
+++ b/tests/data/hashsums/hashsum_test_with_zero.data
Binary files differ
diff --git a/tests/data/misc/foo_Release b/tests/data/misc/foo_Release
new file mode 100644
index 0000000..0f42220
--- /dev/null
+++ b/tests/data/misc/foo_Release
@@ -0,0 +1,492 @@
+Origin: Ubuntu
+Label: Ubuntu
+Suite: precise
+Version: 12.04
+Codename: precise
+Date: Wed, 25 Apr 2012 22:49:23 UTC
+Architectures: amd64 armel armhf i386 powerpc
+Components: main restricted universe multiverse
+Description: Ubuntu Precise 12.04
+MD5Sum:
+ 6a815674c5b3178152d449a9396cb2e5 1640344 main/binary-amd64/Packages.gz
+ 98140fa3444c9e945f6944acbb9ddcff 7818931 main/binary-amd64/Packages
+ 846e0e856bcbf9b64d9119c8727cda8c 97 main/binary-amd64/Release
+ cfd5f57de107941bbe661ffada4dce88 1272844 main/binary-amd64/Packages.bz2
+ 5b220aea8056a900c8eaf28e79cdd64a 97 main/binary-armel/Release
+ 92e3160bbb664f8b5d7d4f2d161dd81c 1619078 main/binary-armel/Packages.gz
+ 28fbf69ee965639c92c8a54256cd6ba1 1257389 main/binary-armel/Packages.bz2
+ 4e3ba67129f73f8afe28186b31eb3112 7743353 main/binary-armel/Packages
+ 81cfd32bf54d2b007542e14e09953bae 97 main/binary-armhf/Release
+ ed404b1123d1497cb09aceae8850fc06 7620333 main/binary-armhf/Packages
+ 64156d29df19fdaa36fa4eafd4c17dc1 1257653 main/binary-armhf/Packages.bz2
+ 3d3238bd89cb1e8e23a1a4a5bf574739 1617483 main/binary-armhf/Packages.gz
+ dc2fe62f05e29f36ffe4e58499796ae6 1273857 main/binary-i386/Packages.bz2
+ 7c678c50ce682e9f5cc5ba8f8eb2d6ad 1641082 main/binary-i386/Packages.gz
+ b6aa9a96c765bbd4202dae7dbaf18cc2 96 main/binary-i386/Release
+ eb3b4870b877863fecf1d3307c3beb54 7816415 main/binary-i386/Packages
+ 54658d89b612a38592431fe6b8a780a1 1627734 main/binary-powerpc/Packages.gz
+ 4185aca6984f00fd65aa0c0de12367d4 99 main/binary-powerpc/Release
+ fc9bf1959082733fc42c682285bf46e7 1263942 main/binary-powerpc/Packages.bz2
+ c32afd79c9163cffe013f98669deb727 7661552 main/binary-powerpc/Packages
+ ff5434612595a569236f7ba4990a3b63 62166 main/debian-installer/binary-amd64/Packages.gz
+ 7dd470290bacbacc7bec24830100aade 234592 main/debian-installer/binary-amd64/Packages
+ 013d9839e9c8f8c102daf6eb77841bf7 48784 main/debian-installer/binary-amd64/Packages.bz2
+ 08608ef832f2410970d07fdd802fc4e4 47964 main/debian-installer/binary-armel/Packages.bz2
+ 0a0a559023aac4096a38e2fb51870ddf 230310 main/debian-installer/binary-armel/Packages
+ f79f29b841a05b971ac318a9e0a396e0 61118 main/debian-installer/binary-armel/Packages.gz
+ 20a72276b6607fa58f52fcc5b7742ff4 62777 main/debian-installer/binary-armhf/Packages.gz
+ 8da4a15400cec4b06e2aa1bba6cfc2c3 49051 main/debian-installer/binary-armhf/Packages.bz2
+ 49f9f82a7c7e9e13c399d15df43148f1 238862 main/debian-installer/binary-armhf/Packages
+ 3dd181be504206f7abb41039dc508ed9 52279 main/debian-installer/binary-i386/Packages.bz2
+ 5c2c6ca586bb6ffb2de414e2cd063c70 259996 main/debian-installer/binary-i386/Packages
+ ca1b7db323ebef593617ef0a7c833dfb 67180 main/debian-installer/binary-i386/Packages.gz
+ 876f115ee26518319458fc26ec785485 246636 main/debian-installer/binary-powerpc/Packages
+ 49402b5b12b10804abfae14629d23f1c 50309 main/debian-installer/binary-powerpc/Packages.bz2
+ 29010a7ba3ac1e60a0efaef04f6f2e42 64468 main/debian-installer/binary-powerpc/Packages.gz
+ adf74189a01512a8f68d4bfc411dc692 3706 main/i18n/Index
+ 2f2ddab9be4ecc2c9e190bb639304943 4356187 main/source/Sources
+ 5c2893c352ebbf3ee380ebdab6b5e487 933770 main/source/Sources.bz2
+ 323e5ca1ba86c7a503c4c7b0772749b1 1174967 main/source/Sources.gz
+ 3c3104465b2c7e54f4b8f566ac825339 98 main/source/Release
+ f75f8a98e9e952194046da388a11c42a 119109 multiverse/binary-amd64/Packages.bz2
+ c2df9a9ff319486e3d6f46d9c35a8530 151924 multiverse/binary-amd64/Packages.gz
+ ece719c6a770134150f158cdb65ca6fc 581550 multiverse/binary-amd64/Packages
+ d3f34ec2fc86c95e4d3bc4606d751c66 103 multiverse/binary-amd64/Release
+ f296954282382f88b12dd8c64d813f5c 136295 multiverse/binary-armel/Packages.gz
+ d0da88f8a408fba12d44d5b6e107cfc0 519605 multiverse/binary-armel/Packages
+ 6d0c32b1cae4bdcfd6f18fd2d6fbf31f 103 multiverse/binary-armel/Release
+ 6744aaa8a3457d139348ea18379781ce 106873 multiverse/binary-armel/Packages.bz2
+ 1cc2613becf46fd578aa6d52ab12db94 104529 multiverse/binary-armhf/Packages.bz2
+ b0f8df0c9d700a1f7d295be4d4c03788 505901 multiverse/binary-armhf/Packages
+ 73cb5bc3d0c5021e5a4a413a73bcefce 103 multiverse/binary-armhf/Release
+ 00c07f1601226b8387fe4c9afaf2b044 133117 multiverse/binary-armhf/Packages.gz
+ bf0237c8c5d06a6df172db28710b8a36 121196 multiverse/binary-i386/Packages.bz2
+ cf110f58668bf5731e593ed78af54c27 591662 multiverse/binary-i386/Packages
+ 8a915986a504c0f3eb95b61ae909c9a4 102 multiverse/binary-i386/Release
+ c6d89a2752d1154a219c295e6d70b697 154762 multiverse/binary-i386/Packages.gz
+ ae93681c6b316c95207091b3c91042a7 105 multiverse/binary-powerpc/Release
+ ec463c2070c515de099f8baa3a4b5993 107209 multiverse/binary-powerpc/Packages.bz2
+ 675204a2b4aabeb9e99e406070b2af9b 520882 multiverse/binary-powerpc/Packages
+ c806ea7584c782d57363df12ddb28839 136930 multiverse/binary-powerpc/Packages.gz
+ d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-amd64/Packages
+ 4a4dd3598707603b3f76a2378a4504aa 20 multiverse/debian-installer/binary-amd64/Packages.gz
+ 4059d198768f9f8dc9372dc1c54bc3c3 14 multiverse/debian-installer/binary-amd64/Packages.bz2
+ 4059d198768f9f8dc9372dc1c54bc3c3 14 multiverse/debian-installer/binary-armel/Packages.bz2
+ d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-armel/Packages
+ 4a4dd3598707603b3f76a2378a4504aa 20 multiverse/debian-installer/binary-armel/Packages.gz
+ 4a4dd3598707603b3f76a2378a4504aa 20 multiverse/debian-installer/binary-armhf/Packages.gz
+ d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-armhf/Packages
+ 4059d198768f9f8dc9372dc1c54bc3c3 14 multiverse/debian-installer/binary-armhf/Packages.bz2
+ 4059d198768f9f8dc9372dc1c54bc3c3 14 multiverse/debian-installer/binary-i386/Packages.bz2
+ 4a4dd3598707603b3f76a2378a4504aa 20 multiverse/debian-installer/binary-i386/Packages.gz
+ d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-i386/Packages
+ 4a4dd3598707603b3f76a2378a4504aa 20 multiverse/debian-installer/binary-powerpc/Packages.gz
+ d41d8cd98f00b204e9800998ecf8427e 0 multiverse/debian-installer/binary-powerpc/Packages
+ 4059d198768f9f8dc9372dc1c54bc3c3 14 multiverse/debian-installer/binary-powerpc/Packages.bz2
+ a2bcfa86da39328db94629011506a877 2676 multiverse/i18n/Index
+ c2ffda2a848000b71573dbc3dc7d4402 154990 multiverse/source/Sources.bz2
+ f1d64bb88933686a71108de73b1f2262 628753 multiverse/source/Sources
+ 3d35747578528fa13640b98184120e51 188325 multiverse/source/Sources.gz
+ c9828946b46ac900fec682369c52bfa7 104 multiverse/source/Release
+ 5644835af0d1ed82cc7c14e34c2a543f 9098 restricted/binary-amd64/Packages.gz
+ d3058923c862e74c2c08b9a4ad7ec51e 134705 restricted/binary-amd64/Packages
+ 97ecff09a695f1460177843ff5d2b3e6 103 restricted/binary-amd64/Release
+ f8ed966e5400930411a32a7183357810 8452 restricted/binary-amd64/Packages.bz2
+ 60e74c701a6e40b7f869dd83b335ec5c 103 restricted/binary-armel/Release
+ 4a4dd3598707603b3f76a2378a4504aa 20 restricted/binary-armel/Packages.gz
+ 4059d198768f9f8dc9372dc1c54bc3c3 14 restricted/binary-armel/Packages.bz2
+ d41d8cd98f00b204e9800998ecf8427e 0 restricted/binary-armel/Packages
+ 55c08a48fc4b3370acc95a391bde1189 103 restricted/binary-armhf/Release
+ ce2da4621bbbaf55d858ae4243e17715 1103 restricted/binary-armhf/Packages.bz2
+ b26814493282faf0c5b269c44b799653 2477 restricted/binary-armhf/Packages
+ 784050b4fd16ea71e10cf130e47132d9 941 restricted/binary-armhf/Packages.gz
+ c4280a67444afbb8e2564d6f2249d397 9108 restricted/binary-i386/Packages.gz
+ 6dfd90555b37a912f82894b4ec3b63a0 8431 restricted/binary-i386/Packages.bz2
+ 48d3e36bf54be9b3fc7576cfcd0aac79 134582 restricted/binary-i386/Packages
+ e49b38cbf4dc409e8c25f6ab4b32fbe4 102 restricted/binary-i386/Release
+ 4a4dd3598707603b3f76a2378a4504aa 20 restricted/binary-powerpc/Packages.gz
+ 4059d198768f9f8dc9372dc1c54bc3c3 14 restricted/binary-powerpc/Packages.bz2
+ d41d8cd98f00b204e9800998ecf8427e 0 restricted/binary-powerpc/Packages
+ edb68a453747a9faa9e98389787fb79d 105 restricted/binary-powerpc/Release
+ d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-amd64/Packages
+ 4059d198768f9f8dc9372dc1c54bc3c3 14 restricted/debian-installer/binary-amd64/Packages.bz2
+ 4a4dd3598707603b3f76a2378a4504aa 20 restricted/debian-installer/binary-amd64/Packages.gz
+ 4a4dd3598707603b3f76a2378a4504aa 20 restricted/debian-installer/binary-armel/Packages.gz
+ 4059d198768f9f8dc9372dc1c54bc3c3 14 restricted/debian-installer/binary-armel/Packages.bz2
+ d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-armel/Packages
+ 4059d198768f9f8dc9372dc1c54bc3c3 14 restricted/debian-installer/binary-armhf/Packages.bz2
+ 4a4dd3598707603b3f76a2378a4504aa 20 restricted/debian-installer/binary-armhf/Packages.gz
+ d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-armhf/Packages
+ 4a4dd3598707603b3f76a2378a4504aa 20 restricted/debian-installer/binary-i386/Packages.gz
+ 4059d198768f9f8dc9372dc1c54bc3c3 14 restricted/debian-installer/binary-i386/Packages.bz2
+ d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-i386/Packages
+ 4a4dd3598707603b3f76a2378a4504aa 20 restricted/debian-installer/binary-powerpc/Packages.gz
+ 4059d198768f9f8dc9372dc1c54bc3c3 14 restricted/debian-installer/binary-powerpc/Packages.bz2
+ d41d8cd98f00b204e9800998ecf8427e 0 restricted/debian-installer/binary-powerpc/Packages
+ 39ef6c1d54f83252b07406f9cc3e9204 2596 restricted/i18n/Index
+ a76089a7a653d4f2196c38093058d1aa 19001 restricted/source/Sources
+ 3990b36e6ef2846251b58a58cde3768d 5470 restricted/source/Sources.bz2
+ f0a32107aaf12daf3e64b4dc3ee11321 5306 restricted/source/Sources.gz
+ 7871a3f6f83e033ab93c06bf886e305d 104 restricted/source/Release
+ 3464b1b15950e714151f8bb43982951e 25546870 universe/binary-amd64/Packages
+ 9340d5c4051da92820bb5bd5ba05f7a7 4785960 universe/binary-amd64/Packages.bz2
+ 1f6974aea9904921afdb4d1c5d0e8578 6166988 universe/binary-amd64/Packages.gz
+ 75ea366982b4862a41d9640687778dd2 101 universe/binary-amd64/Release
+ 5492f7f7183461d52f8d58871026c3e8 101 universe/binary-armel/Release
+ 420abe7ca02b5cb2abcd1b273efcfea9 4667308 universe/binary-armel/Packages.bz2
+ 4da3448f54e710222b26646b0bee14e8 6009219 universe/binary-armel/Packages.gz
+ e99405db74ba425a3f898fdda0a42113 24901082 universe/binary-armel/Packages
+ d0545845bbcff572a5c852b80582b269 5948128 universe/binary-armhf/Packages.gz
+ 8f0d262f1eb03fc3523cd80b3112a7e0 101 universe/binary-armhf/Release
+ a10169d0cc23f526bcee5769d27778b0 4618508 universe/binary-armhf/Packages.bz2
+ 952259d2cdb2c85df177702ac92075a0 24642528 universe/binary-armhf/Packages
+ df43c3b4ec37e79a00023475a09e2bfa 6179579 universe/binary-i386/Packages.gz
+ cc44a3e2ad759febb38ef2bec15ab29e 25568759 universe/binary-i386/Packages
+ 826b1d8609f0944a6d2ae95617e4d05a 100 universe/binary-i386/Release
+ 50690005918dd03ad9b71ffffa678d6f 4795820 universe/binary-i386/Packages.bz2
+ 8a1f21204f4178574251a2238c00f317 25188905 universe/binary-powerpc/Packages
+ 7409876b9e1238d5a147720b41233a26 6080488 universe/binary-powerpc/Packages.gz
+ 9bcc15c59aa3fbbd15f0d187fe90b353 4716652 universe/binary-powerpc/Packages.bz2
+ c3bdb05d2be1efa2dca9d2bac4e85b13 103 universe/binary-powerpc/Release
+ f8259410f0e1ad66324dbce895fdde2d 15255 universe/debian-installer/binary-amd64/Packages.bz2
+ ce60affbf23a069735c29951bfb0b818 17243 universe/debian-installer/binary-amd64/Packages.gz
+ 13ce59ecd4540ddef3670dbbed73cdbc 61801 universe/debian-installer/binary-amd64/Packages
+ 988da583db40ce21d400926641fe6ed8 113584 universe/debian-installer/binary-armel/Packages
+ 75381e3ed15a3a2fa1480fdea72cfd24 23193 universe/debian-installer/binary-armel/Packages.bz2
+ 4a8e6a98277f254660e8690fd050b232 27397 universe/debian-installer/binary-armel/Packages.gz
+ 02e7128ef42cd61e66c768520961fb11 20065 universe/debian-installer/binary-armhf/Packages.gz
+ 851afb686177a6819b291a714fa15813 17619 universe/debian-installer/binary-armhf/Packages.bz2
+ ce3b6645e37226c4047546a40675ecdd 76034 universe/debian-installer/binary-armhf/Packages
+ 229235ad9979a343e3bea9aedb5af8da 17260 universe/debian-installer/binary-i386/Packages.gz
+ 8065c9994844e578af00ef8794709b18 15272 universe/debian-installer/binary-i386/Packages.bz2
+ f28d6328611ab1e382fb0d0e798aca97 61718 universe/debian-installer/binary-i386/Packages
+ 60f54adbd38213dbbfe5d638f98a17e9 61121 universe/debian-installer/binary-powerpc/Packages
+ 1d48a07b3f4214200ff3eb1c5894e4a1 15024 universe/debian-installer/binary-powerpc/Packages.bz2
+ 51eb13c4b9baf089e4e8b0e85556b90e 16860 universe/debian-installer/binary-powerpc/Packages.gz
+ 155e0b646671f37a7fe235c4579e59f2 2922 universe/i18n/Index
+ 2ef7ccbe106edb394dc69d8511775122 21256524 universe/source/Sources
+ e52b7cb491cc6a950cd11fa6263d7330 5019105 universe/source/Sources.bz2
+ c722166709cfe9c398643b9c1a443610 102 universe/source/Release
+ 5ddd8bd0dda063b203d1a1da150983a0 6238766 universe/source/Sources.gz
+SHA1:
+ 0b326daa3b2ac8748ca10942aaec15ebdcc78b36 1640344 main/binary-amd64/Packages.gz
+ 8a6068a75feb86e12b088dad2478600f6670f2d7 7818931 main/binary-amd64/Packages
+ 19655f20d48d9819ad95f2c9ecc59e5b1d94c3d0 97 main/binary-amd64/Release
+ f9761ecf8536859ef38b670a7f17d83febec4a37 1272844 main/binary-amd64/Packages.bz2
+ a3a5faadbdf0a49d1587f07181b9eca870cb24ce 97 main/binary-armel/Release
+ 3c5d8f3a401427110adfd7f7d65513bfa47b933a 1619078 main/binary-armel/Packages.gz
+ 4d54e387978fdf829b4ff7336ed4d02e61c54d6a 1257389 main/binary-armel/Packages.bz2
+ 969dfd243cc3f514eea9914de571db28621cd9bd 7743353 main/binary-armel/Packages
+ f8f33265eab2ff9fd8a6353e014e36a9d318a54d 97 main/binary-armhf/Release
+ 1d210f8ad3547b771bc8c8d2a9eb5ee99d437d81 7620333 main/binary-armhf/Packages
+ 5057a8b2d11d2325bb2546ad6613517b208fff18 1257653 main/binary-armhf/Packages.bz2
+ 25fc010c2789028727308fbf6132a8da387423b8 1617483 main/binary-armhf/Packages.gz
+ 79369a31bc481f7f9f71f666906c3bdb356c44d8 1273857 main/binary-i386/Packages.bz2
+ aaadaa6eaf0b7e73b0d371cdebae573f28833a43 1641082 main/binary-i386/Packages.gz
+ 6f8e5e9dd04a2379a7c6d8dc23b4ff8df5584741 96 main/binary-i386/Release
+ 8509eead0c5e9410e23d4226ee5d190220db1275 7816415 main/binary-i386/Packages
+ 7da3fd8ad7102912a4cc9882c66fbb1b65edd4c0 1627734 main/binary-powerpc/Packages.gz
+ 8a0c3b7757b738a89644e8c5a3b9afed806d2f83 99 main/binary-powerpc/Release
+ 4063338a28f6504f7bc2f9c00fc34ac26fc5a1fa 1263942 main/binary-powerpc/Packages.bz2
+ 81844e44aaf0ed255ca200e7316bc9279ef238b6 7661552 main/binary-powerpc/Packages
+ d17439034551742aaa4762b4c174c1d72881f49f 62166 main/debian-installer/binary-amd64/Packages.gz
+ 05e024776b06a40253b9e252caad6dc4a13a90b9 234592 main/debian-installer/binary-amd64/Packages
+ fa692a307ecab69c106a5253107f980527cf58a9 48784 main/debian-installer/binary-amd64/Packages.bz2
+ fb8be98ef08265d2f101cf265b9bf37654d750ac 47964 main/debian-installer/binary-armel/Packages.bz2
+ 8953964fd2b54539e8276237cd5e4e2d4f9dc2ab 230310 main/debian-installer/binary-armel/Packages
+ cfdde60bbcbfb25c66fa474d28217e03a4c06789 61118 main/debian-installer/binary-armel/Packages.gz
+ 468306dc06acc828c50196427ba324c8ce225c79 62777 main/debian-installer/binary-armhf/Packages.gz
+ 17e7331a837675de31c365529ea9454cc48baf98 49051 main/debian-installer/binary-armhf/Packages.bz2
+ 3404140163a5acece9a054912abf72d11a5591d0 238862 main/debian-installer/binary-armhf/Packages
+ 2bb7e052999eadc333a9f6481bdc20acb238d4c1 52279 main/debian-installer/binary-i386/Packages.bz2
+ b2ad69cd6759724461c3566d91c1c5ec209eba07 259996 main/debian-installer/binary-i386/Packages
+ ebd0ab3923b3df4eca53eb863773bf0a9a4c3a67 67180 main/debian-installer/binary-i386/Packages.gz
+ eff551b62b72a1c0b183d4903a1452f2be08f3da 246636 main/debian-installer/binary-powerpc/Packages
+ 5c8aa09d48d7a792d555e7b16f4c89733e8441af 50309 main/debian-installer/binary-powerpc/Packages.bz2
+ 582a86208e89e77a7f284e10a50b9cceaf4358ee 64468 main/debian-installer/binary-powerpc/Packages.gz
+ f426621b1015147e766dc003d1d2f140dc9c062d 3706 main/i18n/Index
+ b22a946cdae39d29535a63b020c0f2ad74c3c992 4356187 main/source/Sources
+ 711225fd252e77d85c7bb992aeb5aa5e45414ae1 933770 main/source/Sources.bz2
+ a8807bf20dbaceb9d408f959840951131fd7d9f9 1174967 main/source/Sources.gz
+ cc2d2f6ce53597666df1a5ab216a08f08995f43f 98 main/source/Release
+ 06b070fc1ae771806c65e791742832320cc588e7 119109 multiverse/binary-amd64/Packages.bz2
+ ae591617f4b6f988d6534270d4c32e3f0876166b 151924 multiverse/binary-amd64/Packages.gz
+ a28786a87a6136fb74a005c480c78a152123b9c0 581550 multiverse/binary-amd64/Packages
+ 3860a3ce6d87bb8a43803a1048cc96d4de3ae8a7 103 multiverse/binary-amd64/Release
+ 4bae80c3f5270e45f825141bcadb94d7dd82cd0e 136295 multiverse/binary-armel/Packages.gz
+ 9534dfc2e4a40386f48eff79062ee1742212b7d3 519605 multiverse/binary-armel/Packages
+ 92d1536ae99c958afd74299ffe30bdc6800cd8d0 103 multiverse/binary-armel/Release
+ 8e3ec557dc8f750e82e8ed5cbb0c509182feba79 106873 multiverse/binary-armel/Packages.bz2
+ 009531e7cfb08701883a9e7a5f50235325e109cd 104529 multiverse/binary-armhf/Packages.bz2
+ 596a666233560852574d9bca0109f4933a12c949 505901 multiverse/binary-armhf/Packages
+ eca4ab620c41d76c00a9615d8530db5a9c918fe8 103 multiverse/binary-armhf/Release
+ 53916a24cff0523e218bfee2c5464866d2099042 133117 multiverse/binary-armhf/Packages.gz
+ a7518b6b3e693d840a49b125020785d2049e75b9 121196 multiverse/binary-i386/Packages.bz2
+ 84069f1643bd0092f4ec1b24eb921310532d72b2 591662 multiverse/binary-i386/Packages
+ 1d42b054d297b33cf69af011fa91f601c6b1a1b9 102 multiverse/binary-i386/Release
+ 8eae892b29234e9aac4c58a7098b097fe5ebde16 154762 multiverse/binary-i386/Packages.gz
+ 873bc8f864de428a4a47a3afaf53bc3a3a6e81c0 105 multiverse/binary-powerpc/Release
+ 665942774934afa56721d082a398735a067afe91 107209 multiverse/binary-powerpc/Packages.bz2
+ c965416be0f7bf88c78394becf4a72aee342b829 520882 multiverse/binary-powerpc/Packages
+ e304951d28df0b75065fd3ef8166c65415a3cad2 136930 multiverse/binary-powerpc/Packages.gz
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-amd64/Packages
+ a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 multiverse/debian-installer/binary-amd64/Packages.gz
+ 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 multiverse/debian-installer/binary-amd64/Packages.bz2
+ 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 multiverse/debian-installer/binary-armel/Packages.bz2
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-armel/Packages
+ a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 multiverse/debian-installer/binary-armel/Packages.gz
+ a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 multiverse/debian-installer/binary-armhf/Packages.gz
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-armhf/Packages
+ 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 multiverse/debian-installer/binary-armhf/Packages.bz2
+ 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 multiverse/debian-installer/binary-i386/Packages.bz2
+ a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 multiverse/debian-installer/binary-i386/Packages.gz
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-i386/Packages
+ a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 multiverse/debian-installer/binary-powerpc/Packages.gz
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 multiverse/debian-installer/binary-powerpc/Packages
+ 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 multiverse/debian-installer/binary-powerpc/Packages.bz2
+ 09a8aff01c419e0eae19749acf3b223e1f95eccc 2676 multiverse/i18n/Index
+ a088c61bca210afebf4c53904049e2a969d8ece3 154990 multiverse/source/Sources.bz2
+ df4078a91645024ce93b1ee363003c83ffa2aa84 628753 multiverse/source/Sources
+ e6ec0430d594fa7fa2c98cb9a5fa8ac7ed0d506d 188325 multiverse/source/Sources.gz
+ 0750588f28b1c2cf6a005501c7d027cfa663cce1 104 multiverse/source/Release
+ 200fdeee1984ac78b6fdabfad34d2b485512ca2d 9098 restricted/binary-amd64/Packages.gz
+ 0e7ebd3d2690c7f89d19f8b337cf292cac913c18 134705 restricted/binary-amd64/Packages
+ c968d68baa554ffc7009688ee7d0d3e70663243c 103 restricted/binary-amd64/Release
+ 4185a5b6c2e702ea4754437ebfef23d828ec67a0 8452 restricted/binary-amd64/Packages.bz2
+ 27886dcc6c7d08369cc65d4c35f26b806f57be56 103 restricted/binary-armel/Release
+ a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 restricted/binary-armel/Packages.gz
+ 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 restricted/binary-armel/Packages.bz2
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/binary-armel/Packages
+ d83803a5f6c2f5c7321d68d126733b6015c8e2a9 103 restricted/binary-armhf/Release
+ 6607111b78dcd3de05cb88692c1e2142abf9a4c1 1103 restricted/binary-armhf/Packages.bz2
+ b9f55d161632f89b5125a638ca6ad4fe5e9d11fe 2477 restricted/binary-armhf/Packages
+ d0c0a616aeac580d13256693cfbd507ae7c9b280 941 restricted/binary-armhf/Packages.gz
+ f3e483bbe77cbf0f64accbd0291df19b4e4d694b 9108 restricted/binary-i386/Packages.gz
+ 48d76b03a19e4d66d6f7a20339dab91acebaba99 8431 restricted/binary-i386/Packages.bz2
+ 3ca46ea8f32b7cbc49ce76dd1c1ab91589900fd7 134582 restricted/binary-i386/Packages
+ 2aad61f5084ecdd64ff520520d77e980e8b21f81 102 restricted/binary-i386/Release
+ a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 restricted/binary-powerpc/Packages.gz
+ 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 restricted/binary-powerpc/Packages.bz2
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/binary-powerpc/Packages
+ 6b0a230bbf47463e3d7e88f535e210aaa96a1251 105 restricted/binary-powerpc/Release
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-amd64/Packages
+ 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 restricted/debian-installer/binary-amd64/Packages.bz2
+ a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 restricted/debian-installer/binary-amd64/Packages.gz
+ a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 restricted/debian-installer/binary-armel/Packages.gz
+ 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 restricted/debian-installer/binary-armel/Packages.bz2
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-armel/Packages
+ 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 restricted/debian-installer/binary-armhf/Packages.bz2
+ a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 restricted/debian-installer/binary-armhf/Packages.gz
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-armhf/Packages
+ a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 restricted/debian-installer/binary-i386/Packages.gz
+ 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 restricted/debian-installer/binary-i386/Packages.bz2
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-i386/Packages
+ a0fddd5458378c1bf3c10dd2f5c060d1347741ed 20 restricted/debian-installer/binary-powerpc/Packages.gz
+ 64a543afbb5f4bf728636bdcbbe7a2ed0804adc2 14 restricted/debian-installer/binary-powerpc/Packages.bz2
+ da39a3ee5e6b4b0d3255bfef95601890afd80709 0 restricted/debian-installer/binary-powerpc/Packages
+ d59f5ac2532dcc7cb2f1e5b788c700bf8ab13b66 2596 restricted/i18n/Index
+ 8a80cdcf50cdcabcf4f47c772d7b252587cc9dc1 19001 restricted/source/Sources
+ ff7d82cf1d965953c745d224a1d4adc67b586528 5470 restricted/source/Sources.bz2
+ f64da9da2038712c5d73ce3337e91d92ee39cd30 5306 restricted/source/Sources.gz
+ 5c963f5f4d4720afa5fbb914375d0033bcd50078 104 restricted/source/Release
+ 10c6989f4a241aabe00146905e776391fc4d9ac0 25546870 universe/binary-amd64/Packages
+ f6900616102430e0eafa8ac89795efff7edc0710 4785960 universe/binary-amd64/Packages.bz2
+ 4f938bde9dff32a49bccd917613666017185882d 6166988 universe/binary-amd64/Packages.gz
+ 9d6cba1ed46b5eee1f6c065934e5f4854b3efee5 101 universe/binary-amd64/Release
+ 3573e863c714c0a083f9c962ea9136916f796e92 101 universe/binary-armel/Release
+ bb8f53ead3723737c13950e5343327884f737da9 4667308 universe/binary-armel/Packages.bz2
+ 43d9a98706b6e50c21092ce35313b5b034f30d01 6009219 universe/binary-armel/Packages.gz
+ 7f97dcbe710882d281efcfd2f8d70ca7d4e47265 24901082 universe/binary-armel/Packages
+ 8f6d42d7f8178a51a7065d7bf3234eecbca12810 5948128 universe/binary-armhf/Packages.gz
+ ea648f433d0edd47de820582fa6a1cd89ef66681 101 universe/binary-armhf/Release
+ e6ad9bdb18ce9e09e262b9e6f85b0b307a3456f1 4618508 universe/binary-armhf/Packages.bz2
+ ba55530da6c3a9604977ea4a37b4b4d8943ff994 24642528 universe/binary-armhf/Packages
+ 05de59263866a33c104787943347164e7b124aba 6179579 universe/binary-i386/Packages.gz
+ 286a3dd2fda0d98d2ff14eafee0bba5c912d6df2 25568759 universe/binary-i386/Packages
+ 839529b6f6e2a64465e4a825fa5ad46a36f1c73d 100 universe/binary-i386/Release
+ 70c27be4d8bc87dc26bbe6f21fbb7328dc1c4d01 4795820 universe/binary-i386/Packages.bz2
+ 06af7fdef1a54920a7667728a7b810553d00a9c8 25188905 universe/binary-powerpc/Packages
+ 4df1b07075b25ccae6e60d26d5a5761444ece689 6080488 universe/binary-powerpc/Packages.gz
+ 15d041ad1284527b07f75218cf6eb32322092f84 4716652 universe/binary-powerpc/Packages.bz2
+ 42d6a46ed2e525b219f8f6dc076dadbd06fc7f1b 103 universe/binary-powerpc/Release
+ 92c3bef6ad40051021a4e9dadb16e5edc7410b57 15255 universe/debian-installer/binary-amd64/Packages.bz2
+ 31d8725b0d238c282d9b572bb56b7e45c0ff53f8 17243 universe/debian-installer/binary-amd64/Packages.gz
+ e334a7c80e14dae3055950d9e45213db65d4087b 61801 universe/debian-installer/binary-amd64/Packages
+ 1fd1ae2b87eb85525b173ea982d15f3c98e6e33d 113584 universe/debian-installer/binary-armel/Packages
+ c56579feb77b5aac2d261abbbb6c89a1458ca4d9 23193 universe/debian-installer/binary-armel/Packages.bz2
+ e4b08e57397f3ab0599e0bf6a2fbcba3aed438b5 27397 universe/debian-installer/binary-armel/Packages.gz
+ 5a9608213061eab983392258288e8aec36d006f0 20065 universe/debian-installer/binary-armhf/Packages.gz
+ dc558d56aef72991a3188909a14f752aabdee325 17619 universe/debian-installer/binary-armhf/Packages.bz2
+ c6afa0e14c706aceea60aad033b6a067320fc165 76034 universe/debian-installer/binary-armhf/Packages
+ f359cf916a19055133546a7b7f3e35c7c260488e 17260 universe/debian-installer/binary-i386/Packages.gz
+ 23920ee4974d88ba824b0e884f8df6e2711a20db 15272 universe/debian-installer/binary-i386/Packages.bz2
+ 21cfd020eefc848307fac14b8f0efe0d6cd9c6ea 61718 universe/debian-installer/binary-i386/Packages
+ f2ff72c8f1fe4ce40ccf44f8ccd6623cedf4e6f0 61121 universe/debian-installer/binary-powerpc/Packages
+ bd9c731941b261b22c022d97ca139bfeb1ad70ba 15024 universe/debian-installer/binary-powerpc/Packages.bz2
+ e173071bda799068783c9192bff6536db9790a27 16860 universe/debian-installer/binary-powerpc/Packages.gz
+ 59a86abaed7cab292600b6766b18752b7e7c3d49 2922 universe/i18n/Index
+ 4b0ed5f327b0fa9b3f9d9410933a3d2afe467a7e 21256524 universe/source/Sources
+ d0525203f9ad5ec9183996e6765d0ef9a024691f 5019105 universe/source/Sources.bz2
+ 00847d46051ba44d436000b0394b218503de125b 102 universe/source/Release
+ d9706a8ab2ffeadb51b50d042712536a95dc4343 6238766 universe/source/Sources.gz
+SHA256:
+ 0d61aacd269015c0abfe01fe7f90a4f534c368e9c513f7e90d3111af82656b3d 1640344 main/binary-amd64/Packages.gz
+ a1bc8d839ca9966a0b924e4a4c60f1c23b4d431deb81e1bc529edf95f30fc29d 7818931 main/binary-amd64/Packages
+ a55d3b2e6e2a175529d73e6ca92989018cf57745e705f7ff675b05f80e5141f7 97 main/binary-amd64/Release
+ cc0d3a19c51188b4b4acb80e3013264462c6e0f60759bfd46206c60681bd4ba9 1272844 main/binary-amd64/Packages.bz2
+ a841750f49bd11f99b9dae6941d2fa6ec1fd87906139d0ceeacf0d4df57a87cf 97 main/binary-armel/Release
+ e80eaf12c1aa520b353de8ad97e79364779e82ef011cb93db372edc900eb7be9 1619078 main/binary-armel/Packages.gz
+ 0ab0929c3c44837886e532f8ec4bee77f3664bdcc2cc3192a02b991c52b156ce 1257389 main/binary-armel/Packages.bz2
+ ea400cf67f84c12265e4bf419de442a38880fab37d76999985972fc6df3e13b3 7743353 main/binary-armel/Packages
+ f3c40f057bb085f28ef2ed950f62366483d2a418571435c355dc27c0912dccff 97 main/binary-armhf/Release
+ cde037224f43e4619213c5195f2ea5c2f91d078f449579652dbd4128793d5062 7620333 main/binary-armhf/Packages
+ 7c6ea67e609b96dec6f2185df4cd81160e37ba467f9132a9bfb101da3f9a0468 1257653 main/binary-armhf/Packages.bz2
+ 2b83bf5501ecae3347e5c96658edae7d48eef42108b4786471a3de241e75e7d3 1617483 main/binary-armhf/Packages.gz
+ 4d74c53917b84d37cb3277e2b755672a8733e2cfaf949f6e644e6e88094cdaa2 1273857 main/binary-i386/Packages.bz2
+ 07f33bddfefdb4a0c44ddee59fd3eca497df2ad0456e0eeade136e4f0302ee3c 1641082 main/binary-i386/Packages.gz
+ 5182e22f799fe66c8db6dfb073fb040e9e583d88bb9d4d77e058b2afb87e9479 96 main/binary-i386/Release
+ 4cecfc8c0d2113d51b03afa8fdcdcc963d9ad74474696472ec1cdbdb38b856e2 7816415 main/binary-i386/Packages
+ 2795904625f466b4a2fb96d41c00b000ab7f2bdc7f288b0c9ef1283d7e110f87 1627734 main/binary-powerpc/Packages.gz
+ a9247e6d8b0c5977bc1e72be09b1f42a83b5f5a6a70b17f4fef35a0657e3c206 99 main/binary-powerpc/Release
+ 6e745b7edcd67755fa09f54cc3afdd0ffbd0475302a74293472e97e46ba75ddc 1263942 main/binary-powerpc/Packages.bz2
+ 8b953dbae4a14e7ab47151044a47c7b0f0e1dd2a6480170b8172e00d9dad7a2a 7661552 main/binary-powerpc/Packages
+ a5bdf4116ecfeda052d5b3751138c6153e814ac58b2f551503f3ed90e6c3510c 62166 main/debian-installer/binary-amd64/Packages.gz
+ 6a9a4837a4a7df3e7e0566b354b7f1dd2dcf46254335ae3d06a72538f85e410a 234592 main/debian-installer/binary-amd64/Packages
+ 0da4f8190eebfef22103f1f6f7051adbe9489d454ab7224f09d05646407881e8 48784 main/debian-installer/binary-amd64/Packages.bz2
+ ea01244357deb22c2a4bd7eaa34a1635c0915b89ce174f312c0e0a4b081eaf30 47964 main/debian-installer/binary-armel/Packages.bz2
+ 4177b0519c75f7f950e5a0f0d72d40cc0c4ccf29ebe89fbb9bc1f11a80874526 230310 main/debian-installer/binary-armel/Packages
+ 2b6f81ef9fa687bfb2eb56bb3e90faef0c012351d096b141caa710fd50846043 61118 main/debian-installer/binary-armel/Packages.gz
+ 52c834247ff3a5475466e647802f6eff393f85589e5da5fd3e5b497669b8b49c 62777 main/debian-installer/binary-armhf/Packages.gz
+ 7090f1ad1307a012fbfff883885f16099ed66ebffdeece356f837e632b177a4f 49051 main/debian-installer/binary-armhf/Packages.bz2
+ 4bae13f507993e977c279937406fc03e37fede7805a92508f6d3cba76f1aaf95 238862 main/debian-installer/binary-armhf/Packages
+ d1d23926ff15cfaa6160c5fd0327d181721093fbd2f2e8125be5559a991a81a8 52279 main/debian-installer/binary-i386/Packages.bz2
+ 30e0ec7a2c3d47d5501de8414b436482ff523e9c4983b536b2c9911a30618b98 259996 main/debian-installer/binary-i386/Packages
+ 7a4dcb001ed4bb5fa2458af901de312e11a745bc86a0f877e47567d0f911bc0d 67180 main/debian-installer/binary-i386/Packages.gz
+ 30c2f590c2dedc9f78dfc7f0026b51bc9baa712ac8c9310404d9d6577af77d90 246636 main/debian-installer/binary-powerpc/Packages
+ 6c53fab780cf774c5cabb1788eea1e3446c528cde4352d106907f4ec22449370 50309 main/debian-installer/binary-powerpc/Packages.bz2
+ b003f3fbf2fa6dbf7d47cf3fbd029ecd86b316ec497a9ac4eea3614cf4ec76af 64468 main/debian-installer/binary-powerpc/Packages.gz
+ fefed230e286d832ab6eb0fb7b72442165b50df23a68402ae6e9d265a31920a2 3706 main/i18n/Index
+ bb618cebb361a2a7148be0bad9af012c8d9de23dbc32d6d9ba035fa6ee0078ab 4356187 main/source/Sources
+ 0aeef2c2258136f9f774c36a158cf759389acf6a35a3153a03d3fa41d4f346d5 933770 main/source/Sources.bz2
+ 4a058ba65244e8eaf17d159b72edebe4e621d54c274a82d4a973b358b4af9a28 1174967 main/source/Sources.gz
+ 864ba9a26e348c6297c08c047d8c228e5ed031ec3d46ef7aad93c3fa550395a8 98 main/source/Release
+ 85477d2b2e7ea2f46b6a059f7526cf52d75fea1a5120aa3b256c576e904d40ff 119109 multiverse/binary-amd64/Packages.bz2
+ 2967ae6c1cc065bec03225d808b4511b138cc13b8de801a0562fec6e30710f36 151924 multiverse/binary-amd64/Packages.gz
+ 18fcf61bb74ef2a01c3d4a8d4646a75836f43244168b43d6ae202f368167b224 581550 multiverse/binary-amd64/Packages
+ fbc4931ef84d50a39da65d110f787aee274df8819a758a3c0aa1ff13f0ba6ee0 103 multiverse/binary-amd64/Release
+ 49f48a34696d08a13a0fdc19a0f6896af2cb477e72860a8880954926c7d45e60 136295 multiverse/binary-armel/Packages.gz
+ f20d7f0bc32b5b2fbcb442f7c128aaad7e18aece3781d53f560932bb191d6830 519605 multiverse/binary-armel/Packages
+ f7ea72b2c07af81f2e342414025dce7a658a6a9915c4d8adc13b992cb3b9fd2f 103 multiverse/binary-armel/Release
+ dd3d4e8a6ec9055d5b553af49822de74648f071ceb0fd314d6cd1aaf7ad6882b 106873 multiverse/binary-armel/Packages.bz2
+ 567c1f9d30a4d6650552d66c5fb43d2d8910d3fed69793daed622d2c699f4bc8 104529 multiverse/binary-armhf/Packages.bz2
+ a47ef2c0a68adeb70a0bc6b22c94b08402396ff6f5c77664e06c2fb7ee0e7ab0 505901 multiverse/binary-armhf/Packages
+ 6b95e8edaa2bb799f6e15a4a6aaf223da0faea670cd03340395bdcea90205afc 103 multiverse/binary-armhf/Release
+ 14721b333f19a6344addb185f161d1cd14e04ac284c8fa9d726064ec228269a8 133117 multiverse/binary-armhf/Packages.gz
+ 454436f374186007075445c1f206ba5c926f30609baa732c495f1ba456d71e59 121196 multiverse/binary-i386/Packages.bz2
+ 9fabd7bfdbfd216968f7a17265e5609cdd72f1ea7c8f50941e294694e76b180d 591662 multiverse/binary-i386/Packages
+ 7141881b898ac6a78f1ca6f3e81481ee6657f6762fa22768816ab39f6b17e695 102 multiverse/binary-i386/Release
+ 3f4cae31df741f55d523ecea758d05a7e012a205bb03974ee20eb09e3f4fa63b 154762 multiverse/binary-i386/Packages.gz
+ 332dde644a8467496eb5f45ffd2d735ca61ea781da21cd205b3267cd83fa0563 105 multiverse/binary-powerpc/Release
+ 99ef0a611aa32ffa4f16a006d641fbd8dd9e3e73bde3c93b831cd6583746e64b 107209 multiverse/binary-powerpc/Packages.bz2
+ 60f2431dab7bd02fe2c2428bf400c3535be49641cc9d5645a8f1b4fd44f5086f 520882 multiverse/binary-powerpc/Packages
+ cacfd10b40992a617ce32c479f9505531c8cc57e4cf964687d663a5f41f8dcbd 136930 multiverse/binary-powerpc/Packages.gz
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-amd64/Packages
+ f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 multiverse/debian-installer/binary-amd64/Packages.gz
+ d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 multiverse/debian-installer/binary-amd64/Packages.bz2
+ d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 multiverse/debian-installer/binary-armel/Packages.bz2
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-armel/Packages
+ f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 multiverse/debian-installer/binary-armel/Packages.gz
+ f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 multiverse/debian-installer/binary-armhf/Packages.gz
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-armhf/Packages
+ d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 multiverse/debian-installer/binary-armhf/Packages.bz2
+ d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 multiverse/debian-installer/binary-i386/Packages.bz2
+ f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 multiverse/debian-installer/binary-i386/Packages.gz
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-i386/Packages
+ f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 multiverse/debian-installer/binary-powerpc/Packages.gz
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 multiverse/debian-installer/binary-powerpc/Packages
+ d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 multiverse/debian-installer/binary-powerpc/Packages.bz2
+ f0b16a5cfd2d633c9ddecfadfa6742544b18c23ed30023286e2b20ef29f33c73 2676 multiverse/i18n/Index
+ faa0360612fc00453dfdd55b6a1bb20e4f876e041ad6fca410d5da65608ab31e 154990 multiverse/source/Sources.bz2
+ 2f0deae62e2cf7e5257bbd858cb0bf2a94122c4eb82be13e13768d0b9ce84c9e 628753 multiverse/source/Sources
+ 28f6d95fcba03e442cf24dc547653d5ec60177a29d7cfea771efcc5501077747 188325 multiverse/source/Sources.gz
+ f35f721bf16691842cc916c3563fab535f6bb83329f40c33ac02f4ba637707d3 104 multiverse/source/Release
+ 3e872fa356cbce4dfd75a88caa4fc6b47616e1fc7d224f4fc2123650fd7f4be3 9098 restricted/binary-amd64/Packages.gz
+ 459a26c3ef3cb5db8c8355ea6abfa8cfe0a7a266a197929d86d37686daf8a337 134705 restricted/binary-amd64/Packages
+ ea47572182da041b46543e471cb7a6fcc4e001fbe19a27740085ebf5d77252a9 103 restricted/binary-amd64/Release
+ adb08d7f0fa444f2869e8d932db7adb1515839f11af6032284cf1e20060e2dd6 8452 restricted/binary-amd64/Packages.bz2
+ 65a5ac0820d61383f7dcf33699aa029b5965b7906bc8341f94f8f7f354cdcd83 103 restricted/binary-armel/Release
+ f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 restricted/binary-armel/Packages.gz
+ d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 restricted/binary-armel/Packages.bz2
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/binary-armel/Packages
+ ee447ce81793bd3bc8c127d4e065c6ec24e5901573dffb7cc5abedfbcb86592c 103 restricted/binary-armhf/Release
+ d88ed7df97cd60cdce35c3ca81de66e2bedf0f22e67ec8922dbd5eca545b5e50 1103 restricted/binary-armhf/Packages.bz2
+ 9ade66f4a49598fb371705a79244e5f3abb74c04467f9f9954641ae5acec6766 2477 restricted/binary-armhf/Packages
+ 03d8b64c445f327ce9e369bca815652844bd6aafb344d0287fb4e71f321d0414 941 restricted/binary-armhf/Packages.gz
+ 07e344ed07234876c3fddd9aa763e04bfc2e013fc18428738be71abfb9e1ca77 9108 restricted/binary-i386/Packages.gz
+ 8061335b923c49e72a2b60b437d5bbad1b98a45ac178a68fd8359cec9fad27ec 8431 restricted/binary-i386/Packages.bz2
+ 122336146860047af3d5817dbc423f01d57a90cbf41db1ee0ad9235c0559a43e 134582 restricted/binary-i386/Packages
+ 58634ed42b6fadb280d48f419b960e28151320a62b4486e520ca327719db554a 102 restricted/binary-i386/Release
+ f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 restricted/binary-powerpc/Packages.gz
+ d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 restricted/binary-powerpc/Packages.bz2
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/binary-powerpc/Packages
+ d9bce398e46f0eac57d1d33fd8a6caa0bd7ab6334508c0640956cb7adbe1eba1 105 restricted/binary-powerpc/Release
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-amd64/Packages
+ d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 restricted/debian-installer/binary-amd64/Packages.bz2
+ f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 restricted/debian-installer/binary-amd64/Packages.gz
+ f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 restricted/debian-installer/binary-armel/Packages.gz
+ d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 restricted/debian-installer/binary-armel/Packages.bz2
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-armel/Packages
+ d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 restricted/debian-installer/binary-armhf/Packages.bz2
+ f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 restricted/debian-installer/binary-armhf/Packages.gz
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-armhf/Packages
+ f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 restricted/debian-installer/binary-i386/Packages.gz
+ d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 restricted/debian-installer/binary-i386/Packages.bz2
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-i386/Packages
+ f61f27bd17de546264aa58f40f3aafaac7021e0ef69c17f6b1b4cd7664a037ec 20 restricted/debian-installer/binary-powerpc/Packages.gz
+ d3dda84eb03b9738d118eb2be78e246106900493c0ae07819ad60815134a8058 14 restricted/debian-installer/binary-powerpc/Packages.bz2
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 restricted/debian-installer/binary-powerpc/Packages
+ 17dde58abfdb4dfdad9c8a82db09c9dbc3d8a7cd84b51dd9167579d6899e9ff5 2596 restricted/i18n/Index
+ ee3655459e45778fdfa06fb649565e66b25d2dd0870c75890005fb3597bb71d7 19001 restricted/source/Sources
+ cff18d2ad74ead8712f1b77a23b32e84e54269b03ba2a409ae4227860d1181f5 5470 restricted/source/Sources.bz2
+ cf085bdcb323dd2c2a599ddb7a9b3ae7bd37121f42024d68b367a4f735df900f 5306 restricted/source/Sources.gz
+ 9137393fc24cf64808d55ca7665bc5a7bd46b48918e6720a95ba239a8fab092e 104 restricted/source/Release
+ 901469729d2354891be94c192dabd8c1d0bc31e1497ea8360b70d2e847c1f3c1 25546870 universe/binary-amd64/Packages
+ 799347395d4e011a215aa5ce0c9006449d8af884795ffbce7a35767a55f99074 4785960 universe/binary-amd64/Packages.bz2
+ 68b08847604c4efe7d6f56ba79f923ce0ab82127dfcc6e8cffaf12af25d7adba 6166988 universe/binary-amd64/Packages.gz
+ 52ffdd1777a886edc5e1e1ef430b03a72937920f9722fd453ee8243cb0aac860 101 universe/binary-amd64/Release
+ a78a1304e105b2fe4c950c77c1794f715c1256d14d8541cca8f5cd13db48119e 101 universe/binary-armel/Release
+ d6d4bfa5d0891086f5a4f2aabfaecd7a1e0c0d8b46aef33b3470e349e7a9210e 4667308 universe/binary-armel/Packages.bz2
+ 374d50d0335c655da46f9cd54cd00d9a20058d2fe7c56989aa121b49883cfb88 6009219 universe/binary-armel/Packages.gz
+ ab5073e90417b729d1fe3b68052e6a8e66e48986c35470944f6a58676e967450 24901082 universe/binary-armel/Packages
+ af74034d1a3e1f90745dc48b996a98c471d997b12a1d810eb8754088540591d7 5948128 universe/binary-armhf/Packages.gz
+ 6697d196b35850817476e884fdc013d9670b4bac73310c54a4d62cd810f02c70 101 universe/binary-armhf/Release
+ 1ca17d3aecec2325cba53e1c299aeb6a1fed01d7acbc40163595de9e651abdeb 4618508 universe/binary-armhf/Packages.bz2
+ 2b422ffa77d4374650d4cc543c5a1123b2535effb2c8cdaf25fd77d1dde632c4 24642528 universe/binary-armhf/Packages
+ cd6b5cb8165553482abee1bf85e5cd3288abadccb6acf34239ec45f79a090784 6179579 universe/binary-i386/Packages.gz
+ 8ef7db20ba08cf1b4d98a618189c615c69865f4da025ac654e3e6b8a4382a3ae 25568759 universe/binary-i386/Packages
+ 06af492500145bd64762d885417d167269db6ea03022c6968f1a5d0515ac55dd 100 universe/binary-i386/Release
+ 530a2efb8051a63ed17431ae0c7243df79ecb418acf1dadc2487cd6fd79fb420 4795820 universe/binary-i386/Packages.bz2
+ 1e8fa52a64292d2c73cee0645d0eed5583ea7cc1138af4744838c6833716d638 25188905 universe/binary-powerpc/Packages
+ 5d2b8e23e0a16f13e25595b63807fb64afd9074aadf7a37b8e238b2011e894b8 6080488 universe/binary-powerpc/Packages.gz
+ eb482b008c8c882b349230abaa812ed6e545a2ef9132bb0d3d3bffa74da0c6c7 4716652 universe/binary-powerpc/Packages.bz2
+ 98d44cc7544f79c18b8e8ea697d476e9b85d91088385b643c82d4064b21f4657 103 universe/binary-powerpc/Release
+ 3da2d1e57aaca628148e2688a951cbb784a9a54b7f6b1f84d530add1b66fcceb 15255 universe/debian-installer/binary-amd64/Packages.bz2
+ 43f891ac590f44fde5854de9ba15222c088b70562f5dc4ff26064909e60cf62e 17243 universe/debian-installer/binary-amd64/Packages.gz
+ 3084a8a441e961eeb3865ff411557166ec105be86a55df268cdb6725f49e1f67 61801 universe/debian-installer/binary-amd64/Packages
+ a1ff01f18766744f36d0774a68d8a89355246c585c4b28ee18e5e139fafae530 113584 universe/debian-installer/binary-armel/Packages
+ e0713f86f5f5121deb60ce61d774951468625184a7ae9576f81d70202ef585b7 23193 universe/debian-installer/binary-armel/Packages.bz2
+ 5a8411e2b0648e553fa25ac82ea83fb17dd2d2a77bd10cec14cab12f5582d4c4 27397 universe/debian-installer/binary-armel/Packages.gz
+ b79c86d926c3129f5c27e50185157a78d85abde8ada90a9910338e660c4318be 20065 universe/debian-installer/binary-armhf/Packages.gz
+ b2113b25380423be8f6202a4860479e44a00072e46fa035f0da2f3a5a280de20 17619 universe/debian-installer/binary-armhf/Packages.bz2
+ 3f023d2cc55d6ebab883f6f2d7305a4e3564f918f63ca4f745d6fd1318e67ab7 76034 universe/debian-installer/binary-armhf/Packages
+ 5ea61a62a3e8fc755c22e23c9d87b20924707c0986a490458472a3d7e9cbe117 17260 universe/debian-installer/binary-i386/Packages.gz
+ 7a90b014c655311e92de1ea4cf24e100665c564a2ed699df63d17c82ad9e1349 15272 universe/debian-installer/binary-i386/Packages.bz2
+ 7e39417ce073e3a35d048847a29a0414af69c4e923c018dc22438319c79adea5 61718 universe/debian-installer/binary-i386/Packages
+ cdf17a791544d0c522fa853a23b317deffa76ac643e88bec0b84b0aa5afe957b 61121 universe/debian-installer/binary-powerpc/Packages
+ b470146da791dc4f4593d2bb00ea4e305d6f55f346a5f3ac6755d890a3318080 15024 universe/debian-installer/binary-powerpc/Packages.bz2
+ 810d1590d1cd7298e1fd5465f85ba49b6ae79780b42d8e8b68aebb42283785ea 16860 universe/debian-installer/binary-powerpc/Packages.gz
+ 563a55a892e1ec8bf565e3294c033b4e8dbbbe4651e73733eac7338db77282f7 2922 universe/i18n/Index
+ 7bc01d4f10bbcf882ce6931aa9371b2de6b35277efc2ae52e233280dcd12a18d 21256524 universe/source/Sources
+ 95135631873f4dce05ba657478475033d02462bbb8f7263832585d1decb5c9b8 5019105 universe/source/Sources.bz2
+ 0fd2ae580be352cb8ab4bb87e5504b78f78bcb7249b644719b3c2db3b5d3ca8c 102 universe/source/Release
+ d1dd96015e24dd369ea22413a2b876686a60c5d9d91958a5df3745a66289910f 6238766 universe/source/Sources.gz
diff --git a/tests/data/tagfile/history.1.log.gz b/tests/data/tagfile/history.1.log.gz
new file mode 100644
index 0000000..4174e02
--- /dev/null
+++ b/tests/data/tagfile/history.1.log.gz
Binary files differ
diff --git a/tests/data/tagfile/history.log b/tests/data/tagfile/history.log
new file mode 100644
index 0000000..f1d7266
--- /dev/null
+++ b/tests/data/tagfile/history.log
@@ -0,0 +1,15 @@
+
+Start-Date: 2012-02-01 13:54:52
+Commandline: apt-get install chromium-browser
+Install: chromium-browser:amd64 (16.0.912.77~r118311-0ubuntu1), chromium-browser-l10n:amd64 (16.0.912.77~r118311-0ubuntu1, automatic), chromium-codecs-ffmpeg:amd64 (16.0.912.77~r118311-0ubuntu1, automatic)
+End-Date: 2012-02-01 13:55:01
+
+Start-Date: 2012-02-02 10:39:04
+Commandline: apt-get install python-geoclue
+Install: python-geoclue:amd64 (0.1.0-4build1)
+End-Date: 2012-02-02 10:39:08
+
+Start-Date: 2012-02-03 10:20:50
+Commandline: apt-get install python-qt4
+Install: python-qt4:amd64 (4.9-3ubuntu1)
+End-Date: 2012-02-03 10:20:55
diff --git a/tests/data/test-provides/etc/apt/sources.list b/tests/data/test-provides/etc/apt/sources.list
new file mode 100644
index 0000000..2955b5c
--- /dev/null
+++ b/tests/data/test-provides/etc/apt/sources.list
@@ -0,0 +1 @@
+deb http://archive.ubuntu.com/ubuntu oneiric main
diff --git a/tests/data/test-provides/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_oneiric_main_binary-i386_Packages b/tests/data/test-provides/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_oneiric_main_binary-i386_Packages
new file mode 100644
index 0000000..f991cb9
--- /dev/null
+++ b/tests/data/test-provides/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_oneiric_main_binary-i386_Packages
@@ -0,0 +1,75 @@
+Package: exim4-daemon-light
+Priority: extra
+Section: mail
+Installed-Size: 1092
+Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+Original-Maintainer: Exim4 Maintainers <pkg-exim4-maintainers@lists.alioth.debian.org>
+Architecture: all
+Source: exim4
+Version: 4.76-2ubuntu1
+Replaces: mail-transport-agent
+Provides: mail-transport-agent
+Conflicts: mail-transport-agent
+Filename: pool/main/e/exim4/exim4-daemon-light_4.76-2ubuntu1_i386.deb
+Size: 451088
+MD5sum: 81795887e233ecfb3471e8a3da4c4b8b
+SHA1: da0202f7da88b9e9c5ee326a24c3fdd640543a5d
+SHA256: 5ffa03674b5198d1c0e9e0d9e3442cdd7fbe13226c7df91aa53def1f8b6c0d86
+Description: lightweight Exim MTA (v4) daemon
+ Exim (v4) is a mail transport agent. This package contains the exim4
+ daemon with only basic features enabled. It works well with the
+ standard setups that are provided by Debian and includes support for
+ TLS encryption and the dlopen patch to allow dynamic loading of a
+ local_scan function.
+ .
+ The Debian exim4 packages have their own web page,
+ http://pkg-exim4.alioth.debian.org/. There is also a Debian-specific
+ FAQ list. Information about the way the Debian packages are
+ configured can be found in
+ /usr/share/doc/exim4-base/README.Debian.gz, which additionally contains
+ information about the way the Debian binary packages are built. The
+ very extensive upstream documentation is shipped in
+ /usr/share/doc/exim4-base/spec.txt.gz. To repeat the debconf-driven
+ configuration process in a standard setup, invoke dpkg-reconfigure
+ exim4-config. There is a Debian-centered mailing list,
+ pkg-exim4-users@lists.alioth.debian.org. Please ask Debian-specific
+ questions there, and only write to the upstream exim-users mailing
+ list if you are sure that your question is not Debian-specific. You
+ can find the subscription web page on
+ http://lists.alioth.debian.org/mailman/listinfo/pkg-exim4-users
+Homepage: http://www.exim.org/
+Bugs: https://bugs.launchpad.net/ubuntu/+filebug
+Origin: Ubuntu
+Supported: 18m
+
+Package: postfix
+Priority: optional
+Section: mail
+Installed-Size: 3472
+Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+Original-Maintainer: LaMont Jones <lamont@debian.org>
+Architecture: all
+Version: 2.8.3-1ubuntu1
+Replaces: mail-transport-agent
+Provides: default-mta, mail-transport-agent
+Recommends: python
+Suggests: procmail, postfix-mysql, postfix-pgsql, postfix-ldap, postfix-pcre, sasl2-bin, libsasl2-modules, dovecot-common, resolvconf, postfix-cdb, mail-reader, ufw
+Conflicts: libnss-db (<< 2.2-3), mail-transport-agent, smail
+Filename: pool/main/p/postfix/postfix_2.8.3-1ubuntu1_i386.deb
+Size: 1221834
+MD5sum: c4575f03ef0711cc744d394ae36f4a8c
+SHA1: f0f636c942981782cf23cee3d8a95aee188081b9
+SHA256: 13a4bfaf10c8addfc7dce01c0ea65bfbd6a759c4a2aaaafbae5855d099925fe0
+Description: High-performance mail transport agent
+ Postfix is Wietse Venema's mail transport agent that started life as an
+ alternative to the widely-used Sendmail program. Postfix attempts to
+ be fast, easy to administer, and secure, while at the same time being
+ sendmail compatible enough to not upset existing users. Thus, the outside
+ has a sendmail-ish flavor, but the inside is completely different.
+Homepage: http://www.postfix.org
+Bugs: https://bugs.launchpad.net/ubuntu/+filebug
+Origin: Ubuntu
+Supported: 18m
+Task: mail-server
+
+
diff --git a/tests/data/test-provides/var/lib/dpkg/status b/tests/data/test-provides/var/lib/dpkg/status
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/data/test-provides/var/lib/dpkg/status
diff --git a/tests/data/test-repo/Packages.gz b/tests/data/test-repo/Packages.gz
new file mode 100644
index 0000000..81daf2b
--- /dev/null
+++ b/tests/data/test-repo/Packages.gz
Binary files differ
diff --git a/tests/data/test-repo2/Packages.gz b/tests/data/test-repo2/Packages.gz
new file mode 100644
index 0000000..81daf2b
--- /dev/null
+++ b/tests/data/test-repo2/Packages.gz
Binary files differ
diff --git a/tests/data/test-signed-usable-repo/key.gpg.base64 b/tests/data/test-signed-usable-repo/key.gpg.base64
new file mode 100644
index 0000000..3bdec88
--- /dev/null
+++ b/tests/data/test-signed-usable-repo/key.gpg.base64
@@ -0,0 +1,62 @@
+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
+qipSuMIYrosA
diff --git a/tests/data/test-signed-usable-repo/seckey.asc b/tests/data/test-signed-usable-repo/seckey.asc
new file mode 100644
index 0000000..bcbf153
--- /dev/null
+++ b/tests/data/test-signed-usable-repo/seckey.asc
@@ -0,0 +1,158 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+
+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
+DZeT+S/3tXCwy1Jtoi7vcaoqUrjCGK6LAA==
+=mcY/
+-----END PGP PRIVATE KEY BLOCK-----
diff --git a/tests/data/test-signed-usable-repo/signed/Packages b/tests/data/test-signed-usable-repo/signed/Packages
new file mode 100644
index 0000000..3ca29c9
--- /dev/null
+++ b/tests/data/test-signed-usable-repo/signed/Packages
@@ -0,0 +1,26 @@
+Package: signed-not-usable
+Architecture: all
+Version: 1.0
+Multi-Arch: foreign
+Priority: optional
+Section: misc
+Maintainer: Julian Andres Klode <juliank@ubuntu.com>
+Installed-Size: 9
+Filename: ./signed-not-usable_1.0_all.deb
+Size: 35
+MD5sum: f3034b91e355ba6046ff535ed044dcb5
+Description: Dummy package
+
+Package: signed-usable
+Architecture: all
+Version: 1.0
+Multi-Arch: foreign
+Priority: optional
+Section: misc
+Maintainer: Julian Andres Klode <juliank@ubuntu.com>
+Installed-Size: 9
+Filename: ./signed-usable_1.0_all.deb
+Size: 31
+MD5sum: 2d5166c110423c2d7c016642af4bd2eb
+SHA256: 20edddf30a1bedf669f19caffa347299239b2a700ff428e33abc75e902f1d737
+Description: Dummy package
diff --git a/tests/data/test-signed-usable-repo/signed/Release b/tests/data/test-signed-usable-repo/signed/Release
new file mode 100644
index 0000000..135ce78
--- /dev/null
+++ b/tests/data/test-signed-usable-repo/signed/Release
@@ -0,0 +1,17 @@
+Date: Fri, 13 Dec 2019 16:11:05 +0000
+MD5Sum:
+ 21b1df48cfe01053dd2245763423cefb 668 Packages
+ 6715b0d77e7ea43fb252c78f1f826096 38 Release
+ 30432690119177a7f0c822c12f96e665 579 Sources
+SHA1:
+ 910742c214560ce8ecdcb89c86fb1c0afa5a5373 668 Packages
+ 4a252d8c4ebc30d7330938a7dee06288877c4e3f 38 Release
+ 3147ec4b880bbb2cfa2a3f903b86d1dd2249488d 579 Sources
+SHA256:
+ 445d75ee95443b89849b17f73ae434423d01c603f03c894a7be7851d36a5365d 668 Packages
+ c0ac431b6ea236c4d12e904b556bbb1f8d0abc733041cab4d65bfd3fd9704183 38 Release
+ 99140dde6468a8e073d8830faaf90e40c374957b98c1be446308dc8296ce5934 579 Sources
+SHA512:
+ fdc2f7552c06ea197c8c40a44602218703a08d1b6a86f9319cc13a56ea8e7f7103c10aea6ee221b0bc88432ebb2651e3d2e5a93a3900fec5630db2e41684c57b 668 Packages
+ 5800d40647c79881b15119bbc576b12926151a2df37b96e18abc5fb04986663fa01867a83a1919225365b590a15e22032d12175eaf76336f167688c5a817ca13 38 Release
+ d0dd2a03687df03e4728219b9b149d514f8a734fcdeee8e45cb31229fc0f915eab634a50f934a60df42682e6dfbc65b962109bda616af642d6ee4cf821a877a1 579 Sources
diff --git a/tests/data/test-signed-usable-repo/signed/Release.gpg b/tests/data/test-signed-usable-repo/signed/Release.gpg
new file mode 100644
index 0000000..6b853bf
--- /dev/null
+++ b/tests/data/test-signed-usable-repo/signed/Release.gpg
@@ -0,0 +1,14 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQHFBAABCgAvFiEEJIBsPhVxGFtaHobENN7p9ZyTYfkFAl3zuBkRHHRlc3RAZXhh
+bXBsZS5jb20ACgkQNN7p9ZyTYfkkrwv9HBgaZjSf3Bib9FH2alGZkBHKRcE2gWKf
+d4PBXKLGgUutCy2g1Dn+FyBYB4OCf3H7ZsD6JFqrYSmLyGHAo+5QTMaPcRlVGoN8
+GL/fxJOEkRJruyC3k36toZTgXI/DGvSv1882syHTG0jOm8to95wiaQKDysTKOOTp
+p1HJbX5DX+Wf2rXA/CL8KdH7rHzkIdhjdL4hucDw+JvNSywPEn0RSfvjtUS04d7G
+Rw8H2PXyEnreO6OmMdwEmtImouIVxNZDFEEWUhdwujMnDtxM16uIIRLXa1sWQAzZ
+M7RHNIhQHP1EvPuQRnKknacKUwchpg/IklxxruaRtjELSUMF5UOXJdnJFb3t6+pT
+HBaW/c5piLfMsIV5U/bjQA76Ta4NccH5hah8x6+SjEZiP5RxSfQ9wpblYQeOmaUB
+xi0tF6Ukqp5zfPi1RblziWZbN2/ty5zXpPOYbDT2geWo+8bVEhG6HwtHvpzuVymR
+qtjOlCujLlkNZKG5jBHoOhuQjNPm+8RY
+=wnyK
+-----END PGP SIGNATURE-----
diff --git a/tests/data/test-signed-usable-repo/signed/Sources b/tests/data/test-signed-usable-repo/signed/Sources
new file mode 100644
index 0000000..52a8ee0
--- /dev/null
+++ b/tests/data/test-signed-usable-repo/signed/Sources
@@ -0,0 +1,21 @@
+Package: signed-not-usable
+Format: 3.0 (native)
+Binary: signed-not-usable
+Architecture: any all
+Version: 1.0
+Package-List:
+ signed-not-usable deb admin important arch=all
+Files:
+ f2ae6d320641e8e241efcba02b5540fb 323 signed-not-usable_1.0.dsc
+
+Package: signed-usable
+Format: 3.0 (native)
+Binary: signed-usable
+Architecture: any all
+Version: 1.0
+Package-List:
+ signed-usable deb admin important arch=all
+Files:
+ 491c58f65f493e838880a6ff30064c57 311 signed-usable_1.0.dsc
+Checksums-Sha256:
+ 7a277156805101ce917dbb8d443d7087cc0ad333d25304f284976dba36339487 311 signed-usable_1.0.dsc
diff --git a/tests/data/test-signed-usable-repo/signed/signed-not-usable_1.0.dsc b/tests/data/test-signed-usable-repo/signed/signed-not-usable_1.0.dsc
new file mode 100644
index 0000000..2ebb766
--- /dev/null
+++ b/tests/data/test-signed-usable-repo/signed/signed-not-usable_1.0.dsc
@@ -0,0 +1,11 @@
+Format: 3.0 (native)
+Source: signed-not-usable
+Binary: signed-not-usable
+Architecture: any all
+Version: 1.0
+Package-List:
+ signed-not-usable deb admin important arch=all
+Checksums-Sha256:
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 test.tar.xz
+Files:
+ d41d8cd98f00b204e9800998ecf8427e 0 test.tar.xz
diff --git a/tests/data/test-signed-usable-repo/signed/signed-not-usable_1.0_all.deb b/tests/data/test-signed-usable-repo/signed/signed-not-usable_1.0_all.deb
new file mode 100644
index 0000000..e098ca5
--- /dev/null
+++ b/tests/data/test-signed-usable-repo/signed/signed-not-usable_1.0_all.deb
@@ -0,0 +1 @@
+i am signed-not-usable_1.0_all.deb
diff --git a/tests/data/test-signed-usable-repo/signed/signed-usable_1.0.dsc b/tests/data/test-signed-usable-repo/signed/signed-usable_1.0.dsc
new file mode 100644
index 0000000..04b4f64
--- /dev/null
+++ b/tests/data/test-signed-usable-repo/signed/signed-usable_1.0.dsc
@@ -0,0 +1,11 @@
+Format: 3.0 (native)
+Source: signed-usable
+Binary: signed-usable
+Architecture: any all
+Version: 1.0
+Package-List:
+ signed-usable deb admin important arch=all
+Checksums-Sha256:
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 test.tar.xz
+Files:
+ d41d8cd98f00b204e9800998ecf8427e 0 test.tar.xz
diff --git a/tests/data/test-signed-usable-repo/signed/signed-usable_1.0_all.deb b/tests/data/test-signed-usable-repo/signed/signed-usable_1.0_all.deb
new file mode 100644
index 0000000..cd58831
--- /dev/null
+++ b/tests/data/test-signed-usable-repo/signed/signed-usable_1.0_all.deb
@@ -0,0 +1 @@
+i am signed-usable_1.0_all.deb
diff --git a/tests/data/test-signed-usable-repo/unsigned/Packages b/tests/data/test-signed-usable-repo/unsigned/Packages
new file mode 100644
index 0000000..22f4fff
--- /dev/null
+++ b/tests/data/test-signed-usable-repo/unsigned/Packages
@@ -0,0 +1,26 @@
+Package: unsigned-unusable
+Architecture: all
+Version: 1.0
+Multi-Arch: foreign
+Priority: optional
+Section: misc
+Maintainer: Julian Andres Klode <juliank@ubuntu.com>
+Installed-Size: 9
+Filename: ./unsigned-unusable_1.0_all.deb
+Size: 35
+MD5sum: 368e155b22e873eb71ae43b80379c0ea
+Description: Dummy package
+
+Package: unsigned-usable
+Architecture: all
+Version: 1.0
+Multi-Arch: foreign
+Priority: optional
+Section: misc
+Maintainer: Julian Andres Klode <juliank@ubuntu.com>
+Installed-Size: 9
+Filename: ./unsigned-usable_1.0_all.deb
+Size: 33
+MD5sum: 0a0bb7636879ae8a38b679bc3d09b627
+SHA256: ddc71095223eb039cce782e813782b917502e0f3c99a1142ea2d92b34737a243
+Description: Dummy package
diff --git a/tests/data/test-signed-usable-repo/unsigned/Release b/tests/data/test-signed-usable-repo/unsigned/Release
new file mode 100644
index 0000000..18552a2
--- /dev/null
+++ b/tests/data/test-signed-usable-repo/unsigned/Release
@@ -0,0 +1,17 @@
+Date: Fri, 13 Dec 2019 16:04:58 +0000
+MD5Sum:
+ dd9db6eb770d950241fc5dfd1b94d4c5 672 Packages
+ 808b5c1ee28bdae1ffe98a8ee632a4ff 38 Release
+ bb3efd66e75fa995971ddf8d3bdcd6cb 592 Sources
+SHA1:
+ 4b83e0112704ce4aa20d5833dfad0617404cb48f 672 Packages
+ 9cba73573f2824f5691293802046a707d1493cdf 38 Release
+ 4e4a503ec704e11c963c042f44a3ad9da7f30b88 592 Sources
+SHA256:
+ e7a1760be467c66c570e3a5feddfb8d82a14cb4a201382353e4d9bf4c8fb530a 672 Packages
+ b7efc8e49608e74fefd10f93081843b66200cf9b3d12af7e7772138d8d9fd514 38 Release
+ f1bea3fd5e27e0a2b606ef23690a611b6a1b60baf42284d5469431dfae4c316a 592 Sources
+SHA512:
+ 4d951a837afed5be1aa89e594587b34f533408b28e4f3b0ffd2c69e5edf45873e24ace59126b12c8f83734ca5daa1d901f2f51170ac325b2787b3674e7226d93 672 Packages
+ 31d2d8013ee14aa414abf831819f2896f2d9c323fbfcfbc24bf7d602e1855a97ae049125397afd909e8a78aa0c016469731742f7dded416680fced49cce4b088 38 Release
+ e476cf4357f0f5ec00529dd96b782d9ed52e94db06020ad64ec976f4be6f3783151267fd9916857bedd1c3b693a338f53501d09200a0834c004da54f5acbfca0 592 Sources
diff --git a/tests/data/test-signed-usable-repo/unsigned/Sources b/tests/data/test-signed-usable-repo/unsigned/Sources
new file mode 100644
index 0000000..9eaf48b
--- /dev/null
+++ b/tests/data/test-signed-usable-repo/unsigned/Sources
@@ -0,0 +1,22 @@
+Package: unsigned-unusable
+Format: 3.0 (native)
+Binary: unsigned-unusable
+Architecture: any all
+Version: 1.0
+Package-List:
+ unsigned-unusable deb admin important arch=all
+Files:
+ c9c8d679a878ca08faca12baf5e2398d 323 unsigned-not-usable_1.0.dsc
+
+
+Package: unsigned-usable
+Format: 3.0 (native)
+Binary: unsigned-usable
+Architecture: any all
+Version: 1.0
+Package-List:
+ unsigned-usable deb admin important arch=all
+Files:
+ 864f4834dabbd30e6472902b000d7a76 317 unsigned-usable_1.0.dsc
+Checksums-Sha256:
+ eb6c1ebc468b4687bb6bffe25afd5ab1e1812beb9577fac7560396bcb7775939 317 unsigned-usable_1.0.dsc
diff --git a/tests/data/test-signed-usable-repo/unsigned/unsigned-not-usable_1.0.dsc b/tests/data/test-signed-usable-repo/unsigned/unsigned-not-usable_1.0.dsc
new file mode 100644
index 0000000..f8156c1
--- /dev/null
+++ b/tests/data/test-signed-usable-repo/unsigned/unsigned-not-usable_1.0.dsc
@@ -0,0 +1,11 @@
+Format: 3.0 (native)
+Source: unsigned-unusable
+Binary: unsigned-unusable
+Architecture: any all
+Version: 1.0
+Package-List:
+ unsigned-unusable deb admin important arch=all
+Checksums-Sha256:
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 test.tar.xz
+Files:
+ d41d8cd98f00b204e9800998ecf8427e 0 test.tar.xz
diff --git a/tests/data/test-signed-usable-repo/unsigned/unsigned-unusable_1.0_all.deb b/tests/data/test-signed-usable-repo/unsigned/unsigned-unusable_1.0_all.deb
new file mode 100644
index 0000000..7465ec1
--- /dev/null
+++ b/tests/data/test-signed-usable-repo/unsigned/unsigned-unusable_1.0_all.deb
@@ -0,0 +1 @@
+i am unsigned-unusable_1.0_all.deb
diff --git a/tests/data/test-signed-usable-repo/unsigned/unsigned-usable_1.0.dsc b/tests/data/test-signed-usable-repo/unsigned/unsigned-usable_1.0.dsc
new file mode 100644
index 0000000..a625bb6
--- /dev/null
+++ b/tests/data/test-signed-usable-repo/unsigned/unsigned-usable_1.0.dsc
@@ -0,0 +1,11 @@
+Format: 3.0 (native)
+Source: unsigned-usable
+Binary: unsigned-usable
+Architecture: any all
+Version: 1.0
+Package-List:
+ unsigned-usable deb admin important arch=all
+Checksums-Sha256:
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 test.tar.xz
+Files:
+ d41d8cd98f00b204e9800998ecf8427e 0 test.tar.xz
diff --git a/tests/data/test-signed-usable-repo/unsigned/unsigned-usable_1.0_all.deb b/tests/data/test-signed-usable-repo/unsigned/unsigned-usable_1.0_all.deb
new file mode 100644
index 0000000..2f748b0
--- /dev/null
+++ b/tests/data/test-signed-usable-repo/unsigned/unsigned-usable_1.0_all.deb
@@ -0,0 +1 @@
+i am unsigned-usable_1.0_all.deb
diff --git a/tests/data/test-source-repo/Sources b/tests/data/test-source-repo/Sources
new file mode 100644
index 0000000..4758871
--- /dev/null
+++ b/tests/data/test-source-repo/Sources
@@ -0,0 +1,25 @@
+Package: dh-autoreconf
+Format: 3.0 (native)
+Binary: dh-autoreconf
+Architecture: all
+Version: 16
+Maintainer: Julian Andres Klode <jak@debian.org>
+Standards-Version: 4.1.1
+Build-Depends: debhelper (>= 9)
+Vcs-Browser: https://anonscm.debian.org/git/collab-maint/dh-autoreconf.git
+Vcs-Git: https://anonscm.debian.org/git/collab-maint/dh-autoreconf.git
+Package-List:
+ dh-autoreconf deb devel optional arch=all
+Files:
+ 6576a28fe1918ce10bd31543ba545901 1578 dh-autoreconf_16.dsc
+ 302c8bf43db02412e3f2197fd0f2ee0f 7372 dh-autoreconf_16.tar.xz
+Checksums-Sha1:
+ c9bf7a920013021dad5fbd898dfd5a79c7a150f9 1578 dh-autoreconf_16.dsc
+ 58459600164398ad6807ddd877a6f814c799c62c 7372 dh-autoreconf_16.tar.xz
+Checksums-Sha256:
+ 1c1b2ab5f1ae5496bd50dbb3c30e9b7d181a06c8d02ee8d7e9c35ed6f2a69b5f 1578 dh-autoreconf_16.dsc
+ 5c6a6a362907327bec77a867ff3fd0eceba8015d1b881b48275aff7e4ce0f629 7372 dh-autoreconf_16.tar.xz
+Checksums-Sha512:
+ 4b1a3299f2a8b01b0c75db97fd16cb39919949c74d19ea6cf28e1bbd4891d3515b3e2b90b96a64df665cebf6d95409e704e670909ae91fcfe92409ee1339bffc 1578 dh-autoreconf_16.dsc
+ 10448dd179ec12bf4310a9a514110a85f56e51893aa36a97ac3a6f8d7ce99d099e62cfdb78e271e2d94431e8832da0f643de821b6643b80e3f0b0f5d682cf9a9 7372 dh-autoreconf_16.tar.xz
+
diff --git a/tests/data/test_debs/data-tar-broken.deb b/tests/data/test_debs/data-tar-broken.deb
new file mode 100644
index 0000000..4fd42e0
--- /dev/null
+++ b/tests/data/test_debs/data-tar-broken.deb
Binary files differ
diff --git a/tests/data/test_debs/data-tar-xz.deb b/tests/data/test_debs/data-tar-xz.deb
new file mode 100644
index 0000000..9dd4d67
--- /dev/null
+++ b/tests/data/test_debs/data-tar-xz.deb
Binary files differ
diff --git a/tests/data/test_debs/data-tar.deb b/tests/data/test_debs/data-tar.deb
new file mode 100644
index 0000000..d8e4a98
--- /dev/null
+++ b/tests/data/test_debs/data-tar.deb
Binary files differ
diff --git a/tests/data/test_debs/etc/apt/sources.list b/tests/data/test_debs/etc/apt/sources.list
new file mode 100644
index 0000000..3cc95c7
--- /dev/null
+++ b/tests/data/test_debs/etc/apt/sources.list
@@ -0,0 +1 @@
+deb http://archive.ubuntu.com/ubuntu maverick main
diff --git a/tests/data/test_debs/gdebi-test1.deb b/tests/data/test_debs/gdebi-test1.deb
new file mode 100644
index 0000000..ea9991a
--- /dev/null
+++ b/tests/data/test_debs/gdebi-test1.deb
Binary files differ
diff --git a/tests/data/test_debs/gdebi-test10.deb b/tests/data/test_debs/gdebi-test10.deb
new file mode 100644
index 0000000..ca43ace
--- /dev/null
+++ b/tests/data/test_debs/gdebi-test10.deb
Binary files differ
diff --git a/tests/data/test_debs/gdebi-test11.deb b/tests/data/test_debs/gdebi-test11.deb
new file mode 100644
index 0000000..af9b441
--- /dev/null
+++ b/tests/data/test_debs/gdebi-test11.deb
Binary files differ
diff --git a/tests/data/test_debs/gdebi-test12.deb b/tests/data/test_debs/gdebi-test12.deb
new file mode 100644
index 0000000..36544cc
--- /dev/null
+++ b/tests/data/test_debs/gdebi-test12.deb
Binary files differ
diff --git a/tests/data/test_debs/gdebi-test13.deb b/tests/data/test_debs/gdebi-test13.deb
new file mode 100644
index 0000000..4e7a828
--- /dev/null
+++ b/tests/data/test_debs/gdebi-test13.deb
Binary files differ
diff --git a/tests/data/test_debs/gdebi-test2.deb b/tests/data/test_debs/gdebi-test2.deb
new file mode 100644
index 0000000..307ac68
--- /dev/null
+++ b/tests/data/test_debs/gdebi-test2.deb
Binary files differ
diff --git a/tests/data/test_debs/gdebi-test3.deb b/tests/data/test_debs/gdebi-test3.deb
new file mode 100644
index 0000000..436b925
--- /dev/null
+++ b/tests/data/test_debs/gdebi-test3.deb
Binary files differ
diff --git a/tests/data/test_debs/gdebi-test4.deb b/tests/data/test_debs/gdebi-test4.deb
new file mode 100644
index 0000000..9eb92d1
--- /dev/null
+++ b/tests/data/test_debs/gdebi-test4.deb
Binary files differ
diff --git a/tests/data/test_debs/gdebi-test5.deb b/tests/data/test_debs/gdebi-test5.deb
new file mode 100644
index 0000000..0c98c03
--- /dev/null
+++ b/tests/data/test_debs/gdebi-test5.deb
Binary files differ
diff --git a/tests/data/test_debs/gdebi-test6.deb b/tests/data/test_debs/gdebi-test6.deb
new file mode 100644
index 0000000..8ceacad
--- /dev/null
+++ b/tests/data/test_debs/gdebi-test6.deb
Binary files differ
diff --git a/tests/data/test_debs/gdebi-test7.deb b/tests/data/test_debs/gdebi-test7.deb
new file mode 100644
index 0000000..c041499
--- /dev/null
+++ b/tests/data/test_debs/gdebi-test7.deb
Binary files differ
diff --git a/tests/data/test_debs/gdebi-test8.deb b/tests/data/test_debs/gdebi-test8.deb
new file mode 100644
index 0000000..439f8ca
--- /dev/null
+++ b/tests/data/test_debs/gdebi-test8.deb
Binary files differ
diff --git a/tests/data/test_debs/gdebi-test9.deb b/tests/data/test_debs/gdebi-test9.deb
new file mode 100644
index 0000000..9901d90
--- /dev/null
+++ b/tests/data/test_debs/gdebi-test9.deb
Binary files differ
diff --git a/tests/data/test_debs/hello_2.5-1.dsc b/tests/data/test_debs/hello_2.5-1.dsc
new file mode 100644
index 0000000..d00db52
--- /dev/null
+++ b/tests/data/test_debs/hello_2.5-1.dsc
@@ -0,0 +1,34 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA256
+
+Format: 1.0
+Source: hello
+Binary: hello, bello,
+ cello
+Architecture: any
+Version: 2.5-1
+Maintainer: Santiago Vila <sanvila@debian.org>
+Homepage: http://www.gnu.org/software/hello
+Standards-Version: 3.8.4
+Build-Depends: autotools-dev
+Checksums-Sha1:
+ dfaf92bfc8144141bcd5ffb4a30777cd57501bfa 582535 hello_2.5.orig.tar.gz
+ a98ab19072b35295ded3560721662131933f7c05 5965 hello_2.5-1.diff.gz
+Checksums-Sha256:
+ 22934a7d3a62f247ce3b5a77a2c7f7dd095ad8aef305efa2d0d15e0fef31c446 582535 hello_2.5.orig.tar.gz
+ c2f17c08a6a94bdab4f4316beb4687e8468de03a5162f1d694a0bab4b90e5962 5965 hello_2.5-1.diff.gz
+Files:
+ cf4b73d837692b93676ccd723bf6f797 582535 hello_2.5.orig.tar.gz
+ fa6c41ce60b975294781ed00d67f32fe 5965 hello_2.5-1.diff.gz
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.10 (GNU/Linux)
+
+iQEcBAEBCAAGBQJLeDJTAAoJEEHOfwufG4syQSAH/0S9vYKRbs71bfGNmKTmRQy6
+TJM1oj3hdjJoE9HBvCBoBhRSB7l5Iz+TDVyvKTcFOnXTWrZfhnNRCoErtOxP8Z0R
+MOEeLDTPf3JEvpwaRbfzngp+dRkAXTJSI+equqOvj4jjPd1lrNegWtwTF6VIlXAX
+5s+onfBp29/MesxzTGAdviobMYCdazmi2XceC+t5ZEJzEsMMPO6i/v22SrvE/Jkg
+LiD/U6gea3/1dovsUY6I7/VyzMhHgdcuUW4dzovr2daLwE+uRp9R90XDtKN5aWUM
+oBzk8vBIsw/3EV5kjCKdMtqFGdHBjn4nTKvyoXmJ1SvEzeEd21WvLsBFpcJVKaw=
+=YMqG
+-----END PGP SIGNATURE-----
diff --git a/tests/data/test_debs/impossible-build-depends_2.5-1.dsc b/tests/data/test_debs/impossible-build-depends_2.5-1.dsc
new file mode 100644
index 0000000..7f7364a
--- /dev/null
+++ b/tests/data/test_debs/impossible-build-depends_2.5-1.dsc
@@ -0,0 +1,33 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA256
+
+Format: 1.0
+Source: hello
+Binary: hello
+Architecture: any
+Version: 2.5-1
+Maintainer: Santiago Vila <sanvila@debian.org>
+Homepage: http://www.gnu.org/software/hello
+Standards-Version: 3.8.4
+Build-Depends: debhelper (>> 101)
+Checksums-Sha1:
+ dfaf92bfc8144141bcd5ffb4a30777cd57501bfa 582535 hello_2.5.orig.tar.gz
+ a98ab19072b35295ded3560721662131933f7c05 5965 hello_2.5-1.diff.gz
+Checksums-Sha256:
+ 22934a7d3a62f247ce3b5a77a2c7f7dd095ad8aef305efa2d0d15e0fef31c446 582535 hello_2.5.orig.tar.gz
+ c2f17c08a6a94bdab4f4316beb4687e8468de03a5162f1d694a0bab4b90e5962 5965 hello_2.5-1.diff.gz
+Files:
+ cf4b73d837692b93676ccd723bf6f797 582535 hello_2.5.orig.tar.gz
+ fa6c41ce60b975294781ed00d67f32fe 5965 hello_2.5-1.diff.gz
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.10 (GNU/Linux)
+
+iQEcBAEBCAAGBQJLeDJTAAoJEEHOfwufG4syQSAH/0S9vYKRbs71bfGNmKTmRQy6
+TJM1oj3hdjJoE9HBvCBoBhRSB7l5Iz+TDVyvKTcFOnXTWrZfhnNRCoErtOxP8Z0R
+MOEeLDTPf3JEvpwaRbfzngp+dRkAXTJSI+equqOvj4jjPd1lrNegWtwTF6VIlXAX
+5s+onfBp29/MesxzTGAdviobMYCdazmi2XceC+t5ZEJzEsMMPO6i/v22SrvE/Jkg
+LiD/U6gea3/1dovsUY6I7/VyzMhHgdcuUW4dzovr2daLwE+uRp9R90XDtKN5aWUM
+oBzk8vBIsw/3EV5kjCKdMtqFGdHBjn4nTKvyoXmJ1SvEzeEd21WvLsBFpcJVKaw=
+=YMqG
+-----END PGP SIGNATURE-----
diff --git a/tests/data/test_debs/large-package-content_1.0_all.deb b/tests/data/test_debs/large-package-content_1.0_all.deb
new file mode 100644
index 0000000..56bdce3
--- /dev/null
+++ b/tests/data/test_debs/large-package-content_1.0_all.deb
Binary files differ
diff --git a/tests/data/test_debs/multiarch-test1_i386.deb b/tests/data/test_debs/multiarch-test1_i386.deb
new file mode 100644
index 0000000..439a9f4
--- /dev/null
+++ b/tests/data/test_debs/multiarch-test1_i386.deb
Binary files differ
diff --git a/tests/data/test_debs/testdep-allowed-any_1.0-1_i386.deb b/tests/data/test_debs/testdep-allowed-any_1.0-1_i386.deb
new file mode 100644
index 0000000..c56f819
--- /dev/null
+++ b/tests/data/test_debs/testdep-allowed-any_1.0-1_i386.deb
Binary files differ
diff --git a/tests/data/test_debs/testdep-same-arch_1.0-1_i386.deb b/tests/data/test_debs/testdep-same-arch_1.0-1_i386.deb
new file mode 100644
index 0000000..872f3b6
--- /dev/null
+++ b/tests/data/test_debs/testdep-same-arch_1.0-1_i386.deb
Binary files differ
diff --git a/tests/data/test_debs/utf8-package_1.0-1_all.deb b/tests/data/test_debs/utf8-package_1.0-1_all.deb
new file mode 100644
index 0000000..e0339c2
--- /dev/null
+++ b/tests/data/test_debs/utf8-package_1.0-1_all.deb
Binary files differ
diff --git a/tests/data/test_debs/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_maverick_main_binary-i386_Packages b/tests/data/test_debs/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_maverick_main_binary-i386_Packages
new file mode 100644
index 0000000..b3b30d0
--- /dev/null
+++ b/tests/data/test_debs/var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_maverick_main_binary-i386_Packages
@@ -0,0 +1,31 @@
+Package: autotools-dev
+Priority: optional
+Section: devel
+Installed-Size: 216
+Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
+Original-Maintainer: Henrique de Moraes Holschuh <hmh@debian.org>
+Architecture: all
+Version: 20100122.1
+Filename: pool/main/a/autotools-dev/autotools-dev_20100122.1_all.deb
+Size: 70700
+MD5sum: dc25cef0035a3fc7c64b9f2eeddac5d2
+SHA1: 40bcffb6bc2ec7aeaae747c300a0ad70ef3bb38c
+SHA256: 290aaf1d5d5a6c560a128b6fe5557fe981ebb85e105410f3bca2e468ce54aaac
+Description: Update infrastructure for config.{guess,sub} files
+ This package installs an up-to-date version of config.guess and
+ config.sub, used by the automake and libtool packages. It provides
+ the canonical copy of those files for other packages as well.
+ .
+ It also documents in /usr/share/doc/autotools-dev/README.Debian.gz
+ best practices and guidelines for using autoconf, automake and
+ friends on Debian packages. This is a must-read for any developers
+ packaging software that uses the GNU autotools, or GNU gettext.
+ .
+ Additionally this package provides seamless integration into Debhelper
+ or CDBS, allowing maintainers to easily update config.{guess,sub} files
+ in their packages.
+Enhances: cdbs, debhelper
+Homepage: http://savannah.gnu.org/projects/config/
+Bugs: https://bugs.launchpad.net/ubuntu/+filebug
+Origin: Ubuntu
+Supported: 18m
diff --git a/tests/data/test_debs/var/lib/dpkg/status b/tests/data/test_debs/var/lib/dpkg/status
new file mode 100644
index 0000000..5c2c48a
--- /dev/null
+++ b/tests/data/test_debs/var/lib/dpkg/status
@@ -0,0 +1,92 @@
+Package: apt
+Status: install ok installed
+Priority: important
+Section: admin
+Installed-Size: 5456
+Maintainer: APT Development Team <deity@lists.debian.org>
+Architecture: i386
+Version: 0.7.25.3
+Replaces: libapt-pkg-dev (<< 0.3.7), libapt-pkg-doc (<< 0.3.7)
+Provides: libapt-pkg-libc6.10-6-4.8
+Description: Advanced front-end for dpkg
+ This is Debian's next generation front-end for the dpkg package manager.
+ It provides the apt-get utility and APT dselect method that provides a
+ simpler, safer way to install and upgrade packages.
+ .
+ APT features complete installation ordering, multiple source capability
+ and several other unique features, see the Users Guide in apt-doc.
+
+Package: postfix
+Status: install ok installed
+Priority: extra
+Section: mail
+Installed-Size: 3488
+Maintainer: LaMont Jones <lamont@debian.org>
+Architecture: i386
+Version: 2.7.0-1
+Replaces: mail-transport-agent, postfix-tls
+Provides: default-mta, mail-transport-agent, postfix-tls
+Recommends: python
+Conflicts: libnss-db (<< 2.2-3), mail-transport-agent, postfix-tls, smail
+Description: High-performance mail transport agent
+ Postfix is Wietse Venema's mail transport agent that started life as an
+ alternative to the widely-used Sendmail program. Postfix attempts to
+ be fast, easy to administer, and secure, while at the same time being
+ sendmail compatible enough to not upset existing users. Thus, the outside
+ has a sendmail-ish flavor, but the inside is completely different.
+
+Package: debconf
+Status: install ok installed
+Priority: important
+Section: admin
+Installed-Size: 924
+Maintainer: Debconf Developers <debconf-devel@lists.alioth.debian.org>
+Architecture: all
+Version: 1.5.28
+Replaces: debconf-tiny
+Provides: debconf-2.0
+Recommends: apt-utils (>= 0.5.1)
+Conflicts: apt (<< 0.3.12.1), cdebconf (<< 0.96), debconf-tiny, debconf-utils (<< 1.3.22), dialog (<< 0.9b-20020814-1), menu (<= 2.1.3-1), whiptail (<< 0.51.4-11), whiptail-utf8 (<= 0.50.17-13)
+Description: Debian configuration management system
+ Debconf is a configuration management system for debian packages. Packages
+ use Debconf to ask questions when they are installed.
+Python-Version: 2.6, 3.1
+
+Package: testdep-same
+Status: install ok installed
+Source: testdep
+Version: 1.0-1
+Architecture: amd64
+Maintainer: Francois Gouget <fgouget@codeweavers.com>
+Installed-Size: 25
+Provides: testdep-virtual-same
+Section: unknown
+Priority: extra
+Multi-Arch: same
+Description: Multi-arch = same package, no dependency
+ This is a multiarch package that can only satisfy dependencies for the same
+ architecture. But multiple instances of this package can be co-installed,
+ one for each architecture.
+ .
+ Library and development packages typically fall into this category.
+
+Package: testdep-allowed
+Status: install ok installed
+Source: testdep
+Version: 1.0-1
+Architecture: amd64
+Maintainer: Francois Gouget <fgouget@codeweavers.com>
+Installed-Size: 25
+Provides: testdep-virtual-allowed
+Section: unknown
+Priority: extra
+Multi-Arch: allowed
+Description: Multi-arch = allowed package, no dependency
+ This is a multiarch package. By default it can only satisfy dependencies for
+ packages of the same architecture but that can be changed with ':any'
+ dependencies.
+ .
+ It can only be installed for one architecture at a time. Trying to install the
+ same package for another architecture will remove the first one.
+ .
+ Packages providing tools or shells typically fall into this category.
diff --git a/tests/fakeroot-apt-key b/tests/fakeroot-apt-key
new file mode 100755
index 0000000..997161a
--- /dev/null
+++ b/tests/fakeroot-apt-key
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec fakeroot /usr/bin/apt-key $*
diff --git a/tests/helper_install_progress_run.py b/tests/helper_install_progress_run.py
new file mode 100755
index 0000000..02e601d
--- /dev/null
+++ b/tests/helper_install_progress_run.py
@@ -0,0 +1,15 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2019 Colomban Wendling <cwendling@hypra.fr>
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+"""Helper checking argv[1] is a valid, writable, file descriptor"""
+
+import os
+from sys import argv
+
+assert len(argv) == 2
+with os.fdopen(int(argv[1]), "w") as f:
+ assert f.write("test") == 4
diff --git a/tests/old/__init__.py b/tests/old/__init__.py
new file mode 100644
index 0000000..5311641
--- /dev/null
+++ b/tests/old/__init__.py
@@ -0,0 +1,11 @@
+import os
+import unittest
+
+if __name__ == "__main__":
+ os.chdir(os.path.dirname(__file__))
+ print(os.getcwd())
+
+ for path in os.listdir("."):
+ if path.endswith(".py"):
+ exec("from %s import *" % path[:-3])
+ unittest.main()
diff --git a/tests/old/apt-test.py b/tests/old/apt-test.py
new file mode 100644
index 0000000..8c66aeb
--- /dev/null
+++ b/tests/old/apt-test.py
@@ -0,0 +1,27 @@
+import warnings
+
+warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
+import apt
+
+if __name__ == "__main__":
+ progress = apt.progress.OpTextProgress()
+ cache = apt.Cache(progress)
+ print(cache)
+ for pkg in cache:
+ if pkg.is_upgradable:
+ pkg.mark_install()
+ for pkg in cache.get_changes():
+ # print pkg.name()
+ pass
+ print("Broken: %s " % cache._depcache.broken_count)
+ print("inst_count: %s " % cache._depcache.inst_count)
+
+ # get a new cache
+ cache = apt.Cache(progress)
+ for name in cache.keys():
+ import random
+
+ if random.randint(0, 1) == 1:
+ cache[name].mark_delete()
+ print("Broken: %s " % cache._depcache.broken_count)
+ print("del_count: %s " % cache._depcache.del_count)
diff --git a/tests/old/cache.py b/tests/old/cache.py
new file mode 100644
index 0000000..3f4aa0c
--- /dev/null
+++ b/tests/old/cache.py
@@ -0,0 +1,51 @@
+#!/usr/bin/python3
+#
+# Test for the pkgCache code
+#
+
+import sys
+
+import apt_pkg
+
+
+def main():
+ apt_pkg.init()
+ cache = apt_pkg.Cache()
+ depcache = apt_pkg.DepCache(cache)
+ depcache.init()
+ i = 0
+ all = cache.package_count
+ print("Running Cache test on all packages:")
+ # first, get all pkgs
+ for pkg in cache.packages:
+ i += 1
+ pkg.name
+ # then get each version
+ for ver in pkg.version_list:
+ # get some version information
+ ver.file_list
+ ver.ver_str
+ ver.arch
+ ver.depends_listStr
+ dl = ver.depends_list
+ # get all dependencies (a dict of string->list,
+ # e.g. "depends:" -> [ver1,ver2,..]
+ for dep in dl.keys():
+ # get the list of each dependency object
+ for depVerList in dl[dep]:
+ for z in depVerList:
+ # get all TargetVersions of
+ # the dependency object
+ for j in z.all_targets():
+ j.file_list
+ ver.ver_str
+ ver.arch
+ ver.depends_listStr
+ j = ver.depends_list
+
+ print("\r%i/%i=%.3f%% " % (i, all, (float(i) / float(all) * 100)))
+
+
+if __name__ == "__main__":
+ main()
+ sys.exit(0)
diff --git a/tests/old/depcache.py b/tests/old/depcache.py
new file mode 100644
index 0000000..81ca574
--- /dev/null
+++ b/tests/old/depcache.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python2.4
+#
+# Test for the DepCache code
+#
+
+import sys
+
+import apt_pkg
+
+
+def main():
+ apt_pkg.init()
+ cache = apt_pkg.Cache()
+ depcache = apt_pkg.DepCache(cache)
+ depcache.init()
+ i = 0
+ all = cache.package_count
+ print("Running DepCache test on all packages")
+ print("(trying to install each and then mark it keep again):")
+ # first, get all pkgs
+ for pkg in cache.packages:
+ i += 1
+ x = pkg.name
+ # then get each version
+ ver = depcache.get_candidate_ver(pkg)
+ if ver is not None:
+ depcache.mark_install(pkg)
+ if depcache.inst_count == 0:
+ if depcache.is_upgradable(pkg):
+ print("Error marking %s for install" % x)
+ for p in cache.packages:
+ if depcache.marked_install(p):
+ depcache.mark_keep(p)
+ if depcache.inst_count != 0:
+ print(
+ "Error undoing the selection for %s (inst_count: %s)"
+ % (x, depcache.inst_count)
+ )
+ print("\r%i/%i=%.3f%% " % (i, all, (float(i) / float(all) * 100)))
+
+ print()
+ print("Trying upgrade:")
+ depcache.upgrade()
+ print("To install: %s " % depcache.inst_count)
+ print("To remove: %s " % depcache.del_count)
+ print("Kept back: %s " % depcache.keep_count)
+
+ print("Trying DistUpgrade:")
+ depcache.upgrade(True)
+ print("To install: %s " % depcache.inst_count)
+ print("To remove: %s " % depcache.del_count)
+ print("Kept back: %s " % depcache.keep_count)
+
+
+if __name__ == "__main__":
+ main()
+ sys.exit(0)
diff --git a/tests/old/lock.py b/tests/old/lock.py
new file mode 100644
index 0000000..c09983c
--- /dev/null
+++ b/tests/old/lock.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python2.4
+#
+# Test for the pkgCache code
+#
+
+import os
+import sys
+
+import apt_pkg
+
+if __name__ == "__main__":
+ lock = "/tmp/test.lck"
+
+ apt_pkg.init()
+
+ # system-lock
+ apt_pkg.pkgsystem_lock()
+
+ pid = os.fork()
+ if pid == 0:
+ try:
+ apt_pkg.pkgsystem_lock()
+ except SystemError as s:
+ print("Can't get lock: (error text:\n%s)" % s)
+ sys.exit(0)
+
+ apt_pkg.pkgsystem_unlock()
+
+ # low-level lock
+ fd = apt_pkg.get_lock(lock, True)
+ print("Lockfile fd: %s" % fd)
+
+ # try to get lock without error flag
+ pid = os.fork()
+ if pid == 0:
+ # child
+ fd = apt_pkg.get_lock(lock, False)
+ print("Lockfile fd (child): %s" % fd)
+ sys.exit(0)
+
+ # try to get lock with error flag
+ pid = os.fork()
+ if pid == 0:
+ # child
+ fd = apt_pkg.get_lock(lock, True)
+ print("Lockfile fd (child): %s" % fd)
+ sys.exit(0)
diff --git a/tests/old/memleak.py b/tests/old/memleak.py
new file mode 100644
index 0000000..44b123e
--- /dev/null
+++ b/tests/old/memleak.py
@@ -0,0 +1,45 @@
+#!/usr/bin/python3
+
+import gc
+import time
+
+import apt_pkg
+
+import apt
+
+cache = apt.Cache()
+
+# memleak
+for i in range(100):
+ cache.open(None)
+ print(cache["apt"].name)
+ time.sleep(1)
+ gc.collect()
+ f = open("%s" % i, "w")
+ for obj in gc.get_objects():
+ f.write("%s\n" % str(obj))
+ f.close()
+
+# memleak
+# for i in range(100):
+# cache = apt.Cache()
+# time.sleep(1)
+# cache = None
+# gc.collect()
+
+# no memleak, but more or less the apt.Cache.open() code
+for i in range(100):
+ cache = apt_pkg.Cache()
+ depcache = apt_pkg.DepCache(cache)
+ records = apt_pkg.PackageRecords(cache)
+ list = apt_pkg.SourceList()
+ list.read_main_list()
+ dict = {}
+ for pkg in cache.packages:
+ if len(pkg.version_list) > 0:
+ dict[pkg.name] = apt.package(cache, depcache, records, list, None, pkg)
+
+ print(cache["apt"])
+ time.sleep(1)
+
+ gc.collect()
diff --git a/tests/old/pkgproblemresolver.py b/tests/old/pkgproblemresolver.py
new file mode 100644
index 0000000..89190cf
--- /dev/null
+++ b/tests/old/pkgproblemresolver.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python2.4
+#
+# Test for the DepCache code
+#
+
+import sys
+
+import apt_pkg
+
+
+def main():
+ apt_pkg.init()
+ cache = apt_pkg.Cache()
+ depcache = apt_pkg.DepCache(cache)
+ depcache.init()
+ i = 0
+ all = cache.package_count
+ print("Running DepCache test on all packages")
+ print("(trying to install each and then mark it keep again):")
+ # first, get all pkgs
+ for pkg in cache.packages:
+ i += 1
+ x = pkg.name
+ # then get each version
+ ver = depcache.get_candidate_ver(pkg)
+ if ver is not None:
+ depcache.mark_install(pkg)
+ if depcache.broken_count > 0:
+ fixer = apt_pkg.ProblemResolver(depcache)
+ fixer.clear(pkg)
+ fixer.protect(pkg)
+ # we first try to resolve the problem
+ # with the package that should be installed
+ # protected
+ try:
+ fixer.resolve(True)
+ except SystemError:
+ # the pkg seems to be broken, the
+ # returns a exception
+ fixer.clear(pkg)
+ fixer.resolve(True)
+ if not depcache.marked_install(pkg):
+ print("broken in archive: %s " % pkg.name)
+ fixer = None
+ if depcache.inst_count == 0:
+ if depcache.is_upgradable(pkg):
+ print("Error marking %s for install" % x)
+ for p in cache.packages:
+ if depcache.marked_install(p) or depcache.marked_upgrade(p):
+ depcache.mark_keep(p)
+ if depcache.inst_count != 0:
+ print("Error undoing the selection for %s" % x)
+ print("\r%i/%i=%.3f%% " % (i, all, (float(i) / float(all) * 100)))
+
+ print()
+ print("Trying upgrade:")
+ depcache.upgrade()
+ print("To install: %s " % depcache.inst_count)
+ print("To remove: %s " % depcache.del_count)
+ print("Kept back: %s " % depcache.keep_count)
+
+ print("Trying DistUpgrade:")
+ depcache.upgrade(True)
+ print("To install: %s " % depcache.inst_count)
+ print("To remove: %s " % depcache.del_count)
+ print("Kept back: %s " % depcache.keep_count)
+
+
+if __name__ == "__main__":
+ main()
+ sys.exit(0)
diff --git a/tests/old/pkgrecords.py b/tests/old/pkgrecords.py
new file mode 100644
index 0000000..b6be005
--- /dev/null
+++ b/tests/old/pkgrecords.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python2.4
+#
+# Test for the PkgSrcRecords code
+# it segfaults for python-apt < 0.5.37
+#
+
+import sys
+
+import apt_pkg
+
+
+def main():
+ apt_pkg.init()
+ cache = apt_pkg.Cache()
+ depcache = apt_pkg.DepCache(cache)
+ depcache.init()
+ i = 0
+ print("Running PkgRecords test on all packages:")
+ for pkg in cache.packages:
+ i += 1
+ records = apt_pkg.PackageRecords(cache)
+ if len(pkg.version_list) == 0:
+ # print "no available version, cruft"
+ continue
+ version = depcache.get_candidate_ver(pkg)
+ if not version:
+ continue
+ file, index = version.file_list.pop(0)
+ if records.lookup((file, index)):
+ # print records.filename
+ records.filename
+ records.long_desc
+ print(
+ "\r%i/%i=%.3f%% "
+ % (i, cache.package_count, (float(i) / float(cache.package_count) * 100))
+ )
+
+
+if __name__ == "__main__":
+ main()
+ sys.exit(0)
diff --git a/tests/old/pkgsrcrecords.py b/tests/old/pkgsrcrecords.py
new file mode 100644
index 0000000..ccf791f
--- /dev/null
+++ b/tests/old/pkgsrcrecords.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python2.4
+#
+# Test for the PkgSrcRecords code
+# it segfaults for python-apt < 0.5.37
+#
+
+import sys
+
+import apt_pkg
+
+
+def main():
+ apt_pkg.init()
+ cache = apt_pkg.Cache()
+ i = 0
+ print("Running PkgSrcRecords test on all packages:")
+ for x in cache.packages:
+ i += 1
+ src = apt_pkg.SourceRecords()
+ if src.lookup(x.name):
+ # print src.package
+ pass
+ print(
+ "\r%i/%i=%.3f%% "
+ % (i, cache.package_count, (float(i) / float(cache.package_count) * 100))
+ )
+
+
+if __name__ == "__main__":
+ main()
+ sys.exit(0)
diff --git a/tests/old/refcount.py b/tests/old/refcount.py
new file mode 100755
index 0000000..0fad676
--- /dev/null
+++ b/tests/old/refcount.py
@@ -0,0 +1,54 @@
+#!/usr/bin/python-dbg
+
+import gc
+import sys
+from pprint import pprint
+
+import apt
+
+# get initial cache
+print(sys.gettotalrefcount())
+progress = apt.progress.OpTextProgress()
+c = apt.Cache(progress)
+print("refcount after first cache instance: ", sys.gettotalrefcount())
+
+# test open()
+c.open(progress)
+print("refcount after cache open: ", sys.gettotalrefcount())
+# pprint(sys.getobjects(10))
+
+c.open(apt.progress.OpProgress())
+print("refcount after seconf cache open: ", sys.gettotalrefcount())
+# pprint(sys.getobjects(10))
+
+# FIXME: find a way to get a efficient diff
+# before = gc.get_objects()
+# c.open(apt.progress.OpProgress())
+# after = gc.get_objects()
+
+
+# test update()
+print("refcount before cache.update(): ", sys.gettotalrefcount())
+c.update()
+gc.collect()
+print("refcount after cache.update(): ", sys.gettotalrefcount())
+c.update()
+gc.collect()
+print("refcount after second cache.update(): ", sys.gettotalrefcount())
+# pprint(sys.getobjects(20))
+
+
+# test install()
+c.open(apt.progress.OpProgress())
+gc.collect()
+print("refcount before cache['hello'].mark_install(): ", sys.gettotalrefcount())
+c["hello"].mark_install()
+c.commit(apt.progress.FetchProgress(), apt.progress.InstallProgress())
+gc.collect()
+print("refcount after: ", sys.gettotalrefcount())
+c.open(apt.progress.OpProgress())
+c["hello"].mark_delete()
+c.commit(apt.progress.FetchProgress(), apt.progress.InstallProgress())
+gc.collect()
+print("refcount after: ", sys.gettotalrefcount())
+pprint(sys.getobjects(10))
diff --git a/tests/old/test_enhances.py b/tests/old/test_enhances.py
new file mode 100644
index 0000000..79aad9a
--- /dev/null
+++ b/tests/old/test_enhances.py
@@ -0,0 +1,16 @@
+#!/usr/bin/python3
+
+import apt
+
+cache = apt.Cache()
+
+for pkg in cache:
+ if pkg.installed and pkg.installed.enhances:
+ s = "%s enhances:" % pkg.name
+ for or_list in pkg.installed.enhances:
+ for enhances in or_list.or_dependencies:
+ s += " %s" % enhances.name
+ if enhances.name in cache and not cache[enhances.name].is_installed:
+ s += "(*missing*) "
+ s += ","
+ print(s[:-1])
diff --git a/tests/test_all.py b/tests/test_all.py
new file mode 100644
index 0000000..c97ed5d
--- /dev/null
+++ b/tests/test_all.py
@@ -0,0 +1,63 @@
+#!/usr/bin/python3
+# Copyright (C) 2009 Julian Andres Klode <jak@debian.org>
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+"""Run all available unit tests."""
+import os
+import sys
+import unittest
+import unittest.runner
+
+# Python 3 only provides abiflags since 3.2
+if not hasattr(sys, "pydebug"):
+ if sys.abiflags.startswith("d"):
+ sys.pydebug = True
+ else:
+ sys.pydebug = False
+
+
+def get_library_dir():
+ # Find the path to the built apt_pkg and apt_inst extensions
+ if not os.path.exists("../build"):
+ return None
+ from sysconfig import get_platform, get_python_version
+
+ # Set the path to the build directory.
+ plat_specifier = f".{get_platform()}-{get_python_version()}"
+ library_dir = "../build/lib{}{}".format(
+ plat_specifier,
+ (sys.pydebug and "-pydebug" or ""),
+ )
+ return os.path.abspath(library_dir)
+
+
+class MyTestRunner(unittest.runner.TextTestRunner):
+ def __init__(self, *args, **kwargs):
+ kwargs["stream"] = sys.stdout
+ super().__init__(*args, **kwargs)
+
+
+if __name__ == "__main__":
+ if not os.access("/etc/apt/sources.list", os.R_OK):
+ sys.stdout.write("[tests] Skipping because sources.list is not readable\n")
+ sys.exit(0)
+
+ sys.stdout.write("[tests] Running on %s\n" % sys.version.replace("\n", ""))
+ dirname = os.path.dirname(__file__)
+ if dirname:
+ os.chdir(dirname)
+ library_dir = get_library_dir()
+ if "pybuild" in os.getenv("PYTHONPATH", ""):
+ # pybuild already supplied us with a path to check for
+ sys.stdout.write("Using pybuild supplied build dir\n")
+ elif library_dir:
+ sys.stdout.write("Using library_dir: '%s'\n" % library_dir)
+ sys.path.insert(0, os.path.abspath(library_dir))
+
+ for path in os.listdir("."):
+ if path.endswith(".py") and os.path.isfile(path) and path.startswith("test_"):
+ exec("from %s import *" % path[:-3])
+
+ unittest.main(testRunner=MyTestRunner)
diff --git a/tests/test_apt_cache.py b/tests/test_apt_cache.py
new file mode 100644
index 0000000..cc52400
--- /dev/null
+++ b/tests/test_apt_cache.py
@@ -0,0 +1,400 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2010 Julian Andres Klode <jak@debian.org>
+# 2010 Michael Vogt <mvo@ubuntu.com>
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+"""Unit tests for verifying the correctness of check_dep, etc in apt_pkg."""
+
+import glob
+import logging
+import os
+import shutil
+import sys
+import tempfile
+import unittest
+
+from test_all import get_library_dir
+
+libdir = get_library_dir()
+if libdir:
+ sys.path.insert(0, libdir)
+
+import apt_pkg
+import testcommon
+
+import apt
+
+
+def if_sources_list_is_readable(f):
+ def wrapper(*args, **kwargs):
+ if os.access("/etc/apt/sources.list", os.R_OK):
+ f(*args, **kwargs)
+ else:
+ logging.warning("skipping '%s' because sources.list is not readable" % f)
+
+ return wrapper
+
+
+def get_open_file_descriptors():
+ try:
+ fds = os.listdir("/proc/self/fd")
+ except OSError:
+ logging.warning("failed to list /proc/self/fd")
+ return set()
+ return set(map(int, fds))
+
+
+class TestAptCache(testcommon.TestCase):
+ """test the apt cache"""
+
+ def setUp(self):
+ testcommon.TestCase.setUp(self)
+ apt_pkg.config.clear("APT::Update::Post-Invoke")
+ apt_pkg.config.clear("APT::Update::Post-Invoke-Success")
+
+ @if_sources_list_is_readable
+ def test_apt_cache(self):
+ """cache: iterate all packages and all dependencies"""
+ cache = apt.Cache()
+ # number is not meaningful and just need to be "big enough",
+ # the important bit is the test against __len__
+ self.assertTrue(len(cache) > 100)
+ # go over the cache and all dependencies, just to see if
+ # that is possible and does not crash
+ for pkg in cache:
+ if pkg.candidate:
+ for or_deps in pkg.candidate.dependencies:
+ for dep in or_deps:
+ self.assertTrue(dep.name)
+ self.assertTrue(isinstance(dep.relation, str))
+ self.assertTrue(dep.pre_depend in (True, False))
+
+ # accessing record should take a reasonable time; in
+ # particular, when using compressed indexes, it should not use
+ # tons of seek operations
+ r = pkg.candidate.record
+ self.assertEqual(r["Package"], pkg.shortname)
+ self.assertTrue("Version" in r)
+ self.assertTrue(len(r["Description"]) > 0)
+ self.assertTrue(str(r).startswith("Package: %s\n" % pkg.shortname))
+
+ @if_sources_list_is_readable
+ def test_cache_close_leak_fd(self):
+ fds_before_open = get_open_file_descriptors()
+ cache = apt.Cache()
+ opened_fd = get_open_file_descriptors().difference(fds_before_open)
+ cache.close()
+ fds_after_close = get_open_file_descriptors()
+ unclosed_fd = opened_fd.intersection(fds_after_close)
+ self.assertEqual(fds_before_open, fds_after_close)
+ self.assertEqual(unclosed_fd, set())
+
+ def test_cache_open_twice_leaks_fds(self):
+ cache = apt.Cache()
+ fds_before_open = get_open_file_descriptors()
+ cache.open()
+ fds_after_open_twice = get_open_file_descriptors()
+ self.assertEqual(fds_before_open, fds_after_open_twice)
+
+ @if_sources_list_is_readable
+ def test_cache_close_download_fails(self):
+ cache = apt.Cache()
+ self.assertEqual(cache.required_download, 0)
+ cache.close()
+ with self.assertRaises(apt.cache.CacheClosedException):
+ cache.required_download
+
+ def test_get_provided_packages(self):
+ apt.apt_pkg.config.set("Apt::architecture", "i386")
+ cache = apt.Cache(rootdir="./data/test-provides/")
+ cache.open()
+ if len(cache) == 0:
+ logging.warning("skipping test_get_provided_packages, cache empty?!?")
+ return
+ # a true virtual pkg
+ li = cache.get_providing_packages("mail-transport-agent")
+ self.assertTrue(len(li) > 0)
+ self.assertTrue("postfix" in [p.name for p in li])
+ self.assertTrue("mail-transport-agent" in cache["postfix"].candidate.provides)
+
+ def test_low_level_pkg_provides(self):
+ apt.apt_pkg.config.set("Apt::architecture", "i386")
+ # create highlevel cache and get the lowlevel one from it
+ highlevel_cache = apt.Cache(rootdir="./data/test-provides")
+ if len(highlevel_cache) == 0:
+ logging.warning("skipping test_log_level_pkg_provides, cache empty?!?")
+ return
+ # low level cache provides list of the pkg
+ cache = highlevel_cache._cache
+ li = cache["mail-transport-agent"].provides_list
+ # arbitrary number, just needs to be higher enough
+ self.assertEqual(len(li), 2)
+ for providesname, providesver, version in li:
+ self.assertEqual(providesname, "mail-transport-agent")
+ if version.parent_pkg.name == "postfix":
+ break
+ else:
+ self.assertNotReached()
+
+ @if_sources_list_is_readable
+ def test_dpkg_journal_dirty(self):
+ # create tmp env
+ tmpdir = tempfile.mkdtemp()
+ dpkg_dir = os.path.join(tmpdir, "var", "lib", "dpkg")
+ os.makedirs(os.path.join(dpkg_dir, "updates"))
+ open(os.path.join(dpkg_dir, "status"), "w").close()
+ apt_pkg.config.set("Dir::State::status", os.path.join(dpkg_dir, "status"))
+ cache = apt.Cache()
+ # test empty
+ self.assertFalse(cache.dpkg_journal_dirty)
+ # that is ok, only [0-9] are dpkg jounral entries
+ open(os.path.join(dpkg_dir, "updates", "xxx"), "w").close()
+ self.assertFalse(cache.dpkg_journal_dirty)
+ # that is a dirty journal
+ open(os.path.join(dpkg_dir, "updates", "000"), "w").close()
+ self.assertTrue(cache.dpkg_journal_dirty)
+
+ @if_sources_list_is_readable
+ def test_apt_update(self):
+ rootdir = "./data/tmp"
+ if os.path.exists(rootdir):
+ shutil.rmtree(rootdir)
+ try:
+ os.makedirs(os.path.join(rootdir, "var/lib/apt/lists/partial"))
+ except OSError:
+ pass
+ state_dir = os.path.join(rootdir, "var/lib/apt")
+ lists_dir = os.path.join(rootdir, "var/lib/apt/lists")
+ old_state = apt_pkg.config.find("dir::state")
+ apt_pkg.config.set("dir::state", state_dir)
+ # set a local sources.list that does not need the network
+ base_sources = os.path.abspath(os.path.join(rootdir, "sources.list"))
+ old_source_list = apt_pkg.config.find("dir::etc::sourcelist")
+ old_source_parts = apt_pkg.config.find("dir::etc::sourceparts")
+ apt_pkg.config.set("dir::etc::sourcelist", base_sources)
+ # TODO: /dev/null is not a dir, perhaps find something better
+ apt_pkg.config.set("dir::etc::sourceparts", "/dev/null")
+ # main sources.list
+ sources_list = base_sources
+ with open(sources_list, "w") as f:
+ repo = os.path.abspath("./data/test-repo2")
+ f.write("deb [allow-insecure=yes] copy:%s /\n" % repo)
+
+ # test single sources.list fetching
+ sources_list = os.path.join(rootdir, "test.list")
+ with open(sources_list, "w") as f:
+ repo_dir = os.path.abspath("./data/test-repo")
+ f.write("deb [allow-insecure=yes] copy:%s /\n" % repo_dir)
+
+ self.assertTrue(os.path.exists(sources_list))
+ # write marker to ensure listcleaner is not run
+ open("./data/tmp/var/lib/apt/lists/marker", "w").close()
+
+ # update a single sources.list
+ cache = apt.Cache()
+ cache.update(sources_list=sources_list)
+ # verify we just got the excpected package file
+ needle_packages = glob.glob(lists_dir + "/*tests_data_test-repo_Packages*")
+ self.assertEqual(len(needle_packages), 1)
+ # verify that we *only* got the Packages file from a single source
+ all_packages = glob.glob(lists_dir + "/*_Packages*")
+ self.assertEqual(needle_packages, all_packages)
+ # verify that the listcleaner was not run and the marker file is
+ # still there
+ self.assertTrue("marker" in os.listdir(lists_dir))
+ # now run update again (without the "normal" sources.list that
+ # contains test-repo2 and verify that we got the normal sources.list
+ cache.update()
+ needle_packages = glob.glob(lists_dir + "/*tests_data_test-repo2_Packages*")
+ self.assertEqual(len(needle_packages), 1)
+ all_packages = glob.glob(lists_dir + "/*_Packages*")
+ self.assertEqual(needle_packages, all_packages)
+
+ # and another update with a single source only
+ cache = apt.Cache()
+ cache.update(sources_list=sources_list)
+ all_packages = glob.glob(lists_dir + "/*_Packages*")
+ self.assertEqual(len(all_packages), 2)
+ apt_pkg.config.set("dir::state", old_state)
+ apt_pkg.config.set("dir::etc::sourcelist", old_source_list)
+ apt_pkg.config.set("dir::etc::sourceparts", old_source_parts)
+
+ def test_package_cmp(self):
+ cache = apt.Cache(rootdir="/")
+ li = []
+ li.append(cache["intltool"])
+ li.append(cache["python3"])
+ li.append(cache["apt"])
+ li.sort()
+ self.assertEqual([p.name for p in li], ["apt", "intltool", "python3"])
+
+ def test_get_architectures(self):
+ main_arch = apt.apt_pkg.config.get("APT::Architecture")
+ arches = apt_pkg.get_architectures()
+ self.assertTrue(main_arch in arches)
+
+ def test_phasing_applied(self):
+ """checks the return type of phasing_applied."""
+ cache = apt.Cache()
+ pkg = cache["apt"]
+ self.assertIsInstance(pkg.phasing_applied, bool)
+
+ def test_is_security_update(self):
+ """checks the return type of is_security_update."""
+ cache = apt.Cache()
+ pkg = cache["apt"]
+ self.assertIsInstance(pkg.installed.is_security_update, bool)
+
+ def test_apt_cache_reopen_is_safe(self):
+ """cache: check that we cannot use old package objects after reopen"""
+ cache = apt.Cache()
+ old_depcache = cache._depcache
+ old_package = cache["apt"]
+ old_pkg = old_package._pkg
+ old_version = old_package.candidate
+ old_ver = old_version._cand
+
+ cache.open()
+ new_depcache = cache._depcache
+ new_package = cache["apt"]
+ new_pkg = new_package._pkg
+ new_version = new_package.candidate
+ new_ver = new_version._cand
+
+ # get candidate
+ self.assertRaises(ValueError, old_depcache.get_candidate_ver, new_pkg)
+ self.assertRaises(ValueError, new_depcache.get_candidate_ver, old_pkg)
+ self.assertEqual(new_ver, new_depcache.get_candidate_ver(new_pkg))
+ self.assertEqual(old_package.candidate._cand, old_ver) # Remap success
+ self.assertEqual(
+ old_package.candidate._cand, new_depcache.get_candidate_ver(new_pkg)
+ )
+
+ # set candidate
+ new_package.candidate = old_version
+ old_depcache.set_candidate_ver(old_pkg, old_ver)
+ self.assertRaises(ValueError, old_depcache.set_candidate_ver, old_pkg, new_ver)
+ self.assertRaises(ValueError, new_depcache.set_candidate_ver, old_pkg, old_ver)
+ self.assertRaises(ValueError, new_depcache.set_candidate_ver, old_pkg, new_ver)
+ self.assertRaises(ValueError, new_depcache.set_candidate_ver, new_pkg, old_ver)
+ new_depcache.set_candidate_ver(new_pkg, new_ver)
+
+ @staticmethod
+ def write_status_file(packages):
+ with open(apt_pkg.config["Dir::State::Status"], "w") as fobj:
+ for package in packages:
+ print("Package:", package, file=fobj)
+ print("Status: install ok installed", file=fobj)
+ print("Priority: optional", file=fobj)
+ print("Section: admin", file=fobj)
+ print("Installed-Size: 1", file=fobj)
+ print("Maintainer: X <x@x.invalid>", file=fobj)
+ print("Architecture: all", file=fobj)
+ print("Version: 1", file=fobj)
+ print("Description: blah", file=fobj)
+ print("", file=fobj)
+
+ def test_apt_cache_reopen_is_safe_out_of_bounds(self):
+ """Check that out of bounds access is remapped correctly."""
+ with tempfile.NamedTemporaryFile() as status:
+ apt_pkg.config["Dir::Etc::SourceList"] = "/dev/null"
+ apt_pkg.config["Dir::Etc::SourceParts"] = "/dev/null"
+ apt_pkg.config["Dir::State::Status"] = status.name
+ apt_pkg.init_system()
+
+ self.write_status_file("abcdefghijklmnopqrstuvwxyz")
+ c = apt.Cache()
+ p = c["z"]
+ p_id = p.id
+ self.write_status_file("az")
+ apt_pkg.init_system()
+ c.open()
+ self.assertNotEqual(p.id, p_id)
+ self.assertLess(p.id, 2)
+ p.mark_delete()
+ self.assertEqual([p], c.get_changes())
+
+ def test_apt_cache_reopen_is_safe_out_of_bounds_no_match(self):
+ """Check that installing gone package raises correct exception"""
+ with tempfile.NamedTemporaryFile() as status:
+ apt_pkg.config["Dir::Etc::SourceList"] = "/dev/null"
+ apt_pkg.config["Dir::Etc::SourceParts"] = "/dev/null"
+ apt_pkg.config["Dir::State::Status"] = status.name
+ apt_pkg.init_system()
+
+ self.write_status_file("abcdefghijklmnopqrstuvwxyz")
+ c = apt.Cache()
+ p = c["z"]
+ p_id = p.id
+ self.write_status_file("a")
+ apt_pkg.init_system()
+ c.open()
+ self.assertEqual(p.id, p_id) # Could not be remapped
+ self.assertRaises(apt_pkg.CacheMismatchError, p.mark_delete)
+
+ def test_apt_cache_reopen_is_safe_swap(self):
+ """Check that swapping a and b does not mark the wrong package."""
+ with tempfile.NamedTemporaryFile() as status:
+ apt_pkg.config["Dir::Etc::SourceList"] = "/dev/null"
+ apt_pkg.config["Dir::Etc::SourceParts"] = "/dev/null"
+ apt_pkg.config["Dir::State::Status"] = status.name
+ apt_pkg.init_system()
+
+ self.write_status_file("abcdefghijklmnopqrstuvwxyz")
+ c = apt.Cache()
+ p = c["a"]
+ a_id = p.id
+ p_hash = hash(p)
+ set_of_p = {p}
+ self.write_status_file("baz")
+ apt_pkg.init_system()
+ c.open()
+ # b now has the same id as a in the old cache
+ self.assertEqual(c["b"].id, a_id)
+ self.assertNotEqual(p.id, a_id)
+ # Marking a should still mark a, not b.
+ p.mark_delete()
+ self.assertEqual([p], c.get_changes())
+
+ # Ensure that p can still be found in a set of it, as a test
+ # for bug https://bugs.launchpad.net/bugs/1780099
+ self.assertEqual(hash(p), p_hash)
+ self.assertIn(p, set_of_p)
+
+ def test_apt_cache_iteration_safe(self):
+ """Check that iterating does not produce different results.
+
+ This failed in 1.7.0~alpha2, because one part of the code
+ looked up packages in the weak dict using the pretty name,
+ and the other using the full name."""
+
+ with tempfile.NamedTemporaryFile() as status:
+ apt_pkg.config["Dir::Etc::SourceList"] = "/dev/null"
+ apt_pkg.config["Dir::Etc::SourceParts"] = "/dev/null"
+ apt_pkg.config["Dir::State::Status"] = status.name
+ apt_pkg.init_system()
+
+ self.write_status_file("abcdefghijklmnopqrstuvwxyz")
+
+ c = apt.Cache()
+ c["a"].mark_delete()
+ self.assertEqual([c["a"]], [p for p in c if p.marked_delete])
+
+ def test_problemresolver_keep_phased_updates(self):
+ """Check that the c++ function can be called."""
+ with tempfile.NamedTemporaryFile() as status:
+ apt_pkg.config["Dir::Etc::SourceList"] = "/dev/null"
+ apt_pkg.config["Dir::Etc::SourceParts"] = "/dev/null"
+ apt_pkg.config["Dir::State::Status"] = status.name
+ apt_pkg.init_system()
+
+ cache = apt.Cache()
+ problemresolver = apt.ProblemResolver(cache)
+ self.assertIsNone(problemresolver.keep_phased_updates())
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_aptsources.py b/tests/test_aptsources.py
new file mode 100644
index 0000000..1e04552
--- /dev/null
+++ b/tests/test_aptsources.py
@@ -0,0 +1,1573 @@
+#!/usr/bin/python3
+
+import copy
+import os
+import tempfile
+import unittest
+
+import apt_pkg
+import testcommon
+
+import aptsources.distro
+import aptsources.sourceslist
+
+
+class TestAptSources(testcommon.TestCase):
+ def setUp(self):
+ testcommon.TestCase.setUp(self)
+ if apt_pkg.config["APT::Architecture"] not in ("i386", "amd64"):
+ apt_pkg.config.set("APT::Architecture", "i386")
+ apt_pkg.config.set("Dir::Etc", os.getcwd())
+ apt_pkg.config.set("Dir::Etc::sourceparts", tempfile.mkdtemp())
+ if os.path.exists("./build/data/templates"):
+ self.templates = os.path.abspath("./build/data/templates")
+ elif os.path.exists("../build/data/templates"):
+ self.templates = os.path.abspath("../build/data/templates")
+ else:
+ self.templates = "/usr/share/python-apt/templates/"
+
+ def tearDown(self):
+ aptsources.distro._OSRelease.OS_RELEASE_FILE = (
+ aptsources.distro._OSRelease.DEFAULT_OS_RELEASE_FILE
+ )
+ if "LSB_ETC_LSB_RELEASE" in os.environ:
+ del os.environ["LSB_ETC_LSB_RELEASE"]
+
+ def testIsMirror(self):
+ """aptsources: Test mirror detection."""
+ yes = aptsources.sourceslist.is_mirror(
+ "http://archive.ubuntu.com", "http://de.archive.ubuntu.com"
+ )
+ no = aptsources.sourceslist.is_mirror(
+ "http://archive.ubuntu.com", "http://ftp.debian.org"
+ )
+ self.assertTrue(yes)
+ self.assertFalse(no)
+
+ def testSourcesListReading(self):
+ """aptsources: Test sources.list parsing."""
+ apt_pkg.config.set("Dir::Etc::sourcelist", "data/aptsources/" "sources.list")
+ sources = aptsources.sourceslist.SourcesList(True, self.templates)
+ self.assertEqual(len(sources.list), 10)
+ # test load
+ sources.list = []
+ sources.load("data/aptsources/sources.list")
+ self.assertEqual(len(sources.list), 10)
+
+ def testSourcesListReading_deb822(self):
+ """aptsources: Test sources.list parsing."""
+ apt_pkg.config.set("Dir::Etc::sourceparts", "data/aptsources/" "sources.list.d")
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+ self.assertEqual(len(sources.list), 5)
+ # test load
+ sources.list = []
+ sources.load("data/aptsources/sources.list.d/main.sources")
+ self.assertEqual(len(sources.list), 5)
+
+ for entry in sources.list:
+ self.assertFalse(entry.invalid)
+ self.assertFalse(entry.disabled)
+ self.assertEqual(entry.types, ["deb"])
+ self.assertEqual(entry.type, "deb")
+ self.assertEqual(entry.uris, ["http://de.archive.ubuntu.com/ubuntu/"])
+ self.assertEqual(entry.uri, "http://de.archive.ubuntu.com/ubuntu/")
+
+ self.assertEqual(sources.list[0].comps, ["main"])
+ self.assertEqual(sources.list[1].comps, ["restricted"])
+ self.assertEqual(sources.list[2].comps, ["universe"])
+ self.assertEqual(sources.list[3].comps, ["main"])
+ self.assertEqual(sources.list[4].comps, ["main"])
+
+ for entry in sources.list[:-1]:
+ self.assertIsNone(entry.trusted)
+
+ self.assertTrue(sources.list[-1].trusted)
+
+ for entry in sources.list[:-2]:
+ self.assertEqual(entry.architectures, [])
+ self.assertEqual(entry.suites, ["edgy"])
+ self.assertEqual(entry.dist, "edgy")
+
+ for entry in sources.list[-2:]:
+ self.assertEqual(entry.suites, ["natty"])
+ self.assertEqual(entry.dist, "natty")
+ self.assertEqual(entry.architectures, ["amd64", "i386"])
+
+ def testSourcesListWriting_deb822(self):
+ """aptsources: Test sources.list parsing."""
+ apt_pkg.config.set("Dir::Etc::sourceparts", "data/aptsources/" "sources.list.d")
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+ with tempfile.TemporaryDirectory() as tmpdir:
+ for entry in sources.list:
+ entry.file = os.path.join(tmpdir, os.path.basename(entry.file))
+
+ sources.save()
+
+ maxDiff = self.maxDiff
+ self.maxDiff = None
+ for file in os.listdir("data/aptsources/sources.list.d"):
+ with open(os.path.join("data/aptsources/sources.list.d", file)) as a:
+ with open(os.path.join(tmpdir, file)) as b:
+ self.assertEqual(a.read(), b.read(), f"file {file}")
+ self.maxDiff = maxDiff
+
+ def testSourcesListAdding(self):
+ """aptsources: Test additions to sources.list"""
+ apt_pkg.config.set("Dir::Etc::sourcelist", "data/aptsources/" "sources.list")
+ sources = aptsources.sourceslist.SourcesList(True, self.templates)
+ # test to add something that is already there (main)
+ before = copy.deepcopy(sources)
+ sources.add("deb", "http://de.archive.ubuntu.com/ubuntu/", "edgy", ["main"])
+ self.assertTrue(sources.list == before.list)
+ # test to add something that is already there (restricted)
+ before = copy.deepcopy(sources)
+ sources.add(
+ "deb", "http://de.archive.ubuntu.com/ubuntu/", "edgy", ["restricted"]
+ )
+ self.assertTrue(sources.list == before.list)
+
+ before = copy.deepcopy(sources)
+ sources.add(
+ "deb",
+ "http://de.archive.ubuntu.com/ubuntu/",
+ "natty",
+ ["main"],
+ architectures=["amd64", "i386"],
+ )
+ self.assertTrue(sources.list == before.list)
+
+ # test to add something new: multiverse
+ sources.add(
+ "deb", "http://de.archive.ubuntu.com/ubuntu/", "edgy", ["multiverse"]
+ )
+ found = False
+ for entry in sources:
+ if (
+ entry.type == "deb"
+ and entry.uri == "http://de.archive.ubuntu.com/ubuntu/"
+ and entry.dist == "edgy"
+ and "multiverse" in entry.comps
+ ):
+ found = True
+ break
+ self.assertTrue(found)
+
+ # add a new natty entry without architecture specification
+ sources.add(
+ "deb", "http://de.archive.ubuntu.com/ubuntu/", "natty", ["multiverse"]
+ )
+ found = False
+ for entry in sources:
+ if (
+ entry.type == "deb"
+ and entry.uri == "http://de.archive.ubuntu.com/ubuntu/"
+ and entry.dist == "natty"
+ and entry.architectures == []
+ and "multiverse" in entry.comps
+ ):
+ found = True
+ break
+ self.assertTrue(found)
+
+ # Add universe to existing multi-arch line
+ sources.add(
+ "deb",
+ "http://de.archive.ubuntu.com/ubuntu/",
+ "natty",
+ ["universe"],
+ architectures=["i386", "amd64"],
+ )
+ found = False
+ for entry in sources:
+ if (
+ entry.type == "deb"
+ and entry.uri == "http://de.archive.ubuntu.com/ubuntu/"
+ and entry.dist == "natty"
+ and set(entry.architectures) == {"amd64", "i386"}
+ and set(entry.comps) == {"main", "universe"}
+ ):
+ found = True
+ break
+ self.assertTrue(found)
+ # test to add something new: multiverse *and*
+ # something that is already there
+ before = copy.deepcopy(sources)
+ sources.add(
+ "deb",
+ "http://de.archive.ubuntu.com/ubuntu/",
+ "edgy",
+ ["universe", "something"],
+ )
+ found_universe = 0
+ found_something = 0
+ for entry in sources:
+ if (
+ entry.type == "deb"
+ and entry.uri == "http://de.archive.ubuntu.com/ubuntu/"
+ and entry.dist == "edgy"
+ ):
+ for c in entry.comps:
+ if c == "universe":
+ found_universe += 1
+ if c == "something":
+ found_something += 1
+ # print "\n".join([s.str() for s in sources])
+ self.assertEqual(found_something, 1)
+ self.assertEqual(found_universe, 1)
+
+ def testSourcesListAdding_deb822(self):
+ """aptsources: Test additions to sources.list"""
+ apt_pkg.config.set("Dir::Etc::sourceparts", "data/aptsources/" "sources.list.d")
+
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+ # test to add something that is already there (main)
+ before = copy.deepcopy(sources)
+ sources.add("deb", "http://de.archive.ubuntu.com/ubuntu/", "edgy", ["main"])
+ self.assertEqual(sources.list, before.list)
+ # test to add something that is already there (restricted)
+ before = copy.deepcopy(sources)
+ sources.add(
+ "deb", "http://de.archive.ubuntu.com/ubuntu/", "edgy", ["restricted"]
+ )
+ self.assertTrue(sources.list == before.list)
+
+ before = copy.deepcopy(sources)
+ sources.add(
+ "deb",
+ "http://de.archive.ubuntu.com/ubuntu/",
+ "natty",
+ ["main"],
+ architectures=["amd64", "i386"],
+ )
+ self.assertEqual(
+ [e.str() for e in sources.list], [e.str() for e in before.list]
+ )
+
+ # test to add something new: multiverse
+ sources.add(
+ "deb", "http://de.archive.ubuntu.com/ubuntu/", "edgy", ["multiverse"]
+ )
+ found = False
+ for entry in sources:
+ if (
+ entry.type == "deb"
+ and entry.uri == "http://de.archive.ubuntu.com/ubuntu/"
+ and entry.dist == "edgy"
+ and "multiverse" in entry.comps
+ ):
+ found = True
+ break
+ self.assertTrue(found)
+
+ # add a new natty entry without architecture specification
+ sources.add(
+ "deb", "http://de.archive.ubuntu.com/ubuntu/", "natty", ["multiverse"]
+ )
+ found = False
+ for entry in sources:
+ if (
+ entry.type == "deb"
+ and entry.uri == "http://de.archive.ubuntu.com/ubuntu/"
+ and entry.dist == "natty"
+ and entry.architectures == []
+ and "multiverse" in entry.comps
+ ):
+ found = True
+ break
+ self.assertTrue(found)
+
+ # Add universe to existing multi-arch line
+ sources.add(
+ "deb",
+ "http://de.archive.ubuntu.com/ubuntu/",
+ "natty",
+ ["universe"],
+ architectures=["i386", "amd64"],
+ )
+ found = False
+ for entry in sources:
+ if (
+ entry.type == "deb"
+ and entry.uri == "http://de.archive.ubuntu.com/ubuntu/"
+ and entry.dist == "natty"
+ and set(entry.architectures) == {"amd64", "i386"}
+ and set(entry.comps) == {"main", "universe"}
+ ):
+ found = True
+ break
+ self.assertTrue(found)
+ # test to add something new: multiverse *and*
+ # something that is already there
+ before = copy.deepcopy(sources)
+ sources.add(
+ "deb",
+ "http://de.archive.ubuntu.com/ubuntu/",
+ "edgy",
+ ["universe", "something"],
+ )
+ found_universe = 0
+ found_something = 0
+ for entry in sources:
+ if (
+ entry.type == "deb"
+ and entry.uri == "http://de.archive.ubuntu.com/ubuntu/"
+ and entry.dist == "edgy"
+ ):
+ for c in entry.comps:
+ if c == "universe":
+ found_universe += 1
+ if c == "something":
+ found_something += 1
+ # print "\n".join([s.str() for s in sources])
+ self.assertEqual(found_something, 1)
+ self.assertEqual(found_universe, 1)
+
+ def testAddingWithComment_deb822(self):
+ apt_pkg.config.set("Dir::Etc::sourceparts", "data/aptsources/" "sources.list.d")
+ self._commonTestAddingWithComment()
+
+ def testAddingWithComment_short(self):
+ apt_pkg.config.set("Dir::Etc::sourcelist", "data/aptsources/" "sources.list")
+ self._commonTestAddingWithComment()
+
+ def _commonTestAddingWithComment(self):
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+
+ # test to add something that is already there (main); loses comment
+ before = copy.deepcopy(sources)
+ sources.add(
+ "deb",
+ "http://de.archive.ubuntu.com/ubuntu/",
+ "edgy",
+ ["main"],
+ comment="this will be lost",
+ )
+ self.assertTrue(sources.list == before.list)
+ for entry in sources:
+ self.assertNotEqual(entry.comment, "this will be lost")
+
+ # test to add component to existing entry: multiverse; loses comment
+ sources.add(
+ "deb",
+ "http://de.archive.ubuntu.com/ubuntu/",
+ "edgy",
+ ["multiverse"],
+ comment="this will be lost",
+ )
+ for entry in sources:
+ self.assertNotEqual(entry.comment, "this will be lost")
+
+ before = copy.deepcopy(sources)
+ # test to add entirely new entry; retains comment
+ sources.add(
+ "deb-src",
+ "http://de.archive.ubuntu.com/ubuntu/",
+ "edgy",
+ ["main"],
+ comment="this will appear",
+ file=sources.list[0].file, # make sure we test the deb822 code path
+ )
+ self.assertNotEqual(sources.list, before.list)
+ self.assertEqual(len(sources.list), len(before.list) + 1)
+ found = False
+ for entry in sources:
+ if (
+ entry.type == "deb-src"
+ and entry.uri == "http://de.archive.ubuntu.com/ubuntu/"
+ and entry.dist == "edgy"
+ and entry.comment == "this will appear"
+ and "main" in entry.comps
+ ):
+ found = True
+ break
+ self.assertTrue(found)
+
+ def testInsertion_deb822(self):
+ apt_pkg.config.set("Dir::Etc::sourceparts", "data/aptsources/" "sources.list.d")
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+
+ # test to insert something that is already there (universe); does not
+ # move existing entry (remains at index 2)
+ before = copy.deepcopy(sources)
+ sources.add(
+ "deb", "http://de.archive.ubuntu.com/ubuntu/", "edgy", ["universe"], pos=0
+ )
+ self.assertTrue(sources.list == before.list)
+ entry = list(sources)[2]
+ self.assertEqual(entry.type, "deb")
+ self.assertEqual(entry.uri, "http://de.archive.ubuntu.com/ubuntu/")
+ self.assertEqual(entry.dist, "edgy")
+ self.assertIn("universe", entry.comps)
+
+ # test add component to existing entry: multiverse; does not move
+ # entry to which it is appended (remains at index 0)
+ sources.add(
+ "deb", "http://de.archive.ubuntu.com/ubuntu/", "edgy", ["multiverse"], pos=2
+ )
+ entry = list(sources)[0]
+ self.assertTrue(
+ entry.type == "deb"
+ and entry.uri == "http://de.archive.ubuntu.com/ubuntu/"
+ and entry.dist == "edgy"
+ and {"main", "multiverse"} <= set(entry.comps)
+ )
+
+ # test to add entirely new entry; inserted at 0
+ sources.add(
+ "deb-src", "http://de.archive.ubuntu.com/ubuntu/", "edgy", ["main"], pos=0
+ )
+ entry = list(sources)[0]
+ self.assertTrue(
+ entry.type == "deb-src"
+ and entry.uri == "http://de.archive.ubuntu.com/ubuntu/"
+ and entry.dist == "edgy"
+ and "main" in entry.comps
+ )
+
+ def testInsertion(self):
+ apt_pkg.config.set("Dir::Etc::sourcelist", "data/aptsources/" "sources.list")
+ sources = aptsources.sourceslist.SourcesList(True, self.templates)
+
+ # test to insert something that is already there (universe); does not
+ # move existing entry (remains at index 2)
+ before = copy.deepcopy(sources)
+ sources.add(
+ "deb", "http://de.archive.ubuntu.com/ubuntu/", "edgy", ["universe"], pos=0
+ )
+ self.assertTrue(sources.list == before.list)
+ entry = list(sources)[5]
+ self.assertTrue(
+ entry.type == "deb"
+ and entry.uri == "http://de.archive.ubuntu.com/ubuntu/"
+ and entry.dist == "edgy"
+ and "universe" in entry.comps
+ )
+
+ # test add component to existing entry: multiverse; does not move
+ # entry to which it is appended (remains at index 0)
+ sources.add(
+ "deb", "http://de.archive.ubuntu.com/ubuntu/", "edgy", ["multiverse"], pos=2
+ )
+ entry = list(sources)[1]
+ self.assertTrue(
+ entry.type == "deb"
+ and entry.uri == "http://de.archive.ubuntu.com/ubuntu/"
+ and entry.dist == "edgy"
+ and {"main", "multiverse"} <= set(entry.comps)
+ )
+
+ # test to add entirely new entry; inserted at 0
+ sources.add(
+ "deb-src", "http://de.archive.ubuntu.com/ubuntu/", "edgy", ["main"], pos=0
+ )
+ entry = list(sources)[0]
+ self.assertTrue(
+ entry.type == "deb-src"
+ and entry.uri == "http://de.archive.ubuntu.com/ubuntu/"
+ and entry.dist == "edgy"
+ and "main" in entry.comps
+ )
+
+ def testDuplication_short(self):
+ apt_pkg.config.set(
+ "Dir::Etc::sourcelist", "data/aptsources/sources.list.testDuplication"
+ )
+ return self.commonTestDuplication()
+
+ def testDuplication_deb822(self):
+ apt_pkg.config.set(
+ "Dir::Etc::sourceparts", "data/aptsources/sources.list.d.testDuplication"
+ )
+ return self.commonTestDuplication()
+
+ def commonTestDuplication(self):
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+ test_url = "http://ppa.launchpad.net/me/myproject/ubuntu"
+ # test to add something that is already there (enabled)
+ before = copy.deepcopy(sources)
+ sources.add("deb", test_url, "xenial", ["main"])
+ self.assertEqual(sources.list, before.list)
+ # test to add something that is already there (disabled)
+ sources.add("# deb-src", test_url, "xenial", ["main"])
+ self.assertEqual(sources.list, before.list)
+ # test to enable something that is already there
+ sources.add("deb-src", test_url, "xenial", ["main"])
+ found = False
+ self.assertEqual(len(sources.list), 2)
+ for entry in sources:
+ if (
+ entry.type == "deb-src"
+ and not entry.disabled
+ and entry.uri == test_url
+ and entry.dist == "xenial"
+ and entry.architectures == []
+ and entry.comps == ["main"]
+ ):
+ found = True
+ break
+ self.assertTrue(found)
+
+ def testMatcher_short(self):
+ """aptsources: Test matcher"""
+ apt_pkg.config.set(
+ "Dir::Etc::sourcelist", "data/aptsources/" "sources.list.testDistribution"
+ )
+ return self.commonTestMatcher()
+
+ def testMatcher_deb822(self):
+ """aptsources: Test matcher"""
+ apt_pkg.config.set(
+ "Dir::Etc::sourceparts",
+ "data/aptsources/" "sources.list.d.testDistribution",
+ )
+ return self.commonTestMatcher()
+
+ def commonTestMatcher(self):
+ """aptsources: Test matcher"""
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+ distro = aptsources.distro.get_distro(
+ id="Ubuntu",
+ codename="bionic",
+ description="Ubuntu 18.04 LTS",
+ release="18.04",
+ )
+ distro.get_sources(sources)
+ # test if all suits of the current distro were detected correctly
+ self.assertNotEqual(sources.list, [])
+ for s in sources:
+ if not s.template:
+ self.fail("source entry '%s' has no matcher" % s)
+
+ # Hack in a check for splitting of fields here.
+ if sources.list[-1].file.endswith(".sources"):
+ self.assertEqual(
+ sources.list[-1].uris, ["cdrom:[Ubuntu 8.04 _Hardy Heron_]"]
+ )
+ self.assertEqual(sources.list[-1].uri, "cdrom:[Ubuntu 8.04 _Hardy Heron_]")
+
+ def testMultiArch(self):
+ """aptsources: Test multi-arch parsing"""
+
+ apt_pkg.config.set("Dir::Etc::sourcelist", "data/aptsources/" "sources.list")
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+ assert not sources.list[8].invalid
+ assert sources.list[8].type == "deb"
+ assert sources.list[8].architectures == ["amd64", "i386"]
+ assert sources.list[8].uri == "http://de.archive.ubuntu.com/ubuntu/"
+ assert sources.list[8].dist == "natty"
+ assert sources.list[8].comps == ["main"]
+ assert sources.list[8].line.strip() == str(sources.list[8])
+ assert sources.list[8].trusted is None
+
+ def testMultiArch_deb822(self):
+ """aptsources: Test multi-arch parsing"""
+
+ apt_pkg.config.set("Dir::Etc::sourceparts", "data/aptsources/" "sources.list.d")
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+ assert not sources.list[3].invalid
+ assert sources.list[3].type == "deb"
+ assert sources.list[3].architectures == ["amd64", "i386"]
+ assert sources.list[3].uri == "http://de.archive.ubuntu.com/ubuntu/"
+ assert sources.list[3].dist == "natty"
+ assert sources.list[3].comps == ["main"]
+ self.assertEqual(sources.list[3].line.strip(), str(sources.list[3]))
+ self.assertIsNone(sources.list[3].trusted)
+
+ def testMultipleOptions(self):
+ """aptsources: Test multi-arch parsing"""
+
+ apt_pkg.config.set("Dir::Etc::sourcelist", "data/aptsources/" "sources.list")
+ sources = aptsources.sourceslist.SourcesList(True, self.templates)
+ assert sources.list[9].invalid is False
+ assert sources.list[9].type == "deb"
+ assert sources.list[9].architectures == ["amd64", "i386"]
+ self.assertEqual(sources.list[9].uri, "http://de.archive.ubuntu.com/ubuntu/")
+ assert sources.list[9].dist == "natty"
+ assert sources.list[9].comps == ["main"]
+ assert sources.list[9].trusted
+ assert sources.list[9].line.strip() == str(sources.list[9])
+
+ def testMultipleOptions_deb822(self):
+ """aptsources: Test multi-arch parsing"""
+
+ apt_pkg.config.set("Dir::Etc::sourceparts", "data/aptsources/" "sources.list.d")
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+ assert sources.list[4].invalid is False
+ assert sources.list[4].type == "deb"
+ assert sources.list[4].architectures == ["amd64", "i386"]
+ self.assertEqual(sources.list[4].uri, "http://de.archive.ubuntu.com/ubuntu/")
+ assert sources.list[4].dist == "natty"
+ assert sources.list[4].comps == ["main"]
+ assert sources.list[4].trusted
+ assert sources.list[4].line.strip() == str(sources.list[4])
+
+ def test_enable_component(self):
+ target = "./data/aptsources/sources.list.enable_comps"
+ line = "deb http://archive.ubuntu.com/ubuntu lucid main\n"
+ with open(target, "w") as target_file:
+ target_file.write(line)
+ apt_pkg.config.set("Dir::Etc::sourcelist", target)
+ sources = aptsources.sourceslist.SourcesList(True, self.templates)
+ distro = aptsources.distro.get_distro(
+ id="Ubuntu", codename="lucid", release="10.04", description="Ubuntu 10.04"
+ )
+ # and get the sources
+ distro.get_sources(sources)
+ # test enable_component
+ comp = "multiverse"
+ distro.enable_component(comp)
+ comps = set()
+ for entry in sources:
+ comps = comps.union(set(entry.comps))
+ self.assertTrue("multiverse" in comps)
+ self.assertTrue("universe" in comps)
+
+ def test_enable_component_deb822(self):
+ target = (
+ apt_pkg.config.find_dir("dir::etc::sourceparts") + "enable_comps.sources"
+ )
+ line = "Types: deb\nURIs: http://archive.ubuntu.com/ubuntu\nSuites: lucid\nComponents: main\n"
+ with open(target, "w") as target_file:
+ target_file.write(line)
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+ distro = aptsources.distro.get_distro(
+ id="Ubuntu", codename="lucid", release="10.04", description="Ubuntu 10.04"
+ )
+ # and get the sources
+ distro.get_sources(sources)
+ # test enable_component
+ comp = "multiverse"
+ distro.enable_component(comp)
+ comps = set()
+ for entry in sources:
+ comps = comps.union(set(entry.comps))
+ self.assertTrue("multiverse" in comps)
+ self.assertTrue("universe" in comps)
+
+ def test_enable_component_deb822_multi(self):
+ apt_pkg.config.set("Dir::Etc::sourcelist", "/dev/null")
+
+ target = (
+ apt_pkg.config.find_dir("dir::etc::sourceparts") + "enable_comps.sources"
+ )
+ line = "Types: deb\nURIs: http://archive.ubuntu.com/ubuntu\nSuites: lucid lucid-updates\nComponents: main\n"
+ with open(target, "w") as target_file:
+ target_file.write(line)
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+ distro = aptsources.distro.get_distro(
+ id="Ubuntu", codename="lucid", release="10.04", description="Ubuntu 10.04"
+ )
+ # and get the sources
+ distro.get_sources(sources)
+ self.assertEqual(len(distro.main_sources), 1)
+ self.assertEqual(len(sources.list), 1)
+ # test enable_component
+ comp = "multiverse"
+ distro.enable_component(comp)
+ self.assertEqual(len(sources.list), 2) # split into two
+
+ self.assertEqual(
+ "Types: deb\n"
+ "URIs: http://archive.ubuntu.com/ubuntu\n"
+ "Suites: lucid\n"
+ "Components: main multiverse universe",
+ str(sources.list[0]),
+ )
+ self.assertEqual(
+ "Types: deb\n"
+ "URIs: http://archive.ubuntu.com/ubuntu\n"
+ "Suites: lucid-updates\n"
+ "Components: main multiverse universe",
+ str(sources.list[1]),
+ )
+
+ comps = set()
+ for entry in sources:
+ comps = comps.union(set(entry.comps))
+ self.assertTrue("multiverse" in comps)
+ self.assertTrue("universe" in comps)
+
+ sources.save()
+ self.assertEqual(
+ "Types: deb\n"
+ "URIs: http://archive.ubuntu.com/ubuntu\n"
+ "Suites: lucid lucid-updates\n"
+ "Components: main multiverse universe",
+ str(sources.list[0]),
+ )
+
+ def test_enable_component_deb822_multi_mixed_origin(self):
+ apt_pkg.config.set("Dir::Etc::sourcelist", "/dev/null")
+
+ target = (
+ apt_pkg.config.find_dir("dir::etc::sourceparts") + "enable_comps.sources"
+ )
+ line = "Types: deb\nURIs: http://archive.ubuntu.com/ubuntu http://example.com/\nSuites: lucid\nComponents: main\n"
+ with open(target, "w") as target_file:
+ target_file.write(line)
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+ distro = aptsources.distro.get_distro(
+ id="Ubuntu", codename="lucid", release="10.04", description="Ubuntu 10.04"
+ )
+ # and get the sources
+ distro.get_sources(sources)
+ self.assertEqual(len(distro.main_sources), 2)
+ self.assertEqual(len(sources.list), 1)
+ # test enable_component
+ comp = "multiverse"
+ distro.enable_component(comp)
+ self.assertEqual(len(sources.list), 2) # split into two
+
+ self.assertEqual(
+ "Types: deb\n"
+ "URIs: http://archive.ubuntu.com/ubuntu\n"
+ "Suites: lucid\n"
+ "Components: main multiverse universe",
+ str(sources.list[0]),
+ )
+ self.assertEqual(
+ "Types: deb\n"
+ "URIs: http://example.com/\n"
+ "Suites: lucid\n"
+ "Components: main",
+ str(sources.list[1]),
+ )
+
+ sources.save()
+ self.assertEqual(
+ "Types: deb\n"
+ "URIs: http://archive.ubuntu.com/ubuntu\n"
+ "Suites: lucid\n"
+ "Components: main multiverse universe",
+ str(sources.list[0]),
+ )
+ self.assertEqual(
+ "Types: deb\n"
+ "URIs: http://example.com/\n"
+ "Suites: lucid\n"
+ "Components: main",
+ str(sources.list[1]),
+ )
+
+ def test_enable_component_deb822_multi_mixed_ultimate(self):
+ apt_pkg.config.set("Dir::Etc::sourcelist", "/dev/null")
+
+ target = (
+ apt_pkg.config.find_dir("dir::etc::sourceparts") + "enable_comps.sources"
+ )
+ line = "Types: deb deb-src\nURIs: http://archive.ubuntu.com/ubuntu http://example.com/\nSuites: lucid lucid-updates notalucid\nComponents: main\n"
+ with open(target, "w") as target_file:
+ target_file.write(line)
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+ distro = aptsources.distro.get_distro(
+ id="Ubuntu", codename="lucid", release="10.04", description="Ubuntu 10.04"
+ )
+ # and get the sources
+ distro.get_sources(sources)
+ self.assertEqual(len(distro.main_sources), 1)
+ self.assertEqual(len(sources.list), 1)
+ self.assertEqual(len(sources.exploded_list()), 12)
+ # test enable_component
+ comp = "multiverse"
+ distro.enable_component(comp)
+ self.assertEqual(len(sources.list), 12) # split into two
+
+ expected = []
+ for typ in "deb", "deb-src":
+ for uri in "http://archive.ubuntu.com/ubuntu", "http://example.com/":
+ for suite in "lucid", "lucid-updates", "notalucid":
+ comps = "main multiverse universe"
+ # unofficial source ends up without enablement
+ if uri == "http://example.com/" or suite == "notalucid":
+ comps = "main"
+ expected.append(
+ f"Types: {typ}\n"
+ f"URIs: {uri}\n"
+ f"Suites: {suite}\n"
+ f"Components: {comps}"
+ )
+
+ self.maxDiff = None
+ self.assertEqual(expected, list(map(str, sources.list)))
+ sources.save()
+
+ expected = [
+ "Types: deb deb-src\n"
+ "URIs: http://archive.ubuntu.com/ubuntu\n"
+ "Suites: lucid lucid-updates\n"
+ "Components: main multiverse universe",
+ # unofficial suite
+ "Types: deb deb-src\n"
+ "URIs: http://archive.ubuntu.com/ubuntu http://example.com/\n"
+ "Suites: notalucid\n"
+ "Components: main",
+ # unofficial mirror, FIXME: We'd rather merge the notalucid into here
+ "Types: deb deb-src\n"
+ "URIs: http://example.com/\n"
+ "Suites: lucid lucid-updates\n"
+ "Components: main",
+ ]
+ self.maxDiff = None
+ self.assertEqual(expected, list(map(str, sources.list)))
+
+ def test_deb822_explode(self):
+ apt_pkg.config.set("Dir::Etc::sourcelist", "/dev/null")
+ target = (
+ apt_pkg.config.find_dir("dir::etc::sourceparts") + "enable_comps.sources"
+ )
+ line = "Types: deb\nURIs: http://archive.ubuntu.com/ubuntu\nSuites: lucid\nComponents: main\n"
+ with open(target, "w") as target_file:
+ target_file.write(line)
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+
+ self.assertEqual(len(sources.list), 1)
+ self.assertEqual(len(sources.exploded_list()), 1)
+ self.assertIsInstance(
+ sources.exploded_list()[0], aptsources.sourceslist.Deb822SourceEntry
+ )
+
+ sources.list[0].suites += ["fakesuite"]
+ self.assertEqual(len(sources.list), 1)
+ self.assertEqual(len(sources.exploded_list()), 2)
+ self.assertIsInstance(
+ sources.exploded_list()[0], aptsources.sourceslist.ExplodedDeb822SourceEntry
+ )
+ self.assertIsInstance(
+ sources.exploded_list()[1], aptsources.sourceslist.ExplodedDeb822SourceEntry
+ )
+ self.assertEqual(sources.list[0].suites, ["lucid", "fakesuite"])
+ sources.remove(sources.exploded_list()[1])
+ self.assertEqual(len(sources.list), 1)
+ self.assertEqual(len(sources.exploded_list()), 1)
+ self.assertEqual(sources.list[0].suites, ["lucid"])
+
+ sources.list[0].types += ["deb-src"]
+ self.assertEqual(len(sources.list), 1)
+ self.assertEqual(len(sources.exploded_list()), 2)
+ self.assertIsInstance(
+ sources.exploded_list()[0], aptsources.sourceslist.ExplodedDeb822SourceEntry
+ )
+ self.assertIsInstance(
+ sources.exploded_list()[1], aptsources.sourceslist.ExplodedDeb822SourceEntry
+ )
+ self.assertEqual(sources.list[0].types, ["deb", "deb-src"])
+ sources.remove(sources.exploded_list()[1])
+ self.assertEqual(len(sources.list), 1)
+ self.assertEqual(len(sources.exploded_list()), 1)
+ self.assertEqual(sources.list[0].types, ["deb"])
+
+ sources.list[0].uris += ["http://example.com"]
+ self.assertEqual(len(sources.list), 1)
+ self.assertEqual(len(sources.exploded_list()), 2)
+ self.assertIsInstance(
+ sources.exploded_list()[0], aptsources.sourceslist.ExplodedDeb822SourceEntry
+ )
+ self.assertIsInstance(
+ sources.exploded_list()[1], aptsources.sourceslist.ExplodedDeb822SourceEntry
+ )
+ self.assertEqual(
+ sources.list[0].uris,
+ ["http://archive.ubuntu.com/ubuntu", "http://example.com"],
+ )
+ sources.remove(sources.exploded_list()[1])
+ self.assertEqual(len(sources.list), 1)
+ self.assertEqual(len(sources.exploded_list()), 1)
+ self.assertEqual(sources.list[0].uris, ["http://archive.ubuntu.com/ubuntu"])
+
+ # test setting attributes
+ sources.list[0].uris += ["http://example.com"]
+ with self.assertRaises(AttributeError):
+ sources.exploded_list()[0].types = ["does not work"]
+ with self.assertRaises(AttributeError):
+ sources.exploded_list()[0].uris = ["does not work"]
+ with self.assertRaises(AttributeError):
+ sources.exploded_list()[0].suites = ["does not work"]
+ with self.assertRaises(AttributeError):
+ sources.exploded_list()[0].doesnotexist = ["does not work"]
+
+ # test overriding
+ sources.exploded_list()[0].type = "faketype"
+ self.assertEqual(sources.list[0].type, "faketype")
+ self.assertEqual(sources.list[1].type, "deb")
+ sources.exploded_list()[0].type = "deb"
+ self.assertEqual(sources.list[0].type, "deb")
+ self.assertEqual(sources.list[1].type, "deb")
+ sources.save()
+ self.assertEqual(len(sources.list), 1)
+
+ def testDistribution_short(self):
+ """aptsources: Test distribution detection."""
+ apt_pkg.config.set(
+ "Dir::Etc::sourcelist", "data/aptsources/" "sources.list.testDistribution"
+ )
+ return self.commonTestDistribution()
+
+ def testDistribution_deb822(self):
+ """aptsources: Test distribution detection."""
+ apt_pkg.config.set(
+ "Dir::Etc::sourceparts",
+ "data/aptsources/" "sources.list.d.testDistribution",
+ )
+ return self.commonTestDistribution()
+
+ def commonTestDistribution(self):
+ """aptsources: Test distribution detection."""
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+ distro = aptsources.distro.get_distro(
+ id="Ubuntu",
+ codename="bionic",
+ description="Ubuntu 18.04 LTS",
+ release="18.04",
+ )
+ distro.get_sources(sources)
+ # test if all suits of the current distro were detected correctly
+ dist_templates = set()
+ for s in sources:
+ if s.template:
+ dist_templates.add(s.template.name)
+ # print dist_templates
+ for d in (
+ "hardy",
+ "hardy-security",
+ "hardy-updates",
+ "intrepid",
+ "hardy-backports",
+ ):
+ self.assertTrue(d in dist_templates)
+ # test enable
+ comp = "restricted"
+ distro.enable_component(comp)
+ found = {}
+ for entry in sources:
+ if (
+ entry.type == "deb"
+ and entry.uri == "http://de.archive.ubuntu.com/ubuntu/"
+ and "edgy" in entry.dist
+ ):
+ for c in entry.comps:
+ if c == comp:
+ if entry.dist not in found:
+ found[entry.dist] = 0
+ found[entry.dist] += 1
+ # print "".join([s.str() for s in sources])
+ for key in found:
+ self.assertEqual(found[key], 1)
+
+ # add a not-already available component
+ comp = "multiverse"
+ distro.enable_component(comp)
+ found = {}
+ for entry in sources:
+ if entry.type == "deb" and entry.template and entry.template.name == "edgy":
+ for c in entry.comps:
+ if c == comp:
+ if entry.dist not in found.has_key:
+ found[entry.dist] = 0
+ found[entry.dist] += 1
+ # print "".join([s.str() for s in sources])
+ for key in found:
+ self.assertEqual(found[key], 1)
+
+ @unittest.skip("lsb-release test broken when it was added")
+ def test_os_release_distribution(self):
+ """os-release file can be read and is_like is populated accordingly"""
+ os.environ["LSB_ETC_LSB_RELEASE"] = os.path.abspath(
+ "./data/aptsources/lsb-release"
+ )
+ aptsources.distro._OSRelease.OS_RELEASE_FILE = os.path.abspath(
+ "./data/aptsources/os-release"
+ )
+ distro = aptsources.distro.get_distro()
+ # Everything but is_like comes from lsb_release, see TODO in
+ # get_distro.
+ self.assertEqual("Ubuntu", distro.id)
+ self.assertEqual("xenial", distro.codename)
+ self.assertEqual("Ubuntu 16.04.1 LTS", distro.description)
+ self.assertEqual("16.04", distro.release)
+ self.assertEqual(["ubuntu", "debian"], distro.is_like)
+
+ def test_enable_disabled_short(self):
+ """LP: #1042916: Test enabling disabled entry."""
+ apt_pkg.config.set("Dir::Etc::sourcelist", "data/aptsources/" "sources.list")
+ return self.common_test_enable_disabled()
+
+ def test_enable_disabled_deb822(self):
+ """LP: #1042916: Test enabling disabled entry."""
+ apt_pkg.config.set("Dir::Etc::sourceparts", "data/aptsources/" "sources.list.d")
+ return self.common_test_enable_disabled()
+
+ def common_test_enable_disabled(self):
+ """LP: #1042916: Test enabling disabled entry."""
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+ disabled = sources.add(
+ "deb",
+ "http://fi.archive.ubuntu.com/ubuntu/",
+ "precise",
+ ["main"],
+ file=sources.list[0].file, # if we use deb822, enable deb822
+ )
+ disabled.set_enabled(False)
+ enabled = sources.add(
+ "deb",
+ "http://fi.archive.ubuntu.com/ubuntu/",
+ "precise",
+ ["main"],
+ file=sources.list[0].file, # if we use deb822, enable deb822
+ )
+ self.assertEqual(disabled, enabled)
+ self.assertFalse(disabled.disabled)
+
+ def test_duplicate_uri_with_trailing_slash_short(self):
+ """Test replacing entry with same uri except trailing slash"""
+ apt_pkg.config.set("Dir::Etc::sourcelist", "data/aptsources/" "sources.list")
+ return self.common_test_duplicate_uri_with_trailing_slash()
+
+ def test_duplicate_uri_with_trailing_slash_deb822(self):
+ """Test replacing entry with same uri except trailing slash"""
+ apt_pkg.config.set("Dir::Etc::sourceparts", "data/aptsources/" "sources.list.d")
+ return self.common_test_duplicate_uri_with_trailing_slash()
+
+ def common_test_duplicate_uri_with_trailing_slash(self):
+ """Test replacing entry with same uri except trailing slash"""
+ sources = aptsources.sourceslist.SourcesList(True, self.templates, deb822=True)
+ line_wslash = "deb http://rslash.ubuntu.com/ubuntu/ precise main"
+ line_woslash = "deb http://rslash.ubuntu.com/ubuntu precise main"
+ entry_wslash = aptsources.sourceslist.SourceEntry(line_wslash)
+ entry_woslash = aptsources.sourceslist.SourceEntry(line_woslash)
+ self.assertEqual(entry_wslash, entry_woslash)
+ count = len(sources.list)
+ sourceslist_wslash = sources.add(
+ entry_wslash.type, entry_wslash.uri, entry_wslash.dist, entry_wslash.comps
+ )
+ self.assertEqual(count + 1, len(sources.list))
+ count = len(sources.list)
+ sourceslist_woslash = sources.add(
+ entry_woslash.type,
+ entry_woslash.uri,
+ entry_woslash.dist,
+ entry_woslash.comps,
+ )
+ self.assertEqual(count, len(sources.list))
+ self.assertEqual(sourceslist_wslash, sourceslist_woslash)
+
+ def test_deb822_distro_enable_disable_component(self):
+ """Test enabling and disabling a component in the distro sources.
+
+ This ensures reasonable behavior when enabling and then disabling a component"""
+ with tempfile.NamedTemporaryFile("w", suffix=".sources") as file:
+ file.write(
+ "# main archive\n"
+ "Types: deb deb-src\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# security\n"
+ "Types: deb deb-src\n"
+ "URIs: http://security.ubuntu.com/ubuntu/\n"
+ "Suites: noble-security\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ )
+ file.flush()
+
+ apt_pkg.config.set("Dir::Etc::sourcelist", file.name)
+ sources = aptsources.sourceslist.SourcesList(
+ True, self.templates, deb822=True
+ )
+ distro = aptsources.distro.get_distro(
+ id="Ubuntu",
+ codename="noble",
+ description="Ubuntu 24.04 LTS",
+ release="24.04",
+ )
+
+ self.assertEqual(len(sources.list), 2)
+ distro.get_sources(sources)
+ distro.enable_component("multiverse")
+ sources.save()
+
+ with open(file.name) as readonly:
+ self.maxDiff = None
+ self.assertEqual(
+ readonly.read(),
+ "# main archive\n"
+ "Types: deb deb-src\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main universe multiverse\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# security\n"
+ "Types: deb deb-src\n"
+ "URIs: http://security.ubuntu.com/ubuntu/\n"
+ "Suites: noble-security\n"
+ "Components: main universe multiverse\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n",
+ )
+
+ # Disable it again
+ # FIXME: The child entries will no longer be valid at this point, so we have to call
+ # get_sources(), but it's not clear whether this should be considered a bug -
+ # there may have been other changes rendering entries no longer valid that distro
+ # is holding on to.
+ distro.get_sources(sources)
+ distro.disable_component("multiverse")
+ sources.save()
+
+ with open(file.name) as readonly:
+ self.maxDiff = None
+ self.assertEqual(
+ readonly.read(),
+ "# main archive\n"
+ "Types: deb deb-src\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# security\n"
+ "Types: deb deb-src\n"
+ "URIs: http://security.ubuntu.com/ubuntu/\n"
+ "Suites: noble-security\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n",
+ )
+
+ # Disable universe as well
+ distro.get_sources(sources)
+ distro.disable_component("universe")
+ sources.save()
+
+ with open(file.name) as readonly:
+ self.maxDiff = None
+ self.assertEqual(
+ readonly.read(),
+ "# main archive\n"
+ "Types: deb deb-src\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# security\n"
+ "Types: deb deb-src\n"
+ "URIs: http://security.ubuntu.com/ubuntu/\n"
+ "Suites: noble-security\n"
+ "Components: main\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n",
+ )
+
+ def test_deb822_distro_enable_disable_component_mixed_origin(self):
+ """Test enabling and disabling a component in the distro sources, with mixed origin
+
+ Here we ensure that we still get idempotent behavior of disable after enable even if the
+ entry we were modifying also had a non-official repository in it."""
+ with tempfile.NamedTemporaryFile("w", suffix=".sources") as file:
+ file.write(
+ "# main archive\n"
+ "Types: deb deb-src\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/ http://unofficial.example.com/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# security\n"
+ "Types: deb deb-src\n"
+ "URIs: http://security.ubuntu.com/ubuntu/\n"
+ "Suites: noble-security\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ )
+ file.flush()
+
+ apt_pkg.config.set("Dir::Etc::sourcelist", file.name)
+ sources = aptsources.sourceslist.SourcesList(
+ True, self.templates, deb822=True
+ )
+ distro = aptsources.distro.get_distro(
+ id="Ubuntu",
+ codename="noble",
+ description="Ubuntu 24.04 LTS",
+ release="24.04",
+ )
+
+ self.assertEqual(len(sources.list), 2)
+ distro.get_sources(sources)
+ distro.enable_component("multiverse")
+ sources.save()
+
+ with open(file.name) as readonly:
+ self.maxDiff = None
+ self.assertEqual(
+ readonly.read(),
+ "# main archive\n"
+ "Types: deb deb-src\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main universe multiverse\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# main archive\n"
+ "Types: deb deb-src\n"
+ "URIs: http://unofficial.example.com/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# security\n"
+ "Types: deb deb-src\n"
+ "URIs: http://security.ubuntu.com/ubuntu/\n"
+ "Suites: noble-security\n"
+ "Components: main universe multiverse\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n",
+ )
+
+ # Disable it again
+ # FIXME: The child entries will no longer be valid at this point, so we have to call
+ # get_sources(), but it's not clear whether this should be considered a bug -
+ # there may have been other changes rendering entries no longer valid that distro
+ # is holding on to.
+ distro.get_sources(sources)
+ distro.disable_component("multiverse")
+ sources.save()
+
+ with open(file.name) as readonly:
+ self.maxDiff = None
+ self.assertEqual(
+ readonly.read(),
+ "# main archive\n"
+ "Types: deb deb-src\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/ http://unofficial.example.com/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# security\n"
+ "Types: deb deb-src\n"
+ "URIs: http://security.ubuntu.com/ubuntu/\n"
+ "Suites: noble-security\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n",
+ )
+
+ # Disable universe too. The behaviour here is interesting: Distro only disables
+ # universe for the official source, so we end up with the non-official source split out.
+ distro.get_sources(sources)
+ distro.disable_component("universe")
+ sources.save()
+
+ with open(file.name) as readonly:
+ self.maxDiff = None
+ self.assertEqual(
+ readonly.read(),
+ "# main archive\n"
+ "Types: deb deb-src\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# main archive\n" # note it keeps the comment on the split out child
+ "Types: deb deb-src\n"
+ "URIs: http://unofficial.example.com/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# security\n"
+ "Types: deb deb-src\n"
+ "URIs: http://security.ubuntu.com/ubuntu/\n"
+ "Suites: noble-security\n"
+ "Components: main\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n",
+ )
+
+ def test_deb822_distro_enable_disable_child_source_mixed_origins(self):
+ """Test enabling and disabling a child source (proposed) in the distro sources, with mixed origin
+
+ Here we ensure that we still get idempotent behavior of disable after enable even if the
+ entry we were modifying also had a non-official repository in it."""
+ with tempfile.NamedTemporaryFile("w", suffix=".sources") as file:
+ file.write(
+ "# main archive\n"
+ "Types: deb deb-src\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/ http://unofficial.example.com/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# security\n"
+ "Types: deb deb-src\n"
+ "URIs: http://security.ubuntu.com/ubuntu/\n"
+ "Suites: noble-security\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ )
+ file.flush()
+
+ apt_pkg.config.set("Dir::Etc::sourcelist", file.name)
+ sources = aptsources.sourceslist.SourcesList(
+ True, self.templates, deb822=True
+ )
+ distro = aptsources.distro.get_distro(
+ id="Ubuntu",
+ codename="noble",
+ description="Ubuntu 24.04 LTS",
+ release="24.04",
+ )
+
+ self.assertEqual(len(sources.list), 2)
+ distro.get_sources(sources)
+ distro.get_source_code = True
+ distro.add_source(dist="noble-proposed")
+
+ # FIXME: Component ordering is not stable right now
+ for entry in sources.list:
+ entry.comps = sorted(entry.comps)
+ sources.save()
+
+ with open(file.name) as readonly:
+ self.maxDiff = None
+
+ # FIXME: In an optimal world it would look like this
+ self.assertNotEqual(
+ readonly.read(),
+ "# main archive\n"
+ "Types: deb deb-src\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/\n"
+ "Suites: noble noble-updates noble-proposed\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# main archive\n"
+ "Types: deb deb-src\n"
+ "URIs: http://unofficial.example.com/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# security\n"
+ "Types: deb deb-src\n"
+ "URIs: http://security.ubuntu.com/ubuntu/\n"
+ "Suites: noble-security\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n",
+ )
+
+ with open(file.name) as readonly:
+ self.maxDiff = None
+ # Sadly our merge algorithm does not always produce optimal merges, because it merges the unofficial entry first
+ self.assertEqual(
+ readonly.read(),
+ "# main archive\n"
+ "Types: deb deb-src\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/ http://unofficial.example.com/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# security\n"
+ "Types: deb deb-src\n"
+ "URIs: http://security.ubuntu.com/ubuntu/\n"
+ "Suites: noble-security\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "Types: deb deb-src\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/\n"
+ "Suites: noble-proposed\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n",
+ )
+
+ # Disable it again
+ # FIXME: The child entries will no longer be valid at this point, so we have to call
+ # get_sources(), but it's not clear whether this should be considered a bug -
+ # there may have been other changes rendering entries no longer valid that distro
+ # is holding on to.
+ distro.get_sources(sources)
+ for child in distro.child_sources + distro.source_code_sources:
+ if child.dist.endswith("proposed"):
+ sources.remove(child)
+ sources.save()
+
+ with open(file.name) as readonly:
+ self.maxDiff = None
+ self.assertEqual(
+ readonly.read(),
+ "# main archive\n"
+ "Types: deb deb-src\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/ http://unofficial.example.com/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# security\n"
+ "Types: deb deb-src\n"
+ "URIs: http://security.ubuntu.com/ubuntu/\n"
+ "Suites: noble-security\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n",
+ )
+
+ def test_deb822_distro_enable_disable_child_source_mixed_origins_no_source_code(
+ self,
+ ):
+ """Test enabling and disabling a child source (proposed) in the distro sources, with mixed origin
+
+ Here we ensure that we still get idempotent behavior of disable after enable even if the
+ entry we were modifying also had a non-official repository in it."""
+ with tempfile.NamedTemporaryFile("w", suffix=".sources") as file:
+ file.write(
+ "# main archive\n"
+ "Types: deb\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/ http://unofficial.example.com/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# security\n"
+ "Types: deb\n"
+ "URIs: http://security.ubuntu.com/ubuntu/\n"
+ "Suites: noble-security\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ )
+ file.flush()
+
+ apt_pkg.config.set("Dir::Etc::sourcelist", file.name)
+ sources = aptsources.sourceslist.SourcesList(
+ True, self.templates, deb822=True
+ )
+ distro = aptsources.distro.get_distro(
+ id="Ubuntu",
+ codename="noble",
+ description="Ubuntu 24.04 LTS",
+ release="24.04",
+ )
+
+ self.assertEqual(len(sources.list), 2)
+ distro.get_sources(sources)
+ self.assertEqual(
+ [(s.type, s.uri, s.dist, s.comps) for s in distro.main_sources]
+ + ["separator"]
+ + [(s.type, s.uri, s.dist, s.comps) for s in distro.child_sources],
+ [
+ (
+ "deb",
+ "http://archive.ubuntu.com/ubuntu/",
+ "noble",
+ ["main", "universe"],
+ ),
+ "separator",
+ (
+ "deb",
+ "http://archive.ubuntu.com/ubuntu/",
+ "noble-updates",
+ ["main", "universe"],
+ ),
+ (
+ "deb",
+ "http://security.ubuntu.com/ubuntu/",
+ "noble-security",
+ ["main", "universe"],
+ ),
+ ],
+ )
+ distro.add_source(dist="noble-proposed")
+
+ # FIXME: Component ordering is not stable right now
+ for entry in sources.list:
+ entry.comps = sorted(entry.comps)
+ sources.save()
+
+ with open(file.name) as readonly:
+ self.maxDiff = None
+
+ # FIXME: In an optimal world it would look like this
+ self.assertNotEqual(
+ readonly.read(),
+ "# main archive\n"
+ "Types: deb\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/\n"
+ "Suites: noble noble-updates noble-proposed\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# main archive\n"
+ "Types: deb\n"
+ "URIs: http://unofficial.example.com/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# security\n"
+ "Types: deb\n"
+ "URIs: http://security.ubuntu.com/ubuntu/\n"
+ "Suites: noble-security\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n",
+ )
+
+ with open(file.name) as readonly:
+ self.maxDiff = None
+ # Sadly our merge algorithm does not always produce optimal merges, because it merges the unofficial entry first
+ self.assertEqual(
+ readonly.read(),
+ "# main archive\n"
+ "Types: deb\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/ http://unofficial.example.com/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# security\n"
+ "Types: deb\n"
+ "URIs: http://security.ubuntu.com/ubuntu/\n"
+ "Suites: noble-security\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "Types: deb\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/\n"
+ "Suites: noble-proposed\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n",
+ )
+
+ # Disable it again
+ # FIXME: The child entries will no longer be valid at this point, so we have to call
+ # get_sources(), but it's not clear whether this should be considered a bug -
+ # there may have been other changes rendering entries no longer valid that distro
+ # is holding on to.
+ distro.get_sources(sources)
+ for child in distro.child_sources + distro.source_code_sources:
+ if child.dist.endswith("proposed"):
+ sources.remove(child)
+ sources.save()
+
+ with open(file.name) as readonly:
+ self.maxDiff = None
+ self.assertEqual(
+ readonly.read(),
+ "# main archive\n"
+ "Types: deb\n"
+ "URIs: http://archive.ubuntu.com/ubuntu/ http://unofficial.example.com/\n"
+ "Suites: noble noble-updates\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n"
+ "\n"
+ "# security\n"
+ "Types: deb\n"
+ "URIs: http://security.ubuntu.com/ubuntu/\n"
+ "Suites: noble-security\n"
+ "Components: main universe\n"
+ "Signed-By: /usr/share/keyrings/ubuntu-archive-keyrings.gpg\n",
+ )
+
+
+if __name__ == "__main__":
+ os.chdir(os.path.dirname(__file__))
+ unittest.main()
diff --git a/tests/test_aptsources_deb822.py b/tests/test_aptsources_deb822.py
new file mode 100755
index 0000000..6a1bb61
--- /dev/null
+++ b/tests/test_aptsources_deb822.py
@@ -0,0 +1,63 @@
+#!/usr/bin/python3
+
+import io
+import os
+import tempfile
+import unittest
+
+import apt_pkg
+import testcommon
+
+import aptsources.distro
+import aptsources.sourceslist
+
+
+class TestAptSources(testcommon.TestCase):
+ def setUp(self):
+ testcommon.TestCase.setUp(self)
+ self.tempfile = tempfile.NamedTemporaryFile(suffix=".sources")
+ apt_pkg.config.set("Dir::Etc::sourcelist", self.tempfile.name)
+ apt_pkg.config.set("Dir::Etc::sourceparts", "/dev/null")
+
+ def tearDown(self):
+ self.tempfile.close()
+
+ def testEmptyDeb822(self):
+ """aptsources: Test sources.list parsing."""
+ sources = aptsources.sourceslist.SourcesList(True)
+ self.assertListEqual(sources.list, [])
+
+ def testDeb822SectionRecognizedWithoutEndLine(self):
+ """aptsources: Test sources.list parsing."""
+ section = aptsources._deb822.Section("key: value\notherkey: othervalue")
+
+ # Writing it back out gives us an extra newline at the end
+ self.assertEqual(section["key"], "value")
+ self.assertEqual(section["otherkey"], "othervalue")
+ self.assertEqual(str(section), "key: value\notherkey: othervalue\n")
+
+ file = aptsources._deb822.File(io.StringIO("key: value\notherkey: othervalue"))
+ self.assertEqual(len(file.sections), 1)
+
+ section = next(iter(file))
+ self.assertEqual(section["key"], "value")
+ self.assertEqual(section["otherkey"], "othervalue")
+ self.assertEqual(str(section), "key: value\notherkey: othervalue\n")
+
+ def testDeb822MultipleLinesSeparator(self):
+ """aptsources: Test sources.list parsing."""
+ for separator in "\n\n\n\n", "\n\n\n", "\n\n":
+ with self.subTest(f"{len(separator)} separators"):
+ file = aptsources._deb822.File(
+ io.StringIO("key: value" + separator + "otherkey: othervalue\n")
+ )
+ self.assertEqual(len(file.sections), 2)
+
+ self.assertEqual(file.sections[0]["key"], "value")
+ self.assertEqual(file.sections[1]["otherkey"], "othervalue")
+ self.assertEqual(str(file), "key: value\n\notherkey: othervalue\n")
+
+
+if __name__ == "__main__":
+ os.chdir(os.path.dirname(__file__))
+ unittest.main()
diff --git a/tests/test_aptsources_ports.py b/tests/test_aptsources_ports.py
new file mode 100644
index 0000000..234135b
--- /dev/null
+++ b/tests/test_aptsources_ports.py
@@ -0,0 +1,41 @@
+#!/usr/bin/python3
+import os
+import tempfile
+import unittest
+
+import apt_pkg
+import testcommon
+
+import aptsources.distro
+import aptsources.sourceslist
+
+
+class TestAptSourcesPorts(testcommon.TestCase):
+ """Test aptsources on ports.ubuntu.com."""
+
+ def setUp(self):
+ testcommon.TestCase.setUp(self)
+ apt_pkg.config.set("APT::Architecture", "powerpc")
+ apt_pkg.config.set("Dir::Etc", os.path.abspath("data/aptsources_ports"))
+ apt_pkg.config.set("Dir::Etc::sourceparts", tempfile.mkdtemp())
+ if os.path.exists("../build/data/templates"):
+ self.templates = os.path.abspath("../build/data/templates")
+ else:
+ self.templates = "/usr/share/python-apt/templates/"
+
+ def testMatcher(self):
+ """aptsources_ports: Test matcher."""
+ apt_pkg.config.set("Dir::Etc::sourcelist", "sources.list")
+ sources = aptsources.sourceslist.SourcesList(True, self.templates)
+ distro = aptsources.distro.get_distro("Ubuntu", "hardy", "desc", "8.04")
+ distro.get_sources(sources)
+ # test if all suits of the current distro were detected correctly
+ for s in sources:
+ if not s.line.strip() or s.line.startswith("#"):
+ continue
+ if not s.template:
+ self.fail("source entry '%s' has no matcher" % s)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_auth.py b/tests/test_auth.py
new file mode 100644
index 0000000..811cbb9
--- /dev/null
+++ b/tests/test_auth.py
@@ -0,0 +1,314 @@
+#!/usr/bin/python3
+
+import contextlib
+import errno
+import itertools
+import os
+import shutil
+import sys
+import tempfile
+import time
+import unittest
+from http.server import HTTPServer
+from http.server import SimpleHTTPRequestHandler as HTTPRequestHandler
+
+import apt_pkg
+import testcommon
+
+import apt.auth
+
+WHEEZY_KEYID = "8B48AD6246925553"
+WHEEZY_KEYDATE = "1335553717"
+WHEEZY_KEY = """-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.12 (GNU/Linux)
+
+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
+DHcut3Yey8o=
+=id4q
+-----END PGP PUBLIC KEY BLOCK-----"""
+
+
+def normalize_key(keystr):
+ """Remove the Version: header from a key block"""
+ lines = keystr.split("\n")
+ if lines[1].startswith("Version:"):
+ return lines[:1] + lines[2:]
+ return lines
+
+
+class TestAuthKeys(testcommon.TestCase):
+
+ """Test handling of keys for signed repositories."""
+
+ def setUp(self):
+ testcommon.TestCase.setUp(self)
+
+ self.tmpdir = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmpdir)
+ apt_pkg.config.set("Dir", self.tmpdir)
+ apt_pkg.config.set("Dir::Bin::Apt-key", "fakeroot-apt-key")
+ apt_pkg.config.set("Dir::Etc", "etc/apt/")
+ trustedparts_dir = apt_pkg.config.find_dir("Dir::Etc::Trustedparts")
+ confparts_dir = apt_pkg.config.find_dir("Dir::Etc::parts")
+ self.assertTrue(trustedparts_dir.startswith(self.tmpdir))
+ os.makedirs(trustedparts_dir)
+ os.makedirs(confparts_dir)
+ shutil.copy("fakeroot-apt-key", self.tmpdir)
+
+ @contextlib.contextmanager
+ def _discard_stderr(self):
+ stderr_fd = sys.stderr.fileno()
+ stderr_save = os.dup(stderr_fd)
+ try:
+ devnull = os.open("/dev/null", os.O_WRONLY)
+ try:
+ os.dup2(devnull, stderr_fd)
+ yield
+ finally:
+ os.close(devnull)
+ finally:
+ os.dup2(stderr_save, stderr_fd)
+ os.close(stderr_save)
+
+ def testAddAndExportKey(self):
+ """Add an example key."""
+ apt.auth.add_key(WHEEZY_KEY)
+ # Strip the headers from the keys to avoid test errors because
+ # the exported key used a differenct GnuPG version than the
+ # original example key
+ self.assertEqual(
+ normalize_key(apt.auth.export_key(WHEEZY_KEYID)), normalize_key(WHEEZY_KEY)
+ )
+
+ def testAddAndListKey(self):
+ """Add an example key and test if it is correctly returned by
+ list_keys()
+ """
+ apt.auth.add_key(WHEEZY_KEY)
+ ret = apt.auth.list_keys()
+ self.assertEqual(len(ret), 1)
+ key = ret[0]
+ self.assertEqual(
+ key.name,
+ "Debian Archive Automatic Signing Key (7.0/wheezy) "
+ "<ftpmaster@debian.org>",
+ )
+ self.assertEqual(key.keyid, WHEEZY_KEYID)
+ self.assertEqual(key.date, WHEEZY_KEYDATE)
+
+ def testAddKeyFromFile(self):
+ """Test adding a key from file."""
+ keyfd, keyname = tempfile.mkstemp()
+ self.addCleanup(os.close, keyfd)
+ os.write(keyfd, WHEEZY_KEY.encode("UTF-8"))
+
+ apt.auth.add_key_from_file(keyname)
+
+ ret = apt.auth.list_keys()
+ self.assertEqual(len(ret), 1)
+ key = ret[0]
+ self.assertEqual(
+ key.name,
+ "Debian Archive Automatic Signing Key (7.0/wheezy) "
+ "<ftpmaster@debian.org>",
+ )
+ self.assertEqual(key.keyid, WHEEZY_KEYID)
+ self.assertEqual(key.date, WHEEZY_KEYDATE)
+
+ def test_add_key_from_keyserver_too_short(self):
+ """Ensure that short keyids are not imported"""
+ with self.assertRaises(apt.auth.AptKeyIDTooShortError):
+ apt.auth.add_key_from_keyserver(WHEEZY_KEYID, "hkp://localhost:19191")
+ with self.assertRaises(apt.auth.AptKeyIDTooShortError):
+ apt.auth.add_key_from_keyserver(
+ "0101010178F7FE5C3E65D8AF8B48AD624692555", "hkp://localhost:19191"
+ )
+ with self.assertRaises(apt.auth.AptKeyIDTooShortError):
+ apt.auth.add_key_from_keyserver(
+ "0x0101010178F7FE5C3E65D8AF8B48AD624692555", "hkp://localhost:19191"
+ )
+ with self.assertRaises(apt.auth.AptKeyIDTooShortError):
+ apt.auth.add_key_from_keyserver(
+ "0101 0101 78F7 FE5C 3E65 D8AF 8B48 AD62 4692 555",
+ "hkp://localhost:19191",
+ )
+
+ def test_add_key_from_server_mitm(self):
+ """Verify that the key fingerprint is verified after download"""
+ self._start_keyserver()
+ self.addCleanup(self._stop_keyserver)
+ with self.assertRaises(apt.auth.AptKeyError) as cm:
+ with self._discard_stderr():
+ apt.auth.add_key_from_keyserver(
+ "0101010178F7FE5C3E65D8AF8B48AD6246925553",
+ "hkp://localhost:%d" % self.keyserver_port,
+ )
+ self.assertTrue(
+ str(cm.exception).startswith(
+ "recv from 'hkp://localhost:%d' failed for '%s'"
+ % (self.keyserver_port, "0101010178F7FE5C3E65D8AF8B48AD6246925553")
+ ),
+ cm.exception,
+ )
+
+ def testAddKeyFromServer(self):
+ """Install a GnuPG key from a remote server."""
+ self._start_keyserver()
+ self.addCleanup(self._stop_keyserver)
+
+ with self._discard_stderr():
+ apt.auth.add_key_from_keyserver(
+ "0xa1bD8E9D78F7FE5C3E65D8AF8B48AD6246925553",
+ "hkp://localhost:%d" % self.keyserver_port,
+ )
+
+ ret = apt.auth.list_keys()
+ self.assertEqual(len(ret), 1)
+ key = ret[0]
+ self.assertEqual(
+ key.name,
+ "Debian Archive Automatic Signing Key (7.0/wheezy) "
+ "<ftpmaster@debian.org>",
+ )
+ self.assertEqual(key.keyid, WHEEZY_KEYID)
+ self.assertEqual(key.date, WHEEZY_KEYDATE)
+
+ def _start_keyserver(self):
+ """Start a fake keyserver on http://localhost:19191
+ If port 19191 is unavailable, try successive ports until one is.
+ Store the port actually in use in self.keyserver_port.
+ Thanks pitti.
+ """
+ dir = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, dir)
+ os.mkdir(os.path.join(dir, "pks"))
+ with open(os.path.join(dir, "pks", "lookup"), "w") as key_file:
+ key_file.write(WHEEZY_KEY)
+
+ keyserver_pipe = os.pipe()
+ self.keyserver_pid = os.fork()
+ if self.keyserver_pid == 0:
+ os.close(keyserver_pipe[0])
+ # quiesce server log
+ os.dup2(os.open("/dev/null", os.O_WRONLY), sys.stderr.fileno())
+ os.chdir(dir)
+ for port in itertools.count(19191):
+ try:
+ httpd = HTTPServer(("localhost", port), HTTPRequestHandler)
+ break
+ except OSError as e:
+ if e.errno != errno.EADDRINUSE:
+ raise
+ keyserver_write = os.fdopen(keyserver_pipe[1], "w")
+ print(port, file=keyserver_write)
+ keyserver_write.close()
+ httpd.serve_forever()
+ os._exit(0)
+
+ os.close(keyserver_pipe[1])
+ keyserver_read = os.fdopen(keyserver_pipe[0])
+ self.keyserver_port = int(keyserver_read.readline())
+ keyserver_read.close()
+
+ # temporarily disable proxy, as gnupg does not get along with that
+ # (LP #789049)
+ self.orig_proxy = os.environ.get("http_proxy")
+ try:
+ del os.environ["http_proxy"]
+ except KeyError:
+ pass
+
+ # wait a bit until server is ready
+ time.sleep(0.5)
+
+ def _stop_keyserver(self):
+ """Stop fake keyserver"""
+ assert self.keyserver_pid
+
+ os.kill(self.keyserver_pid, 15)
+ os.wait()
+
+ # restore proxy
+ if self.orig_proxy is not None:
+ os.environ["http_proxy"] = self.orig_proxy
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_cache_invocation.py b/tests/test_cache_invocation.py
new file mode 100644
index 0000000..a608034
--- /dev/null
+++ b/tests/test_cache_invocation.py
@@ -0,0 +1,31 @@
+#!/usr/bin/python3
+import unittest
+
+import apt_pkg
+import testcommon
+
+import apt.progress.base
+
+
+class TestCache(testcommon.TestCase):
+ """Test invocation of apt_pkg.Cache()"""
+
+ def test_wrong_invocation(self):
+ """cache_invocation: Test wrong invocation."""
+ apt_cache = apt_pkg.Cache(progress=None)
+
+ self.assertRaises(ValueError, apt_pkg.Cache, apt_cache)
+ self.assertRaises(
+ ValueError, apt_pkg.Cache, apt.progress.base.AcquireProgress()
+ )
+ self.assertRaises(ValueError, apt_pkg.Cache, 0)
+
+ def test_proper_invocation(self):
+ """cache_invocation: Test correct invocation."""
+ apt_cache = apt_pkg.Cache(progress=None)
+ apt_depcache = apt_pkg.DepCache(apt_cache)
+ self.assertNotEqual(apt_depcache, None)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_configuration.py b/tests/test_configuration.py
new file mode 100644
index 0000000..c59acfa
--- /dev/null
+++ b/tests/test_configuration.py
@@ -0,0 +1,31 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2011 Julian Andres Klode <jak@debian.org>
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+"""Unit tests for verifying the correctness of apt_pkg.Configuration"""
+import unittest
+
+import apt_pkg
+import testcommon
+
+
+class TestConfiguration(testcommon.TestCase):
+ """Test various configuration things"""
+
+ def test_lp707416(self):
+ """configuration: Test empty arguments (LP: #707416)"""
+ self.assertRaises(ValueError, apt_pkg.parse_commandline, apt_pkg.config, [], [])
+ self.assertRaises(
+ SystemError,
+ apt_pkg.parse_commandline,
+ apt_pkg.config,
+ [],
+ ["cmd", "--arg0"],
+ )
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_cve_2020_27351.py b/tests/test_cve_2020_27351.py
new file mode 100644
index 0000000..1ec1c76
--- /dev/null
+++ b/tests/test_cve_2020_27351.py
@@ -0,0 +1,149 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2020 Canonical Ltd
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+"""Unit tests for verifying the correctness of DebFile descriptor handling."""
+import os
+import sys
+import unittest
+
+from test_all import get_library_dir
+
+libdir = get_library_dir()
+if libdir:
+ sys.path.insert(0, libdir)
+import gc
+import subprocess
+import tempfile
+import warnings
+
+import apt_inst
+
+
+@unittest.skipIf(not os.path.exists("/proc/self/fd"), "no /proc/self/fd available")
+class TestCVE_2020_27351(unittest.TestCase):
+ """test the debfile"""
+
+ GOOD_DEB = "data/test_debs/utf8-package_1.0-1_all.deb"
+
+ def test_success(self):
+ """opening package successfully should not leak fd"""
+ before = os.listdir("/proc/self/fd")
+ apt_inst.DebFile(self.GOOD_DEB)
+ after = os.listdir("/proc/self/fd")
+ self.assertEqual(before, after)
+
+ def test_regression_bug_977000(self):
+ """opening with a file handle should work correctly"""
+ with open(self.GOOD_DEB) as good_deb:
+ apt_inst.DebFile(good_deb).control.extractdata("control")
+
+ def test_regression_bug_977000_2(self):
+ """file object <-> debfile cycles should be collected by gc."""
+
+ class Cycle:
+ def __init__(self, fname):
+ self.file = open(fname)
+ self.deb = apt_inst.DebFile(self)
+
+ def fileno(self):
+ return self.file.fileno()
+
+ before = os.listdir("/proc/self/fd")
+ Cycle(self.GOOD_DEB).deb.control.extractdata("control")
+ warnings.filterwarnings("ignore", category=ResourceWarning)
+ gc.collect()
+ warnings.resetwarnings()
+ after = os.listdir("/proc/self/fd")
+ self.assertEqual(before, after)
+
+ def test_regression_bug_977000_2_ar(self):
+ """file object <-> debfile cycles should be collected by gc."""
+
+ class Cycle:
+ def __init__(self, fname):
+ self.file = open(fname)
+ self.deb = apt_inst.ArArchive(self)
+
+ def fileno(self):
+ return self.file.fileno()
+
+ before = os.listdir("/proc/self/fd")
+ Cycle(self.GOOD_DEB).deb.gettar("control.tar.gz", "gzip").extractdata("control")
+ warnings.filterwarnings("ignore", category=ResourceWarning)
+ gc.collect()
+ warnings.resetwarnings()
+ after = os.listdir("/proc/self/fd")
+ self.assertEqual(before, after)
+
+ def test_success_a_member(self):
+ """fd should be kept around as long as a tarfile member"""
+ before = os.listdir("/proc/self/fd")
+ data = apt_inst.DebFile(self.GOOD_DEB).data
+ after = os.listdir("/proc/self/fd")
+ self.assertEqual(len(before), len(after) - 1)
+ del data
+ after = os.listdir("/proc/self/fd")
+ self.assertEqual(before, after)
+
+ def _create_deb_without(self, member):
+ temp = tempfile.NamedTemporaryFile(mode="wb")
+ try:
+ with open(self.GOOD_DEB, "rb") as deb:
+ temp.write(deb.read())
+ temp.flush()
+ subprocess.check_call(["ar", "d", temp.name, member])
+ return temp
+ except Exception as e:
+ temp.close()
+ raise e
+
+ def test_nocontrol(self):
+ """opening package without control.tar.gz should not leak fd"""
+ before = os.listdir("/proc/self/fd")
+ with self._create_deb_without("control.tar.gz") as temp:
+ try:
+ apt_inst.DebFile(temp.name)
+ except SystemError as e:
+ self.assertIn("control.tar", str(e))
+ else:
+ self.fail("Did not raise an exception")
+
+ after = os.listdir("/proc/self/fd")
+ self.assertEqual(before, after)
+
+ def test_nodata(self):
+ """opening package without data.tar.gz should not leak fd"""
+ before = os.listdir("/proc/self/fd")
+ with self._create_deb_without("data.tar.gz") as temp:
+ try:
+ apt_inst.DebFile(temp.name)
+ except SystemError as e:
+ self.assertIn("data.tar", str(e))
+ else:
+ self.fail("Did not raise an exception")
+
+ after = os.listdir("/proc/self/fd")
+ self.assertEqual(before, after)
+
+ def test_no_debian_binary(self):
+ """opening package without debian-binary should not leak fd"""
+ before = os.listdir("/proc/self/fd")
+ with self._create_deb_without("debian-binary") as temp:
+ try:
+ apt_inst.DebFile(temp.name)
+ except SystemError as e:
+ self.assertIn("missing debian-binary", str(e))
+ else:
+ self.fail("Did not raise an exception")
+
+ after = os.listdir("/proc/self/fd")
+ self.assertEqual(before, after)
+
+
+if __name__ == "__main__":
+ # logging.basicConfig(level=logging.DEBUG)
+ unittest.main()
diff --git a/tests/test_debfile.py b/tests/test_debfile.py
new file mode 100644
index 0000000..bb65a94
--- /dev/null
+++ b/tests/test_debfile.py
@@ -0,0 +1,192 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2010 Michael Vogt <mvo@ubuntu.com>
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+"""Unit tests for verifying the correctness of DebPackage in apt.debfile."""
+import logging
+import os
+import sys
+import unittest
+
+from test_all import get_library_dir
+
+libdir = get_library_dir()
+if libdir:
+ sys.path.insert(0, libdir)
+import apt_pkg
+import testcommon
+
+import apt.debfile
+
+
+class TestDebfile(testcommon.TestCase):
+ """test the debfile"""
+
+ TEST_DEBS = [
+ # conflicts with apt
+ ("gdebi-test1.deb", False),
+ # impossible dependency
+ ("gdebi-test2.deb", False),
+ # or-group (impossible-dependency|apt)
+ ("gdebi-test3.deb", True),
+ # Conflicts: apt (<= 0.1)
+ ("gdebi-test4.deb", True),
+ # Conflicts: apt (>= 0.1)
+ ("gdebi-test5.deb", False),
+ # invalid unicode in descr
+ ("gdebi-test6.deb", True),
+ # provides/conflicts against "foobarbaz"
+ ("gdebi-test7.deb", True),
+ # provides/conflicts/replaces against "mail-transport-agent"
+ # (should fails if mail-transport-agent is installed)
+ ("gdebi-test8.deb", False),
+ # provides/conflicts against real pkg
+ ("gdebi-test9.deb", True),
+ # provides debconf-tiny and the real debconf conflicts with
+ ("gdebi-test10.deb", False),
+ ]
+
+ def setUp(self):
+ testcommon.TestCase.setUp(self)
+ apt_pkg.config.set("APT::Architecture", "i386")
+ # FIXME: When run via test_all.py, the tests fail without this if it
+ # is set in the system.
+ apt_pkg.config.clear("APT::Architectures")
+ apt_pkg.config.set("Dir::State::status", "./data/test_debs/var/lib/dpkg/status")
+ apt_pkg.config.set("Dir::State::lists", "./data/test_debs/var/lib/apt/lists")
+ apt_pkg.config.set(
+ "Dir::Etc::sourcelist", "./data/test_debs/etc/apt/sources.list"
+ )
+ apt_pkg.init_system()
+ self.cache = apt.Cache()
+
+ def test_dsc_file(self):
+ filename = "hello_2.5-1.dsc"
+ deb = apt.debfile.DscSrcPackage(cache=self.cache)
+ deb.open(os.path.join("data", "test_debs", filename))
+ self.assertTrue(deb.check(), "got failure '%s'" % deb._failure_string)
+ missing = {"autotools-dev"}
+ self.assertEqual(set(deb.missing_deps), missing)
+ # specialized properties
+ self.assertEqual(deb.pkgname, "hello")
+ self.assertEqual(deb.binaries, ["hello", "bello", "cello"])
+ self.assertEqual(deb.filelist, ["hello_2.5.orig.tar.gz", "hello_2.5-1.diff.gz"])
+ self.assertEqual(deb.depends, [[("autotools-dev", "", "")]])
+ # tag fields are available as a dict
+ self.assertEqual(deb["Format"], "1.0")
+ self.assertEqual(deb["Source"], "hello")
+ self.assertEqual(deb["Binary"], "hello, bello,\n cello")
+ self.assertEqual(deb["Architecture"], "any")
+ self.assertEqual(deb["Version"], "2.5-1")
+ self.assertEqual(deb["Maintainer"], "Santiago Vila <sanvila@debian.org>")
+ self.assertEqual(deb["Homepage"], "http://www.gnu.org/software/hello")
+ self.assertEqual(deb["Standards-Version"], "3.8.4")
+
+ def test_dsc_file_with_impossible_build_dep(self):
+ filename = "impossible-build-depends_2.5-1.dsc"
+ deb = apt.debfile.DscSrcPackage(cache=self.cache)
+ deb.open(os.path.join("data", "test_debs", filename))
+ self.assertFalse(deb.check())
+ self.assertEqual(deb.depends, [[("debhelper", "101", ">")]])
+
+ def test_deb_file(self):
+ deb = apt.debfile.DebPackage(cache=self.cache)
+ for filename, expected_res in self.TEST_DEBS:
+ logging.debug(f"testing {filename}, expecting {expected_res}")
+ deb.open(os.path.join("data", "test_debs", filename))
+ res = deb.check()
+ self.assertEqual(
+ res,
+ expected_res,
+ "Unexpected result for package '%s' (got %s wanted %s)\n%s"
+ % (filename, res, expected_res, deb._failure_string),
+ )
+
+ def test_utf8_sections(self):
+ deb = apt.debfile.DebPackage(cache=self.cache)
+ deb.open(os.path.join("data", "test_debs", "utf8-package_1.0-1_all.deb"))
+ self.assertEqual(deb["Maintainer"], "Samuel Lidén Borell <samuel@slbdata.se>")
+
+ def test_content(self):
+ # normal
+ deb = apt.debfile.DebPackage(cache=self.cache)
+ deb.open(os.path.join("data", "test_debs", "gdebi-test11.deb"))
+ self.assertEqual('#!/bin/sh\necho "test"\n', deb.data_content("usr/bin/test"))
+ # binary
+ deb = apt.debfile.DebPackage(cache=self.cache)
+ deb.open(os.path.join("data", "test_debs", "gdebi-test12.deb"))
+ content = deb.data_content("usr/bin/binary")
+ self.assertTrue(
+ content.startswith("Automatically converted to printable ascii:\n\x7fELF ")
+ )
+ # control file
+ needle = """Package: gdebi-test12
+Version: 1.0
+Architecture: all
+Description: testpackage for gdebi - contains usr/bin/binary for file reading
+ This tests the binary file reading for debfile.py
+"""
+ content = deb.control_content("./control")
+ self.assertEqual(content, needle)
+ content = deb.control_content("control")
+ self.assertEqual(content, needle)
+
+ def test_xz_data(self):
+ deb = apt.debfile.DebPackage("./data/test_debs/data-tar-xz.deb")
+ self.assertEqual(deb.filelist, ["./", "usr/", "usr/bin/"])
+
+ @unittest.skipIf(
+ apt_pkg.version_compare(apt_pkg.VERSION, "0.9.15.4~") < 0,
+ "APT too old for uncompressed control.tar/data.tar",
+ )
+ def test_uncompressed_data(self):
+ deb = apt.debfile.DebPackage("./data/test_debs/data-tar.deb")
+ self.assertEqual(deb.filelist, ["./", "usr/", "usr/bin/"])
+
+ def test_check_exception(self):
+ deb = apt.debfile.DebPackage("./data/test_debs/data-tar-xz.deb")
+ self.assertRaises(AttributeError, lambda: deb.missing_deps)
+ deb.check()
+ deb.missing_deps
+
+ def test_no_supported_data_tar(self):
+ with self.assertRaises(SystemError):
+ apt.debfile.DebPackage("./data/test_debs/data-tar-broken.deb")
+
+ def test_contains(self):
+ deb = apt.debfile.DebPackage("./data/test_debs/data-tar-xz.deb")
+ self.assertTrue("Package" in deb)
+
+ def test_multi_arch_allowed(self):
+ apt_pkg.config["APT::Architectures::"] = "i386"
+ apt_pkg.config["APT::Architectures::"] = "amd64"
+ apt_pkg.config["APT::Architecture"] = "amd64"
+ apt_pkg.init_system()
+
+ allowed_any = apt.debfile.DebPackage(
+ "./data/test_debs/testdep-allowed-any_1.0-1_i386.deb"
+ )
+ self.assertTrue(allowed_any.check(), allowed_any._failure_string)
+
+ def test_multi_arch_same(self):
+ apt_pkg.config["APT::Architectures::"] = "i386"
+ apt_pkg.config["APT::Architectures::"] = "amd64"
+ apt_pkg.config["APT::Architecture"] = "amd64"
+ apt_pkg.init_system()
+ same = apt.debfile.DebPackage(
+ "./data/test_debs/testdep-same-arch_1.0-1_i386.deb"
+ )
+ self.assertTrue(same.check(), same._failure_string)
+
+ def test_get_content_gzip_data(self):
+ deb = apt.debfile.DebPackage("./data/test_debs/gdebi-test13.deb")
+ data = deb.data_content("./lala.gz")
+ self.assertEqual(data, "Automatically decompressed:\n\nlala\n")
+
+
+if __name__ == "__main__":
+ # logging.basicConfig(level=logging.DEBUG)
+ unittest.main()
diff --git a/tests/test_debfile_multiarch.py b/tests/test_debfile_multiarch.py
new file mode 100644
index 0000000..24f0265
--- /dev/null
+++ b/tests/test_debfile_multiarch.py
@@ -0,0 +1,63 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2010 Michael Vogt <mvo@ubuntu.com>
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+"""Unit tests for verifying the correctness of DebPackage in apt.debfile."""
+import sys
+import unittest
+
+from test_all import get_library_dir
+
+libdir = get_library_dir()
+if libdir:
+ sys.path.insert(0, libdir)
+import apt_pkg
+import testcommon
+
+import apt
+import apt.debfile
+
+
+class TestDebfileMultiarch(testcommon.TestCase):
+ """test the multiarch debfile"""
+
+ def test_multiarch_deb_check(self):
+ if apt_pkg.get_architectures() != ["amd64", "i386"]:
+ # TODO: use unittest.skip
+ # logging.warning("skipping test because running on a "
+ # "non-multiarch system")
+ return
+ deb = apt.debfile.DebPackage("./data/test_debs/multiarch-test1_i386.deb")
+ deb.check()
+ missing = deb.missing_deps
+ # print missing
+ self.assertFalse("dpkg:i386" in missing)
+
+ @unittest.skip("BROKEN, lib3ds-1-3 is m-a now")
+ def test_multiarch_conflicts(self):
+ cache = apt.Cache()
+ # WARNING: this assumes that lib3ds-1-3 is a non-multiarch lib
+ # use "lib3ds-1-3" as a test to see if non-multiach lib conflicts work
+ canary = "lib3ds-1-3"
+ if canary not in cache:
+ # TODO: use unittest.skip
+ # logging.warning("skipping test because %s is missing" % canary)
+ return
+ cache[canary].mark_install()
+ deb = apt.debfile.DebPackage(
+ "./data/test_debs/multiarch-test1_i386.deb", cache=cache
+ )
+ # this deb should now not be installable
+ installable = deb.check()
+ # print deb._failure_string
+ self.assertFalse(installable)
+ self.assertEqual(
+ deb._failure_string, "Conflicts with the installed package 'lib3ds-1-3'"
+ )
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_deps.py b/tests/test_deps.py
new file mode 100644
index 0000000..860856c
--- /dev/null
+++ b/tests/test_deps.py
@@ -0,0 +1,142 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2010 Julian Andres Klode <jak@debian.org>
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+"""Unit tests for verifying the correctness of check_dep, etc in apt_pkg."""
+import itertools
+import unittest
+
+import apt_pkg
+import testcommon
+
+import apt.package
+
+
+class TestDependencies(testcommon.TestCase):
+ def testCheckDep(self):
+ """dependencies: Test apt_pkg.CheckDep() for '<' and '>'
+
+ The CheckDep function should treat '<' as '<=' and '>' as '>=', for
+ compatibility reasons."""
+ if not hasattr(apt_pkg, "CheckDep"):
+ return
+ self.assertFalse(apt_pkg.CheckDep("1", "<", "0"))
+ self.assertTrue(apt_pkg.CheckDep("1", "<", "1"))
+ self.assertTrue(apt_pkg.CheckDep("1", "<", "2"))
+
+ self.assertFalse(apt_pkg.CheckDep("0", ">", "1"))
+ self.assertTrue(apt_pkg.CheckDep("1", ">", "1"))
+ self.assertTrue(apt_pkg.CheckDep("2", ">", "1"))
+
+ def test_check_dep(self):
+ "dependencies: Test apt_pkg.check_dep()"
+ self.assertFalse(apt_pkg.check_dep("1", "<<", "0"))
+ self.assertFalse(apt_pkg.check_dep("1", "<<", "1"))
+ self.assertTrue(apt_pkg.check_dep("1", "<<", "2"))
+
+ self.assertFalse(apt_pkg.check_dep("1", "<", "0"))
+ self.assertFalse(apt_pkg.check_dep("1", "<", "1"))
+ self.assertTrue(apt_pkg.check_dep("1", "<", "2"))
+
+ self.assertFalse(apt_pkg.check_dep("1", "<=", "0"))
+ self.assertTrue(apt_pkg.check_dep("1", "<=", "1"))
+ self.assertTrue(apt_pkg.check_dep("1", "<=", "2"))
+
+ self.assertFalse(apt_pkg.check_dep("0", "=", "1"))
+ self.assertTrue(apt_pkg.check_dep("1", "=", "1"))
+ self.assertFalse(apt_pkg.check_dep("2", "=", "1"))
+
+ self.assertFalse(apt_pkg.check_dep("0", ">=", "1"))
+ self.assertTrue(apt_pkg.check_dep("1", ">=", "1"))
+ self.assertTrue(apt_pkg.check_dep("2", ">=", "1"))
+
+ self.assertFalse(apt_pkg.check_dep("0", ">", "1"))
+ self.assertFalse(apt_pkg.check_dep("1", ">", "1"))
+ self.assertTrue(apt_pkg.check_dep("2", ">", "1"))
+
+ self.assertFalse(apt_pkg.check_dep("0", ">>", "1"))
+ self.assertFalse(apt_pkg.check_dep("1", ">>", "1"))
+ self.assertTrue(apt_pkg.check_dep("2", ">>", "1"))
+
+ def test_parse_depends_multiarch(self):
+ # strip multiarch
+ deps = apt_pkg.parse_depends("po4a:native", True)
+ self.assertEqual(deps[0][0][0], "po4a")
+ # do not strip multiarch
+ deps = apt_pkg.parse_depends("po4a:native", False)
+ self.assertEqual(deps[0][0][0], "po4a:native")
+
+ def test_parse_depends(self):
+ """dependencies: Test apt_pkg.parse_depends()"""
+ deps = apt_pkg.parse_depends("p1a (<< 1a) | p1b (>> 1b)")
+ self.assertTrue(isinstance(deps, list))
+ self.assertEqual(len(deps), 1)
+ self.assertTrue(isinstance(deps[0], list))
+ self.assertEqual(len(deps[0]), 2)
+ self.assertEqual(len(deps[0][0]), 3)
+ self.assertEqual(len(deps[0][1]), 3)
+ self.assertEqual(deps[0][0][0], "p1a")
+ self.assertEqual(deps[0][0][1], "1a")
+ self.assertEqual(deps[0][0][2], "<")
+ self.assertEqual(deps[0][1][0], "p1b")
+ self.assertEqual(deps[0][1][1], "1b")
+ self.assertEqual(deps[0][1][2], ">")
+
+ # Check that the type of comparison is parsed correctly.
+ self.assertEqual("<", apt_pkg.parse_depends("p1 (<< 1)")[0][0][2])
+ self.assertEqual("<=", apt_pkg.parse_depends("p1 (< 1)")[0][0][2])
+ self.assertEqual("<=", apt_pkg.parse_depends("p1 (<= 1)")[0][0][2])
+ self.assertEqual("=", apt_pkg.parse_depends("p1 (= 1)")[0][0][2])
+ self.assertEqual(">=", apt_pkg.parse_depends("p1 (>= 1)")[0][0][2])
+ self.assertEqual(">=", apt_pkg.parse_depends("p1 (> 1)")[0][0][2])
+ self.assertEqual(">", apt_pkg.parse_depends("p1 (>> 1)")[0][0][2])
+
+ def test_parse_src_depends(self):
+ """dependencies: Test apt_pkg.parse_src_depends()."""
+ # Check that architecture exclusion works
+ # depends_this: Current architecture is included
+ # depends_this_too: Another architecture is excluded
+ # depends_other: The current architecture is excluded
+ # depends_other: Another architecture is requested.
+ architecture = apt_pkg.config["APT::Architecture"]
+ depends_this = apt_pkg.parse_src_depends("p [%s]" % architecture)
+ depends_this_too = apt_pkg.parse_src_depends("p [!not-existing-arch]")
+ depends_other = apt_pkg.parse_src_depends("p [!%s]" % architecture)
+ depends_other_too = apt_pkg.parse_src_depends("p [not-existing-arch]")
+
+ self.assertEqual(len(depends_this), len(depends_this_too), 1)
+ self.assertEqual(len(depends_other), len(depends_other_too), 0)
+
+ def test_dstr(self):
+ """Test apt.package.BaseDependency.__dstr"""
+ dstr = apt.package.BaseDependency._BaseDependency__dstr
+ equal = {"<": {"<<", "<"}, "=": {"==", "="}, ">": {">>", ">"}}
+ operators = ["<<", "<", "<=", "!=", "=", "==", ">=", ">", ">>"]
+
+ for a, b in itertools.product(equal.keys(), operators):
+ if b in equal[a]:
+ self.assertEqual(dstr(a), b)
+ self.assertEqual(b, dstr(a))
+ else:
+ self.assertNotEqual(dstr(a), b)
+ self.assertNotEqual(b, dstr(a))
+
+ def testParseDepends(self):
+ """dependencies: Test apt_pkg.ParseDepends()."""
+ if not hasattr(apt_pkg, "ParseDepends"):
+ return
+ # Check that the type of comparison is parsed correctly.
+ self.assertEqual("<<", apt_pkg.ParseDepends("p1 (<< 1)")[0][0][2])
+ self.assertEqual("<=", apt_pkg.ParseDepends("p1 (< 1)")[0][0][2])
+ self.assertEqual("<=", apt_pkg.ParseDepends("p1 (<= 1)")[0][0][2])
+ self.assertEqual("=", apt_pkg.ParseDepends("p1 (= 1)")[0][0][2])
+ self.assertEqual(">=", apt_pkg.ParseDepends("p1 (>= 1)")[0][0][2])
+ self.assertEqual(">=", apt_pkg.ParseDepends("p1 (> 1)")[0][0][2])
+ self.assertEqual(">>", apt_pkg.ParseDepends("p1 (>> 1)")[0][0][2])
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_group.py b/tests/test_group.py
new file mode 100644
index 0000000..d08ed06
--- /dev/null
+++ b/tests/test_group.py
@@ -0,0 +1,31 @@
+import unittest
+
+import apt_pkg
+import testcommon
+
+
+class TestGroup(testcommon.TestCase):
+ def setUp(self):
+ testcommon.TestCase.setUp(self)
+ self.cache = apt_pkg.Cache(progress=None)
+
+ def test_pkgingroup(self):
+ """Check that each package belongs to the corresponding group"""
+ for pkg in self.cache.packages:
+ group = apt_pkg.Group(self.cache, pkg.name)
+ assert any(pkg.id == p.id for p in group)
+
+ def test_iteration(self):
+ """Check that iteration works correctly."""
+ for pkg in self.cache.packages:
+ group = apt_pkg.Group(self.cache, pkg.name)
+
+ list(group) == list(group)
+
+ def test_cache_groups(self):
+ """group: Iterate over all groups"""
+ assert len(list(self.cache.groups)) == self.cache.group_count
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_hashes.py b/tests/test_hashes.py
new file mode 100644
index 0000000..d322a1f
--- /dev/null
+++ b/tests/test_hashes.py
@@ -0,0 +1,206 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2009 Julian Andres Klode <jak@debian.org>
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+"""Unit tests for verifying the correctness of hashsums in apt_pkg.
+
+Unit tests to verify the correctness of Hashes, HashString and the various
+functions like md5sum."""
+import hashlib
+import unittest
+import warnings
+
+import apt_pkg
+import testcommon
+
+
+class TestHashes(testcommon.TestCase):
+ """Test apt_pkg.Hashes() and the various apt_pkg.*sum() functions."""
+
+ def setUp(self):
+ """Prepare the tests, create reference values..."""
+ testcommon.TestCase.setUp(self)
+ self.file = open(apt_pkg.__file__, "rb")
+ self.value = self.file.read()
+ self.hashes = apt_pkg.Hashes(self.value)
+ self.file.seek(0)
+ self.fhashes = apt_pkg.Hashes(self.file)
+ # Reference values.
+ self.md5 = hashlib.md5(self.value).hexdigest()
+ self.sha1 = hashlib.sha1(self.value).hexdigest()
+ self.sha256 = hashlib.sha256(self.value).hexdigest()
+ self.file.seek(0)
+
+ warnings.filterwarnings("ignore", category=DeprecationWarning)
+
+ def tearDown(self):
+ """Cleanup, Close the file object used for the tests."""
+ testcommon.TestCase.tearDown(self)
+ warnings.resetwarnings()
+ self.file.close()
+
+ def test_md5sum(self):
+ """hashes: Test apt_pkg.md5sum()"""
+ self.assertEqual(apt_pkg.md5sum(self.value), self.md5)
+ self.assertEqual(apt_pkg.md5sum(self.file), self.md5)
+
+ def test_sha1sum(self):
+ """hashes: Test apt_pkg.sha1sum()"""
+ self.assertEqual(apt_pkg.sha1sum(self.value), self.sha1)
+ self.assertEqual(apt_pkg.sha1sum(self.file), self.sha1)
+
+ def test_sha256sum(self):
+ """hashes: Test apt_pkg.sha256sum()"""
+ self.assertEqual(apt_pkg.sha256sum(self.value), self.sha256)
+ self.assertEqual(apt_pkg.sha256sum(self.file), self.sha256)
+
+ def test_bytes(self):
+ """hashes: Test apt_pkg.Hashes(bytes)"""
+ self.assertEqual(self.hashes.hashes.find("md5sum").hashvalue, self.md5)
+ self.assertEqual(
+ self.hashes.hashes.find("md5sum"), apt_pkg.HashString("MD5Sum", self.md5)
+ )
+ self.assertEqual(
+ self.hashes.hashes.find("sha1"), apt_pkg.HashString("SHA1", self.sha1)
+ )
+ self.assertEqual(
+ self.hashes.hashes.find("sha256"), apt_pkg.HashString("SHA256", self.sha256)
+ )
+ self.assertRaises(KeyError, self.hashes.hashes.find, "md5")
+
+ def test_file(self):
+ """hashes: Test apt_pkg.Hashes(file)."""
+ self.assertEqual(self.fhashes.hashes.find("md5sum").hashvalue, self.md5)
+ self.assertEqual(
+ self.fhashes.hashes.find("md5sum"), apt_pkg.HashString("MD5Sum", self.md5)
+ )
+ self.assertEqual(
+ self.fhashes.hashes.find("sha1"), apt_pkg.HashString("SHA1", self.sha1)
+ )
+ self.assertEqual(
+ self.fhashes.hashes.find("sha256"),
+ apt_pkg.HashString("SHA256", self.sha256),
+ )
+
+ def test_unicode(self):
+ """hashes: Test apt_pkg.Hashes(unicode)."""
+ self.assertRaises(TypeError, apt_pkg.Hashes, "D")
+ self.assertRaises(TypeError, apt_pkg.md5sum, "D")
+ self.assertRaises(TypeError, apt_pkg.sha1sum, "D")
+ self.assertRaises(TypeError, apt_pkg.sha256sum, "D")
+
+
+class TestHashString(testcommon.TestCase):
+ """Test apt_pkg.HashString()."""
+
+ def setUp(self):
+ """Prepare the test by reading the file."""
+ testcommon.TestCase.setUp(self)
+ self.file = open(apt_pkg.__file__)
+ self.hashes = apt_pkg.Hashes(self.file)
+
+ self.md5 = self.hashes.hashes.find("md5sum")
+ self.sha1 = self.hashes.hashes.find("sha1")
+ self.sha256 = self.hashes.hashes.find("sha256")
+
+ def tearDown(self):
+ """Cleanup, Close the file object used for the tests."""
+ self.file.close()
+
+ def test_md5(self):
+ """hashes: Test apt_pkg.HashString().md5"""
+ self.assertIn("MD5Sum:", str(self.md5))
+ self.assertTrue(self.md5.verify_file(apt_pkg.__file__))
+
+ def test_sha1(self):
+ """hashes: Test apt_pkg.HashString().sha1"""
+ self.assertIn("SHA1:", str(self.sha1))
+ self.assertTrue(self.sha1.verify_file(apt_pkg.__file__))
+
+ def test_sha256(self):
+ """hashes: Test apt_pkg.HashString().sha256"""
+ self.assertIn("SHA256:", str(self.sha256))
+ self.assertTrue(self.sha256.verify_file(apt_pkg.__file__))
+
+ def test_wrong(self):
+ """hashes: Test apt_pkg.HashString(wrong_type)."""
+ self.assertRaises(TypeError, apt_pkg.HashString, 0)
+ self.assertRaises(TypeError, apt_pkg.HashString, b"")
+
+
+class TestHashStringList(testcommon.TestCase):
+ """Test apt_pkg.HashStringList()"""
+
+ def test_file_size(self):
+ hsl = apt_pkg.HashStringList()
+ self.assertEqual(hsl.file_size, 0)
+ hsl.file_size = 42
+ self.assertEqual(hsl.file_size, 42)
+ self.assertEqual(len(hsl), 1)
+
+ # Verify that I can re-assign value (this handles the long case on
+ # Python 2).
+ hsl.file_size = hsl.file_size
+
+ with self.assertRaises(OverflowError):
+ hsl.file_size = -1
+
+ hsl.file_size = 0
+
+ def test_append(self):
+ """Testing whether append works correctly."""
+ hs1 = apt_pkg.HashString("MD5Sum", "a60599e6200b60050d7a30721e3532ed")
+ hs2 = apt_pkg.HashString("SHA1", "ef113338e654b1ada807a939ad47b3a67633391b")
+
+ hsl = apt_pkg.HashStringList()
+ hsl.append(hs1)
+ hsl.append(hs2)
+ self.assertEqual(len(hsl), 2)
+ self.assertEqual(hsl[0].hashtype, "MD5Sum")
+ self.assertEqual(hsl[1].hashtype, "SHA1")
+ self.assertEqual(str(hsl[0]), str(hs1))
+ self.assertEqual(str(hsl[1]), str(hs2))
+
+ def test_find(self):
+ """Testing whether append works correctly."""
+ hs1 = apt_pkg.HashString("MD5Sum", "a60599e6200b60050d7a30721e3532ed")
+ hs2 = apt_pkg.HashString("SHA1", "ef113338e654b1ada807a939ad47b3a67633391b")
+
+ hsl = apt_pkg.HashStringList()
+ hsl.append(hs1)
+ hsl.append(hs2)
+
+ self.assertEqual(hsl.find("MD5Sum").hashtype, "MD5Sum")
+ self.assertEqual(hsl.find("SHA1").hashtype, "SHA1")
+ self.assertEqual(hsl.find().hashtype, "SHA1")
+
+ def test_verify_file(self):
+ with open(apt_pkg.__file__) as fobj:
+ hashes = apt_pkg.Hashes(fobj)
+ with warnings.catch_warnings(record=True):
+ warnings.simplefilter("always")
+ sha1 = hashes.hashes.find("sha1")
+ sha256 = hashes.hashes.find("sha256")
+
+ hsl = apt_pkg.HashStringList()
+ hsl.append(sha1)
+ hsl.append(sha256)
+
+ self.assertTrue(hsl.verify_file(apt_pkg.__file__))
+
+ md5sum = apt_pkg.HashString("MD5Sum", "a60599e6200b60050d7a30721e3532ed")
+ hsl.append(md5sum)
+
+ self.assertFalse(hsl.verify_file(apt_pkg.__file__))
+
+ hsl2 = hashes.hashes
+ self.assertIsInstance(hsl2, apt_pkg.HashStringList)
+ self.assertGreater(len(hsl2), 0)
+ self.assertTrue(hsl2.verify_file(apt_pkg.__file__))
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_hashsums.py b/tests/test_hashsums.py
new file mode 100644
index 0000000..1d7cc0e
--- /dev/null
+++ b/tests/test_hashsums.py
@@ -0,0 +1,106 @@
+#!/usr/bin/python3
+
+import unittest
+import warnings
+
+import apt_pkg
+import testcommon
+
+
+class testHashes(testcommon.TestCase):
+ "test the hashsum functions against strings and files"
+
+ DATA_PATH = "data/hashsums/hashsum_test.data"
+ DATA_WITH_ZERO_PATH = "data/hashsums/hashsum_test_with_zero.data"
+
+ def setUp(self):
+ testcommon.TestCase.setUp(self)
+ warnings.filterwarnings("ignore", category=DeprecationWarning)
+
+ def tearDown(self):
+ testcommon.TestCase.tearDown(self)
+ warnings.resetwarnings()
+
+ def testMD5(self):
+ # simple
+ s = b"foo"
+ s_md5 = "acbd18db4cc2f85cedef654fccc4a4d8"
+ res = apt_pkg.md5sum(s)
+ self.assertEqual(res, s_md5)
+ # file
+ with open(self.DATA_PATH) as fobj:
+ self.assertEqual(apt_pkg.md5sum(fobj), s_md5)
+ # with zero (\0) in the string
+ s = b"foo\0bar"
+ s_md5 = "f6f5f8cd0cb63668898ba29025ae824e"
+ res = apt_pkg.md5sum(s)
+ self.assertEqual(res, s_md5)
+ # file
+ with open(self.DATA_WITH_ZERO_PATH) as fobj:
+ self.assertEqual(apt_pkg.md5sum(fobj), s_md5)
+
+ def testSHA1(self):
+ # simple
+ s = b"foo"
+ s_hash = "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
+ res = apt_pkg.sha1sum(s)
+ self.assertEqual(res, s_hash)
+ # file
+ with open(self.DATA_PATH) as fobj:
+ self.assertEqual(apt_pkg.sha1sum(fobj), s_hash)
+ # with zero (\0) in the string
+ s = b"foo\0bar"
+ s_hash = "e2c300a39311a2dfcaff799528415cb74c19317f"
+ res = apt_pkg.sha1sum(s)
+ self.assertEqual(res, s_hash)
+ # file
+ with open(self.DATA_WITH_ZERO_PATH) as fobj:
+ self.assertEqual(apt_pkg.sha1sum(fobj), s_hash)
+
+ def testSHA256(self):
+ # simple
+ s = b"foo"
+ s_hash = "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
+ res = apt_pkg.sha256sum(s)
+ self.assertEqual(res, s_hash)
+ # file
+ with open(self.DATA_PATH) as fobj:
+ self.assertEqual(apt_pkg.sha256sum(fobj), s_hash)
+ # with zero (\0) in the string
+ s = b"foo\0bar"
+ s_hash = "d6b681bfce7155d44721afb79c296ef4f0fa80a9dd6b43c5cf74dd0f64c85512"
+ res = apt_pkg.sha256sum(s)
+ self.assertEqual(res, s_hash)
+ # file
+ with open(self.DATA_WITH_ZERO_PATH) as fobj:
+ self.assertEqual(apt_pkg.sha256sum(fobj), s_hash)
+
+ def testSHA512(self):
+ # simple
+ s = b"foo"
+ s_hash = (
+ "f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d"
+ "0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19"
+ "594a7eb539453e1ed7"
+ )
+ res = apt_pkg.sha512sum(s)
+ self.assertEqual(res, s_hash)
+ # file
+ with open(self.DATA_PATH) as fobj:
+ self.assertEqual(apt_pkg.sha512sum(fobj), s_hash)
+ # with zero (\0) in the string
+ s = b"foo\0bar"
+ s_hash = (
+ "8c5e791db8f6bfb40eba884f70c9ac52231f01a393e4e55b4576d45"
+ "9a827f34f77e41e7fac806724517b9e96bb42387c5f9bbf325d2f99"
+ "ed52a4aa6abebc3350"
+ )
+ res = apt_pkg.sha512sum(s)
+ self.assertEqual(res, s_hash)
+ # file
+ with open(self.DATA_WITH_ZERO_PATH) as fobj:
+ self.assertEqual(apt_pkg.sha512sum(fobj), s_hash)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_install_progress_exec.py b/tests/test_install_progress_exec.py
new file mode 100644
index 0000000..064eb81
--- /dev/null
+++ b/tests/test_install_progress_exec.py
@@ -0,0 +1,43 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2019 Colomban Wendling <cwendling@hypra.fr>
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+"""Unit tests for verifying InstallProgress works when spawning dpkg directly
+(i.e. when installing standalone .debs)."""
+import os
+import sys
+import unittest
+
+from test_all import get_library_dir
+
+libdir = get_library_dir()
+if libdir:
+ sys.path.insert(0, libdir)
+import testcommon
+
+from apt.progress.base import InstallProgress
+
+
+class RunHelper:
+ def __init__(self):
+ self.script = "helper_install_progress_run.py"
+
+ def do_install(self, fd):
+ return os.spawnl(os.P_WAIT, self.script, self.script, str(fd))
+
+
+class TestInstallProgressExec(testcommon.TestCase):
+ """test that InstallProgress.run() passes a valid file descriptor to
+ a child process"""
+
+ def test_run(self):
+ with InstallProgress() as prog:
+ self.assertEqual(prog.run(RunHelper()), 0)
+
+
+if __name__ == "__main__":
+ os.chdir(os.path.dirname(__file__))
+ unittest.main()
diff --git a/tests/test_large_file.py b/tests/test_large_file.py
new file mode 100644
index 0000000..87bc9b6
--- /dev/null
+++ b/tests/test_large_file.py
@@ -0,0 +1,25 @@
+#!/usr/bin/python3
+
+import sys
+import unittest
+
+import apt_inst
+import testcommon
+
+IS_NOT_32BIT = sys.maxsize > 2**32
+
+
+@unittest.skipIf(IS_NOT_32BIT, "Large File support is for 32 bit systems")
+class testHashes(testcommon.TestCase):
+ "test the hashsum functions against strings and files"
+
+ LARGE_PACKAGE_CONTENT = "data/test_debs/large-package-content_1.0_all.deb"
+
+ def testExtractData(self):
+ deb = apt_inst.DebFile(self.LARGE_PACKAGE_CONTENT)
+
+ self.assertRaises(MemoryError, deb.data.extractdata, "large-file")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_lfs.py b/tests/test_lfs.py
new file mode 100644
index 0000000..d82e762
--- /dev/null
+++ b/tests/test_lfs.py
@@ -0,0 +1,19 @@
+#!/usr/bin/python3
+import unittest
+
+import apt_pkg
+import testcommon
+
+
+class TestLargeFileSupport(testcommon.TestCase):
+ """Test large file support"""
+
+ def test_acquire_file(self):
+ """Test apt_pkg.AcquireFile() accepts large file size"""
+ acq = apt_pkg.Acquire()
+ fil = apt_pkg.AcquireFile(acq, "http://foo", "foo", size=2875204834)
+ self.assertEqual(fil.filesize, 2875204834)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_lp659438.py b/tests/test_lp659438.py
new file mode 100644
index 0000000..21ad061
--- /dev/null
+++ b/tests/test_lp659438.py
@@ -0,0 +1,80 @@
+#!/usr/bin/python3
+"""Regression test for LP: #981896, LP: #659438"""
+# 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 os
+import shutil
+import tempfile
+import unittest
+
+import apt_pkg
+import testcommon
+
+import apt
+
+
+class RegressionTestCase(testcommon.TestCase):
+
+ """Test suite for LP: #981896, LP: #659438
+ 'Cannot locate a file for package X'
+ """
+
+ def setUp(self):
+ testcommon.TestCase.setUp(self)
+ apt_pkg.config.clear("APT::Update::Post-Invoke")
+ apt_pkg.config.clear("APT::Update::Post-Invoke-Success")
+ self.chroot_path = chroot_path = tempfile.mkdtemp()
+ # Create a damaged status file
+ self.cache = apt.cache.Cache(rootdir=chroot_path)
+ with open(apt_pkg.config.find_file("Dir::State::status"), "a") as status:
+ status.write(
+ """Package: abrowser
+Status: install reinstreq half-installed
+Priority: optional
+Section: admin
+Version: 3.6.9+build1+nobinonly-0ubuntu1
+Architecture: all"""
+ )
+ sources_list_path = apt_pkg.config.find_file("Dir::Etc::sourcelist")
+ repo_path = os.path.abspath("./data/test-repo")
+ with open(sources_list_path, "w") as sources_list:
+ sources_list.write("deb [allow-insecure=yes] copy:%s /\n" % repo_path)
+ # os.makedirs(os.path.join(chroot_path, "etc/apt/sources.list.d/"))
+ self.cache.update(sources_list=sources_list_path)
+ self.cache.open()
+
+ def tearDown(self):
+ # this resets the rootdir apt_pkg.config to ensure it does not
+ # "pollute" the later tests
+ apt.cache.Cache(rootdir="/")
+ shutil.rmtree(self.chroot_path)
+
+ def test_survive_reqreinst(self):
+ """Test that we survive a package in require reinstallation state"""
+ # this should be 82324L but python3.2 gets unhappy about the "L"
+ self.assertEqual(self.cache.required_download, 82324)
+
+
+if __name__ == "__main__":
+ unittest.main()
+
+# vim: ts=4 et sts=4
diff --git a/tests/test_paths.py b/tests/test_paths.py
new file mode 100644
index 0000000..4920a25
--- /dev/null
+++ b/tests/test_paths.py
@@ -0,0 +1,158 @@
+#
+# Test that both unicode and bytes path names work
+#
+import os
+import shutil
+import unittest
+
+import apt_inst
+import apt_pkg
+import testcommon
+
+
+class TestPath(testcommon.TestCase):
+ dir_unicode = "data/tmp"
+ dir_bytes = b"data/tmp"
+ file_unicode = "data/tmp/python-apt-test"
+ file_bytes = b"data/tmp/python-apt-test"
+
+ def setUp(self):
+ testcommon.TestCase.setUp(self)
+ if os.path.exists(self.dir_bytes):
+ shutil.rmtree(self.dir_bytes)
+
+ os.mkdir(self.dir_bytes)
+
+ def tearDown(self):
+ apt_pkg.config["dir"] = "/"
+ shutil.rmtree(self.dir_bytes)
+
+ def test_acquire(self):
+ apt_pkg.AcquireFile(
+ apt_pkg.Acquire(),
+ "http://example.com",
+ destdir=self.file_bytes,
+ destfile=self.file_bytes,
+ )
+ apt_pkg.AcquireFile(
+ apt_pkg.Acquire(),
+ "http://example.com",
+ destdir=self.file_unicode,
+ destfile=self.file_unicode,
+ )
+
+ def test_acquire_hashes(self):
+ hs = apt_pkg.HashString("d41d8cd98f00b204e9800998ecf8427e")
+ hsl = apt_pkg.HashStringList()
+ hsl.append(hs)
+ apt_pkg.AcquireFile(
+ apt_pkg.Acquire(),
+ "http://example.com",
+ hash=hsl,
+ destdir=self.file_unicode,
+ destfile=self.file_unicode,
+ )
+ apt_pkg.AcquireFile(
+ apt_pkg.Acquire(),
+ "http://example.com",
+ hash=str(hs),
+ destdir=self.file_unicode,
+ destfile=self.file_unicode,
+ )
+ self.assertRaises(
+ TypeError,
+ apt_pkg.AcquireFile,
+ apt_pkg.Acquire(),
+ "http://example.com",
+ hash=hs,
+ destdir=self.file_unicode,
+ destfile=self.file_unicode,
+ )
+
+ def test_ararchive(self):
+ archive = apt_inst.ArArchive("data/test_debs/data-tar-xz.deb")
+
+ apt_inst.ArArchive(b"data/test_debs/data-tar-xz.deb")
+
+ archive.extract("debian-binary", "data/tmp")
+ archive.extract(b"debian-binary", b"data/tmp")
+ archive.extractall("data/tmp")
+ archive.extractall(b"data/tmp")
+ self.assertEqual(archive.extractdata("debian-binary"), b"2.0\n")
+ self.assertEqual(archive.extractdata(b"debian-binary"), b"2.0\n")
+ self.assertTrue(archive.getmember("debian-binary"))
+ self.assertTrue(archive.getmember(b"debian-binary"))
+ self.assertTrue("debian-binary" in archive)
+ self.assertTrue(b"debian-binary" in archive)
+ self.assertTrue(archive[b"debian-binary"])
+ self.assertTrue(archive["debian-binary"])
+
+ tar = archive.gettar("control.tar.xz", "xz")
+ tar = archive.gettar(b"control.tar.xz", "xz")
+
+ tar.extractall(self.dir_unicode)
+ tar.extractall(self.dir_bytes)
+ self.assertRaises(LookupError, tar.extractdata, "Do-not-exist")
+ self.assertRaises(LookupError, tar.extractdata, b"Do-not-exist")
+ tar.extractdata(b"control")
+ tar.extractdata("control")
+
+ apt_inst.TarFile(os.path.join(self.dir_unicode, "control.tar.xz"))
+ apt_inst.TarFile(os.path.join(self.dir_bytes, b"control.tar.xz"))
+
+ def test_configuration(self):
+ with open(self.file_unicode, "w") as config:
+ config.write("Hello { World 1; };")
+ apt_pkg.read_config_file(apt_pkg.config, self.file_bytes)
+ apt_pkg.read_config_file(apt_pkg.config, self.file_unicode)
+ apt_pkg.read_config_file_isc(apt_pkg.config, self.file_bytes)
+ apt_pkg.read_config_file_isc(apt_pkg.config, self.file_unicode)
+ apt_pkg.read_config_dir(apt_pkg.config, self.dir_unicode)
+ apt_pkg.read_config_dir(apt_pkg.config, b"/etc/apt/apt.conf.d")
+
+ def test_index_file(self):
+ apt_pkg.config["dir"] = "data/test_debs"
+ slist = apt_pkg.SourceList()
+ slist.read_main_list()
+
+ for meta in slist.list:
+ for index in meta.index_files:
+ index.archive_uri(self.file_bytes)
+ index.archive_uri(self.file_unicode)
+
+ def test_lock(self):
+ apt_pkg.get_lock(self.file_unicode, True)
+ apt_pkg.get_lock(self.file_bytes, True)
+
+ with apt_pkg.FileLock(self.file_unicode):
+ pass
+ with apt_pkg.FileLock(self.file_bytes):
+ pass
+
+ def test_policy(self):
+ apt_pkg.config["dir"] = "data/test_debs"
+ cache = apt_pkg.Cache(None)
+ policy = apt_pkg.Policy(cache)
+ file_unicode = os.path.join(self.dir_unicode, "test.prefs")
+ file_bytes = os.path.join(self.dir_bytes, b"test.prefs")
+
+ self.assertTrue(policy.read_pinfile(file_unicode))
+ self.assertTrue(policy.read_pinfile(file_bytes))
+ self.assertTrue(policy.read_pindir(self.dir_unicode))
+ self.assertTrue(policy.read_pindir(self.dir_bytes))
+
+ def test_tag(self):
+ with open(self.file_bytes, "w") as tag:
+ tag.write("Key: value\n")
+ tag1 = apt_pkg.TagFile(self.file_unicode)
+ tag2 = apt_pkg.TagFile(self.file_bytes)
+
+ self.assertEqual(next(tag1)["Key"], "value")
+ self.assertEqual(next(tag2)["Key"], "value")
+
+ self.assertRaises(StopIteration, next, tag1)
+ self.assertRaises(StopIteration, next, tag2)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_pep484.py b/tests/test_pep484.py
new file mode 100644
index 0000000..01711dc
--- /dev/null
+++ b/tests/test_pep484.py
@@ -0,0 +1,28 @@
+#!/usr/bin/python3
+
+import os
+import subprocess
+import unittest
+
+
+def hasMyPy():
+ try:
+ subprocess.check_call(["mypy", "--version"])
+ except Exception:
+ return False
+ return True
+
+
+class PackagePep484TestCase(unittest.TestCase):
+ @unittest.skipIf(not hasMyPy(), "no mypy available")
+ def test_pep484_clean(self):
+ # FIXME: check all of it
+ top_src_dir = os.path.join(os.path.dirname(__file__), "..", "apt")
+ os.environ["MYPYPATH"] = os.path.join(
+ os.path.dirname(__file__), "..", "typehinting"
+ )
+ self.assertEqual(subprocess.call(["mypy", "--strict", top_src_dir]), 0)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_policy.py b/tests/test_policy.py
new file mode 100644
index 0000000..ad0032c
--- /dev/null
+++ b/tests/test_policy.py
@@ -0,0 +1,70 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2012 Michael Vogt <mvo@ubuntu.com>
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+
+import unittest
+
+import apt_pkg
+import testcommon
+
+import apt
+
+
+class TestAptPolicy(testcommon.TestCase):
+ def test_apt_policy_lowlevel(self):
+ return # TODO: Make tests independent of system state
+ # get a policy
+ cache = apt.Cache()
+ policy = cache._depcache.policy
+ self.assertNotEqual(policy, None)
+ # basic tests
+ pkg = cache["apt"]
+ self.assertEqual(policy.get_priority(pkg._pkg), 0)
+ # get priority for all pkgfiles
+ for ver in pkg.versions:
+ lowlevel_ver = ver._cand
+ for pkgfile, i in lowlevel_ver.file_list:
+ # print pkgfile, i, policy.get_priority(pkgfile)
+ self.assertTrue(policy.get_priority(pkgfile) >= 1)
+ self.assertTrue(policy.get_priority(pkgfile) < 1001)
+
+ def test_apt_policy_lowlevel_files(self):
+ cache = apt_pkg.Cache()
+ depcache = apt_pkg.DepCache(cache)
+ policy = cache.policy
+ dpolicy = depcache.policy
+ self.assertNotEqual(policy, None)
+ self.assertNotEqual(dpolicy, None)
+
+ for f in cache.file_list:
+ policy.get_priority(f)
+ dpolicy.get_priority(f)
+
+ def test_apt_policy_lowlevel_versions(self):
+ cache = apt_pkg.Cache()
+ depcache = apt_pkg.DepCache(cache)
+ policy = cache.policy
+ dpolicy = depcache.policy
+ self.assertNotEqual(policy, None)
+ self.assertNotEqual(dpolicy, None)
+
+ for pkg in cache.packages:
+ for ver in pkg.version_list:
+ policy.get_priority(ver)
+ dpolicy.get_priority(ver)
+
+ def test_apt_policy_highlevel(self):
+ return # TODO: Make tests independent of system state
+ cache = apt.Cache()
+ pkg = cache["apt"]
+ self.assertTrue(
+ pkg.candidate.policy_priority > 1 and pkg.candidate.policy_priority < 1001
+ )
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_progress.py b/tests/test_progress.py
new file mode 100644
index 0000000..c823019
--- /dev/null
+++ b/tests/test_progress.py
@@ -0,0 +1,56 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2010 Michael Vogt <mvo@ubuntu.com>
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+"""Unit tests for verifying the correctness of apt.progress"""
+import os
+import unittest
+
+import apt_pkg
+import testcommon
+
+import apt
+
+
+class TestAcquireProgress(apt.progress.base.AcquireProgress):
+ def pulse(self, owner):
+ self.pulsed = True
+ # there should be a return value here, either (True,False)
+ # but often this is forgoten (and causes odd error messages)
+ # so the lib supports it. we test the lack of support value here
+
+
+class TestProgress(testcommon.TestCase):
+ def setUp(self):
+ testcommon.TestCase.setUp(self)
+ basedir = os.path.abspath(os.path.dirname(__file__))
+ # setup apt_pkg config
+ apt_pkg.init()
+ apt_pkg.config.set("APT::Architecture", "amd64")
+ apt_pkg.config.set("Dir::Etc", basedir)
+ # TODO: /dev/null is not a dir, perhaps find something better
+ apt_pkg.config.set("Dir::Etc::sourceparts", "/dev/null")
+ # setup lists dir
+ if not os.path.exists("./tmp/partial"):
+ os.makedirs("./tmp/partial")
+ apt_pkg.config.set("Dir::state::lists", "./tmp")
+ # create artifical line
+ deb_line = "deb [allow-insecure=yes] file:%s/data/fake-packages/ /\n" % basedir
+ with open("fetch_sources.list", "w") as fobj:
+ fobj.write(deb_line)
+ apt_pkg.config.set("Dir::Etc::sourcelist", "fetch_sources.list")
+ apt_pkg.config.clear("APT::Update::Post-Invoke")
+ apt_pkg.config.clear("APT::Update::Post-Invoke-Success")
+
+ def test_acquire_progress(self):
+ progress = TestAcquireProgress()
+ cache = apt.Cache()
+ cache.update(progress)
+ self.assertTrue(progress.pulsed)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_pyflakes.py b/tests/test_pyflakes.py
new file mode 100644
index 0000000..f26433a
--- /dev/null
+++ b/tests/test_pyflakes.py
@@ -0,0 +1,43 @@
+#!/usr/bin/python3
+
+import os
+import subprocess
+import unittest
+
+import testcommon
+
+
+class TestPyflakesClean(testcommon.TestCase):
+ EXCLUDES = ["build", "tests/old", ".pybuild"]
+ TOPLEVEL = os.path.normpath(
+ os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
+ )
+
+ def is_excluded_path(self, path):
+ for exclude in self.EXCLUDES:
+ if path.startswith(os.path.join(self.TOPLEVEL, exclude)):
+ return True
+ return False
+
+ def get_py_files(self, toplevel):
+ files = []
+ for path, dirnames, filenames in os.walk(self.TOPLEVEL):
+ if self.is_excluded_path(path):
+ continue
+ for filename in filenames:
+ if os.path.splitext(filename)[1] == ".py":
+ files.append(os.path.join(path, filename))
+ return files
+
+ def test_pyflakes_clean(self):
+ cmd = ["pyflakes3"] + self.get_py_files(self.TOPLEVEL)
+ res = subprocess.call(cmd)
+ if res != 0:
+ self.fail("pyflakes failed with: %s" % res)
+
+
+if __name__ == "__main__":
+ import logging
+
+ logging.basicConfig(level=logging.DEBUG)
+ unittest.main()
diff --git a/tests/test_signed_usable.py b/tests/test_signed_usable.py
new file mode 100644
index 0000000..1bd5087
--- /dev/null
+++ b/tests/test_signed_usable.py
@@ -0,0 +1,403 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2019 Canonical Ltd
+#
+# SPDX-License-Identifier: GPL-2.0+
+
+
+import base64
+import os
+import shutil
+import tempfile
+import unittest
+
+import apt_pkg
+import testcommon
+
+import apt
+import apt.progress.base
+import apt.progress.text
+
+# A change in APT to early fail acquire items with weak hashes caused it
+# to call progress methods before calling Start(), which confused python-apt's
+# locking handling. Hence we need to skip such tests for now, until we can test
+# against an apt version with the fix:
+#
+# https://salsa.debian.org/apt-team/apt/commit/84176f6c
+#
+RUN_CRASHING_TESTS = False
+
+# Message APT gives us when hashes are too weak
+CACHE_MSG_WEAK_HASH = (
+ "Insufficient information available to perform this download securely"
+)
+
+
+class TestSignedUsable(testcommon.TestCase):
+ """Test fetch_binary() and fetch_source() signature checking."""
+
+ def setUp(self):
+ testcommon.TestCase.setUp(self)
+ apt_pkg.config.clear("APT::Update::Post-Invoke")
+ apt_pkg.config.clear("APT::Update::Post-Invoke-Success")
+ self.chroot_path = chroot_path = tempfile.mkdtemp()
+ repo_path = os.path.abspath("./data/test-signed-usable-repo/")
+ # Inits the dirs for us
+ apt.cache.Cache(rootdir=chroot_path)
+ # Change directory
+ self.cwd = os.getcwd()
+ os.chdir(chroot_path)
+ with open(
+ os.path.join(self.chroot_path, "etc/apt/sources.list"), "w"
+ ) as sources_list:
+ sources_list.write("deb copy:%s/signed/ /\n" % repo_path)
+ sources_list.write("deb copy:%s/unsigned/ /\n" % repo_path)
+ sources_list.write("deb-src copy:%s/signed/ /\n" % repo_path)
+ sources_list.write("deb-src copy:%s/unsigned/ /\n" % repo_path)
+
+ with open(os.path.join(repo_path, "key.gpg.base64"), "rb") as pubkey64:
+ with open(
+ os.path.join(self.chroot_path, "etc/apt/trusted.gpg"), "wb"
+ ) as tgt:
+ tgt.write(base64.b64decode(pubkey64.read()))
+
+ self.cache = apt.cache.Cache(rootdir=chroot_path)
+ apt_pkg.config["Acquire::AllowInsecureRepositories"] = "true"
+ self.cache.update()
+ apt_pkg.config["Acquire::AllowInsecureRepositories"] = "false"
+ self.cache.open()
+
+ self.progress = apt.progress.text.AcquireProgress
+ apt.progress.text.AcquireProgress = apt.progress.base.AcquireProgress
+
+ # Disable actual installation of downloaded items
+ self.cache.install_archives = (
+ lambda *a, **b: apt_pkg.PackageManager.RESULT_COMPLETED
+ )
+
+ def tearDown(self):
+ # this resets the rootdir apt_pkg.config to ensure it does not
+ # "pollute" the later tests
+ apt.cache.Cache(rootdir="/")
+ os.chdir(self.cwd)
+ shutil.rmtree(self.chroot_path)
+
+ apt.progress.text.AcquireProgress = self.progress
+
+ def doInstall(self, name, bargs):
+ self.cache[name].mark_install()
+ try:
+ with apt.progress.base.InstallProgress() as ip:
+ self.cache.commit(install_progress=ip, **bargs)
+ finally:
+ for fname in os.listdir(
+ os.path.join(self.chroot_path, "var/cache/apt/archives")
+ ):
+ if os.path.isfile(
+ os.path.join(self.chroot_path, "var/cache/apt/archives", fname)
+ ):
+ os.unlink(
+ os.path.join(self.chroot_path, "var/cache/apt/archives", fname)
+ )
+ self.cache[name].mark_keep()
+
+ def doFetchArchives(self, name, bargs):
+ fetcher = apt_pkg.Acquire()
+ self.cache[name].mark_install()
+ try:
+ self.cache.fetch_archives(fetcher=fetcher, **bargs)
+ finally:
+ for fname in os.listdir(
+ os.path.join(self.chroot_path, "var/cache/apt/archives")
+ ):
+ if fname.endswith(".deb"):
+ os.unlink(
+ os.path.join(self.chroot_path, "var/cache/apt/archives", fname)
+ )
+ self.cache[name].mark_keep()
+
+ def testDefaultDenyButExplicitAllowUnauthenticated(self):
+ """Deny by config (default), but pass allow_unauthenticated=True"""
+
+ bargs = dict(allow_unauthenticated=True)
+ sargs = dict(allow_unauthenticated=True, unpack=False)
+
+ self.doInstall("signed-usable", bargs)
+ if RUN_CRASHING_TESTS:
+ self.assertRaisesRegex(
+ apt.cache.FetchFailedException,
+ CACHE_MSG_WEAK_HASH,
+ self.doInstall,
+ "signed-not-usable",
+ bargs,
+ )
+
+ self.doInstall("unsigned-usable", bargs)
+ if RUN_CRASHING_TESTS:
+ self.assertRaisesRegex(
+ apt.cache.FetchFailedException,
+ CACHE_MSG_WEAK_HASH,
+ self.doInstall,
+ "unsigned-unusable",
+ bargs,
+ )
+
+ self.doFetchArchives("signed-usable", bargs)
+ self.doFetchArchives("unsigned-usable", bargs)
+ self.assertRaisesRegex(
+ apt.cache.FetchFailedException,
+ CACHE_MSG_WEAK_HASH,
+ self.doFetchArchives,
+ "signed-not-usable",
+ bargs,
+ )
+ self.assertRaisesRegex(
+ apt.cache.FetchFailedException,
+ CACHE_MSG_WEAK_HASH,
+ self.doFetchArchives,
+ "unsigned-unusable",
+ bargs,
+ )
+
+ self.cache["signed-usable"].candidate.fetch_binary(**bargs)
+ self.cache["signed-usable"].candidate.fetch_source(**sargs)
+ self.cache["signed-not-usable"].candidate.fetch_binary(**bargs)
+ self.cache["signed-not-usable"].candidate.fetch_source(**sargs)
+ self.cache["unsigned-usable"].candidate.fetch_binary(**bargs)
+ self.cache["unsigned-usable"].candidate.fetch_source(**sargs)
+ self.cache["unsigned-unusable"].candidate.fetch_binary(**bargs)
+ self.cache["unsigned-unusable"].candidate.fetch_source(**sargs)
+
+ def testDefaultAllow(self):
+ """Allow by config APT::Get::AllowUnauthenticated = True"""
+ apt_pkg.config["APT::Get::AllowUnauthenticated"] = "true"
+
+ bargs = dict()
+ sargs = dict(unpack=False)
+
+ self.doInstall("signed-usable", bargs)
+ if RUN_CRASHING_TESTS:
+ self.assertRaisesRegex(
+ apt.cache.FetchFailedException,
+ CACHE_MSG_WEAK_HASH,
+ self.doInstall,
+ "signed-not-usable",
+ bargs,
+ )
+ self.doInstall("unsigned-usable", bargs)
+ if RUN_CRASHING_TESTS:
+ self.assertRaisesRegex(
+ apt.cache.FetchFailedException,
+ CACHE_MSG_WEAK_HASH,
+ self.doInstall,
+ "unsigned-unusable",
+ bargs,
+ )
+
+ self.doFetchArchives("signed-usable", bargs)
+ self.doFetchArchives("unsigned-usable", bargs)
+ self.assertRaisesRegex(
+ apt.cache.FetchFailedException,
+ CACHE_MSG_WEAK_HASH,
+ self.doFetchArchives,
+ "signed-not-usable",
+ bargs,
+ )
+ self.assertRaisesRegex(
+ apt.cache.FetchFailedException,
+ CACHE_MSG_WEAK_HASH,
+ self.doFetchArchives,
+ "unsigned-unusable",
+ bargs,
+ )
+
+ self.cache["signed-usable"].candidate.fetch_binary(**bargs)
+ self.cache["signed-usable"].candidate.fetch_source(**sargs)
+ self.cache["signed-not-usable"].candidate.fetch_binary(**bargs)
+ self.cache["signed-not-usable"].candidate.fetch_source(**sargs)
+ self.cache["unsigned-usable"].candidate.fetch_binary(**bargs)
+ self.cache["unsigned-usable"].candidate.fetch_source(**sargs)
+ self.cache["unsigned-unusable"].candidate.fetch_binary(**bargs)
+ self.cache["unsigned-unusable"].candidate.fetch_source(**sargs)
+
+ def testDefaultDeny(self):
+ """Test APT::Get::AllowUnauthenticated = False (default)"""
+ self.doInstall("signed-usable", {})
+ if RUN_CRASHING_TESTS:
+ self.assertRaisesRegex(
+ apt.cache.FetchFailedException,
+ CACHE_MSG_WEAK_HASH,
+ self.doInstall,
+ "signed-not-usable",
+ {},
+ )
+ self.assertRaisesRegex(
+ apt.cache.UntrustedException,
+ "Untrusted packages:",
+ self.doInstall,
+ "unsigned-usable",
+ {},
+ )
+ self.assertRaisesRegex(
+ apt.cache.UntrustedException,
+ "Untrusted packages:",
+ self.doInstall,
+ "unsigned-unusable",
+ {},
+ )
+
+ self.doFetchArchives("signed-usable", {})
+ self.assertRaisesRegex(
+ apt.cache.FetchFailedException,
+ CACHE_MSG_WEAK_HASH,
+ self.doFetchArchives,
+ "signed-not-usable",
+ {},
+ )
+ self.assertRaisesRegex(
+ apt.cache.UntrustedException,
+ "Untrusted packages:",
+ self.doFetchArchives,
+ "unsigned-usable",
+ {},
+ )
+ self.assertRaisesRegex(
+ apt.cache.UntrustedException,
+ "Untrusted packages:",
+ self.doFetchArchives,
+ "unsigned-unusable",
+ {},
+ )
+
+ self.cache["signed-usable"].candidate.fetch_binary()
+ self.cache["signed-usable"].candidate.fetch_source(unpack=False)
+ self.assertRaisesRegex(
+ apt.package.UntrustedError,
+ ": No trusted hash",
+ self.cache["signed-not-usable"].candidate.fetch_binary,
+ )
+ self.assertRaisesRegex(
+ apt.package.UntrustedError,
+ ": No trusted hash",
+ self.cache["signed-not-usable"].candidate.fetch_source,
+ unpack=False,
+ )
+ self.assertRaisesRegex(
+ apt.package.UntrustedError,
+ ": Source",
+ self.cache["unsigned-usable"].candidate.fetch_binary,
+ )
+ self.assertRaisesRegex(
+ apt.package.UntrustedError,
+ ": Source",
+ self.cache["unsigned-usable"].candidate.fetch_source,
+ unpack=False,
+ )
+ self.assertRaisesRegex(
+ apt.package.UntrustedError,
+ ": Source",
+ self.cache["unsigned-unusable"].candidate.fetch_binary,
+ )
+ self.assertRaisesRegex(
+ apt.package.UntrustedError,
+ ": Source",
+ self.cache["unsigned-unusable"].candidate.fetch_source,
+ unpack=False,
+ )
+
+ def testDefaultAllowButExplicitDeny(self):
+ """Allow by config, but pass allow_unauthenticated=False"""
+ apt_pkg.config["APT::Get::AllowUnauthenticated"] = "true"
+
+ bargs = dict(allow_unauthenticated=False)
+ sargs = dict(allow_unauthenticated=False, unpack=False)
+
+ self.doInstall("signed-usable", bargs)
+ if RUN_CRASHING_TESTS:
+ self.assertRaisesRegex(
+ apt.cache.FetchFailedException,
+ CACHE_MSG_WEAK_HASH,
+ self.doInstall,
+ "signed-not-usable",
+ bargs,
+ )
+ self.assertRaisesRegex(
+ apt.cache.UntrustedException,
+ "Untrusted packages:",
+ self.doInstall,
+ "unsigned-usable",
+ bargs,
+ )
+ self.assertRaisesRegex(
+ apt.cache.UntrustedException,
+ "Untrusted packages:",
+ self.doInstall,
+ "unsigned-unusable",
+ bargs,
+ )
+
+ self.doFetchArchives("signed-usable", bargs)
+ self.assertRaisesRegex(
+ apt.cache.FetchFailedException,
+ CACHE_MSG_WEAK_HASH,
+ self.doFetchArchives,
+ "signed-not-usable",
+ bargs,
+ )
+ self.assertRaisesRegex(
+ apt.cache.UntrustedException,
+ "Untrusted packages:",
+ self.doFetchArchives,
+ "unsigned-usable",
+ bargs,
+ )
+ self.assertRaisesRegex(
+ apt.cache.UntrustedException,
+ "Untrusted packages:",
+ self.doFetchArchives,
+ "unsigned-unusable",
+ bargs,
+ )
+
+ self.cache["signed-usable"].candidate.fetch_binary(**bargs)
+ self.cache["signed-usable"].candidate.fetch_source(**sargs)
+ self.assertRaisesRegex(
+ apt.package.UntrustedError,
+ ": No trusted hash",
+ self.cache["signed-not-usable"].candidate.fetch_binary,
+ **bargs
+ )
+ self.assertRaisesRegex(
+ apt.package.UntrustedError,
+ ": No trusted hash",
+ self.cache["signed-not-usable"].candidate.fetch_source,
+ **sargs
+ )
+ self.assertRaisesRegex(
+ apt.package.UntrustedError,
+ ": Source",
+ self.cache["unsigned-usable"].candidate.fetch_binary,
+ **bargs
+ )
+ self.assertRaisesRegex(
+ apt.package.UntrustedError,
+ ": Source",
+ self.cache["unsigned-usable"].candidate.fetch_source,
+ **sargs
+ )
+ self.assertRaisesRegex(
+ apt.package.UntrustedError,
+ ": Source",
+ self.cache["unsigned-unusable"].candidate.fetch_binary,
+ **bargs
+ )
+ self.assertRaisesRegex(
+ apt.package.UntrustedError,
+ ": Source",
+ self.cache["unsigned-unusable"].candidate.fetch_source,
+ **sargs
+ )
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_size_to_str.py b/tests/test_size_to_str.py
new file mode 100644
index 0000000..6320e39
--- /dev/null
+++ b/tests/test_size_to_str.py
@@ -0,0 +1,104 @@
+#!/usr/bin/python3
+
+__author__ = "Barry Warsaw <barry@ubuntu.com>, James Hunt, Michael Vogt"
+
+import unittest
+
+import apt_pkg
+import testcommon
+
+
+class SizeToStrTestCase(testcommon.TestCase):
+ """Test apt_pkg.size_to_str"""
+
+ DATA = {
+ # XXX: note the trailing spaces for some of these entries!
+ 10**1: "10 ",
+ 10**2: "100 ",
+ 10**3: "1000 ",
+ 10**4: "10.0 k",
+ 10**5: "100 k",
+ 10**6: "1000 k",
+ 10**7: "10.0 M",
+ 10**8: "100 M",
+ 10**9: "1000 M",
+ 10**10: "10.0 G",
+ 10**11: "100 G",
+ 10**12: "1000 G",
+ 10**13: "10.0 T",
+ 10**14: "100 T",
+ 10**15: "1000 T",
+ 10**16: "10.0 P",
+ 10**17: "100 P",
+ 10**18: "1000 P",
+ 10**19: "10.0 E",
+ 10**20: "100 E",
+ 10**21: "1000 E",
+ 10**22: "10.0 Z",
+ 10**23: "100.0 Z",
+ 10**24: "1000 Z",
+ # 10 ** 25: "10.0 Y",
+ 10**26: "100 Y",
+ 10**27: "1000 Y",
+ # That's our limit :)
+ 10**28: "10000 Y",
+ 0: "0 ",
+ 1: "1 ",
+ 1024: "1024 ",
+ 10240: "10.2 k",
+ 102400: "102 k",
+ 1024000: "1024 k",
+ 10240000: "10.2 M",
+ 102400000: "102 M",
+ 2147483647: "2147 M",
+ 2147483648: "2147 M",
+ 1024000000: "1024 M",
+ 10240000000: "10.2 G",
+ 9: "9 ",
+ 99: "99 ",
+ 999: "999 ",
+ 9999: "9999 ",
+ 99999: "100.0 k",
+ 999999: "1000 k",
+ 9999999: "10000 k",
+ 99999999: "100.0 M",
+ 999999999: "1000 M",
+ 9999999999: "10000 M",
+ 99999999999: "100.0 G",
+ 999999999999: "1000 G",
+ 9999999999999: "10000 G",
+ 99999999999999: "100.0 T",
+ 999999999999999: "1000 T",
+ 9999999999999999: "10.0 P",
+ 99999999999999999: "100 P",
+ 999999999999999999: "1000 P",
+ 9999999999999999999: "10.0 E",
+ 99999999999999999999: "100 E",
+ 999999999999999999999: "1000 E",
+ 9999999999999999999999: "10.0 Z",
+ 999999999999999999999999: "1000 Z",
+ }
+
+ def test_from_data(self):
+ for k, v in self.DATA.items():
+ size = apt_pkg.size_to_str(k)
+ msg = f"size_to_str({k}) returned '{size}', expected '{v}'"
+ self.assertEqual(size, v, msg)
+
+ def test_raise_on_unsupported(self):
+ for v in ["hello", None, {}, [], ()]:
+ with self.assertRaises(TypeError):
+ apt_pkg.size_to_str(v)
+
+
+class RegressionTestCase(testcommon.TestCase):
+ """Regression test for LP: #1030278"""
+
+ def test_no_overflow_error(self):
+ # LP: #1030278 produces an overflow error in size_to_str() with a big
+ # value under Python 3.
+ self.assertEqual(apt_pkg.size_to_str(2147483648000000000000), "2147 E")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_sourcerecords.py b/tests/test_sourcerecords.py
new file mode 100644
index 0000000..4b0e6fb
--- /dev/null
+++ b/tests/test_sourcerecords.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2018 Michael Vogt <mvo@ubuntu.com>
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+"""Unit tests for verifying the correctness of source records in apt_pkg."""
+
+import os
+import shutil
+import sys
+import unittest
+
+from test_all import get_library_dir
+
+libdir = get_library_dir()
+if libdir:
+ sys.path.insert(0, libdir)
+
+import apt_pkg
+import testcommon
+
+import apt
+
+
+class TestSourceRecords(testcommon.TestCase):
+ def setUp(self):
+ testcommon.TestCase.setUp(self)
+
+ rootdir = "./data/tmp"
+ if os.path.exists(rootdir):
+ shutil.rmtree(rootdir)
+ try:
+ os.makedirs(os.path.join(rootdir, "etc", "apt"))
+ except OSError:
+ pass
+
+ for k in apt_pkg.config.keys():
+ apt_pkg.config.clear(k)
+
+ apt_pkg.config["Dir"] = os.path.abspath(rootdir)
+ apt_pkg.init_config()
+
+ # set a local sources.list that does not need the network
+ base_sources = os.path.abspath(
+ os.path.join(rootdir, "etc", "apt", "sources.list")
+ )
+ # main sources.list
+ sources_list = base_sources
+ with open(sources_list, "w") as f:
+ repo = os.path.abspath("./data/test-source-repo")
+ f.write("deb-src [trusted=yes] copy:%s /\n" % repo)
+
+ self.assertTrue(os.path.exists(sources_list))
+
+ # update a single sources.list
+ cache = apt.Cache(rootdir=rootdir)
+ cache.update(sources_list=sources_list)
+
+ def test_source_records_smoke(self):
+ src = apt_pkg.SourceRecords()
+ self.assertTrue(src.step())
+
+ self.assertEqual(src.maintainer, "Julian Andres Klode <jak@debian.org>") # noqa
+ self.assertEqual(src.binaries, ["dh-autoreconf"])
+ self.assertEqual(src.package, "dh-autoreconf")
+
+ self.assertEqual(2, len(src.files))
+
+ # unpacking as a tuple works as before
+ md5, size, path, type_ = f = src.files[0]
+ self.assertEqual(md5, None)
+ self.assertEqual(size, 1578)
+ self.assertEqual(path, "dh-autoreconf_16.dsc")
+ self.assertEqual(type_, "dsc")
+ # access using getters
+ self.assertTrue(isinstance(f.hashes, apt_pkg.HashStringList))
+ self.assertEqual(
+ str(f.hashes[0]),
+ "SHA512:4b1a3299f2a8b01b0c75db97fd16cb39919949c74d19ea6cf28e1bbd4891d3515b3e2b90b96a64df665cebf6d95409e704e670909ae91fcfe92409ee1339bffc",
+ ) # noqa
+ self.assertEqual(str(f.hashes[1]), "Checksum-FileSize:1578")
+ self.assertEqual(
+ str(f.hashes[2]),
+ "SHA256:1c1b2ab5f1ae5496bd50dbb3c30e9b7d181a06c8d02ee8d7e9c35ed6f2a69b5f",
+ ) # noqa
+ self.assertEqual(
+ str(f.hashes[3]), "SHA1:c9bf7a920013021dad5fbd898dfd5a79c7a150f9"
+ ) # noqa
+ self.assertEqual(
+ str(f.hashes[4]), "MD5Sum:6576a28fe1918ce10bd31543ba545901"
+ ) # noqa
+ self.assertEqual(f.size, 1578)
+ self.assertEqual(f.path, "dh-autoreconf_16.dsc")
+ self.assertEqual(f.type, "dsc")
+
+ # unpacking as a tuple works as before
+ md5, size, path, type_ = f = src.files[1]
+ self.assertEqual(md5, None)
+ self.assertEqual(size, 7372)
+ self.assertEqual(path, "dh-autoreconf_16.tar.xz")
+ self.assertEqual(type_, "tar")
+ # access using getters
+ self.assertTrue(isinstance(f.hashes, apt_pkg.HashStringList))
+ self.assertEqual(
+ str(f.hashes[0]),
+ "SHA512:10448dd179ec12bf4310a9a514110a85f56e51893aa36a97ac3a6f8d7ce99d099e62cfdb78e271e2d94431e8832da0f643de821b6643b80e3f0b0f5d682cf9a9",
+ ) # noqa
+ self.assertEqual(str(f.hashes[1]), "Checksum-FileSize:7372") # noqa
+ self.assertEqual(
+ str(f.hashes[2]),
+ "SHA256:5c6a6a362907327bec77a867ff3fd0eceba8015d1b881b48275aff7e4ce0f629",
+ ) # noqa
+ self.assertEqual(
+ str(f.hashes[3]), "SHA1:58459600164398ad6807ddd877a6f814c799c62c"
+ ) # noqa
+ self.assertEqual(
+ str(f.hashes[4]), "MD5Sum:302c8bf43db02412e3f2197fd0f2ee0f"
+ ) # noqa
+ self.assertEqual(f.size, 7372)
+ self.assertEqual(f.path, "dh-autoreconf_16.tar.xz")
+ self.assertEqual(f.type, "tar")
+
+ self.assertFalse(src.step())
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_tagfile.py b/tests/test_tagfile.py
new file mode 100644
index 0000000..29bf9e8
--- /dev/null
+++ b/tests/test_tagfile.py
@@ -0,0 +1,186 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2010 Michael Vogt <mvo@ubuntu.com>
+# Copyright (C) 2012 Canonical Ltd.
+# Author: Colin Watson <cjwatson@ubuntu.com>
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+"""Unit tests for verifying the correctness of apt_pkg.TagFile"""
+
+import glob
+import os
+import shutil
+import sys
+import tempfile
+import unittest
+
+from test_all import get_library_dir
+
+libdir = get_library_dir()
+if libdir:
+ sys.path.insert(0, libdir)
+
+import apt_pkg
+import testcommon
+
+
+class TestOpenMaybeClearSigned(testcommon.TestCase):
+ def test_open_trivial(self):
+ basepath = os.path.dirname(__file__)
+ fd = apt_pkg.open_maybe_clear_signed_file(
+ os.path.join(basepath, "./data/test_debs/hello_2.5-1.dsc")
+ )
+ with os.fdopen(fd) as f:
+ data = f.read()
+ self.assertTrue(data.startswith("Format: 1.0\n"))
+
+ def test_open_normal(self):
+ basepath = os.path.dirname(__file__)
+ fd = apt_pkg.open_maybe_clear_signed_file(
+ os.path.join(basepath, "./data/misc/foo_Release")
+ )
+ with os.fdopen(fd) as f:
+ data = f.read()
+ self.assertTrue(data.startswith("Origin: Ubuntu\n"))
+
+ def xtest_open_does_not_exit(self):
+ with self.assertRaises(SystemError):
+ apt_pkg.open_maybe_clear_signed_file("does-not-exists")
+
+
+class TestTagFile(testcommon.TestCase):
+ """test the apt_pkg.TagFile"""
+
+ def setUp(self):
+ testcommon.TestCase.setUp(self)
+ self.temp_dir = tempfile.mkdtemp()
+
+ def tearDown(self):
+ shutil.rmtree(self.temp_dir)
+
+ def test_tag_file(self):
+ basepath = os.path.dirname(__file__)
+ tagfilepath = os.path.join(basepath, "./data/tagfile/*")
+ # test once for compressed and uncompressed
+ for testfile in glob.glob(tagfilepath):
+ # test once using the open() method and once using the path
+ tagfile = apt_pkg.TagFile(testfile)
+ for i, stanza in enumerate(tagfile):
+ pass
+ self.assertEqual(i, 2)
+ with open(testfile) as f:
+ tagfile = apt_pkg.TagFile(f)
+ for i, stanza in enumerate(tagfile):
+ pass
+ self.assertEqual(i, 2)
+
+ def test_errors(self):
+ # Raises SystemError via lbiapt
+ self.assertRaises(SystemError, apt_pkg.TagFile, "not-there-no-no")
+ # Raises Type error
+ self.assertRaises(TypeError, apt_pkg.TagFile, object())
+
+ def test_utf8(self):
+ value = "Tést Persön <test@example.org>"
+ packages = os.path.join(self.temp_dir, "Packages")
+ with open(packages, "w", encoding="UTF-8") as packages_file:
+ print("Maintainer: %s" % value, file=packages_file)
+ print("", file=packages_file)
+ with open(packages, encoding="UTF-8") as packages_file:
+ tagfile = apt_pkg.TagFile(packages_file)
+ tagfile.step()
+ self.assertEqual(value, tagfile.section["Maintainer"])
+
+ def test_latin1(self):
+ value = "Tést Persön <test@example.org>"
+ packages = os.path.join(self.temp_dir, "Packages")
+ with open(packages, "w", encoding="ISO-8859-1") as packages_file:
+ print("Maintainer: %s" % value, file=packages_file)
+ print("", file=packages_file)
+ with open(packages) as packages_file:
+ tagfile = apt_pkg.TagFile(packages_file, bytes=True)
+ tagfile.step()
+ self.assertEqual(value.encode("ISO-8859-1"), tagfile.section["Maintainer"])
+ with open(packages, encoding="ISO-8859-1") as packages_file:
+ tagfile = apt_pkg.TagFile(packages_file)
+ tagfile.step()
+ self.assertEqual(value, tagfile.section["Maintainer"])
+
+ def test_mixed(self):
+ value = "Tést Persön <test@example.org>"
+ packages = os.path.join(self.temp_dir, "Packages")
+ with open(packages, "w", encoding="UTF-8") as packages_file:
+ print("Maintainer: %s" % value, file=packages_file)
+ print("", file=packages_file)
+ with open(packages, "a", encoding="ISO-8859-1") as packages_file:
+ print("Maintainer: %s" % value, file=packages_file)
+ print("", file=packages_file)
+ with open(packages) as packages_file:
+ tagfile = apt_pkg.TagFile(packages_file, bytes=True)
+ tagfile.step()
+ self.assertEqual(value.encode("UTF-8"), tagfile.section["Maintainer"])
+ tagfile.step()
+ self.assertEqual(value.encode("ISO-8859-1"), tagfile.section["Maintainer"])
+
+
+class TestTagSection(testcommon.TestCase):
+ """test the apt_pkg.TagFile"""
+
+ def setUp(self):
+ testcommon.TestCase.setUp(self)
+ self.temp_dir = tempfile.mkdtemp()
+
+ def tearDown(self):
+ shutil.rmtree(self.temp_dir)
+
+ def test_write(self):
+ ts = apt_pkg.TagSection("a: 1\nb: 2\nc: 3\n")
+ outpath = os.path.join(self.temp_dir, "test")
+ with open(outpath, "w") as outfile:
+ ts.write(outfile, [], [])
+ with open(outpath) as outfile:
+ self.assertEqual(outfile.read(), "a: 1\nb: 2\nc: 3\n")
+
+ def test_write_order(self):
+ ts = apt_pkg.TagSection("a: 1\nb: 2\nc: 3\n")
+ outpath = os.path.join(self.temp_dir, "test")
+ with open(outpath, "w") as outfile:
+ ts.write(outfile, ["a", "c", "b"], [])
+ with open(outpath) as outfile:
+ self.assertEqual(outfile.read(), "a: 1\nc: 3\nb: 2\n")
+
+ def test_write_invalid_order(self):
+ ts = apt_pkg.TagSection("a: 1\nb: 2\nc: 3\n")
+ outpath = os.path.join(self.temp_dir, "test")
+ with open(outpath, "w") as outfile:
+ self.assertRaises(TypeError, ts.write, outfile, ["a", 1, "b"], [])
+
+ def test_write_remove(self):
+ ts = apt_pkg.TagSection("a: 1\nb: 2\nc: 3\n")
+ outpath = os.path.join(self.temp_dir, "test")
+ with open(outpath, "w") as outfile:
+ ts.write(outfile, ["a", "c", "b"], [apt_pkg.TagRemove("a")])
+ with open(outpath) as outfile:
+ self.assertEqual(outfile.read(), "c: 3\nb: 2\n")
+
+ def test_write_rewrite(self):
+ ts = apt_pkg.TagSection("a: 1\nb: 2\nc: 3\n")
+ outpath = os.path.join(self.temp_dir, "test")
+ with open(outpath, "w") as outfile:
+ ts.write(outfile, ["a", "c", "b"], [apt_pkg.TagRewrite("a", "AA")])
+ with open(outpath) as outfile:
+ self.assertEqual(outfile.read(), "a: AA\nc: 3\nb: 2\n")
+
+ def test_write_rename(self):
+ ts = apt_pkg.TagSection("a: 1\nb: 2\nc: 3\n")
+ outpath = os.path.join(self.temp_dir, "test")
+ with open(outpath, "w") as outfile:
+ ts.write(outfile, ["a", "z", "b"], [apt_pkg.TagRename("c", "z")])
+ with open(outpath) as outfile:
+ self.assertEqual(outfile.read(), "a: 1\nz: 3\nb: 2\n")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/test_utils.py b/tests/test_utils.py
new file mode 100644
index 0000000..6f582bb
--- /dev/null
+++ b/tests/test_utils.py
@@ -0,0 +1,74 @@
+#!/usr/bin/python3
+#
+# Copyright (C) 2010 Michael Vogt <michael.vogt@ubuntu.com>
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+
+import datetime
+import os
+import unittest
+
+import testcommon
+
+from apt.utils import get_maintenance_end_date, get_release_date_from_release_file
+
+
+class TestUtils(testcommon.TestCase):
+ def test_get_release_date_from_release_file(self):
+ path = os.path.join(os.path.dirname(__file__), "data", "misc", "foo_Release")
+ t = get_release_date_from_release_file(path)
+ self.assertEqual(
+ str(datetime.datetime.fromtimestamp(t, datetime.UTC)),
+ "2012-04-25 22:49:23+00:00",
+ )
+
+ def test_maintenance_time(self):
+ months_of_support = 18
+ # test historic releases, jaunty
+ release_date = datetime.datetime(2009, 4, 23)
+ (end_year, end_month) = get_maintenance_end_date(
+ release_date, months_of_support
+ )
+ self.assertEqual(end_year, 2010)
+ self.assertEqual(end_month, 10)
+ # test historic releases, karmic
+ release_date = datetime.datetime(2009, 10, 29)
+ (end_year, end_month) = get_maintenance_end_date(
+ release_date, months_of_support
+ )
+ self.assertEqual(end_year, 2011)
+ self.assertEqual(end_month, 4)
+ # test maverick
+ release_date = datetime.datetime(2010, 10, 10)
+ (end_year, end_month) = get_maintenance_end_date(
+ release_date, months_of_support
+ )
+ self.assertEqual(end_year, 2012)
+ self.assertEqual(end_month, 4)
+
+ # test with modulo zero
+ release_date = datetime.datetime(2010, 6, 10)
+ (end_year, end_month) = get_maintenance_end_date(
+ release_date, months_of_support
+ )
+ self.assertEqual(end_year, 2011)
+ self.assertEqual(end_month, 12)
+
+ # test dapper
+ months_of_support = 60
+ release_date = datetime.datetime(2008, 4, 24)
+ (end_year, end_month) = get_maintenance_end_date(
+ release_date, months_of_support
+ )
+ self.assertEqual(end_year, 2013)
+ self.assertEqual(end_month, 4)
+
+ # what datetime says
+ # d = datetime.timedelta(18*30)
+ # print "end date: ", release_date + d
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tests/testcommon.py b/tests/testcommon.py
new file mode 100644
index 0000000..29917a7
--- /dev/null
+++ b/tests/testcommon.py
@@ -0,0 +1,30 @@
+"""Common testing stuff"""
+
+import os
+import unittest
+
+import apt_pkg
+
+
+class TestCase(unittest.TestCase):
+ """Base class for python-apt unittests"""
+
+ def setUp(self):
+ self.resetConfig()
+
+ def resetConfig(self):
+ apt_pkg.config.clear("")
+ for key in apt_pkg.config.list():
+ apt_pkg.config.clear(key)
+
+ # Avoid loading any host config files
+ os.unsetenv("APT_CONFIG")
+ apt_pkg.config["Dir::Etc::main"] = "/dev/null"
+ apt_pkg.config["Dir::Etc::parts"] = "/dev/null"
+
+ apt_pkg.init_config()
+ apt_pkg.init_system()
+
+ # Restore default values
+ apt_pkg.config["Dir::Etc::main"] = "apt.conf"
+ apt_pkg.config["Dir::Etc::parts"] = "apt.conf.d"