home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 July / maximum-cd-2011-07.iso / DiscContents / LibO_3.3.2_Win_x86_install_multi.exe / libreoffice1.cab / test_gc.py < prev    next >
Encoding:
Python Source  |  2011-03-15  |  18.7 KB  |  609 lines

  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()
  609.