Home | History | Annotate | Download | only in test
      1 import unittest
      2 from test.test_support import verbose, run_unittest
      3 import sys
      4 import gc
      5 import weakref
      6 
      7 ### Support code

      8 ###############################################################################

      9 
     10 # Bug 1055820 has several tests of longstanding bugs involving weakrefs and

     11 # cyclic gc.

     12 
     13 # An instance of C1055820 has a self-loop, so becomes cyclic trash when

     14 # unreachable.

     15 class C1055820(object):
     16     def __init__(self, i):
     17         self.i = i
     18         self.loop = self
     19 
     20 class GC_Detector(object):
     21     # Create an instance I.  Then gc hasn't happened again so long as

     22     # I.gc_happened is false.

     23 
     24     def __init__(self):
     25         self.gc_happened = False
     26 
     27         def it_happened(ignored):
     28             self.gc_happened = True
     29 
     30         # Create a piece of cyclic trash that triggers it_happened when

     31         # gc collects it.

     32         self.wr = weakref.ref(C1055820(666), it_happened)
     33 
     34 
     35 ### Tests

     36 ###############################################################################

     37 
     38 class GCTests(unittest.TestCase):
     39     def test_list(self):
     40         l = []
     41         l.append(l)
     42         gc.collect()
     43         del l
     44         self.assertEqual(gc.collect(), 1)
     45 
     46     def test_dict(self):
     47         d = {}
     48         d[1] = d
     49         gc.collect()
     50         del d
     51         self.assertEqual(gc.collect(), 1)
     52 
     53     def test_tuple(self):
     54         # since tuples are immutable we close the loop with a list

     55         l = []
     56         t = (l,)
     57         l.append(t)
     58         gc.collect()
     59         del t
     60         del l
     61         self.assertEqual(gc.collect(), 2)
     62 
     63     def test_class(self):
     64         class A:
     65             pass
     66         A.a = A
     67         gc.collect()
     68         del A
     69         self.assertNotEqual(gc.collect(), 0)
     70 
     71     def test_newstyleclass(self):
     72         class A(object):
     73             pass
     74         gc.collect()
     75         del A
     76         self.assertNotEqual(gc.collect(), 0)
     77 
     78     def test_instance(self):
     79         class A:
     80             pass
     81         a = A()
     82         a.a = a
     83         gc.collect()
     84         del a
     85         self.assertNotEqual(gc.collect(), 0)
     86 
     87     def test_newinstance(self):
     88         class A(object):
     89             pass
     90         a = A()
     91         a.a = a
     92         gc.collect()
     93         del a
     94         self.assertNotEqual(gc.collect(), 0)
     95         class B(list):
     96             pass
     97         class C(B, A):
     98             pass
     99         a = C()
    100         a.a = a
    101         gc.collect()
    102         del a
    103         self.assertNotEqual(gc.collect(), 0)
    104         del B, C
    105         self.assertNotEqual(gc.collect(), 0)
    106         A.a = A()
    107         del A
    108         self.assertNotEqual(gc.collect(), 0)
    109         self.assertEqual(gc.collect(), 0)
    110 
    111     def test_method(self):
    112         # Tricky: self.__init__ is a bound method, it references the instance.

    113         class A:
    114             def __init__(self):
    115                 self.init = self.__init__
    116         a = A()
    117         gc.collect()
    118         del a
    119         self.assertNotEqual(gc.collect(), 0)
    120 
    121     def test_finalizer(self):
    122         # A() is uncollectable if it is part of a cycle, make sure it shows up

    123         # in gc.garbage.

    124         class A:
    125             def __del__(self): pass
    126         class B:
    127             pass
    128         a = A()
    129         a.a = a
    130         id_a = id(a)
    131         b = B()
    132         b.b = b
    133         gc.collect()
    134         del a
    135         del b
    136         self.assertNotEqual(gc.collect(), 0)
    137         for obj in gc.garbage:
    138             if id(obj) == id_a:
    139                 del obj.a
    140                 break
    141         else:
    142             self.fail("didn't find obj in garbage (finalizer)")
    143         gc.garbage.remove(obj)
    144 
    145     def test_finalizer_newclass(self):
    146         # A() is uncollectable if it is part of a cycle, make sure it shows up

    147         # in gc.garbage.

    148         class A(object):
    149             def __del__(self): pass
    150         class B(object):
    151             pass
    152         a = A()
    153         a.a = a
    154         id_a = id(a)
    155         b = B()
    156         b.b = b
    157         gc.collect()
    158         del a
    159         del b
    160         self.assertNotEqual(gc.collect(), 0)
    161         for obj in gc.garbage:
    162             if id(obj) == id_a:
    163                 del obj.a
    164                 break
    165         else:
    166             self.fail("didn't find obj in garbage (finalizer)")
    167         gc.garbage.remove(obj)
    168 
    169     def test_function(self):
    170         # Tricky: f -> d -> f, code should call d.clear() after the exec to

    171         # break the cycle.

    172         d = {}
    173         exec("def f(): pass\n") in d
    174         gc.collect()
    175         del d
    176         self.assertEqual(gc.collect(), 2)
    177 
    178     def test_frame(self):
    179         def f():
    180             frame = sys._getframe()
    181         gc.collect()
    182         f()
    183         self.assertEqual(gc.collect(), 1)
    184 
    185     def test_saveall(self):
    186         # Verify that cyclic garbage like lists show up in gc.garbage if the

    187         # SAVEALL option is enabled.

    188 
    189         # First make sure we don't save away other stuff that just happens to

    190         # be waiting for collection.

    191         gc.collect()
    192         # if this fails, someone else created immortal trash

    193         self.assertEqual(gc.garbage, [])
    194 
    195         L = []
    196         L.append(L)
    197         id_L = id(L)
    198 
    199         debug = gc.get_debug()
    200         gc.set_debug(debug | gc.DEBUG_SAVEALL)
    201         del L
    202         gc.collect()
    203         gc.set_debug(debug)
    204 
    205         self.assertEqual(len(gc.garbage), 1)
    206         obj = gc.garbage.pop()
    207         self.assertEqual(id(obj), id_L)
    208 
    209     def test_del(self):
    210         # __del__ methods can trigger collection, make this to happen

    211         thresholds = gc.get_threshold()
    212         gc.enable()
    213         gc.set_threshold(1)
    214 
    215         class A:
    216             def __del__(self):
    217                 dir(self)
    218         a = A()
    219         del a
    220 
    221         gc.disable()
    222         gc.set_threshold(*thresholds)
    223 
    224     def test_del_newclass(self):
    225         # __del__ methods can trigger collection, make this to happen

    226         thresholds = gc.get_threshold()
    227         gc.enable()
    228         gc.set_threshold(1)
    229 
    230         class A(object):
    231             def __del__(self):
    232                 dir(self)
    233         a = A()
    234         del a
    235 
    236         gc.disable()
    237         gc.set_threshold(*thresholds)
    238 
    239     # The following two tests are fragile:

    240     # They precisely count the number of allocations,

    241     # which is highly implementation-dependent.

    242     # For example:

    243     # - disposed tuples are not freed, but reused

    244     # - the call to assertEqual somehow avoids building its args tuple

    245     def test_get_count(self):
    246         # Avoid future allocation of method object

    247         assertEqual = self._baseAssertEqual
    248         gc.collect()
    249         assertEqual(gc.get_count(), (0, 0, 0))
    250         a = dict()
    251         # since gc.collect(), we created two objects:

    252         # the dict, and the tuple returned by get_count()

    253         assertEqual(gc.get_count(), (2, 0, 0))
    254 
    255     def test_collect_generations(self):
    256         # Avoid future allocation of method object

    257         assertEqual = self.assertEqual
    258         gc.collect()
    259         a = dict()
    260         gc.collect(0)
    261         assertEqual(gc.get_count(), (0, 1, 0))
    262         gc.collect(1)
    263         assertEqual(gc.get_count(), (0, 0, 1))
    264         gc.collect(2)
    265         assertEqual(gc.get_count(), (0, 0, 0))
    266 
    267     def test_trashcan(self):
    268         class Ouch:
    269             n = 0
    270             def __del__(self):
    271                 Ouch.n = Ouch.n + 1
    272                 if Ouch.n % 17 == 0:
    273                     gc.collect()
    274 
    275         # "trashcan" is a hack to prevent stack overflow when deallocating

    276         # very deeply nested tuples etc.  It works in part by abusing the

    277         # type pointer and refcount fields, and that can yield horrible

    278         # problems when gc tries to traverse the structures.

    279         # If this test fails (as it does in 2.0, 2.1 and 2.2), it will

    280         # most likely die via segfault.

    281 
    282         # Note:  In 2.3 the possibility for compiling without cyclic gc was

    283         # removed, and that in turn allows the trashcan mechanism to work

    284         # via much simpler means (e.g., it never abuses the type pointer or

    285         # refcount fields anymore).  Since it's much less likely to cause a

    286         # problem now, the various constants in this expensive (we force a lot

    287         # of full collections) test are cut back from the 2.2 version.

    288         gc.enable()
    289         N = 150
    290         for count in range(2):
    291             t = []
    292             for i in range(N):
    293                 t = [t, Ouch()]
    294             u = []
    295             for i in range(N):
    296                 u = [u, Ouch()]
    297             v = {}
    298             for i in range(N):
    299                 v = {1: v, 2: Ouch()}
    300         gc.disable()
    301 
    302     def test_boom(self):
    303         class Boom:
    304             def __getattr__(self, someattribute):
    305                 del self.attr
    306                 raise AttributeError
    307 
    308         a = Boom()
    309         b = Boom()
    310         a.attr = b
    311         b.attr = a
    312 
    313         gc.collect()
    314         garbagelen = len(gc.garbage)
    315         del a, b
    316         # a<->b are in a trash cycle now.  Collection will invoke

    317         # Boom.__getattr__ (to see whether a and b have __del__ methods), and

    318         # __getattr__ deletes the internal "attr" attributes as a side effect.

    319         # That causes the trash cycle to get reclaimed via refcounts falling to

    320         # 0, thus mutating the trash graph as a side effect of merely asking

    321         # whether __del__ exists.  This used to (before 2.3b1) crash Python.

    322         # Now __getattr__ isn't called.

    323         self.assertEqual(gc.collect(), 4)
    324         self.assertEqual(len(gc.garbage), garbagelen)
    325 
    326     def test_boom2(self):
    327         class Boom2:
    328             def __init__(self):
    329                 self.x = 0
    330 
    331             def __getattr__(self, someattribute):
    332                 self.x += 1
    333                 if self.x > 1:
    334                     del self.attr
    335                 raise AttributeError
    336 
    337         a = Boom2()
    338         b = Boom2()
    339         a.attr = b
    340         b.attr = a
    341 
    342         gc.collect()
    343         garbagelen = len(gc.garbage)
    344         del a, b
    345         # Much like test_boom(), except that __getattr__ doesn't break the

    346         # cycle until the second time gc checks for __del__.  As of 2.3b1,

    347         # there isn't a second time, so this simply cleans up the trash cycle.

    348         # We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get

    349         # reclaimed this way.

    350         self.assertEqual(gc.collect(), 4)
    351         self.assertEqual(len(gc.garbage), garbagelen)
    352 
    353     def test_boom_new(self):
    354         # boom__new and boom2_new are exactly like boom and boom2, except use

    355         # new-style classes.

    356 
    357         class Boom_New(object):
    358             def __getattr__(self, someattribute):
    359                 del self.attr
    360                 raise AttributeError
    361 
    362         a = Boom_New()
    363         b = Boom_New()
    364         a.attr = b
    365         b.attr = a
    366 
    367         gc.collect()
    368         garbagelen = len(gc.garbage)
    369         del a, b
    370         self.assertEqual(gc.collect(), 4)
    371         self.assertEqual(len(gc.garbage), garbagelen)
    372 
    373     def test_boom2_new(self):
    374         class Boom2_New(object):
    375             def __init__(self):
    376                 self.x = 0
    377 
    378             def __getattr__(self, someattribute):
    379                 self.x += 1
    380                 if self.x > 1:
    381                     del self.attr
    382                 raise AttributeError
    383 
    384         a = Boom2_New()
    385         b = Boom2_New()
    386         a.attr = b
    387         b.attr = a
    388 
    389         gc.collect()
    390         garbagelen = len(gc.garbage)
    391         del a, b
    392         self.assertEqual(gc.collect(), 4)
    393         self.assertEqual(len(gc.garbage), garbagelen)
    394 
    395     def test_get_referents(self):
    396         alist = [1, 3, 5]
    397         got = gc.get_referents(alist)
    398         got.sort()
    399         self.assertEqual(got, alist)
    400 
    401         atuple = tuple(alist)
    402         got = gc.get_referents(atuple)
    403         got.sort()
    404         self.assertEqual(got, alist)
    405 
    406         adict = {1: 3, 5: 7}
    407         expected = [1, 3, 5, 7]
    408         got = gc.get_referents(adict)
    409         got.sort()
    410         self.assertEqual(got, expected)
    411 
    412         got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0))
    413         got.sort()
    414         self.assertEqual(got, [0, 0] + range(5))
    415 
    416         self.assertEqual(gc.get_referents(1, 'a', 4j), [])
    417 
    418     def test_is_tracked(self):
    419         # Atomic built-in types are not tracked, user-defined objects and

    420         # mutable containers are.

    421         # NOTE: types with special optimizations (e.g. tuple) have tests

    422         # in their own test files instead.

    423         self.assertFalse(gc.is_tracked(None))
    424         self.assertFalse(gc.is_tracked(1))
    425         self.assertFalse(gc.is_tracked(1.0))
    426         self.assertFalse(gc.is_tracked(1.0 + 5.0j))
    427         self.assertFalse(gc.is_tracked(True))
    428         self.assertFalse(gc.is_tracked(False))
    429         self.assertFalse(gc.is_tracked("a"))
    430         self.assertFalse(gc.is_tracked(u"a"))
    431         self.assertFalse(gc.is_tracked(bytearray("a")))
    432         self.assertFalse(gc.is_tracked(type))
    433         self.assertFalse(gc.is_tracked(int))
    434         self.assertFalse(gc.is_tracked(object))
    435         self.assertFalse(gc.is_tracked(object()))
    436 
    437         class OldStyle:
    438             pass
    439         class NewStyle(object):
    440             pass
    441         self.assertTrue(gc.is_tracked(gc))
    442         self.assertTrue(gc.is_tracked(OldStyle))
    443         self.assertTrue(gc.is_tracked(OldStyle()))
    444         self.assertTrue(gc.is_tracked(NewStyle))
    445         self.assertTrue(gc.is_tracked(NewStyle()))
    446         self.assertTrue(gc.is_tracked([]))
    447         self.assertTrue(gc.is_tracked(set()))
    448 
    449     def test_bug1055820b(self):
    450         # Corresponds to temp2b.py in the bug report.

    451 
    452         ouch = []
    453         def callback(ignored):
    454             ouch[:] = [wr() for wr in WRs]
    455 
    456         Cs = [C1055820(i) for i in range(2)]
    457         WRs = [weakref.ref(c, callback) for c in Cs]
    458         c = None
    459 
    460         gc.collect()
    461         self.assertEqual(len(ouch), 0)
    462         # Make the two instances trash, and collect again.  The bug was that

    463         # the callback materialized a strong reference to an instance, but gc

    464         # cleared the instance's dict anyway.

    465         Cs = None
    466         gc.collect()
    467         self.assertEqual(len(ouch), 2)  # else the callbacks didn't run

    468         for x in ouch:
    469             # If the callback resurrected one of these guys, the instance

    470             # would be damaged, with an empty __dict__.

    471             self.assertEqual(x, None)
    472 
    473 class GCTogglingTests(unittest.TestCase):
    474     def setUp(self):
    475         gc.enable()
    476 
    477     def tearDown(self):
    478         gc.disable()
    479 
    480     def test_bug1055820c(self):
    481         # Corresponds to temp2c.py in the bug report.  This is pretty

    482         # elaborate.

    483 
    484         c0 = C1055820(0)
    485         # Move c0 into generation 2.

    486         gc.collect()
    487 
    488         c1 = C1055820(1)
    489         c1.keep_c0_alive = c0
    490         del c0.loop # now only c1 keeps c0 alive

    491 
    492         c2 = C1055820(2)
    493         c2wr = weakref.ref(c2) # no callback!

    494 
    495         ouch = []
    496         def callback(ignored):
    497             ouch[:] = [c2wr()]
    498 
    499         # The callback gets associated with a wr on an object in generation 2.

    500         c0wr = weakref.ref(c0, callback)
    501 
    502         c0 = c1 = c2 = None
    503 
    504         # What we've set up:  c0, c1, and c2 are all trash now.  c0 is in

    505         # generation 2.  The only thing keeping it alive is that c1 points to

    506         # it. c1 and c2 are in generation 0, and are in self-loops.  There's a

    507         # global weakref to c2 (c2wr), but that weakref has no callback.

    508         # There's also a global weakref to c0 (c0wr), and that does have a

    509         # callback, and that callback references c2 via c2wr().

    510         #

    511         #               c0 has a wr with callback, which references c2wr

    512         #               ^

    513         #               |

    514         #               |     Generation 2 above dots

    515         #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .

    516         #               |     Generation 0 below dots

    517         #               |

    518         #               |

    519         #            ^->c1   ^->c2 has a wr but no callback

    520         #            |  |    |  |

    521         #            <--v    <--v

    522         #

    523         # So this is the nightmare:  when generation 0 gets collected, we see

    524         # that c2 has a callback-free weakref, and c1 doesn't even have a

    525         # weakref.  Collecting generation 0 doesn't see c0 at all, and c0 is

    526         # the only object that has a weakref with a callback.  gc clears c1

    527         # and c2.  Clearing c1 has the side effect of dropping the refcount on

    528         # c0 to 0, so c0 goes away (despite that it's in an older generation)

    529         # and c0's wr callback triggers.  That in turn materializes a reference

    530         # to c2 via c2wr(), but c2 gets cleared anyway by gc.

    531 
    532         # We want to let gc happen "naturally", to preserve the distinction

    533         # between generations.

    534         junk = []
    535         i = 0
    536         detector = GC_Detector()
    537         while not detector.gc_happened:
    538             i += 1
    539             if i > 10000:
    540                 self.fail("gc didn't happen after 10000 iterations")
    541             self.assertEqual(len(ouch), 0)
    542             junk.append([])  # this will eventually trigger gc

    543 
    544         self.assertEqual(len(ouch), 1)  # else the callback wasn't invoked

    545         for x in ouch:
    546             # If the callback resurrected c2, the instance would be damaged,

    547             # with an empty __dict__.

    548             self.assertEqual(x, None)
    549 
    550     def test_bug1055820d(self):
    551         # Corresponds to temp2d.py in the bug report.  This is very much like

    552         # test_bug1055820c, but uses a __del__ method instead of a weakref

    553         # callback to sneak in a resurrection of cyclic trash.

    554 
    555         ouch = []
    556         class D(C1055820):
    557             def __del__(self):
    558                 ouch[:] = [c2wr()]
    559 
    560         d0 = D(0)
    561         # Move all the above into generation 2.

    562         gc.collect()
    563 
    564         c1 = C1055820(1)
    565         c1.keep_d0_alive = d0
    566         del d0.loop # now only c1 keeps d0 alive

    567 
    568         c2 = C1055820(2)
    569         c2wr = weakref.ref(c2) # no callback!

    570 
    571         d0 = c1 = c2 = None
    572 
    573         # What we've set up:  d0, c1, and c2 are all trash now.  d0 is in

    574         # generation 2.  The only thing keeping it alive is that c1 points to

    575         # it.  c1 and c2 are in generation 0, and are in self-loops.  There's

    576         # a global weakref to c2 (c2wr), but that weakref has no callback.

    577         # There are no other weakrefs.

    578         #

    579         #               d0 has a __del__ method that references c2wr

    580         #               ^

    581         #               |

    582         #               |     Generation 2 above dots

    583         #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .

    584         #               |     Generation 0 below dots

    585         #               |

    586         #               |

    587         #            ^->c1   ^->c2 has a wr but no callback

    588         #            |  |    |  |

    589         #            <--v    <--v

    590         #

    591         # So this is the nightmare:  when generation 0 gets collected, we see

    592         # that c2 has a callback-free weakref, and c1 doesn't even have a

    593         # weakref.  Collecting generation 0 doesn't see d0 at all.  gc clears

    594         # c1 and c2.  Clearing c1 has the side effect of dropping the refcount

    595         # on d0 to 0, so d0 goes away (despite that it's in an older

    596         # generation) and d0's __del__ triggers.  That in turn materializes

    597         # a reference to c2 via c2wr(), but c2 gets cleared anyway by gc.

    598 
    599         # We want to let gc happen "naturally", to preserve the distinction

    600         # between generations.

    601         detector = GC_Detector()
    602         junk = []
    603         i = 0
    604         while not detector.gc_happened:
    605             i += 1
    606             if i > 10000:
    607                 self.fail("gc didn't happen after 10000 iterations")
    608             self.assertEqual(len(ouch), 0)
    609             junk.append([])  # this will eventually trigger gc

    610 
    611         self.assertEqual(len(ouch), 1)  # else __del__ wasn't invoked

    612         for x in ouch:
    613             # If __del__ resurrected c2, the instance would be damaged, with an

    614             # empty __dict__.

    615             self.assertEqual(x, None)
    616 
    617 def test_main():
    618     enabled = gc.isenabled()
    619     gc.disable()
    620     assert not gc.isenabled()
    621     debug = gc.get_debug()
    622     gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak

    623 
    624     try:
    625         gc.collect() # Delete 2nd generation garbage

    626         run_unittest(GCTests, GCTogglingTests)
    627     finally:
    628         gc.set_debug(debug)
    629         # test gc.enable() even if GC is disabled by default

    630         if verbose:
    631             print "restoring automatic collection"
    632         # make sure to always test gc.enable()

    633         gc.enable()
    634         assert gc.isenabled()
    635         if not enabled:
    636             gc.disable()
    637 
    638 if __name__ == "__main__":
    639     test_main()
    640