home *** CD-ROM | disk | FTP | other *** search
/ PC World 2005 June / PCWorld_2005-06_cd.bin / software / vyzkuste / firewally / firewally.exe / framework-2.3.exe / test_gc.py < prev    next >
Text File  |  2003-12-30  |  11KB  |  420 lines

  1. from test.test_support import verify, verbose, TestFailed, vereq
  2. import sys
  3. import gc
  4.  
  5. def expect(actual, expected, name):
  6.     if actual != expected:
  7.         raise TestFailed, "test_%s: actual %r, expected %r" % (
  8.             name, actual, expected)
  9.  
  10. def expect_nonzero(actual, name):
  11.     if actual == 0:
  12.         raise TestFailed, "test_%s: unexpected zero" % name
  13.  
  14. def run_test(name, thunk):
  15.     if verbose:
  16.         print "testing %s..." % name,
  17.     thunk()
  18.     if verbose:
  19.         print "ok"
  20.  
  21. def test_list():
  22.     l = []
  23.     l.append(l)
  24.     gc.collect()
  25.     del l
  26.     expect(gc.collect(), 1, "list")
  27.  
  28. def test_dict():
  29.     d = {}
  30.     d[1] = d
  31.     gc.collect()
  32.     del d
  33.     expect(gc.collect(), 1, "dict")
  34.  
  35. def test_tuple():
  36.     # since tuples are immutable we close the loop with a list
  37.     l = []
  38.     t = (l,)
  39.     l.append(t)
  40.     gc.collect()
  41.     del t
  42.     del l
  43.     expect(gc.collect(), 2, "tuple")
  44.  
  45. def test_class():
  46.     class A:
  47.         pass
  48.     A.a = A
  49.     gc.collect()
  50.     del A
  51.     expect_nonzero(gc.collect(), "class")
  52.  
  53. def test_newstyleclass():
  54.     class A(object):
  55.         pass
  56.     gc.collect()
  57.     del A
  58.     expect_nonzero(gc.collect(), "staticclass")
  59.  
  60. def test_instance():
  61.     class A:
  62.         pass
  63.     a = A()
  64.     a.a = a
  65.     gc.collect()
  66.     del a
  67.     expect_nonzero(gc.collect(), "instance")
  68.  
  69. def test_newinstance():
  70.     class A(object):
  71.         pass
  72.     a = A()
  73.     a.a = a
  74.     gc.collect()
  75.     del a
  76.     expect_nonzero(gc.collect(), "newinstance")
  77.     class B(list):
  78.         pass
  79.     class C(B, A):
  80.         pass
  81.     a = C()
  82.     a.a = a
  83.     gc.collect()
  84.     del a
  85.     expect_nonzero(gc.collect(), "newinstance(2)")
  86.     del B, C
  87.     expect_nonzero(gc.collect(), "newinstance(3)")
  88.     A.a = A()
  89.     del A
  90.     expect_nonzero(gc.collect(), "newinstance(4)")
  91.     expect(gc.collect(), 0, "newinstance(5)")
  92.  
  93. def test_method():
  94.     # Tricky: self.__init__ is a bound method, it references the instance.
  95.     class A:
  96.         def __init__(self):
  97.             self.init = self.__init__
  98.     a = A()
  99.     gc.collect()
  100.     del a
  101.     expect_nonzero(gc.collect(), "method")
  102.  
  103. def test_finalizer():
  104.     # A() is uncollectable if it is part of a cycle, make sure it shows up
  105.     # in gc.garbage.
  106.     class A:
  107.         def __del__(self): pass
  108.     class B:
  109.         pass
  110.     a = A()
  111.     a.a = a
  112.     id_a = id(a)
  113.     b = B()
  114.     b.b = b
  115.     gc.collect()
  116.     del a
  117.     del b
  118.     expect_nonzero(gc.collect(), "finalizer")
  119.     for obj in gc.garbage:
  120.         if id(obj) == id_a:
  121.             del obj.a
  122.             break
  123.     else:
  124.         raise TestFailed, "didn't find obj in garbage (finalizer)"
  125.     gc.garbage.remove(obj)
  126.  
  127. def test_finalizer_newclass():
  128.     # A() is uncollectable if it is part of a cycle, make sure it shows up
  129.     # in gc.garbage.
  130.     class A(object):
  131.         def __del__(self): pass
  132.     class B(object):
  133.         pass
  134.     a = A()
  135.     a.a = a
  136.     id_a = id(a)
  137.     b = B()
  138.     b.b = b
  139.     gc.collect()
  140.     del a
  141.     del b
  142.     expect_nonzero(gc.collect(), "finalizer")
  143.     for obj in gc.garbage:
  144.         if id(obj) == id_a:
  145.             del obj.a
  146.             break
  147.     else:
  148.         raise TestFailed, "didn't find obj in garbage (finalizer)"
  149.     gc.garbage.remove(obj)
  150.  
  151. def test_function():
  152.     # Tricky: f -> d -> f, code should call d.clear() after the exec to
  153.     # break the cycle.
  154.     d = {}
  155.     exec("def f(): pass\n") in d
  156.     gc.collect()
  157.     del d
  158.     expect(gc.collect(), 2, "function")
  159.  
  160. def test_frame():
  161.     def f():
  162.         frame = sys._getframe()
  163.     gc.collect()
  164.     f()
  165.     expect(gc.collect(), 1, "frame")
  166.  
  167.  
  168. def test_saveall():
  169.     # Verify that cyclic garbage like lists show up in gc.garbage if the
  170.     # SAVEALL option is enabled.
  171.  
  172.     # First make sure we don't save away other stuff that just happens to
  173.     # be waiting for collection.
  174.     gc.collect()
  175.     vereq(gc.garbage, []) # if this fails, someone else created immortal trash
  176.  
  177.     L = []
  178.     L.append(L)
  179.     id_L = id(L)
  180.  
  181.     debug = gc.get_debug()
  182.     gc.set_debug(debug | gc.DEBUG_SAVEALL)
  183.     del L
  184.     gc.collect()
  185.     gc.set_debug(debug)
  186.  
  187.     vereq(len(gc.garbage), 1)
  188.     obj = gc.garbage.pop()
  189.     vereq(id(obj), id_L)
  190.  
  191. def test_del():
  192.     # __del__ methods can trigger collection, make this to happen
  193.     thresholds = gc.get_threshold()
  194.     gc.enable()
  195.     gc.set_threshold(1)
  196.  
  197.     class A:
  198.         def __del__(self):
  199.             dir(self)
  200.     a = A()
  201.     del a
  202.  
  203.     gc.disable()
  204.     gc.set_threshold(*thresholds)
  205.  
  206. def test_del_newclass():
  207.     # __del__ methods can trigger collection, make this to happen
  208.     thresholds = gc.get_threshold()
  209.     gc.enable()
  210.     gc.set_threshold(1)
  211.  
  212.     class A(object):
  213.         def __del__(self):
  214.             dir(self)
  215.     a = A()
  216.     del a
  217.  
  218.     gc.disable()
  219.     gc.set_threshold(*thresholds)
  220.  
  221. class Ouch:
  222.     n = 0
  223.     def __del__(self):
  224.         Ouch.n = Ouch.n + 1
  225.         if Ouch.n % 17 == 0:
  226.             gc.collect()
  227.  
  228. def test_trashcan():
  229.     # "trashcan" is a hack to prevent stack overflow when deallocating
  230.     # very deeply nested tuples etc.  It works in part by abusing the
  231.     # type pointer and refcount fields, and that can yield horrible
  232.     # problems when gc tries to traverse the structures.
  233.     # If this test fails (as it does in 2.0, 2.1 and 2.2), it will
  234.     # most likely die via segfault.
  235.  
  236.     # Note:  In 2.3 the possibility for compiling without cyclic gc was
  237.     # removed, and that in turn allows the trashcan mechanism to work
  238.     # via much simpler means (e.g., it never abuses the type pointer or
  239.     # refcount fields anymore).  Since it's much less likely to cause a
  240.     # problem now, the various constants in this expensive (we force a lot
  241.     # of full collections) test are cut back from the 2.2 version.
  242.     gc.enable()
  243.     N = 150
  244.     for count in range(2):
  245.         t = []
  246.         for i in range(N):
  247.             t = [t, Ouch()]
  248.         u = []
  249.         for i in range(N):
  250.             u = [u, Ouch()]
  251.         v = {}
  252.         for i in range(N):
  253.             v = {1: v, 2: Ouch()}
  254.     gc.disable()
  255.  
  256. class Boom:
  257.     def __getattr__(self, someattribute):
  258.         del self.attr
  259.         raise AttributeError
  260.  
  261. def test_boom():
  262.     a = Boom()
  263.     b = Boom()
  264.     a.attr = b
  265.     b.attr = a
  266.  
  267.     gc.collect()
  268.     garbagelen = len(gc.garbage)
  269.     del a, b
  270.     # a<->b are in a trash cycle now.  Collection will invoke Boom.__getattr__
  271.     # (to see whether a and b have __del__ methods), and __getattr__ deletes
  272.     # the internal "attr" attributes as a side effect.  That causes the
  273.     # trash cycle to get reclaimed via refcounts falling to 0, thus mutating
  274.     # the trash graph as a side effect of merely asking whether __del__
  275.     # exists.  This used to (before 2.3b1) crash Python.  Now __getattr__
  276.     # isn't called.
  277.     expect(gc.collect(), 4, "boom")
  278.     expect(len(gc.garbage), garbagelen, "boom")
  279.  
  280. class Boom2:
  281.     def __init__(self):
  282.         self.x = 0
  283.  
  284.     def __getattr__(self, someattribute):
  285.         self.x += 1
  286.         if self.x > 1:
  287.             del self.attr
  288.         raise AttributeError
  289.  
  290. def test_boom2():
  291.     a = Boom2()
  292.     b = Boom2()
  293.     a.attr = b
  294.     b.attr = a
  295.  
  296.     gc.collect()
  297.     garbagelen = len(gc.garbage)
  298.     del a, b
  299.     # Much like test_boom(), except that __getattr__ doesn't break the
  300.     # cycle until the second time gc checks for __del__.  As of 2.3b1,
  301.     # there isn't a second time, so this simply cleans up the trash cycle.
  302.     # We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get reclaimed
  303.     # this way.
  304.     expect(gc.collect(), 4, "boom2")
  305.     expect(len(gc.garbage), garbagelen, "boom2")
  306.  
  307. # boom__new and boom2_new are exactly like boom and boom2, except use
  308. # new-style classes.
  309.  
  310. class Boom_New(object):
  311.     def __getattr__(self, someattribute):
  312.         del self.attr
  313.         raise AttributeError
  314.  
  315. def test_boom_new():
  316.     a = Boom_New()
  317.     b = Boom_New()
  318.     a.attr = b
  319.     b.attr = a
  320.  
  321.     gc.collect()
  322.     garbagelen = len(gc.garbage)
  323.     del a, b
  324.     expect(gc.collect(), 4, "boom_new")
  325.     expect(len(gc.garbage), garbagelen, "boom_new")
  326.  
  327. class Boom2_New(object):
  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. def test_boom2_new():
  338.     a = Boom2_New()
  339.     b = Boom2_New()
  340.     a.attr = b
  341.     b.attr = a
  342.  
  343.     gc.collect()
  344.     garbagelen = len(gc.garbage)
  345.     del a, b
  346.     expect(gc.collect(), 4, "boom2_new")
  347.     expect(len(gc.garbage), garbagelen, "boom2_new")
  348.  
  349. def test_get_referents():
  350.     alist = [1, 3, 5]
  351.     got = gc.get_referents(alist)
  352.     got.sort()
  353.     expect(got, alist, "get_referents")
  354.  
  355.     atuple = tuple(alist)
  356.     got = gc.get_referents(atuple)
  357.     got.sort()
  358.     expect(got, alist, "get_referents")
  359.  
  360.     adict = {1: 3, 5: 7}
  361.     expected = [1, 3, 5, 7]
  362.     got = gc.get_referents(adict)
  363.     got.sort()
  364.     expect(got, expected, "get_referents")
  365.  
  366.     got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0))
  367.     got.sort()
  368.     expect(got, [0, 0] + range(5), "get_referents")
  369.  
  370.     expect(gc.get_referents(1, 'a', 4j), [], "get_referents")
  371.  
  372. def test_all():
  373.     gc.collect() # Delete 2nd generation garbage
  374.     run_test("lists", test_list)
  375.     run_test("dicts", test_dict)
  376.     run_test("tuples", test_tuple)
  377.     run_test("classes", test_class)
  378.     run_test("new style classes", test_newstyleclass)
  379.     run_test("instances", test_instance)
  380.     run_test("new instances", test_newinstance)
  381.     run_test("methods", test_method)
  382.     run_test("functions", test_function)
  383.     run_test("frames", test_frame)
  384.     run_test("finalizers", test_finalizer)
  385.     run_test("finalizers (new class)", test_finalizer_newclass)
  386.     run_test("__del__", test_del)
  387.     run_test("__del__ (new class)", test_del_newclass)
  388.     run_test("saveall", test_saveall)
  389.     run_test("trashcan", test_trashcan)
  390.     run_test("boom", test_boom)
  391.     run_test("boom2", test_boom2)
  392.     run_test("boom_new", test_boom_new)
  393.     run_test("boom2_new", test_boom2_new)
  394.     run_test("get_referents", test_get_referents)
  395.  
  396. def test():
  397.     if verbose:
  398.         print "disabling automatic collection"
  399.     enabled = gc.isenabled()
  400.     gc.disable()
  401.     verify(not gc.isenabled())
  402.     debug = gc.get_debug()
  403.     gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
  404.  
  405.     try:
  406.         test_all()
  407.     finally:
  408.         gc.set_debug(debug)
  409.         # test gc.enable() even if GC is disabled by default
  410.         if verbose:
  411.             print "restoring automatic collection"
  412.         # make sure to always test gc.enable()
  413.         gc.enable()
  414.         verify(gc.isenabled())
  415.         if not enabled:
  416.             gc.disable()
  417.  
  418.  
  419. test()
  420.