Merge lp:~bjornt/python-apt/package-broken-dependency-info into lp:~mvo/python-apt/debian-sid-mirrored

Proposed by Björn Tillenius
Status: Needs review
Proposed branch: lp:~bjornt/python-apt/package-broken-dependency-info
Merge into: lp:~mvo/python-apt/debian-sid-mirrored
Diff against target: 621 lines (+565/-0)
5 files modified
apt/package.py (+63/-0)
apt/testing.py (+167/-0)
tests/test_aptsources.py (+8/-0)
tests/test_aptsources_ports.py (+8/-0)
tests/test_package.py (+319/-0)
To merge this branch: bzr merge lp:~bjornt/python-apt/package-broken-dependency-info
Reviewer Review Type Date Requested Status
Michael Vogt Pending
Review via email: mp+93286@code.launchpad.net

Description of the change

Add a method to get information about broken dependency in a format like
apt-get and aptitude displays, e.g.:

    package-name: Depends: missing-name but is not installable

I made the method return a list of such strings so that callsites can
construct their own error message from it. I was considering returning
objects having the necessary information, providing even more freedom to
construct your own formatting, but didn't have time to do it.

I also added an AptTestHelper object to make it easier to test the
things I wanted to test. I brought over functionality I had added to
landscape-client already, so I put it in apt.testing.py rather than in
the tests directory so that it's possible to import it in other
projects.

I also had to save/restore the apt config in the aptsources tests, since
otherwise my added tests would fail when running test_all.py.

To post a comment you must log in.

Unmerged revisions

597. By Björn Tillenius

Save/restore apt config in tests, not to break other tests.

596. By Björn Tillenius

Document AptTestHelper.

595. By Björn Tillenius

Correct docstring.

594. By Björn Tillenius

Document tests.

593. By Björn Tillenius

Improve docstrings.

592. By Björn Tillenius

Rename method, add docstring.

591. By Björn Tillenius

Rename method.

590. By Björn Tillenius

Handle broken installed packages.

589. By Björn Tillenius

Don't include non-broken dependencies.

588. By Björn Tillenius

