Ticket #1930: test.patch

File test.patch, 9.7 KB (added by antoine, 8 years ago)
  • twisted/test/test_libevent.py

    diff -r b0414c7ecaaf twisted/test/test_libevent.py
    a b Tests for libevent wrapper. 
    55Tests for libevent wrapper.
    66"""
    77
    8 import socket, errno, sys
     8import socket, errno, sys, os, weakref, gc
    99
    1010from twisted.trial import unittest
    1111
    class EventTestCase(unittest.TestCase): 
    6464        """
    6565        timerEvents = []
    6666        timerEvt = libevent.createTimer(lambda *args: timerEvents.append(args))
    67         timerEvt.addToLoop(0.1)
     67        timerEvt.addToLoop(0.01)
    6868        libevent.loop(libevent.EVLOOP_ONCE)
    6969        self.assertEquals(timerEvents, [(-1, libevent.EV_TIMEOUT, timerEvt)])
    7070
    class EventTestCase(unittest.TestCase): 
    8080        evt.addToLoop()
    8181        evt.removeFromLoop()
    8282        del evt
     83        gc.collect()
    8384        self.assertEquals(sys.getrefcount(cb), org)
    8485
    8586
    class EventTestCase(unittest.TestCase): 
    8889        Test flag values of a timer object.
    8990        """
    9091        timer = libevent.createTimer(lambda *args: None)
    91         timer.addToLoop(1)
     92        timer.addToLoop(10)
    9293        self.assertEquals(timer.pending() & libevent.EV_TIMEOUT, True)
    9394        timer.removeFromLoop()
    9495        self.assertEquals(timer.pending() & libevent.EV_TIMEOUT, False)
    class ConnectedEventTestCase(unittest.Te 
    204205        clientEvt.removeFromLoop()
    205206        serverEvt.removeFromLoop()
    206207
    207         timerEvt.addToLoop(0.1)
     208        timerEvt.addToLoop(0.01)
    208209        libevent.loop(libevent.EVLOOP_ONCE)
    209210        self.assertEquals(timerEvents, [(-1, libevent.EV_TIMEOUT, timerEvt)])
    210211        self.failIf(clientEvents)
    class EventBaseTestCase(unittest.TestCas 
    216217    """
    217218    Test L{libevent.EventBase} usage.
    218219    """
     220
     221    def setUp(self):
     222        """
     223        Create a weakvaluedict in order to hold potentially leaked objects,
     224        and another dict to hold potentially destroyed objects.
     225        """
     226        self._leaks = weakref.WeakValueDictionary()
     227        self._survivors = {}
     228
     229    def _watchForLeaks(self, *args):
     230        """
     231        Watch the given objects for leaks, by creating weakrefs to them.
     232        """
     233        for obj in args:
     234            key = id(obj), repr(obj)
     235            self._leaks[key] = obj
     236
     237    def _watchForSurvival(self, *args):
     238        """
     239        Watch the given objects for survival, by creating weakrefs to them.
     240        """
     241        for obj in args:
     242            key = id(obj), repr(obj)
     243            self._survivors[key] = weakref.ref(obj)
     244
     245    def _assertLeaks(self):
     246        """
     247        Assert that all objects watched for leaks have been destroyed.
     248        """
     249        # Trigger cycle breaking
     250        gc.collect()
     251        if len(self._leaks):
     252            self.fail("%d objects have leaked: %s" % (
     253                len(self._leaks),
     254                ", ".join([key[1] for key in self._leaks])
     255                ))
     256
     257    def _assertSurvival(self):
     258        """
     259        Assert that all objects watched for survival have survived.
     260        """
     261        # Trigger cycle breaking
     262        gc.collect()
     263        dead = []
     264        for (id_, repr_), ref in self._survivors.items():
     265            if ref() is None:
     266                dead.append(repr_)
     267        if dead:
     268            self.fail("%d objects should have survived "
     269                "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
     270
     271
     272    def _allocateStuff(self):
     273        """
     274        Allocate some objects so as to try to overwrite dead objects with other
     275        stuff. Not guaranteed to work but at least we try :-)
     276        """
     277        # Reclaim memory, then fill it. We create a lot of plain objects so
     278        # that the main allocator is exercised.
     279        gc.collect()
     280        class _Dummy(object):
     281            pass
     282        [_Dummy() for i in xrange(10000)]
    219283
    220284    def test_create(self):
    221285        """
    class EventBaseTestCase(unittest.TestCas 
    291355        def eb(fd, events, obj):
    292356            raise RuntimeError("foo")
    293357        timer = newEventBase.createTimer(eb)
    294         timer.addToLoop(0.01)
     358        timer.addToLoop(0.001)
    295359
    296360        def cb(fd, events, obj):
    297361            fireEvents.append((fd, events, obj))
    298362        timer = newEventBase.createTimer(cb)
    299         timer.addToLoop(0.02)
     363        timer.addToLoop(0.002)
    300364
    301365        self.assertRaises(RuntimeError, newEventBase.dispatch)
    302366        self.assertEquals(len(fireEvents), 0)
    class EventBaseTestCase(unittest.TestCas 
    305369        newEventBase.dispatch()
    306370        self.assertEquals(len(fireEvents), 1)
    307371
     372    def test_successfulCallbackReference(self):
     373        """
     374        Check that successful callbacks aren't leaked.
     375        """
     376        newEventBase = libevent.EventBase()
     377        def cb(fd, events, obj):
     378            pass
     379        self._watchForLeaks(cb)
     380        timer = newEventBase.createTimer(cb)
     381        timer.addToLoop(0.002)
     382        newEventBase.dispatch()
     383
     384        del cb, timer
     385        self._assertLeaks()
     386
     387    def test_failedCallbackReference(self):
     388        """
     389        Check that failed callbacks aren't leaked.
     390        """
     391        newEventBase = libevent.EventBase()
     392        def eb(fd, events, obj):
     393            raise RuntimeError("foo")
     394        self._watchForLeaks(eb)
     395        timer = newEventBase.createTimer(eb)
     396        timer.addToLoop(0.002)
     397        self.assertRaises(RuntimeError, newEventBase.dispatch)
     398
     399        del eb, timer
     400        self._assertLeaks()
     401
     402    def test_unfiredCallbackReference(self):
     403        """
     404        Check that unfired callbacks aren't leaked when the eventBase is
     405        destroyed.
     406        """
     407        newEventBase = libevent.EventBase()
     408        def cb(fd, events, obj):
     409            pass
     410        self._watchForLeaks(cb)
     411        timer = newEventBase.createTimer(cb)
     412        timer.addToLoop(1)
     413
     414        del cb, timer, newEventBase
     415        self._assertLeaks()
     416
     417    def test_callbackReference(self):
     418        """
     419        Check that a simple unregistered callback doesn't leak.
     420        """
     421        newEventBase = libevent.EventBase()
     422        def cb(fd, events, obj):
     423            pass
     424        timer = newEventBase.createTimer(cb)
     425        self._watchForLeaks(cb)
     426
     427        del cb, timer
     428        self._assertLeaks()
     429
     430    def test_callbackExceptionReference(self):
     431        """
     432        Check that exceptions propagated from callbacks aren't leaked.
     433        """
     434        # Custom subclass so that weakref's are possible
     435        class _Exception(RuntimeError):
     436            pass
     437        exc = [None]
     438        newEventBase = libevent.EventBase()
     439        def eb(fd, events, obj):
     440            exc[0] = _Exception("foo")
     441            raise exc[0]
     442        timer = newEventBase.createTimer(eb)
     443        timer.addToLoop(0.002)
     444        self.assertRaises(RuntimeError, newEventBase.dispatch)
     445        self._watchForLeaks(exc[0])
     446
     447        del exc[0]
     448        self._assertLeaks()
     449
     450    def test_callbackSurvival(self):
     451        """
     452        Check that a registered callback survives even when the local reference
     453        dies.
     454        """
     455        newEventBase = libevent.EventBase()
     456        def cb(fd, events, obj):
     457            pass
     458        timer = newEventBase.createTimer(cb)
     459        timer.addToLoop(1)
     460        self._watchForSurvival(cb)
     461
     462        del cb, timer
     463        self._assertSurvival()
     464
     465    def test_persistentCallbackSurvival(self):
     466        """
     467        Check that a persistent callback survives after been fired.
     468        """
     469        rfd, wfd = os.pipe()
     470        newEventBase = libevent.EventBase()
     471        def cb(fd, events, obj):
     472            newEventBase.loopExit(0)
     473        timer = newEventBase.createEvent(rfd,
     474            libevent.EV_READ | libevent.EV_PERSIST, cb)
     475        timer.addToLoop()
     476        os.write(wfd, " ")
     477        newEventBase.dispatch()
     478        self._watchForSurvival(cb)
     479
     480        del cb, timer
     481        self._assertSurvival()
     482
     483    def test_persistentFailedCallbackSurvival(self):
     484        """
     485        Check that a persistent callback survives after raising an exception.
     486        """
     487        rfd, wfd = os.pipe()
     488        newEventBase = libevent.EventBase()
     489        def cb(fd, events, obj):
     490            newEventBase.loopExit(0)
     491            raise RuntimeError("foo")
     492        timer = newEventBase.createEvent(rfd,
     493            libevent.EV_READ | libevent.EV_PERSIST, cb)
     494        timer.addToLoop()
     495        os.write(wfd, " ")
     496        self.assertRaises(RuntimeError, newEventBase.dispatch)
     497        self._watchForSurvival(cb)
     498
     499        del cb, timer
     500        self._assertSurvival()
     501
     502    def test_persistentCallbackReference(self):
     503        """
     504        Check that a persistent callback doesn't leak when the eventBase
     505        is destroyed.
     506        """
     507        rfd, wfd = os.pipe()
     508        newEventBase = libevent.EventBase()
     509        def cb(fd, events, obj):
     510            newEventBase.loopExit(0)
     511        timer = newEventBase.createEvent(rfd,
     512            libevent.EV_READ | libevent.EV_PERSIST, cb)
     513        timer.addToLoop()
     514        os.write(wfd, " ")
     515        newEventBase.dispatch()
     516        self._watchForLeaks(cb)
     517
     518        newEventBase = None
     519        del cb, timer
     520        self._assertLeaks()
     521
     522    def test_dispatchedEventRefCount(self):
     523        """
     524        Check that dispatched event refcounts don't grow.
     525        """
     526        newEventBase = libevent.EventBase()
     527        def cb(fd, events, obj):
     528            pass
     529        timer = newEventBase.createTimer(cb)
     530        orig = sys.getrefcount(timer)
     531        timer.addToLoop(0.01)
     532        newEventBase.dispatch()
     533        # Perhaps some dead cycles involve our object -> break them
     534        gc.collect()
     535        self.assertEquals(orig, sys.getrefcount(timer))
    308536
    309537
    310538if libevent is None: