[Buildbot-commits] buildbot/buildbot/test test_locks.py,1.3,1.4

Brian Warner warner at users.sourceforge.net
Thu Aug 24 10:05:22 UTC 2006


Update of /cvsroot/buildbot/buildbot/buildbot/test
In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv27952/buildbot/test

Modified Files:
	test_locks.py 
Log Message:
[project @ locks: can now have multiple simultaneous owners. fixes SF#1434997]

Original author: warner at lothar.com
Date: 2006-08-24 10:03:52

Index: test_locks.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/test/test_locks.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- test_locks.py	26 Nov 2005 02:14:31 -0000	1.3
+++ test_locks.py	24 Aug 2006 10:05:20 -0000	1.4
@@ -1,14 +1,275 @@
 # -*- test-case-name: buildbot.test.test_locks -*-
 
+import random
+
 from twisted.trial import unittest
-from twisted.internet import defer
+from twisted.internet import defer, reactor
 
-from buildbot import interfaces
+from buildbot import interfaces, master
 from buildbot.process import step
 from buildbot.sourcestamp import SourceStamp
 from buildbot.process.base import BuildRequest
 from buildbot.test.runutils import RunMixin
 from buildbot.twcompat import maybeWait
+from buildbot import locks
+
+def claimHarder(lock, owner):
+    """Return a Deferred that will fire when the lock is claimed. Keep trying
+    until we succeed."""
+    if lock.isAvailable():
+        #print "claimHarder(%s): claiming" % owner
+        lock.claim(owner)
+        return defer.succeed(lock)
+    #print "claimHarder(%s): waiting" % owner
+    d = lock.waitUntilMaybeAvailable(owner)
+    d.addCallback(claimHarder, owner)
+    return d
+
+def hold(lock, owner, mode="now"):
+    if mode == "now":
+        lock.release(owner)
+    elif mode == "very soon":
+        reactor.callLater(0, lock.release, owner)
+    elif mode == "soon":
+        reactor.callLater(0.1, lock.release, owner)
+
+
+class Unit(unittest.TestCase):
+    def testNow(self):
+        l = locks.BaseLock("name")
+        self.failUnless(l.isAvailable())
+        l.claim("owner1")
+        self.failIf(l.isAvailable())
+        l.release("owner1")
+        self.failUnless(l.isAvailable())
+
+    def testLater(self):
+        lock = locks.BaseLock("name")
+        d = claimHarder(lock, "owner1")
+        d.addCallback(lambda lock: lock.release("owner1"))
+        return d
+
+    def testCompetition(self):
+        lock = locks.BaseLock("name")
+        d = claimHarder(lock, "owner1")
+        d.addCallback(self._claim1)
+        return d
+    def _claim1(self, lock):
+        # we should have claimed it by now
+        self.failIf(lock.isAvailable())
+        # now set up two competing owners. We don't know which will get the
+        # lock first.
+        d2 = claimHarder(lock, "owner2")
+        d2.addCallback(hold, "owner2", "now")
+        d3 = claimHarder(lock, "owner3")
+        d3.addCallback(hold, "owner3", "soon")
+        dl = defer.DeferredList([d2,d3])
+        dl.addCallback(self._cleanup, lock)
+        # and release the lock in a moment
+        reactor.callLater(0.1, lock.release, "owner1")
+        return dl
+
+    def _cleanup(self, res, lock):
+        d = claimHarder(lock, "cleanup")
+        d.addCallback(lambda lock: lock.release("cleanup"))
+        return d
+
+    def testRandom(self):
+        lock = locks.BaseLock("name")
+        dl = []
+        for i in range(100):
+            owner = "owner%d" % i
+            mode = random.choice(["now", "very soon", "soon"])
+            d = claimHarder(lock, owner)
+            d.addCallback(hold, owner, mode)
+            dl.append(d)
+        d = defer.DeferredList(dl)
+        d.addCallback(self._cleanup, lock)
+        return d
+
+class Multi(unittest.TestCase):
+    def testNow(self):
+        lock = locks.BaseLock("name", 2)
+        self.failUnless(lock.isAvailable())
+        lock.claim("owner1")
+        self.failUnless(lock.isAvailable())
+        lock.claim("owner2")
+        self.failIf(lock.isAvailable())
+        lock.release("owner1")
+        self.failUnless(lock.isAvailable())
+        lock.release("owner2")
+        self.failUnless(lock.isAvailable())
+
+    def testLater(self):
+        lock = locks.BaseLock("name", 2)
+        lock.claim("owner1")
+        lock.claim("owner2")
+        d = claimHarder(lock, "owner3")
+        d.addCallback(lambda lock: lock.release("owner3"))
+        lock.release("owner2")
+        lock.release("owner1")
+        return d
+
+    def _cleanup(self, res, lock, count):
+        dl = []
+        for i in range(count):
+            d = claimHarder(lock, "cleanup%d" % i)
+            dl.append(d)
+        d2 = defer.DeferredList(dl)
+        # once all locks are claimed, we know that any previous owners have
+        # been flushed out
+        def _release(res):
+            for i in range(count):
+                lock.release("cleanup%d" % i)
+        d2.addCallback(_release)
+        return d2
+
+    def testRandom(self):
+        COUNT = 5
+        lock = locks.BaseLock("name", COUNT)
+        dl = []
+        for i in range(100):
+            owner = "owner%d" % i
+            mode = random.choice(["now", "very soon", "soon"])
+            d = claimHarder(lock, owner)
+            def _check(lock):
+                self.failIf(len(lock.owners) > COUNT)
+                return lock
+            d.addCallback(_check)
+            d.addCallback(hold, owner, mode)
+            dl.append(d)
+        d = defer.DeferredList(dl)
+        d.addCallback(self._cleanup, lock, COUNT)
+        return d
+
+class Dummy:
+    pass
+
+def slave(slavename):
+    slavebuilder = Dummy()
+    slavebuilder.slave = Dummy()
+    slavebuilder.slave.slavename = slavename
+    return slavebuilder
+
+class MakeRealLock(unittest.TestCase):
+
+    def make(self, lockid):
+        return lockid.lockClass(lockid)
+
+    def testMaster(self):
+        mid1 = locks.MasterLock("name1")
+        mid2 = locks.MasterLock("name1")
+        mid3 = locks.MasterLock("name3")
+        mid4 = locks.MasterLock("name1", 3)
+        self.failUnlessEqual(mid1, mid2)
+        self.failIfEqual(mid1, mid3)
+        # they should all be hashable
+        d = {mid1: 1, mid2: 2, mid3: 3, mid4: 4}
+
+        l1 = self.make(mid1)
+        self.failUnlessEqual(l1.name, "name1")
+        self.failUnlessEqual(l1.maxCount, 1)
+        self.failUnlessIdentical(l1.getLock(slave("slave1")), l1)
+        l4 = self.make(mid4)
+        self.failUnlessEqual(l4.name, "name1")
+        self.failUnlessEqual(l4.maxCount, 3)
+        self.failUnlessIdentical(l4.getLock(slave("slave1")), l4)
+
+    def testSlave(self):
+        sid1 = locks.SlaveLock("name1")
+        sid2 = locks.SlaveLock("name1")
+        sid3 = locks.SlaveLock("name3")
+        sid4 = locks.SlaveLock("name1", maxCount=3)
+        mcfs = {"bigslave": 4, "smallslave": 1}
+        sid5 = locks.SlaveLock("name1", maxCount=3, maxCountForSlave=mcfs)
+        mcfs2 = {"bigslave": 4, "smallslave": 1}
+        sid5a = locks.SlaveLock("name1", maxCount=3, maxCountForSlave=mcfs2)
+        mcfs3 = {"bigslave": 1, "smallslave": 99}
+        sid5b = locks.SlaveLock("name1", maxCount=3, maxCountForSlave=mcfs3)
+        self.failUnlessEqual(sid1, sid2)
+        self.failIfEqual(sid1, sid3)
+        self.failIfEqual(sid1, sid4)
+        self.failIfEqual(sid1, sid5)
+        self.failUnlessEqual(sid5, sid5a)
+        self.failIfEqual(sid5a, sid5b)
+        # they should all be hashable
+        d = {sid1: 1, sid2: 2, sid3: 3, sid4: 4, sid5: 5, sid5a: 6, sid5b: 7}
+
+        l1 = self.make(sid1)
+        self.failUnlessEqual(l1.name, "name1")
+        self.failUnlessEqual(l1.maxCount, 1)
+        l1s1 = l1.getLock(slave("slave1"))
+        self.failIfIdentical(l1s1, l1)
+
+        l4 = self.make(sid4)
+        self.failUnlessEqual(l4.maxCount, 3)
+        l4s1 = l4.getLock(slave("slave1"))
+        self.failUnlessEqual(l4s1.maxCount, 3)
+
+        l5 = self.make(sid5)
+        l5s1 = l5.getLock(slave("bigslave"))
+        l5s2 = l5.getLock(slave("smallslave"))
+        l5s3 = l5.getLock(slave("unnamedslave"))
+        self.failUnlessEqual(l5s1.maxCount, 4)
+        self.failUnlessEqual(l5s2.maxCount, 1)
+        self.failUnlessEqual(l5s3.maxCount, 3)
+
+class GetLock(unittest.TestCase):
+    def testGet(self):
+        # the master.cfg file contains "lock ids", which are instances of
+        # MasterLock and SlaveLock but which are not actually Locks per se.
+        # When the build starts, these markers are turned into RealMasterLock
+        # and RealSlaveLock instances. This insures that any builds running
+        # on slaves that were unaffected by the config change are still
+        # referring to the same Lock instance as new builds by builders that
+        # *were* affected by the change. There have been bugs in the past in
+        # which this didn't happen, and the Locks were bypassed because half
+        # the builders were using one incarnation of the lock while the other
+        # half were using a separate (but equal) incarnation.
+        #
+        # Changing the lock id in any way should cause it to be replaced in
+        # the BotMaster. This will result in a couple of funky artifacts:
+        # builds in progress might pay attention to a different lock, so we
+        # might bypass the locking for the duration of a couple builds.
+        # There's also the problem of old Locks lingering around in
+        # BotMaster.locks, but they're small and shouldn't really cause a
+        # problem.
+
+        b = master.BotMaster()
+        l1 = locks.MasterLock("one")
+        l1a = locks.MasterLock("one")
+        l2 = locks.MasterLock("one", maxCount=4)
+
+        rl1 = b.getLockByID(l1)
+        rl2 = b.getLockByID(l1a)
+        self.failUnlessIdentical(rl1, rl2)
+        rl3 = b.getLockByID(l2)
+        self.failIfIdentical(rl1, rl3)
+
+        s1 = locks.SlaveLock("one")
+        s1a = locks.SlaveLock("one")
+        s2 = locks.SlaveLock("one", maxCount=4)
+        s3 = locks.SlaveLock("one", maxCount=4,
+                             maxCountForSlave={"a":1, "b":2})
+        s3a = locks.SlaveLock("one", maxCount=4,
+                              maxCountForSlave={"a":1, "b":2})
+        s4 = locks.SlaveLock("one", maxCount=4,
+                             maxCountForSlave={"a":4, "b":4})
+
+        rl1 = b.getLockByID(s1)
+        rl2 = b.getLockByID(s1a)
+        self.failUnlessIdentical(rl1, rl2)
+        rl3 = b.getLockByID(s2)
+        self.failIfIdentical(rl1, rl3)
+        rl4 = b.getLockByID(s3)
+        self.failIfIdentical(rl1, rl4)
+        self.failIfIdentical(rl3, rl4)
+        rl5 = b.getLockByID(s3a)
+        self.failUnlessIdentical(rl4, rl5)
+        rl6 = b.getLockByID(s4)
+        self.failIfIdentical(rl5, rl6)
+
+
 
 class LockStep(step.Dummy):
     def start(self):





More information about the Commits mailing list