Ticket #1930: test.patch

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

    diff -r b0414c7ecaaf twisted/test/test_libevent.py
    a b  
    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 
     
    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 
     
    8080        evt.addToLoop() 
    8181        evt.removeFromLoop() 
    8282        del evt 
     83        gc.collect() 
    8384        self.assertEquals(sys.getrefcount(cb), org) 
    8485 
    8586 
     
    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) 
     
    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) 
     
    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        """ 
     
    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) 
     
    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: