symbian-qemu-0.9.1-12/python-2.6.1/Lib/test/test_gc.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     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.assertEqual
       
   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_bug1055820b(self):
       
   419         # Corresponds to temp2b.py in the bug report.
       
   420 
       
   421         ouch = []
       
   422         def callback(ignored):
       
   423             ouch[:] = [wr() for wr in WRs]
       
   424 
       
   425         Cs = [C1055820(i) for i in range(2)]
       
   426         WRs = [weakref.ref(c, callback) for c in Cs]
       
   427         c = None
       
   428 
       
   429         gc.collect()
       
   430         self.assertEqual(len(ouch), 0)
       
   431         # Make the two instances trash, and collect again.  The bug was that
       
   432         # the callback materialized a strong reference to an instance, but gc
       
   433         # cleared the instance's dict anyway.
       
   434         Cs = None
       
   435         gc.collect()
       
   436         self.assertEqual(len(ouch), 2)  # else the callbacks didn't run
       
   437         for x in ouch:
       
   438             # If the callback resurrected one of these guys, the instance
       
   439             # would be damaged, with an empty __dict__.
       
   440             self.assertEqual(x, None)
       
   441 
       
   442 class GCTogglingTests(unittest.TestCase):
       
   443     def setUp(self):
       
   444         gc.enable()
       
   445 
       
   446     def tearDown(self):
       
   447         gc.disable()
       
   448 
       
   449     def test_bug1055820c(self):
       
   450         # Corresponds to temp2c.py in the bug report.  This is pretty
       
   451         # elaborate.
       
   452 
       
   453         c0 = C1055820(0)
       
   454         # Move c0 into generation 2.
       
   455         gc.collect()
       
   456 
       
   457         c1 = C1055820(1)
       
   458         c1.keep_c0_alive = c0
       
   459         del c0.loop # now only c1 keeps c0 alive
       
   460 
       
   461         c2 = C1055820(2)
       
   462         c2wr = weakref.ref(c2) # no callback!
       
   463 
       
   464         ouch = []
       
   465         def callback(ignored):
       
   466             ouch[:] = [c2wr()]
       
   467 
       
   468         # The callback gets associated with a wr on an object in generation 2.
       
   469         c0wr = weakref.ref(c0, callback)
       
   470 
       
   471         c0 = c1 = c2 = None
       
   472 
       
   473         # What we've set up:  c0, c1, and c2 are all trash now.  c0 is in
       
   474         # generation 2.  The only thing keeping it alive is that c1 points to
       
   475         # it. c1 and c2 are in generation 0, and are in self-loops.  There's a
       
   476         # global weakref to c2 (c2wr), but that weakref has no callback.
       
   477         # There's also a global weakref to c0 (c0wr), and that does have a
       
   478         # callback, and that callback references c2 via c2wr().
       
   479         #
       
   480         #               c0 has a wr with callback, which references c2wr
       
   481         #               ^
       
   482         #               |
       
   483         #               |     Generation 2 above dots
       
   484         #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
       
   485         #               |     Generation 0 below dots
       
   486         #               |
       
   487         #               |
       
   488         #            ^->c1   ^->c2 has a wr but no callback
       
   489         #            |  |    |  |
       
   490         #            <--v    <--v
       
   491         #
       
   492         # So this is the nightmare:  when generation 0 gets collected, we see
       
   493         # that c2 has a callback-free weakref, and c1 doesn't even have a
       
   494         # weakref.  Collecting generation 0 doesn't see c0 at all, and c0 is
       
   495         # the only object that has a weakref with a callback.  gc clears c1
       
   496         # and c2.  Clearing c1 has the side effect of dropping the refcount on
       
   497         # c0 to 0, so c0 goes away (despite that it's in an older generation)
       
   498         # and c0's wr callback triggers.  That in turn materializes a reference
       
   499         # to c2 via c2wr(), but c2 gets cleared anyway by gc.
       
   500 
       
   501         # We want to let gc happen "naturally", to preserve the distinction
       
   502         # between generations.
       
   503         junk = []
       
   504         i = 0
       
   505         detector = GC_Detector()
       
   506         while not detector.gc_happened:
       
   507             i += 1
       
   508             if i > 10000:
       
   509                 self.fail("gc didn't happen after 10000 iterations")
       
   510             self.assertEqual(len(ouch), 0)
       
   511             junk.append([])  # this will eventually trigger gc
       
   512 
       
   513         self.assertEqual(len(ouch), 1)  # else the callback wasn't invoked
       
   514         for x in ouch:
       
   515             # If the callback resurrected c2, the instance would be damaged,
       
   516             # with an empty __dict__.
       
   517             self.assertEqual(x, None)
       
   518 
       
   519     def test_bug1055820d(self):
       
   520         # Corresponds to temp2d.py in the bug report.  This is very much like
       
   521         # test_bug1055820c, but uses a __del__ method instead of a weakref
       
   522         # callback to sneak in a resurrection of cyclic trash.
       
   523 
       
   524         ouch = []
       
   525         class D(C1055820):
       
   526             def __del__(self):
       
   527                 ouch[:] = [c2wr()]
       
   528 
       
   529         d0 = D(0)
       
   530         # Move all the above into generation 2.
       
   531         gc.collect()
       
   532 
       
   533         c1 = C1055820(1)
       
   534         c1.keep_d0_alive = d0
       
   535         del d0.loop # now only c1 keeps d0 alive
       
   536 
       
   537         c2 = C1055820(2)
       
   538         c2wr = weakref.ref(c2) # no callback!
       
   539 
       
   540         d0 = c1 = c2 = None
       
   541 
       
   542         # What we've set up:  d0, c1, and c2 are all trash now.  d0 is in
       
   543         # generation 2.  The only thing keeping it alive is that c1 points to
       
   544         # it.  c1 and c2 are in generation 0, and are in self-loops.  There's
       
   545         # a global weakref to c2 (c2wr), but that weakref has no callback.
       
   546         # There are no other weakrefs.
       
   547         #
       
   548         #               d0 has a __del__ method that references c2wr
       
   549         #               ^
       
   550         #               |
       
   551         #               |     Generation 2 above dots
       
   552         #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
       
   553         #               |     Generation 0 below dots
       
   554         #               |
       
   555         #               |
       
   556         #            ^->c1   ^->c2 has a wr but no callback
       
   557         #            |  |    |  |
       
   558         #            <--v    <--v
       
   559         #
       
   560         # So this is the nightmare:  when generation 0 gets collected, we see
       
   561         # that c2 has a callback-free weakref, and c1 doesn't even have a
       
   562         # weakref.  Collecting generation 0 doesn't see d0 at all.  gc clears
       
   563         # c1 and c2.  Clearing c1 has the side effect of dropping the refcount
       
   564         # on d0 to 0, so d0 goes away (despite that it's in an older
       
   565         # generation) and d0's __del__ triggers.  That in turn materializes
       
   566         # a reference to c2 via c2wr(), but c2 gets cleared anyway by gc.
       
   567 
       
   568         # We want to let gc happen "naturally", to preserve the distinction
       
   569         # between generations.
       
   570         detector = GC_Detector()
       
   571         junk = []
       
   572         i = 0
       
   573         while not detector.gc_happened:
       
   574             i += 1
       
   575             if i > 10000:
       
   576                 self.fail("gc didn't happen after 10000 iterations")
       
   577             self.assertEqual(len(ouch), 0)
       
   578             junk.append([])  # this will eventually trigger gc
       
   579 
       
   580         self.assertEqual(len(ouch), 1)  # else __del__ wasn't invoked
       
   581         for x in ouch:
       
   582             # If __del__ resurrected c2, the instance would be damaged, with an
       
   583             # empty __dict__.
       
   584             self.assertEqual(x, None)
       
   585 
       
   586 def test_main():
       
   587     enabled = gc.isenabled()
       
   588     gc.disable()
       
   589     assert not gc.isenabled()
       
   590     debug = gc.get_debug()
       
   591     gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
       
   592 
       
   593     try:
       
   594         gc.collect() # Delete 2nd generation garbage
       
   595         run_unittest(GCTests, GCTogglingTests)
       
   596     finally:
       
   597         gc.set_debug(debug)
       
   598         # test gc.enable() even if GC is disabled by default
       
   599         if verbose:
       
   600             print "restoring automatic collection"
       
   601         # make sure to always test gc.enable()
       
   602         gc.enable()
       
   603         assert gc.isenabled()
       
   604         if not enabled:
       
   605             gc.disable()
       
   606 
       
   607 if __name__ == "__main__":
       
   608     test_main()