[yum-commits] Branch 'yum-3_2_X' - 3 commits - test/complexremovetests.py test/testbase.py yum/__init__.py

James Antill james at osuosl.org
Mon Jun 6 15:12:54 UTC 2011


 test/complexremovetests.py |   85 +++++++++++++++++++++++++++++++++++++++++++++
 test/testbase.py           |   47 ++++++++++++++++++++++--
 yum/__init__.py            |   66 ++++++++++++++++++++++++++++++++--
 3 files changed, 189 insertions(+), 9 deletions(-)

New commits:
commit b79cd7f945fcc8a47c0a861185d8510bcb645770
Author: Casey Jao <cjao at ugcs.caltech.edu>
Date:   Mon Jun 6 07:09:20 2011 -0700

    Add complex remove tests for new _remove_old_deps code.

diff --git a/test/complexremovetests.py b/test/complexremovetests.py
new file mode 100644
index 0000000..ecb5f85
--- /dev/null
+++ b/test/complexremovetests.py
@@ -0,0 +1,85 @@
+from testbase import *
+
+class ComplexRemoveTests(OperationsTests):
+    # Three testcases. A -> B means that B requires A. 
+    # 1) 0 -> L1 -> (R1 -> R2, L2 -> L3) where everything but L3 was 
+    # dep-installed (removing L3 should remove everything);
+    # 2) 0 -> L1 -> (R1 -> R2, L2 -> L3) where everything but L3 and R2 was 
+    # dep-installed (removing L3 should only remove L3 and L2);
+    # 3) C1 <--> C2 -> C3 where C1 and C2 were dep-installed (removing 
+    # C3 should remove everything)
+    @staticmethod
+    def buildPkgs(pkgs, *args):
+        pkgs.node0 = FakePackage('foo', '2.5', '1.1', '0', 'noarch')
+        pkgs.node0.addFile('/bin/foo')
+        pkgs.node0.yumdb_info.reason = 'dep'
+        
+        pkgs.lnode1 = FakePackage('bar1', '1.0')
+        pkgs.lnode1.addRequires('foo')
+        pkgs.lnode1.addRequiresPkg(pkgs.node0)
+        pkgs.node0.addRequiringPkg(pkgs.lnode1)
+        pkgs.lnode1.yumdb_info.reason = 'dep'
+        
+        pkgs.lnode2 = FakePackage('bar2', '1.0')
+        pkgs.lnode2.addRequires('bar1')
+        pkgs.lnode2.addRequiresPkg(pkgs.lnode1)
+        pkgs.lnode1.addRequiringPkg(pkgs.lnode2)
+        pkgs.lnode2.yumdb_info.reason = 'dep'
+        
+        pkgs.lnode3 = FakePackage('bar3', '1.0')
+        pkgs.lnode3.addRequires('bar2')
+        pkgs.lnode3.addRequiresPkg(pkgs.lnode2)
+        pkgs.lnode2.addRequiresPkg(pkgs.lnode3)
+        pkgs.lnode3.yumdb_info.reason = 'user'
+
+        pkgs.rnode1 = FakePackage('baz1', '1.0')
+        pkgs.rnode1.addRequires('bar1')
+        pkgs.rnode1.addRequiresPkg(pkgs.lnode1)
+        pkgs.lnode1.addRequiringPkg(pkgs.rnode1)
+        pkgs.rnode1.yumdb_info.reason = 'dep'
+        
+        pkgs.rnode2 = FakePackage('baz2', '1.0')
+        pkgs.rnode2.addRequires('baz1')
+        pkgs.rnode2.addRequiresPkg(pkgs.rnode1)
+        pkgs.rnode1.addRequiringPkg(pkgs.rnode2)
+        pkgs.rnode2.yumdb_info.reason = 'dep'
+
+        pkgs.cycle1 = FakePackage('cycle1', '1.0')
+        pkgs.cycle1.yumdb_info.reason = 'dep'
+
+        pkgs.cycle2 = FakePackage('cycle2', '1.0')
+        pkgs.cycle2.yumdb_info.reason = 'dep'
+
+        pkgs.cycle3 = FakePackage('cycle3', '1.0')
+        pkgs.cycle3.yumdb_info.reason = 'user'
+
+
+        pkgs.cycle1.addRequires('cycle2')
+        pkgs.cycle1.addRequiresPkg(pkgs.cycle2)
+        pkgs.cycle2.addRequiringPkg(pkgs.cycle1)
+
+        pkgs.cycle2.addRequires('cycle1')
+        pkgs.cycle2.addRequiringPkg(pkgs.cycle1)
+        pkgs.cycle1.addRequiringPkg(pkgs.cycle2)
+
+        pkgs.cycle3.addRequires('cycle2')
+        pkgs.cycle3.addRequiresPkg(pkgs.cycle2)
+        pkgs.cycle2.addRequiringPkg(pkgs.cycle3)
+
+    def testRemoveCycle(self):
+        p = self.pkgs
+        res, msg = self.runOperation(['remove', 'cycle3'], [p.cycle1, p.cycle2, p.cycle3], [])
+        self.assertResult( () )
+
+    def testRemoveTree(self):
+        p = self.pkgs
+        res, msg = self.runOperation(['remove', 'bar3'], [p.node0, p.lnode1, p.lnode2, p.lnode3, p.rnode1, p.rnode2], [])
+        self.assertResult( () )
+
+    def testRemoveNeededRevdeps(self):
+        p = self.pkgs
+        p.rnode2.yumdb_info.reason = 'user'
+        res, msg = self.runOperation(['remove', 'bar3'], [p.node0, p.lnode1, p.lnode2, p.lnode3, p.rnode1, p.rnode2], [])
+        p.rnode2.yumdb_info.reason = 'dep'
+        self.assertResult( (p.node0, p.lnode1, p.rnode1, p.rnode2) )
+
commit 664049895f916bf6e6bd3f81abebfbc55ad572ae
Author: Casey Jao <cjao at ugcs.caltech.edu>
Date:   Mon Jun 6 07:08:46 2011 -0700

    Make FakePackage use a simulated RPMDBAdditionalDataPackage for yumdb_info.

diff --git a/test/testbase.py b/test/testbase.py
index 12025ce..d0f22be 100644
--- a/test/testbase.py
+++ b/test/testbase.py
@@ -84,6 +84,39 @@ class FakeRepo(object):
         else:
             return 0
 
+class FakeYumDBInfo(object):
+    """Simulate some functionality of RPMAdditionalDataPackage"""
+    _auto_hardlink_attrs = set(['checksum_type', 'reason',
+                                'installed_by', 'changed_by',
+                                'from_repo', 'from_repo_revision',
+                                'from_repo_timestamp', 'releasever',
+                                'command_line'])
+     
+    def __init__(self, conf=None, pkgdir=None, yumdb_cache=None):
+        self.db = {}
+        for attr in self._auto_hardlink_attrs:
+            self.db[attr] = ''
+            
+    def __getattr__(self, attr):
+        return self.db[attr]
+
+    def __setattr__(self, attr, value):
+        if not attr.startswith("db"):
+            self.db[attr] = value
+        else:
+            object.__setattr__(self, attr, value)
+            
+    def __iter__(self, show_hidden=False):
+        for item in self.db:
+            yield item
+
+    def get(self, attr, default=None):
+        try:
+            res = self.db[attr]
+        except AttributeError:
+            return default
+        return res
+        
 class FakePackage(packages.YumAvailablePackage):
 
     def __init__(self, name, version='1.0', release='1', epoch='0', arch='noarch', repo=None):
@@ -102,18 +135,24 @@ class FakePackage(packages.YumAvailablePackage):
         self.epoch = epoch
         self.arch = arch
         self.pkgtup = (self.name, self.arch, self.epoch, self.version, self.release)
-        self.yumdb_info = {}
+        self.yumdb_info = FakeYumDBInfo()
 
         self.prco['provides'].append((name, 'EQ', (epoch, version, release)))
 
         # Just a unique integer
         self.id = self.__hash__()
         self.pkgKey = self.__hash__()
-
+        
+        self.required_pkgs = []
+        self.requiring_pkgs = []
     def addProvides(self, name, flag=None, evr=(None, None, None)):
         self.prco['provides'].append((name, flag, evr))
     def addRequires(self, name, flag=None, evr=(None, None, None)):
         self.prco['requires'].append((name, flag, evr))
+    def addRequiresPkg(self, pkg):
+        self.required_pkgs.append(pkg)
+    def addRequiringPkg(self, pkg):
+        self.requiring_pkgs.append(pkg)   
     def addConflicts(self, name, flag=None, evr=(None, None, None)):
         self.prco['conflicts'].append((name, flag, evr))
     def addObsoletes(self, name, flag=None, evr=(None, None, None)):
@@ -121,9 +160,9 @@ class FakePackage(packages.YumAvailablePackage):
     def addFile(self, name, ftype='file'):
         self.files[ftype].append(name)
     def required_packages(self):
-        return []
+        return self.required_pkgs
     def requiring_packages(self):
-        return []
+        return self.requiring_pkgs
 
 class _Container(object):
     pass