Add a test for multiple dependencies.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'apt/package.py'
--- apt/package.py 2012-01-30 14:46:34 +0000
+++ apt/package.py 2012-02-15 19:31:18 +0000
@@ -1319,6 +1319,69 @@
1319 """1319 """
1320 self._pcache._depcache.commit(fprogress, iprogress)1320 self._pcache._depcache.commit(fprogress, iprogress)
13211321
1322 def _is_dependency_broken(self, dependency, dep_type):
1323 """Return whether the dependency is broken.
1324
1325 The dependency parameter is a list of apt_pkg.Dependency objects
1326 of which one needs to be satisified to be considered not broken.
1327 """
1328 is_negative = dep_type in ["Breaks", "Conflicts"]
1329 depcache = self._pcache._depcache
1330 for or_dep in dependency:
1331 for target in or_dep.all_targets():
1332 package = target.parent_pkg
1333 if ((package.current_state == apt_pkg.CURSTATE_INSTALLED
1334 or depcache.marked_install(package))
1335 and not depcache.marked_delete(package)):
1336 return is_negative
1337 return not is_negative
1338
1339 def _get_broken_dependency_relation_info(self, dep_relation):
1340 """Return a string representation of a specific dependency relation.
1341
1342 The dep_relation parameter is an apt_pkg.Dependency object.
1343 """
1344 info = dep_relation.target_pkg.name
1345 if dep_relation.target_ver:
1346 info += " (%s %s)" % (
1347 dep_relation.comp_type, dep_relation.target_ver)
1348 reason = " but is not installable"
1349 if (dep_relation.target_pkg and
1350 dep_relation.target_pkg.name in self._pcache):
1351 dep_package = self._pcache[dep_relation.target_pkg.name]
1352 if dep_package.installed or dep_package.marked_install:
1353 version = dep_package.candidate.version
1354 if dep_package not in self._pcache.get_changes():
1355 version = dep_package.installed.version
1356 reason = " but %s is to be installed" % version
1357 info += reason
1358 return info
1359
1360 def get_broken_dependency_info(self):
1361 """Get information about the broken dependencies for this package
1362
1363 It returns a list of strings similar to the ones apt-get and
1364 aptitude display (e.g. "package-name: Depends: missing-package
1365 but is not installable") for each dependency that isn't satisfied.
1366
1367 Pre-Depends, Depends, Conflicts and Breaks dependencies are checked.
1368 """
1369 broken_info = []
1370 for dep_type in ["PreDepends", "Depends", "Conflicts", "Breaks"]:
1371 target = self.installed or self.candidate
1372 dependencies = target._cand.depends_list.get(dep_type, [])
1373 for dependency in dependencies:
1374 if not self._is_dependency_broken(dependency, dep_type):
1375 continue
1376 relation_infos = []
1377 for relation in dependency:
1378 relation_infos.append(
1379 self._get_broken_dependency_relation_info(relation))
1380 info = "%s: %s: " % (self.name, dep_type)
1381 or_divider = " or\n" + " " * len(info)
1382 broken_info.append(info + or_divider.join(relation_infos))
1383 return broken_info
1384
13221385
1323 if not apt_pkg._COMPAT_0_7:1386 if not apt_pkg._COMPAT_0_7:
1324 del installedVersion1387 del installedVersion
13251388
=== added file 'apt/testing.py'
--- apt/testing.py 1970-01-01 00:00:00 +0000
+++ apt/testing.py 2012-02-15 19:31:18 +0000
@@ -0,0 +1,167 @@
1# Copyright (C) 2012 Canonical Ltd.
2#
3# Authors:
4# Bjorn Tillenius <bjorn@canonical.com>
5#
6# This program is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License as
8# published by the Free Software Foundation; either version 2 of the
9# License, or (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful, but WITHOUT
12# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14# details.
15#
16# You should have received a copy of the GNU General Public License along with
17# this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19"""Classes and methods useful for testing."""
20
21import os
22import shutil
23import tempfile
24import textwrap
25
26import apt
27import apt_pkg
28
29
30class AptTestHelper(object):
31 """A helper object to test apt functionality.
32
33 An apt.Cache with a tempdir as the root is available in the cache
34 attribute after set_up() has been called. Call tear_down() to clean
35 up when testing is done.
36 """
37
38 def set_up(self):
39 """Set up the helper object.
40
41 An apt.Cache is created with a tempdir as the root.
42
43 The apt config is also stored so that it can be restored later.
44 """
45 # reset any config manipulations done in the individual tests
46 apt_pkg.init_config()
47 # save/restore the apt config
48 self._cnf = {}
49 for item in apt_pkg.config.keys():
50 self._cnf[item] = apt_pkg.config.find(item)
51 self.apt_root = tempfile.mkdtemp()
52 self._ensure_dir_structure()
53 self.cache = apt.Cache(rootdir=self.apt_root)
54 self.repo = None
55
56 def tear_down(self):
57 """Clean up what the helper object created.
58
59 All created tempdirs are removed and the apt config is restored.
60 """
61 shutil.rmtree(self.apt_root)
62 if self.repo is not None:
63 shutil.rmtree(self.repo)
64 for item in self._cnf:
65 apt_pkg.config.set(item, self._cnf[item])
66
67 def _ensure_dir_structure(self):
68 """Ensure that all directores and files apt.Cache needs are there."""
69 self._ensure_sub_dir("etc/apt")
70 self._ensure_sub_dir("etc/apt/sources.list.d")
71 self._ensure_sub_dir("var/cache/apt/archives/partial")
72 self._ensure_sub_dir("var/lib/apt/lists/partial")
73 dpkg_dir = self._ensure_sub_dir("var/lib/dpkg")
74 self._ensure_sub_dir("var/lib/dpkg/info")
75 self._ensure_sub_dir("var/lib/dpkg/updates")
76 self._ensure_sub_dir("var/lib/dpkg/triggers")
77 self.dpkg_status = os.path.join(dpkg_dir, "status")
78 self._ensure_file(os.path.join(dpkg_dir, "available"))
79 self._ensure_file(self.dpkg_status)
80
81 def _ensure_sub_dir(self, sub_dir):
82 """Ensure that a dir in the Apt root exists."""
83 full_path = os.path.join(self.apt_root, sub_dir)
84 if not os.path.exists(full_path):
85 os.makedirs(full_path)
86 return full_path
87
88 def _ensure_file(self, file_path):
89 """Ensure that a file in the Apt root exists."""
90 if os.path.exists(file_path):
91 return
92 with open(file_path, "w") as empty_file:
93 empty_file.write("")
94
95 def _ensure_repo(self):
96 """Ensure that the internal deb-dir repository exists."""
97 if self.repo is not None:
98 return
99 self.repo = tempfile.mkdtemp()
100 sources_dir = apt_pkg.config.find_dir("Dir::Etc::sourceparts")
101 self.sources_file_path = os.path.join(
102 sources_dir, "apt-test-helper.list")
103 sources_line = "deb file://%s ./" % self.repo
104 with open(self.sources_file_path, "w") as sources_file:
105 sources_file.write(sources_line + "\n")
106
107
108 def add_package(self, packages_filepath, name, architecture="all",
109 version="1.0", control_fields=None):
110 """Add package information to a Packages file.
111
112 A package stanza is added with all required values needed for
113 Apt to parse it. Individual fields can be overrided by passing a
114 dict as the control_fields parameter. Only package information
115 will be added to the Packages file, there won't be an actual
116 package in the dir.
117 """
118 if control_fields is None:
119 control_fields = {}
120 package_stanza = textwrap.dedent("""
121 Package: %(name)s
122 Priority: optional
123 Section: misc
124 Installed-Size: 1234
125 Maintainer: Someone
126 Architecture: %(architecture)s
127 Source: source
128 Version: %(version)s
129 Description: description
130 """ % {"name": name, "version": version,
131 "architecture": architecture})
132 package_stanza = apt_pkg.rewrite_section(
133 apt_pkg.TagSection(package_stanza), apt_pkg.REWRITE_PACKAGE_ORDER,
134 control_fields.items())
135 with open(packages_filepath, "a") as packages_file:
136 packages_file.write("\n" + package_stanza + "\n")
137
138 def add_installed_package(self, name, architecture="all", version="1.0",
139 control_fields=None):
140 """Add a package to the dpkg status file.
141
142 See add_package() for more information.
143 """
144 system_control_fields = {"Status": "install ok installed"}
145 if control_fields is not None:
146 system_control_fields.update(control_fields)
147 self.add_package(
148 self.dpkg_status, name, architecture=architecture, version=version,
149 control_fields=system_control_fields)
150
151 def add_available_package(self, name, architecture="all",
152 version="1.0", control_fields=None):
153 """Add a package to the internal deb-dir repo.
154
155 See add_package() for more information.
156 """
157 self._ensure_repo()
158 self.add_package(
159 os.path.join(self.repo, "Packages"), name,
160 architecture=architecture,
161 version=version, control_fields=control_fields)
162
163 def update(self):
164 """Update the cache and make it ready to use."""
165 self.cache.open(None)
166 self.cache.update()
167 self.cache.open(None)
0168
=== modified file 'tests/test_aptsources.py'
--- tests/test_aptsources.py 2011-10-19 15:40:03 +0000
+++ tests/test_aptsources.py 2012-02-15 19:31:18 +0000
@@ -14,6 +14,10 @@
1414
15 def setUp(self):15 def setUp(self):
16 apt_pkg.init_config()16 apt_pkg.init_config()
17 # save/restore the apt config
18 self._cnf = {}
19 for item in apt_pkg.config.keys():
20 self._cnf[item] = apt_pkg.config.find(item)
17 apt_pkg.init_system()21 apt_pkg.init_system()
18 if apt_pkg.config["APT::Architecture"] not in ('i386', 'amd64'):22 if apt_pkg.config["APT::Architecture"] not in ('i386', 'amd64'):
19 apt_pkg.config.set("APT::Architecture", "i386")23 apt_pkg.config.set("APT::Architecture", "i386")
@@ -26,6 +30,10 @@
26 else:30 else:
27 self.templates = "/usr/share/python-apt/templates/"31 self.templates = "/usr/share/python-apt/templates/"
2832
33 def tearDown(self):
34 for item in self._cnf:
35 apt_pkg.config.set(item, self._cnf[item])
36
29 def testIsMirror(self):37 def testIsMirror(self):
30 """aptsources: Test mirror detection."""38 """aptsources: Test mirror detection."""
31 yes = aptsources.sourceslist.is_mirror("http://archive.ubuntu.com",39 yes = aptsources.sourceslist.is_mirror("http://archive.ubuntu.com",
3240
=== modified file 'tests/test_aptsources_ports.py'
--- tests/test_aptsources_ports.py 2011-07-29 17:17:06 +0000
+++ tests/test_aptsources_ports.py 2012-02-15 19:31:18 +0000
@@ -13,6 +13,10 @@
1313
14 def setUp(self):14 def setUp(self):
15 apt_pkg.init_config()15 apt_pkg.init_config()
16 # save/restore the apt config
17 self._cnf = {}
18 for item in apt_pkg.config.keys():
19 self._cnf[item] = apt_pkg.config.find(item)
16 apt_pkg.init_system()20 apt_pkg.init_system()
17 apt_pkg.config.set("APT::Architecture", "powerpc")21 apt_pkg.config.set("APT::Architecture", "powerpc")
18 apt_pkg.config.set("Dir::Etc",22 apt_pkg.config.set("Dir::Etc",
@@ -23,6 +27,10 @@
23 else:27 else:
24 self.templates = "/usr/share/python-apt/templates/"28 self.templates = "/usr/share/python-apt/templates/"
2529
30 def tearDown(self):
31 for item in self._cnf:
32 apt_pkg.config.set(item, self._cnf[item])
33
26 def testMatcher(self):34 def testMatcher(self):
27 """aptsources_ports: Test matcher."""35 """aptsources_ports: Test matcher."""
28 apt_pkg.config.set("Dir::Etc::sourcelist", "sources.list")36 apt_pkg.config.set("Dir::Etc::sourcelist", "sources.list")
2937
=== added file 'tests/test_package.py'
--- tests/test_package.py 1970-01-01 00:00:00 +0000
+++ tests/test_package.py 2012-02-15 19:31:18 +0000
@@ -0,0 +1,319 @@
1#!/usr/bin/python
2#
3# Copyright (C) 2012 Canonical Ltd.
4#
5# Copying and distribution of this file, with or without modification,
6# are permitted in any medium without royalty provided the copyright
7# notice and this notice are preserved.
8"""Unit tests for the apt.package.Package class."""
9
10import unittest
11
12from test_all import get_library_dir
13import sys
14sys.path.insert(0, get_library_dir())
15
16from apt.testing import AptTestHelper
17
18
19class TestPackageBrokenDependenyInfo(unittest.TestCase):
20 """Tests for getting info about broken dependencies for a package."""
21
22 def setUp(self):
23 self.helper = AptTestHelper()
24 self.helper.set_up()
25 self.cache = self.helper.cache
26
27 def tearDown(self):
28 self.helper.tear_down()
29
30 def test_not_broken(self):
31 """An empty list is returned for packages that aren't broken."""
32 self.helper.add_installed_package("not-broken")
33 self.helper.update()
34 not_broken = self.cache["not-broken"]
35 self.assertFalse(not_broken.is_inst_broken)
36 self.assertEqual([], not_broken.get_broken_dependency_info())
37
38 def test_broken_depends(self):
39 """Broken Depends relations are reported."""
40 self.helper.add_installed_package(
41 "broken-depends", control_fields={"Depends": "missing"})
42 self.helper.update()
43 broken_depends = self.cache["broken-depends"]
44 self.assertTrue(broken_depends.is_inst_broken)
45 self.assertEqual(
46 ["broken-depends: Depends: missing but is not installable"],
47 broken_depends.get_broken_dependency_info())
48
49 def test_broken_predepends(self):
50 """Broken Pre-Depends relations are reported."""
51 self.helper.add_installed_package(
52 "broken-predepends", control_fields={"Pre-Depends": "missing"})
53 self.helper.update()
54 broken_predepends = self.cache["broken-predepends"]
55 self.assertTrue(broken_predepends.is_inst_broken)
56 self.assertEqual(
57 ["broken-predepends: PreDepends: missing but is not installable"],
58 broken_predepends.get_broken_dependency_info())
59
60 def test_broken_conflicts(self):
61 """Broken Conflicts relations are reported."""
62 self.helper.add_installed_package("conflict")
63 self.helper.add_available_package(
64 "broken-conflicts", control_fields={"Conflicts": "conflict"})
65 self.helper.update()
66 broken_conflicts = self.cache["broken-conflicts"]
67 conflict = self.cache["conflict"]
68 broken_conflicts.mark_install(auto_fix=False)
69 conflict.mark_keep()
70 self.assertTrue(broken_conflicts.is_inst_broken)
71 self.assertEqual(
72 ["broken-conflicts: Conflicts: conflict but 1.0 is to be installed"],
73 broken_conflicts.get_broken_dependency_info())
74
75 def test_broken_breaks(self):
76 """Broken Breaks relations are reported."""
77 self.helper.add_installed_package("breakage")
78 self.helper.add_available_package(
79 "broken-breaks", control_fields={"Breaks": "breakage"})
80 self.helper.update()
81 broken_breaks = self.cache["broken-breaks"]
82 breakage = self.cache["breakage"]
83 broken_breaks.mark_install(auto_fix=False)
84 breakage.mark_keep()
85 self.assertTrue(broken_breaks.is_inst_broken)
86 self.assertEqual(
87 ["broken-breaks: Breaks: breakage but 1.0 is to be installed"],
88 broken_breaks.get_broken_dependency_info())
89
90 def test_broken_with_version(self):
91 """
92 If a broken dependency includes a version relation, it's
93 included in the error information about the broken dependencies.
94 """
95 self.helper.add_installed_package(
96 "broken-ver", control_fields={"Depends": "missing (>= 1.0)"})
97 self.helper.update()
98 broken_ver = self.cache["broken-ver"]
99 self.assertTrue(broken_ver.is_inst_broken)
100 self.assertEqual(
101 ["broken-ver: Depends: missing (>= 1.0) but is not installable"],
102 broken_ver.get_broken_dependency_info())
103
104 def test_broken_marked_install(self):
105 """
106 If a broken package is being installed the dependencies of the
107 candidate version is used when looking for broken dependencies.
108 """
109 self.helper.add_available_package(
110 "broken", version="1.0", control_fields={"Depends": "missing1"})
111 self.helper.add_available_package(
112 "broken", version="2.0", control_fields={"Depends": "missing2"})
113 self.helper.add_available_package(
114 "broken", version="3.0", control_fields={"Depends": "missing3"})
115 self.helper.update()
116 broken = self.cache["broken"]
117 broken.candidate = sorted(broken.versions)[1]
118 broken.mark_install(auto_fix=False)
119 self.assertTrue(broken.is_inst_broken)
120 self.assertEqual(
121 ["broken: Depends: missing2 but is not installable"],
122 broken.get_broken_dependency_info())
123
124 def test_broken_installed(self):
125 """
126 If a broken package is installed the dependencies of the
127 installed version is used when looking for broken dependencies.
128 """
129 self.helper.add_installed_package(
130 "broken", version="1.0", control_fields={"Depends": "missing1"})
131 self.helper.add_available_package(
132 "broken", version="2.0", control_fields={"Depends": "missing2"})
133 self.helper.update()
134 broken = self.cache["broken"]
135 self.assertTrue(broken.is_inst_broken)
136 self.assertEqual(
137 ["broken: Depends: missing1 but is not installable"],
138 broken.get_broken_dependency_info())
139
140 def test_broken_with_dep_marked_install(self):
141 """
142 If a broken dependency is being installed (but still doesn't
143 meet the version requirements), the version being installed is
144 included in the error information about the broken dependency.
145 """
146 self.helper.add_installed_package(
147 "broken", control_fields={"Depends": "missing (>= 2.0)"})
148 self.helper.add_available_package("missing", version="1.0")
149 self.helper.update()
150 broken = self.cache["broken"]
151 missing = self.cache["missing"]
152 missing.mark_install(auto_fix=False)
153 self.assertTrue(broken.is_inst_broken)
154 self.assertEqual(
155 ["broken: Depends: missing (>= 2.0) but 1.0 is to be installed"],
156 broken.get_broken_dependency_info())
157
158 def test_broken_with_dep_installed(self):
159 """
160 If an unmet dependency is already installed (but still doesn't
161 meet the version requirements), the version that is installed is
162 included in the error information about the broken dependency.
163 """
164 self.helper.add_available_package(
165 "broken", control_fields={"Depends": "missing (>= 2.0)"})
166 self.helper.add_installed_package("missing", version="1.0")
167 self.helper.update()
168 broken = self.cache["broken"]
169 broken.mark_install(auto_fix=False)
170 self.assertTrue(broken.is_inst_broken)
171 self.assertEqual(
172 ["broken: Depends: missing (>= 2.0) but 1.0 is to be installed"],
173 broken.get_broken_dependency_info())
174
175 def test_broken_with_dep_upgraded(self):
176 """
177 If a broken dependency is being upgraded (but still doesn't meet
178 the version requirements), the version that it is upgraded to is
179 included in the error information about the broken dependeny.
180 """
181 self.helper.add_available_package(
182 "broken", control_fields={"Depends": "missing (>= 3.0)"})
183 self.helper.add_installed_package("missing", version="1.0")
184 self.helper.add_available_package("missing", version="2.0")
185 self.helper.update()
186 broken = self.cache["broken"]
187 broken.mark_install(auto_fix=False)
188 missing = self.cache["missing"]
189 missing.mark_install(auto_fix=False)
190 self.assertTrue(broken.is_inst_broken)
191 self.assertEqual(
192 ["broken: Depends: missing (>= 3.0) but 2.0 is to be installed"],
193 broken.get_broken_dependency_info())
194
195 def test_broken_with_dep_downgraded(self):
196 """
197 If a broken dependency is being downgraded (but still doesn't meet
198 the version requirements), the version that it is downgraded to is
199 included in the error information about the broken dependency.
200 """
201 self.helper.add_installed_package(
202 "broken", control_fields={"Depends": "missing (>= 3.0)"})
203 self.helper.add_installed_package("missing", version="2.0")
204 self.helper.add_available_package("missing", version="1.0")
205 self.helper.update()
206 broken = self.cache["broken"]
207 missing = self.cache["missing"]
208 missing.candidate = sorted(missing.versions)[0]
209 missing.mark_install(auto_fix=False)
210 self.assertTrue(broken.is_inst_broken)
211 self.assertEqual(
212 ["broken: Depends: missing (>= 3.0) but 1.0 is to be installed"],
213 broken.get_broken_dependency_info())
214
215 def test_broken_with_or_deps(self):
216 """
217 If a broken dependency includes an or relation, all of the
218 possible options are included in the error information about the
219 broken dependency.
220 """
221 self.helper.add_installed_package(
222 "broken",
223 control_fields={"Depends": "missing1 | missing2 (>= 1.0)"})
224 self.helper.update()
225 broken = self.cache["broken"]
226 self.assertTrue(broken.is_inst_broken)
227 self.assertEqual(
228 ["broken: Depends: missing1 but is not installable or\n" +
229 " missing2 (>= 1.0) but is not installable"],
230 broken.get_broken_dependency_info())
231
232 def test_broken_with_multiple(self):
233 """
234 If a package has multiple broken dependencies, all of the
235 broken ones are included in the error information about the
236 broken dependency.
237 """
238 self.helper.add_installed_package(
239 "broken",
240 control_fields={"Depends": "missing1, missing2"})
241 self.helper.update()
242 broken = self.cache["broken"]
243 self.assertTrue(broken.is_inst_broken)
244 self.assertEqual(
245 ["broken: Depends: missing1 but is not installable",
246 "broken: Depends: missing2 but is not installable"],
247 broken.get_broken_dependency_info())
248
249 def test_broken_with_conflicts_not_installed(self):
250 """
251 If a broken package conflicts or breaks a package that isn't
252 installed or marked for installation, information about that
253 conflict isn't included in the error information about broken
254 dependency.
255 """
256 self.helper.add_installed_package("needed")
257 self.helper.add_available_package(
258 "broken",
259 control_fields={"Conflicts": "needed, not-installed",
260 "Breaks": "needed, not-installed"})
261 self.helper.add_available_package("not-installed")
262 self.helper.update()
263 broken = self.cache["broken"]
264 broken.mark_install(auto_fix=False)
265 needed = self.cache["needed"]
266 needed.mark_keep()
267 self.assertTrue(broken.is_inst_broken)
268 self.assertEqual(
269 ["broken: Conflicts: needed but 1.0 is to be installed",
270 "broken: Breaks: needed but 1.0 is to be installed"],
271 broken.get_broken_dependency_info())
272
273 def test_get_unmet_dependency_info_with_conflicts_marked_delete(self):
274 """
275 If a broken package conflicts or breaks an installed package
276 that is marekd for removal, information about that conflict
277 isn't included in the error information about broken dependency.
278 """
279 self.helper.add_installed_package("needed")
280 self.helper.add_available_package(
281 "broken",
282 control_fields={"Conflicts": "needed, not-installed",
283 "Breaks": "needed, not-installed"})
284 self.helper.add_installed_package("removed")
285 self.helper.update()
286 removed = self.cache["removed"]
287 removed.mark_delete(auto_fix=False)
288 broken = self.cache["broken"]
289 broken.mark_install(auto_fix=False)
290 needed = self.cache["needed"]
291 needed.mark_keep()
292 self.assertTrue(broken.is_inst_broken)
293 self.assertEqual(
294 ["broken: Conflicts: needed but 1.0 is to be installed",
295 "broken: Breaks: needed but 1.0 is to be installed"],
296 broken.get_broken_dependency_info())
297
298 def test_get_unmet_dependency_info_only_unmet(self):
299 """
300 If a broken package has some dependencies that are being
301 fulfilled, those aren't included in the error information from
302 C{_get_unmet_dependency_info}.
303 """
304 self.helper.add_installed_package("there1")
305 self.helper.add_installed_package("there2")
306 self.helper.add_available_package(
307 "broken",
308 control_fields={"Depends": "there1, missing1, there2 | missing2"})
309 self.helper.update()
310 broken = self.cache["broken"]
311 broken.mark_install(auto_fix=False)
312 self.assertTrue(broken.is_inst_broken)
313 self.assertEqual(
314 ["broken: Depends: missing1 but is not installable"],
315 broken.get_broken_dependency_info())
316
317
318if __name__ == "__main__":
319 unittest.main()

Subscribers

People subscribed via source and target branches

to all changes: