Hi Tim!

I applied your patch to my gold branch to have a fixed base as needed for the test cases. I also cleaned up some whitespace and other quirks.

The main change is adding two test cases that really go wrong. One removes a previously installed package (by cleaning up the broken update) and the other hangs in an endless loop. I am still investigating possible fixes for those problems.

Florian

python test/alltests.py
.......................................................................................................................F............................................
======================================================================
FAIL: testUpdateRequireOld (skipbroken-tests.SkipBrokenTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/ffesti/CVS/yum/test/skipbroken-tests.py", line 75, in 
testUpdateRequireOld
    self.assertResult([ipo, po1])
  File "/home/ffesti/CVS/yum/test/testbase.py", line 142, in assertResult
    self.fail("".join(errors))
AssertionError: Unexpected result after depsolving: 

Package foo - 1-0.noarch was removed!

Test case was:

    def testUpdateRequireOld(self):
        po1 = FakePackage('foo', '1', '0', '0', 'noarch')
        po1.addRequires('foo-tools', 'EQ', ('0', '1', '0'))
        po2 = FakePackage('foo', '2', '0', '0', 'noarch')
        po2.addRequires('foo-tools', 'EQ', ('0', '2', '0'))

        ipo = FakePackage('foo-tools', '1', '0', '0', 'noarch')

        self.rpmdb.addPackage(po1)
        self.tsInfo.addUpdate(po2, oldpo=po1)
        self.rpmdb.addPackage(ipo)
        
        self.assertEquals('ok', *self.resolveCode(skip=True))
        self.assertResult([ipo, po1])



----------------------------------------------------------------------
Ran 164 tests in 0.411s

FAILED (failures=1)
Test modules:
        operationstests
        depsolvetests
        simpleupdatetests
        simpleobsoletestests
        operationsteststemplate
        skipbroken-tests
        simpleremovetests
        packagetests
make: *** [test] Error 1
>From f186c5aeb40d07b2e538662ab76b96ce2ccd55ed Mon Sep 17 00:00:00 2001
From: Florian Festi <[EMAIL PROTECTED]>
Date: Fri, 23 Nov 2007 16:54:41 +0100
Subject: [PATCH] First skip_broken implementation

---
 test/skipbroken-tests.py |  120 ++++++++++++++++++++++++++++++++++++++++++++++
 test/testbase.py         |    1 +
 yum/__init__.py          |   31 ++++++++++++
 yum/config.py            |    1 +
 yum/depsolve.py          |   16 +++++-
 5 files changed, 166 insertions(+), 3 deletions(-)
 create mode 100644 test/skipbroken-tests.py

diff --git a/test/skipbroken-tests.py b/test/skipbroken-tests.py
new file mode 100644
index 0000000..c18866b
--- /dev/null
+++ b/test/skipbroken-tests.py
@@ -0,0 +1,120 @@
+import unittest
+from testbase import *
+
+class SkipBrokenTests(DepsolveTests):
+
+    def testMissingReqNoSkip(self):
+        ''' bar fails because foobar is not provided '''
+        po = FakePackage('foo', '1', '0', '0', 'noarch')
+        po.addRequires('bar', None, (None,None,None))
+        self.tsInfo.addInstall(po)
+
+        xpo = FakePackage('bar', '1', '0', '0', 'noarch')
+        xpo.addRequires('foobar', None, (None,None,None))
+        self.xsack.addPackage(xpo)
+        self.assertEquals('err', *self.resolveCode(skip=False))
+        self.assertResult((po,xpo))
+
+    def testMissingReqSkip(self):
+        ''' foo + bar is skipped, because foobar is not provided '''
+        po = FakePackage('foo', '1', '0', '0', 'noarch')
+        po.addRequires('bar', None, (None,None,None))
+        self.tsInfo.addInstall(po)
+
+        xpo = FakePackage('bar', '1', '0', '0', 'noarch')
+        xpo.addRequires('foobar', None, (None,None,None))
+        self.xsack.addPackage(xpo)
+        self.assertEquals('empty', *self.resolveCode(skip=True))
+        self.assertResult([])
+
+    def testDepWithMissingReqSkip(self):
+        ''' foo + foobar is skipped because barfoo is not provided
+            bar stays in the transaction
+        '''
+        po1 = FakePackage('foo', '1', '0', '0', 'noarch')
+        po1.addRequires('foobar', None, (None,None,None))
+        self.tsInfo.addInstall(po1)
+
+        po2 = FakePackage('bar', '1', '0', '0', 'noarch')
+        self.tsInfo.addInstall(po2)
+
+        xpo1 = FakePackage('foobar', '1', '0', '0', 'noarch')
+        xpo1.addRequires('barfoo', None, (None,None,None))
+        self.xsack.addPackage(xpo1)
+        self.assertEquals('ok', *self.resolveCode(skip=True))
+        self.assertResult([po2])
+
+    def testUpdateOldRequired(self):
+        po1 = FakePackage('foo', '1', '0', '0', 'noarch')
+        po2 = FakePackage('foo', '2', '0', '0', 'noarch')
+
+        ipo = FakePackage('foo-tools', '2.5', '0', '0', 'noarch')
+        ipo.addRequires('foo', 'EQ', ('0', '1', '0'))
+
+        self.rpmdb.addPackage(po1)
+        self.tsInfo.addUpdate(po2, oldpo=po1)
+        self.rpmdb.addPackage(ipo)
+        
+        # XXX endless loop
+        # self.assertEquals('ok', *self.resolveCode(skip=True))
+        self.assertResult([ipo, po2])
+
+    def testUpdateRequireOld(self):
+        po1 = FakePackage('foo', '1', '0', '0', 'noarch')
+        po1.addRequires('foo-tools', 'EQ', ('0', '1', '0'))
+        po2 = FakePackage('foo', '2', '0', '0', 'noarch')
+        po2.addRequires('foo-tools', 'EQ', ('0', '2', '0'))
+
+        ipo = FakePackage('foo-tools', '1', '0', '0', 'noarch')
+
+        self.rpmdb.addPackage(po1)
+        self.tsInfo.addUpdate(po2, oldpo=po1)
+        self.rpmdb.addPackage(ipo)
+        
+        self.assertEquals('ok', *self.resolveCode(skip=True))
+        self.assertResult([ipo, po1])
+
+    def testLoop(self):
+        ipo = FakePackage('foo', '1', '0', '0', 'noarch')
+        ipo2 = FakePackage('foo-tools', '1', '0', '0', 'noarch')
+        ipo2.addRequires('foo', 'EQ', ('0', '1', '0'))
+
+        self.rpmdb.addPackage(ipo)
+        self.rpmdb.addPackage(ipo2)
+
+        self.tsInfo.addErase(ipo)
+        self.assertEquals('ok', *self.resolveCode(skip=True))
+
+
+    def resolveCode(self,skip = False):
+        solver = YumBase()
+        solver.conf = FakeConf()
+        solver.conf.skip_broken = skip
+        solver.tsInfo = solver._tsInfo = self.tsInfo
+        solver.rpmdb = self.rpmdb
+        solver.pkgSack = self.xsack
+
+        for po in self.rpmdb:
+            po.repoid = po.repo.id = "installed"
+        for po in self.xsack:
+            po.repoid = po.repo.id = "TestRepository"
+        for txmbr in solver.tsInfo:
+            if txmbr.ts_state in ('u', 'i'):
+                txmbr.po.repoid = txmbr.po.repo.id = "TestRepository"
+            else:
+                txmbr.po.repoid = txmbr.po.repo.id = "installed"
+
+        res, msg = solver.buildTransaction()
+        #self.tsInfo = copy.copy(solver._tsInfo)
+        return self.res[res], msg
+
+
+
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(SkipBrokenTests))
+    return suite
+
+if __name__ == "__main__":
+    unittest.main(defaultTest="suite")
diff --git a/test/testbase.py b/test/testbase.py
index 50bd9cb..f2f1f6b 100644
--- a/test/testbase.py
+++ b/test/testbase.py
@@ -35,6 +35,7 @@ class FakeConf(object):
         self.installroot = '/'
         self.tsflags = []
         self.installonly_limit = 0
+        self.skip_broken = False
 
 class FakeRepo(object):
 
diff --git a/yum/__init__.py b/yum/__init__.py
index f9618bc..178869e 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -580,9 +580,40 @@ class YumBase(depsolve.Depsolve):
         
         if self.tsInfo.changed:
             (rescode, restring) = self.resolveDeps()
+
+        # if depsolve failed and skipbroken is enabled
+        # The remove the broken packages from the transactions and
+        # Try another depsolve
+        if self.conf.skip_broken and rescode==1:
+            rescode, restring = self._skipPackagesWithProblems()
             
         return rescode, restring
 
+    def _skipPackagesWithProblems(self):
+        ''' Remove the packages with depsolve errors and depsolve again '''
+        rescode = 1
+        # Keep removing packages & Depsolve until all errors is gone
+        # or the transaction is empty
+        while len(self.po_with_problems) > 0 and rescode == 1:
+            for po in self.po_with_problems:
+                self.verbose_logger.debug("skipping %s because of depsolving problems" % po)
+                self._skipDeps(po)
+                self.tsInfo.remove(po.pkgtup)
+            rescode, restring = self.resolveDeps()
+        return rescode, restring
+
+    def _skipDeps(self,pkg):
+        '''
+        Remove the deps for a package with dep problem
+        from the Transaction
+        '''
+        removelist = []
+        for txmbr in self.tsInfo.getMembers(pkg.pkgtup):
+            for dep in txmbr.depends_on:
+                self.verbose_logger.debug("skipping dependency %s because of depsolving problems" % dep)
+                self.tsInfo.remove(dep.pkgtup)
+
+
     def runTransaction(self, cb):
         """takes an rpm callback object, performs the transaction"""
 