commit 96f3a7d325045d2234310faa779c1b0d17227d18
Author: Casey Jao <cjao at ugcs.caltech.edu>
Date:   Mon Jun 6 07:07:20 2011 -0700

    Fix _remove_old_deps to deal with chains of revdeps.

diff --git a/yum/__init__.py b/yum/__init__.py
index 77c4d00..ab2bb9d 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -5363,6 +5363,11 @@ class YumBase(depsolve.Depsolve):
         found_leaves = set()
         checked = set()
         beingremoved = [ t.po for t in self.tsInfo.getMembersWithState(output_states=TS_REMOVE_STATES) ]
+        # cache previously examined packages
+        okay_to_remove = {}
+        for i in self.rpmdb.returnPackages():
+            okay_to_remove[i] = True
+
         for pkg in beingremoved: # for each package required by the pkg being removed
             #print 'removal: %s' % pkg.name
             for required in pkg.required_packages():
@@ -5370,23 +5375,26 @@ class YumBase(depsolve.Depsolve):
                 #    continue # if we've already checked it, skip it.
                 #checked.add(required)
                 if required.yumdb_info.get('reason', '') != 'dep': # if the required pkg is not a dep, then skip it
+                    okay_to_remove[required] = False
                     continue
                 if required in beingremoved:
                     continue
+                if self._has_needed_revdeps(required, beingremoved, okay_to_remove):
+                    continue
                 still_needed = False
                 for requiring in required.requiring_packages(): # so we have required deps - look at all the pkgs which require them
                     if requiring == required: # if they are self-requiring skip them
                         continue
-                    if requiring not in beingremoved: # if the requiring pkg is not in the list of pkgs being removed then it is still needed
-                        still_needed = True
-                        break
+                        
                 # go through the stuff in the ts to be installed - make sure none of that needs the required pkg, either.
                 for (provn,provf,provevr) in required.provides:
                     if self.tsInfo.getNewRequires(provn, provf, provevr).keys():
                         still_needed = True
+                        okay_to_remove[required] = False
                         break
                 for fn in required.filelist + required.dirlist:
                     if self.tsInfo.getNewRequires(fn, None,(None,None,None)).keys():
+                        okay_to_remove[required] = False
                         still_needed = True
                         break
                             
@@ -5406,5 +5414,53 @@ class YumBase(depsolve.Depsolve):
                             beingremoved.append(txmbr.po)
                         found_leaves.add(txmbr)
         self.verbose_logger.log(logginglevels.INFO_2, "Found and removing %s unneeded dependencies" % len(found_leaves))
-        
-                    
+            
+    # Checks if pkg has any reverse deps which cannot be removed. 
+    # Currently this only checks the install reason for each revdep, 
+    # but we may want to check for other reasons that would  
+    # prevent the revdep from being removed (e.g. protected)
+    def _has_needed_revdeps(self, pkg, beingremoved, ok_to_remove):
+        # check if we've already found this package to have user-installed deps
+        if not ok_to_remove[pkg]:
+            # Debugging output
+            self.verbose_logger.log(logginglevels.DEBUG_2, _("%s has been visited already and cannot be removed."), pkg)
+            return True
+        # Debugging output
+        self.verbose_logger.log(logginglevels.DEBUG_2, _("Examining revdeps of %s"), pkg)
+        # track which pkgs we have visited already
+        visited = {}
+        for po in self.rpmdb.returnPackages():
+            visited[po] = False
+        # no need to consider packages that are already being removed
+        for po in beingremoved:
+            visited[po] = True
+        stack = []
+        stack.append(pkg)
+        # depth-first search
+        while stack:
+            curpkg = stack[-1]
+            if not visited[curpkg]:
+                if not ok_to_remove[curpkg]:
+                    # Debugging output
+                    self.verbose_logger.log(logginglevels.DEBUG_2, _("%s has been visited already and cannot be removed."), pkg)
+                    ok_to_remove[pkg] = False
+                    return True
+                if curpkg.yumdb_info.get('reason', '') != 'dep':
+                    # Debugging output
+                    self.verbose_logger.log(logginglevels.DEBUG_2, _("%s has revdep %s which was user-installed."), pkg, curpkg)
+                    ok_to_remove[pkg] = False
+                    return True
+                visited[curpkg] = True
+            all_leaves_visited = True
+            leaves = curpkg.requiring_packages()
+            for leaf in leaves:
+                if not visited[leaf]:
+                    stack.append(leaf)
+                    all_leaves_visited = False
+                    break
+            if all_leaves_visited:
+                stack.pop()
+        # Debugging output
+        self.verbose_logger.log(logginglevels.DEBUG_2, _("%s has no user-installed revdeps."), pkg)
+        return False
+


More information about the Yum-commits mailing list