summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--CMakeLists.txt2
-rw-r--r--apt-pkg/solver3.cc117
-rw-r--r--apt-pkg/solver3.h4
-rw-r--r--doc/apt-verbatim.ent2
-rw-r--r--doc/po/apt-doc.pot4
-rw-r--r--po/apt-all.pot4
-rwxr-xr-xtest/integration/test-apt-get-mark-auto30
-rwxr-xr-xtest/integration/test-bug-549968-install-depends-of-not-installed2
-rwxr-xr-xtest/integration/test-bug-960705-propagate-protected-to-satisfied-depends2
-rwxr-xr-xtest/integration/test-dpkg-i-apt-install-fix-broken6
-rwxr-xr-xtest/integration/test-solver3-dependencies65
11 files changed, 169 insertions, 69 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a457110..1998ca8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -206,7 +206,7 @@ endif()
# Configure some variables like package, version and architecture.
set(PACKAGE ${PROJECT_NAME})
set(PACKAGE_MAIL "APT Development Team <deity@lists.debian.org>")
-set(PACKAGE_VERSION "2.9.5")
+set(PACKAGE_VERSION "2.9.6")
string(REGEX MATCH "^[0-9.]+" PROJECT_VERSION ${PACKAGE_VERSION})
if (NOT DEFINED DPKG_DATADIR)
diff --git a/apt-pkg/solver3.cc b/apt-pkg/solver3.cc
index dc70adb..67aa68d 100644
--- a/apt-pkg/solver3.cc
+++ b/apt-pkg/solver3.cc
@@ -64,8 +64,9 @@ struct APT::Solver::CompareProviders3 /*{{{*/
// Try obsolete choices only after exhausting non-obsolete choices such that we install
// packages replacing them and don't keep back upgrades depending on the replacement to
// keep the obsolete package installed.
- if (auto obsoleteAV = Solver.Obsolete(AV), obsoleteBV = Solver.Obsolete(BV); obsoleteAV != obsoleteBV)
- return obsoleteBV;
+ if (upgrade)
+ if (auto obsoleteA = Solver.Obsolete(A), obsoleteB = Solver.Obsolete(B); obsoleteA != obsoleteB)
+ return obsoleteB;
// Prefer MA:same packages if other architectures for it are installed
if ((AV->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same ||
(BV->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
@@ -154,8 +155,8 @@ class DefaultRootSetFunc2 : public pkgDepCache::DefaultRootSetFunc
std::unique_ptr<APT::CacheFilter::Matcher> Kernels;
public:
- DefaultRootSetFunc2(pkgCache *cache) : Kernels(APT::KernelAutoRemoveHelper::GetProtectedKernelsFilter(cache)){};
- virtual ~DefaultRootSetFunc2(){};
+ DefaultRootSetFunc2(pkgCache *cache) : Kernels(APT::KernelAutoRemoveHelper::GetProtectedKernelsFilter(cache)) {};
+ virtual ~DefaultRootSetFunc2() {};
bool InRootSet(const pkgCache::PkgIterator &pkg) APT_OVERRIDE { return pkg.end() == false && ((*Kernels)(pkg) || DefaultRootSetFunc::InRootSet(pkg)); };
}; // FIXME: DEDUP with pkgDepCache.
@@ -166,7 +167,7 @@ APT::Solver::Solver(pkgCache &cache, pkgDepCache::Policy &policy)
policy(policy),
pkgStates(cache.Head().PackageCount),
verStates(cache.Head().VersionCount),
- verObsolete(cache.Head().VersionCount)
+ pkgObsolete(cache.Head().PackageCount)
{
static_assert(sizeof(APT::Solver::State<pkgCache::PkgIterator>) == 3 * sizeof(int));
static_assert(sizeof(APT::Solver::State<pkgCache::VerIterator>) == 3 * sizeof(int));
@@ -246,23 +247,36 @@ std::string APT::Solver::WhyStr(Reason reason)
return outstr;
}
-bool APT::Solver::Obsolete(pkgCache::VerIterator ver)
+bool APT::Solver::Obsolete(pkgCache::PkgIterator pkg)
{
- if (verObsolete[ver->ID] != 0)
- return verObsolete[ver->ID] == 2;
+ auto ver = policy.GetCandidateVer(pkg);
+
+ if (ver.end() && not StrictPinning)
+ ver = pkg.VersionList();
+ if (ver.end())
+ {
+ std::cerr << "Obsolete: " << pkg.FullName() << " - not installable\n";
+ return true;
+ }
+ if (pkgObsolete[pkg->ID] != 0)
+ return pkgObsolete[pkg->ID] == 2;
for (auto bin = ver.Cache()->FindGrp(ver.SourcePkgName()).VersionsInSource(); not bin.end(); bin = bin.NextInSource())
- if (bin != ver && bin.ParentPkg()->Arch == ver.ParentPkg()->Arch && bin->ParentPkg != ver->ParentPkg && policy.GetCandidateVer(bin.ParentPkg()) == bin && _system->VS->CmpVersion(bin.SourceVerStr(), ver.SourceVerStr()) > 0)
+ if (bin != ver && bin.ParentPkg()->Arch == ver.ParentPkg()->Arch && bin->ParentPkg != ver->ParentPkg && (not StrictPinning || policy.GetCandidateVer(bin.ParentPkg()) == bin) && _system->VS->CmpVersion(bin.SourceVerStr(), ver.SourceVerStr()) > 0)
{
- verObsolete[ver->ID] = 2;
+ pkgObsolete[pkg->ID] = 2;
+ if (debug >= 3)
+ std::cerr << "Obsolete: " << ver.ParentPkg().FullName() << "=" << ver.VerStr() << " due to " << bin.ParentPkg().FullName() << "=" << bin.VerStr() << "\n";
return true;
}
for (auto file = ver.FileList(); !file.end(); file++)
if ((file.File()->Flags & pkgCache::Flag::NotSource) == 0)
{
- verObsolete[ver->ID] = 1;
+ pkgObsolete[pkg->ID] = 1;
return false;
}
- verObsolete[ver->ID] = 2;
+ if (debug >= 3)
+ std::cerr << "Obsolete: " << ver.ParentPkg().FullName() << "=" << ver.VerStr() << " - not installable\n";
+ pkgObsolete[pkg->ID] = 2;
return true;
}
@@ -292,7 +306,7 @@ bool APT::Solver::Install(pkgCache::PkgIterator Pkg, Reason reason, Group group)
// Note decision
if (unlikely(debug >= 1))
std::cerr << "[" << depth() << "] Install:" << Pkg.FullName() << " (" << WhyStr(reason) << ")\n";
- (*this)[Pkg] = {reason, depth(), Decision::MUST,};
+ (*this)[Pkg] = {reason, depth(), Decision::MUST};
// Insert the work item.
Work workItem{Reason(Pkg), depth(), group};
@@ -342,9 +356,9 @@ bool APT::Solver::Install(pkgCache::VerIterator Ver, Reason reason, Group group)
// Note decision
if (unlikely(debug >= 1))
std::cerr << "[" << depth() << "] Install:" << Ver.ParentPkg().FullName() << "=" << Ver.VerStr() << " (" << WhyStr(reason) << ")\n";
- (*this)[Ver] = {reason, depth(), Decision::MUST,};
+ (*this)[Ver] = {reason, depth(), Decision::MUST};
if ((*this)[Ver.ParentPkg()].decision != Decision::MUST)
- (*this)[Ver.ParentPkg()] = {Reason(Ver), depth(), Decision::MUST,};
+ (*this)[Ver.ParentPkg()] = {Reason(Ver), depth(), Decision::MUST};
for (auto OV = Ver.ParentPkg().VersionList(); not OV.end(); ++OV)
{
@@ -381,7 +395,7 @@ bool APT::Solver::Reject(pkgCache::PkgIterator Pkg, Reason reason, Group group)
// Reject the package and its versions.
if (unlikely(debug >= 1))
std::cerr << "[" << depth() << "] Reject:" << Pkg.FullName() << " (" << WhyStr(reason) << ")\n";
- (*this)[Pkg] = {reason, depth(), Decision::MUSTNOT,};
+ (*this)[Pkg] = {reason, depth(), Decision::MUSTNOT};
for (auto ver = Pkg.VersionList(); not ver.end(); ver++)
if (not Reject(ver, Reason(Pkg), group))
return false;
@@ -394,7 +408,7 @@ bool APT::Solver::Reject(pkgCache::PkgIterator Pkg, Reason reason, Group group)
// \brief Do not install this version
bool APT::Solver::Reject(pkgCache::VerIterator Ver, Reason reason, Group group)
{
- (void) group;
+ (void)group;
if ((*this)[Ver].decision == Decision::MUSTNOT)
return true;
@@ -409,7 +423,7 @@ bool APT::Solver::Reject(pkgCache::VerIterator Ver, Reason reason, Group group)
// Mark the package as rejected and propagate up as needed.
if (unlikely(debug >= 1))
std::cerr << "[" << depth() << "] Reject:" << Ver.ParentPkg().FullName() << "=" << Ver.VerStr() << " (" << WhyStr(reason) << ")\n";
- (*this)[Ver] = {reason, depth(), Decision::MUSTNOT,};
+ (*this)[Ver] = {reason, depth(), Decision::MUSTNOT};
if (auto pkg = Ver.ParentPkg(); (*this)[pkg].decision != Decision::MUSTNOT)
{
bool anyInstallable = false;
@@ -480,7 +494,9 @@ bool APT::Solver::EnqueueOrGroup(pkgCache::DepIterator start, pkgCache::DepItera
// below once we have calculated all possible solutions.
if (start.ParentPkg()->CurrentVer == 0 && not policy.IsImportantDep(start))
return true;
-
+ // Replaces and Enhances are not a real dependency.
+ if (start->Type == pkgCache::Dep::Replaces || start->Type == pkgCache::Dep::Enhances)
+ return true;
if (unlikely(debug >= 3))
std::cerr << "Found dependency critical " << Ver.ParentPkg().FullName() << "=" << Ver.VerStr() << " -> " << start.TargetPkg().FullName() << "\n";
@@ -531,7 +547,7 @@ bool APT::Solver::EnqueueOrGroup(pkgCache::DepIterator start, pkgCache::DepItera
{ return pkgCache::VerIterator(cache, V).ParentPkg()->CurrentVer == 0; }))
workItem.group = Group::SatisfyNew;
if (std::any_of(workItem.solutions.begin(), workItem.solutions.end(), [this](auto V) -> auto
- { return Obsolete(pkgCache::VerIterator(cache, V)); }))
+ { return Obsolete(pkgCache::VerIterator(cache, V).ParentPkg()); }))
workItem.group = Group::SatisfyObsolete;
// Try to perserve satisfied Recommends. FIXME: We should check if the Recommends was there in the installed version?
if (workItem.optional && start.ParentPkg()->CurrentVer)
@@ -904,10 +920,7 @@ bool APT::Solver::Solve()
// \brief Apply the selections from the dep cache to the solver
bool APT::Solver::FromDepCache(pkgDepCache &depcache)
{
- bool KeepAuto = not _config->FindB("APT::Get::AutomaticRemove");
- bool AllowRemove = _config->FindB("APT::Solver::Remove", true);
- bool AllowInstall = _config->FindB("APT::Solver::Install", true);
- bool AllowRemoveManual = _config->FindB("APT::Solver::RemoveManual", false);
+ bool AllowRemoveManual = AllowRemove && _config->FindB("APT::Solver::RemoveManual", false);
DefaultRootSetFunc2 rootSet(&cache);
for (auto P = cache.PkgBegin(); not P.end(); P++)
@@ -916,10 +929,6 @@ bool APT::Solver::FromDepCache(pkgDepCache &depcache)
continue;
auto state = depcache[P];
- auto maybeInstall = state.Install() || (state.Keep() && P->CurrentVer);
- auto reject = state.Delete() || (depcache[P].Keep() && not P->CurrentVer && depcache[P].Protect());
- auto isAuto = (depcache[P].Flags & pkgCache::Flag::Auto);
- auto isOptional = isAuto || (AllowRemoveManual && not depcache[P].Protect());
if (P->SelectedState == pkgCache::State::Hold && not state.Protect())
{
if (unlikely(debug >= 1))
@@ -927,45 +936,46 @@ bool APT::Solver::FromDepCache(pkgDepCache &depcache)
if (P->CurrentVer ? not Install(P.CurrentVer(), {}, Group::HoldOrDelete) : not Reject(P, {}, Group::HoldOrDelete))
return false;
}
- else if (reject)
+ else if (state.Delete() // Normal delete request.
+ || (not P->CurrentVer && state.Keep() && state.Protect()) // Delete request of not installed package.
+ || (not P->CurrentVer && state.Keep() && not AllowInstall) // New package installs not allowed.
+ )
{
if (unlikely(debug >= 1))
std::cerr << "Delete " << P.FullName() << "\n";
if (!Reject(P, {}, Group::HoldOrDelete))
return false;
}
- else if (maybeInstall && P->Flags & (pkgCache::Flag::Essential | pkgCache::Flag::Important))
- {
- if (unlikely(debug >= 1))
- std::cerr << "ESSENTIAL " << P.FullName() << "\n";
- if (depcache[P].Keep() ? not Install(P, {}, Group::InstallManual) : not Install(depcache.GetCandidateVersion(P), {}, Group::InstallManual))
- return false;
- }
- else if (maybeInstall && not isOptional)
- {
- auto Upgrade = depcache.GetCandidateVersion(P) != P.CurrentVer();
- auto Group = (Upgrade ? Group::UpgradeManual : Group::InstallManual);
- if (unlikely(debug >= 1))
- std::cerr << "MANUAL " << P.FullName() << "\n";
- if (depcache[P].Keep() ? not Install(P, {}, Group) : not Install(depcache.GetCandidateVersion(P), {}, Group))
- return false;
- }
- else if (maybeInstall && isOptional && (KeepAuto || rootSet.InRootSet(P) || not isAuto))
+ else if (state.Install() || (state.Keep() && P->CurrentVer))
{
+ auto isEssential = P->Flags & (pkgCache::Flag::Essential | pkgCache::Flag::Important);
+ auto isAuto = (depcache[P].Flags & pkgCache::Flag::Auto);
+ auto isOptional = ((isAuto && AllowRemove) || AllowRemoveManual) && not isEssential && not depcache[P].Protect();
+ auto Root = rootSet.InRootSet(P);
auto Upgrade = depcache.GetCandidateVersion(P) != P.CurrentVer();
auto Group = isAuto ? (Upgrade ? Group::UpgradeAuto : Group::KeepAuto)
: (Upgrade ? Group::UpgradeManual : Group::InstallManual);
+
+ if (isAuto && not depcache[P].Protect() && not isEssential && not KeepAuto && not rootSet.InRootSet(P))
+ {
+ if (unlikely(debug >= 1))
+ std::cerr << "Ignore automatic install " << P.FullName() << " (" << (isEssential ? "E" : "") << (isAuto ? "M" : "") << (Root ? "R" : "") << ")"
+ << "\n";
+ continue;
+ }
if (unlikely(debug >= 1))
- std::cerr << "AUTOMATIC " << P.FullName() << (Upgrade ? " - upgrade" : "") << "\n";
+ std::cerr << "Install " << P.FullName() << " (" << (isEssential ? "E" : "") << (isAuto ? "M" : "") << (Root ? "R" : "") << ")"
+ << "\n";
- if (not AllowRemove)
+ if (not isOptional)
{
+ // Pre-empt the non-optional requests, as we don't want to queue them, we can just "unit propagate" here.
if (depcache[P].Keep() ? not Install(P, {}, Group) : not Install(depcache.GetCandidateVersion(P), {}, Group))
return false;
}
else
{
- Work w{Reason(), depth(), Group, true, Upgrade};
+ Work w{Reason(), depth(), Group, isOptional, Upgrade};
for (auto V = P.VersionList(); not V.end(); ++V)
if (IsAllowedVersion(V))
w.solutions.push_back(V);
@@ -973,13 +983,6 @@ bool APT::Solver::FromDepCache(pkgDepCache &depcache)
AddWork(std::move(w));
}
}
- else if (P->CurrentVer == 0 && not AllowInstall)
- {
- if (unlikely(debug >= 1))
- std::cerr << "NOT ALLOWING INSTALL OF " << P.FullName() << "\n";
- if (not Reject(P, {}, Group::HoldOrDelete))
- return false;
- }
}
return true;
@@ -990,6 +993,8 @@ bool APT::Solver::ToDepCache(pkgDepCache &depcache)
pkgDepCache::ActionGroup group(depcache);
for (auto P = cache.PkgBegin(); not P.end(); P++)
{
+ depcache[P].Marked = 0;
+ depcache[P].Garbage = 0;
if ((*this)[P].decision == Decision::MUST)
{
for (auto V = P.VersionList(); not V.end(); V++)
diff --git a/apt-pkg/solver3.h b/apt-pkg/solver3.h
index 9a9d67a..d460c95 100644
--- a/apt-pkg/solver3.h
+++ b/apt-pkg/solver3.h
@@ -104,8 +104,8 @@ class Solver
return verStates[V->ID];
}
- std::vector<char> verObsolete;
- bool Obsolete(pkgCache::VerIterator ver);
+ std::vector<char> pkgObsolete;
+ bool Obsolete(pkgCache::PkgIterator pkg);
// \brief Heap of the remaining work.
//
diff --git a/doc/apt-verbatim.ent b/doc/apt-verbatim.ent
index f849c0c..b2b38f9 100644
--- a/doc/apt-verbatim.ent
+++ b/doc/apt-verbatim.ent
@@ -274,7 +274,7 @@
">
<!-- this will be updated by 'prepare-release' -->
-<!ENTITY apt-product-version "2.9.5">
+<!ENTITY apt-product-version "2.9.6">
<!-- (Code)names for various things used all over the place -->
<!ENTITY debian-oldstable-codename "bullseye">
diff --git a/doc/po/apt-doc.pot b/doc/po/apt-doc.pot
index 979992b..84ac0e8 100644
--- a/doc/po/apt-doc.pot
+++ b/doc/po/apt-doc.pot
@@ -5,9 +5,9 @@
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: apt-doc 2.9.5\n"
+"Project-Id-Version: apt-doc 2.9.6\n"
"Report-Msgid-Bugs-To: APT Development Team <deity@lists.debian.org>\n"
-"POT-Creation-Date: 2024-06-13 19:06+0000\n"
+"POT-Creation-Date: 2024-07-02 20:27+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
diff --git a/po/apt-all.pot b/po/apt-all.pot
index 51f7bf3..1f74e75 100644
--- a/po/apt-all.pot
+++ b/po/apt-all.pot
@@ -5,9 +5,9 @@
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: apt 2.9.5\n"
+"Project-Id-Version: apt 2.9.6\n"
"Report-Msgid-Bugs-To: APT Development Team <deity@lists.debian.org>\n"
-"POT-Creation-Date: 2024-06-13 19:06+0000\n"
+"POT-Creation-Date: 2024-07-02 20:27+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
diff --git a/test/integration/test-apt-get-mark-auto b/test/integration/test-apt-get-mark-auto
new file mode 100755
index 0000000..844412e
--- /dev/null
+++ b/test/integration/test-apt-get-mark-auto
@@ -0,0 +1,30 @@
+#!/bin/sh
+set -e
+
+TESTDIR="$(readlink -f "$(dirname "$0")")"
+. "$TESTDIR/framework"
+setupenvironment
+configarchitecture 'amd64'
+
+insertpackage 'unstable' 'package' 'all' '3'
+
+setupaptarchive
+
+testsuccessequal "Reading package lists...
+Building dependency tree...
+The following NEW packages will be installed:
+ package
+0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
+Inst package (3 unstable [all])
+Conf package (3 unstable [all])" aptget install package --mark-auto -s
+
+# Specifically if we mark the package as automatically installed but also have auto-remove on,
+# the package should still be installed...
+
+testsuccessequal "Reading package lists...
+Building dependency tree...
+The following NEW packages will be installed:
+ package
+0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
+Inst package (3 unstable [all])
+Conf package (3 unstable [all])" aptget install package --mark-auto --auto-remove -s
diff --git a/test/integration/test-bug-549968-install-depends-of-not-installed b/test/integration/test-bug-549968-install-depends-of-not-installed
index 39c86cc..f575f99 100755
--- a/test/integration/test-bug-549968-install-depends-of-not-installed
+++ b/test/integration/test-bug-549968-install-depends-of-not-installed
@@ -32,7 +32,7 @@ Conf coolstuff (1.0 unstable [all])" aptget install coolstuff extracoolstuff- -o
testsuccessequal "Reading package lists...
Building dependency tree...
Package 'extracoolstuff' is not installed, so not removed
-Solving dependencies...MANUAL coolstuff:i386
+Solving dependencies...Install coolstuff:i386 ()
[0] Install:coolstuff:i386=1.0 ()
Delete extracoolstuff:i386
[0] Reject:extracoolstuff:i386 ()
diff --git a/test/integration/test-bug-960705-propagate-protected-to-satisfied-depends b/test/integration/test-bug-960705-propagate-protected-to-satisfied-depends
index da11fc8..6ffb69b 100755
--- a/test/integration/test-bug-960705-propagate-protected-to-satisfied-depends
+++ b/test/integration/test-bug-960705-propagate-protected-to-satisfied-depends
@@ -20,7 +20,7 @@ setupaptarchive
testsuccessequal "Reading package lists...
Building dependency tree...
-Solving dependencies...MANUAL foobar:amd64
+Solving dependencies...Install foobar:amd64 ()
[0] Install:foobar:amd64=1 ()
[0] Install:requires-foo:amd64=1 (foobar:amd64=1)
[0] Install:foo:amd64=1 (foobar:amd64=1 -> requires-foo:amd64=1)
diff --git a/test/integration/test-dpkg-i-apt-install-fix-broken b/test/integration/test-dpkg-i-apt-install-fix-broken
index 5c517aa..a4c3c66 100755
--- a/test/integration/test-dpkg-i-apt-install-fix-broken
+++ b/test/integration/test-dpkg-i-apt-install-fix-broken
@@ -14,13 +14,13 @@ setupaptarchive
testfailure dpkg -i incoming/autopkgtest-*.deb
testsuccessequal 'Reading package lists...
Building dependency tree...
-Correcting dependencies...MANUAL autopkgtest-satdep:amd64
+Correcting dependencies...Install autopkgtest-satdep:amd64 ()
[0] Install:autopkgtest-satdep:amd64 ()
[0] Install:autopkgtest-satdep:amd64=1 (autopkgtest-satdep:amd64)
[0] Install:debhelper:amd64=1 (autopkgtest-satdep:amd64 -> autopkgtest-satdep:amd64=1)
Done
-Solving dependencies...AUTOMATIC debhelper:amd64 - upgrade
-MANUAL autopkgtest-satdep:amd64
+Solving dependencies...Install debhelper:amd64 (M)
+Install autopkgtest-satdep:amd64 ()
[0] Install:autopkgtest-satdep:amd64 ()
[0] Install:autopkgtest-satdep:amd64=1 (autopkgtest-satdep:amd64)
[0] Install:debhelper:amd64=1 (autopkgtest-satdep:amd64 -> autopkgtest-satdep:amd64=1)
diff --git a/test/integration/test-solver3-dependencies b/test/integration/test-solver3-dependencies
new file mode 100755
index 0000000..330ded8
--- /dev/null
+++ b/test/integration/test-solver3-dependencies
@@ -0,0 +1,65 @@
+#!/bin/sh
+set -e
+
+TESTDIR="$(readlink -f "$(dirname "$0")")"
+. "$TESTDIR/framework"
+setupenvironment
+configarchitecture 'amd64'
+
+insertinstalledpackage 'replaces' 'all' '2' 'Replaces: replaced (<< 3)'
+insertinstalledpackage 'replaced' 'all' '2'
+
+insertinstalledpackage 'enhances' 'all' '2' 'Enhances: enhanced (<< 3)'
+insertinstalledpackage 'enhanced' 'all' '2'
+
+insertpackage 'unstable' 'replaces' 'all' '3' 'Replaces: replaced (<< 3)'
+insertpackage 'unstable' 'enhances' 'all' '3' 'Enhances: enhanced (<< 3)'
+insertpackage 'unstable' 'replaced' 'all' '3'
+insertpackage 'unstable' 'enhanced' 'all' '3'
+
+setupaptarchive
+
+testsuccessequal "Reading package lists...
+Building dependency tree...
+Calculating upgrade...
+The following packages will be upgraded:
+ enhanced enhances replaced replaces
+4 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
+Inst enhanced [2] (3 unstable [all])
+Inst enhances [2] (3 unstable [all])
+Inst replaced [2] (3 unstable [all])
+Inst replaces [2] (3 unstable [all])
+Conf enhanced (3 unstable [all])
+Conf enhances (3 unstable [all])
+Conf replaced (3 unstable [all])
+Conf replaces (3 unstable [all])" aptget upgrade -s
+
+testsuccessequal "Reading package lists...
+Building dependency tree...
+Calculating upgrade...
+The following packages will be upgraded:
+ enhanced enhances replaced replaces
+4 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
+Inst enhanced [2] (3 unstable [all])
+Inst enhances [2] (3 unstable [all])
+Inst replaced [2] (3 unstable [all])
+Inst replaces [2] (3 unstable [all])
+Conf enhanced (3 unstable [all])
+Conf enhances (3 unstable [all])
+Conf replaced (3 unstable [all])
+Conf replaces (3 unstable [all])" apt upgrade -s
+
+testsuccessequal "Reading package lists...
+Building dependency tree...
+Calculating upgrade...
+The following packages will be upgraded:
+ enhanced enhances replaced replaces
+4 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
+Inst enhanced [2] (3 unstable [all])
+Inst enhances [2] (3 unstable [all])
+Inst replaced [2] (3 unstable [all])
+Inst replaces [2] (3 unstable [all])
+Conf enhanced (3 unstable [all])
+Conf enhances (3 unstable [all])
+Conf replaced (3 unstable [all])
+Conf replaces (3 unstable [all])" apt dist-upgrade -s