diff --git a/yum/config.py b/yum/config.py
index 174118d..f1fee12 100644
--- a/yum/config.py
+++ b/yum/config.py
@@ -555,6 +555,7 @@ class YumConf(StartupConf):
     mirrorlist_expire = IntOption(86400) # time in seconds (1 day)
     rpm_check_debug = BoolOption(True)
     disable_excludes = ListOption()    
+    skip_broken = BoolOption(False)
 
     
     _reposlist = []
diff --git a/yum/depsolve.py b/yum/depsolve.py
index 665fbdd..3690718 100644
--- a/yum/depsolve.py
+++ b/yum/depsolve.py
@@ -258,6 +258,11 @@ class Depsolve(object):
             CheckDeps, missingdep = self._requiringFromTransaction(po, requirement, errormsgs)
         else:
             CheckDeps, missingdep = self._requiringFromInstalled(po, requirement, errormsgs)
+
+        # Check packages with problems
+        if missingdep:
+            self.po_with_problems.add(po)
+
         return (CheckDeps, missingdep, errormsgs)
 
     def _requiringFromInstalled(self, requiringPo, requirement, errorlist):
@@ -560,13 +565,13 @@ class Depsolve(object):
                 name))
             # FIXME: we should probably handle updating multiple packages...
             txmbr = self.tsInfo.addUpdate(best, inst[0])
-            txmbr.setAsDep()
+            txmbr.setAsDep(requiringPo)
             txmbr.reason = "dep"
         else:
             self.verbose_logger.debug('TSINFO: Marking %s as install for %s', best,
                 name)
             txmbr = self.tsInfo.addInstall(best)
-            txmbr.setAsDep()
+            txmbr.setAsDep(requiringPo)
 
             # if we had other packages with this name.arch that we found
             # before, they're not going to be installed anymore, so we
@@ -649,7 +654,9 @@ class Depsolve(object):
             CheckDeps, conflicts = self._unresolveableConflict(conf, name, errormsgs)
             self.verbose_logger.log(logginglevels.DEBUG_1, '%s conflicts: %s',
                 name, conf)
-        
+            if conflicts:
+                self.po_with_problems.add(po)
+
         return (CheckDeps, conflicts, errormsgs)
 
     def _unresolveableConflict(self, conf, name, errors):
@@ -702,6 +709,7 @@ class Depsolve(object):
         # reset what we've seen as things may have changed between calls
         # to resolveDeps (rh#242368, rh#308321)
         self._dcobj.already_seen = {}
+        self.po_with_problems = set()
 
         CheckDeps = True
         CheckRemoves = False
@@ -769,6 +777,8 @@ class Depsolve(object):
         self.tsInfo.changed = False
         if len(errors) > 0:
             errors = unique(errors)
+            for po in self.po_with_problems:
+                self.verbose_logger.debug("%s has depsolving problems" % po)
             return (1, errors)
 
         if len(self.tsInfo) > 0:
-- 
1.5.3.3

_______________________________________________
Yum-devel mailing list
[email protected]
https://lists.dulug.duke.edu/mailman/listinfo/yum-devel

Reply via email to