]> git.proxmox.com Git - mirror_edk2.git/blame - AppPkg/Applications/Python/Python-2.7.2/Lib/test/test_gc.py
EmbeddedPkg: Extend NvVarStoreFormattedLib LIBRARY_CLASS
[mirror_edk2.git] / AppPkg / Applications / Python / Python-2.7.2 / Lib / test / test_gc.py
CommitLineData
4710c53d 1import unittest\r
2from test.test_support import verbose, run_unittest\r
3import sys\r
4import gc\r
5import weakref\r
6\r
7### Support code\r
8###############################################################################\r
9\r
10# Bug 1055820 has several tests of longstanding bugs involving weakrefs and\r
11# cyclic gc.\r
12\r
13# An instance of C1055820 has a self-loop, so becomes cyclic trash when\r
14# unreachable.\r
15class C1055820(object):\r
16 def __init__(self, i):\r
17 self.i = i\r
18 self.loop = self\r
19\r
20class GC_Detector(object):\r
21 # Create an instance I. Then gc hasn't happened again so long as\r
22 # I.gc_happened is false.\r
23\r
24 def __init__(self):\r
25 self.gc_happened = False\r
26\r
27 def it_happened(ignored):\r
28 self.gc_happened = True\r
29\r
30 # Create a piece of cyclic trash that triggers it_happened when\r
31 # gc collects it.\r
32 self.wr = weakref.ref(C1055820(666), it_happened)\r
33\r
34\r
35### Tests\r
36###############################################################################\r
37\r
38class GCTests(unittest.TestCase):\r
39 def test_list(self):\r
40 l = []\r
41 l.append(l)\r
42 gc.collect()\r
43 del l\r
44 self.assertEqual(gc.collect(), 1)\r
45\r
46 def test_dict(self):\r
47 d = {}\r
48 d[1] = d\r
49 gc.collect()\r
50 del d\r
51 self.assertEqual(gc.collect(), 1)\r
52\r
53 def test_tuple(self):\r
54 # since tuples are immutable we close the loop with a list\r
55 l = []\r
56 t = (l,)\r
57 l.append(t)\r
58 gc.collect()\r
59 del t\r
60 del l\r
61 self.assertEqual(gc.collect(), 2)\r
62\r
63 def test_class(self):\r
64 class A:\r
65 pass\r
66 A.a = A\r
67 gc.collect()\r
68 del A\r
69 self.assertNotEqual(gc.collect(), 0)\r
70\r
71 def test_newstyleclass(self):\r
72 class A(object):\r
73 pass\r
74 gc.collect()\r
75 del A\r
76 self.assertNotEqual(gc.collect(), 0)\r
77\r
78 def test_instance(self):\r
79 class A:\r
80 pass\r
81 a = A()\r
82 a.a = a\r
83 gc.collect()\r
84 del a\r
85 self.assertNotEqual(gc.collect(), 0)\r
86\r
87 def test_newinstance(self):\r
88 class A(object):\r
89 pass\r
90 a = A()\r
91 a.a = a\r
92 gc.collect()\r
93 del a\r
94 self.assertNotEqual(gc.collect(), 0)\r
95 class B(list):\r
96 pass\r
97 class C(B, A):\r
98 pass\r
99 a = C()\r
100 a.a = a\r
101 gc.collect()\r
102 del a\r
103 self.assertNotEqual(gc.collect(), 0)\r
104 del B, C\r
105 self.assertNotEqual(gc.collect(), 0)\r
106 A.a = A()\r
107 del A\r
108 self.assertNotEqual(gc.collect(), 0)\r
109 self.assertEqual(gc.collect(), 0)\r
110\r
111 def test_method(self):\r
112 # Tricky: self.__init__ is a bound method, it references the instance.\r
113 class A:\r
114 def __init__(self):\r
115 self.init = self.__init__\r
116 a = A()\r
117 gc.collect()\r
118 del a\r
119 self.assertNotEqual(gc.collect(), 0)\r
120\r
121 def test_finalizer(self):\r
122 # A() is uncollectable if it is part of a cycle, make sure it shows up\r
123 # in gc.garbage.\r
124 class A:\r
125 def __del__(self): pass\r
126 class B:\r
127 pass\r
128 a = A()\r
129 a.a = a\r
130 id_a = id(a)\r
131 b = B()\r
132 b.b = b\r
133 gc.collect()\r
134 del a\r
135 del b\r
136 self.assertNotEqual(gc.collect(), 0)\r
137 for obj in gc.garbage:\r
138 if id(obj) == id_a:\r
139 del obj.a\r
140 break\r
141 else:\r
142 self.fail("didn't find obj in garbage (finalizer)")\r
143 gc.garbage.remove(obj)\r
144\r
145 def test_finalizer_newclass(self):\r
146 # A() is uncollectable if it is part of a cycle, make sure it shows up\r
147 # in gc.garbage.\r
148 class A(object):\r
149 def __del__(self): pass\r
150 class B(object):\r
151 pass\r
152 a = A()\r
153 a.a = a\r
154 id_a = id(a)\r
155 b = B()\r
156 b.b = b\r
157 gc.collect()\r
158 del a\r
159 del b\r
160 self.assertNotEqual(gc.collect(), 0)\r
161 for obj in gc.garbage:\r
162 if id(obj) == id_a:\r
163 del obj.a\r
164 break\r
165 else:\r
166 self.fail("didn't find obj in garbage (finalizer)")\r
167 gc.garbage.remove(obj)\r
168\r
169 def test_function(self):\r
170 # Tricky: f -> d -> f, code should call d.clear() after the exec to\r
171 # break the cycle.\r
172 d = {}\r
173 exec("def f(): pass\n") in d\r
174 gc.collect()\r
175 del d\r
176 self.assertEqual(gc.collect(), 2)\r
177\r
178 def test_frame(self):\r
179 def f():\r
180 frame = sys._getframe()\r
181 gc.collect()\r
182 f()\r
183 self.assertEqual(gc.collect(), 1)\r
184\r
185 def test_saveall(self):\r
186 # Verify that cyclic garbage like lists show up in gc.garbage if the\r
187 # SAVEALL option is enabled.\r
188\r
189 # First make sure we don't save away other stuff that just happens to\r
190 # be waiting for collection.\r
191 gc.collect()\r
192 # if this fails, someone else created immortal trash\r
193 self.assertEqual(gc.garbage, [])\r
194\r
195 L = []\r
196 L.append(L)\r
197 id_L = id(L)\r
198\r
199 debug = gc.get_debug()\r
200 gc.set_debug(debug | gc.DEBUG_SAVEALL)\r
201 del L\r
202 gc.collect()\r
203 gc.set_debug(debug)\r
204\r
205 self.assertEqual(len(gc.garbage), 1)\r
206 obj = gc.garbage.pop()\r
207 self.assertEqual(id(obj), id_L)\r
208\r
209 def test_del(self):\r
210 # __del__ methods can trigger collection, make this to happen\r
211 thresholds = gc.get_threshold()\r
212 gc.enable()\r
213 gc.set_threshold(1)\r
214\r
215 class A:\r
216 def __del__(self):\r
217 dir(self)\r
218 a = A()\r
219 del a\r
220\r
221 gc.disable()\r
222 gc.set_threshold(*thresholds)\r
223\r
224 def test_del_newclass(self):\r
225 # __del__ methods can trigger collection, make this to happen\r
226 thresholds = gc.get_threshold()\r
227 gc.enable()\r
228 gc.set_threshold(1)\r
229\r
230 class A(object):\r
231 def __del__(self):\r
232 dir(self)\r
233 a = A()\r
234 del a\r
235\r
236 gc.disable()\r
237 gc.set_threshold(*thresholds)\r
238\r
239 # The following two tests are fragile:\r
240 # They precisely count the number of allocations,\r
241 # which is highly implementation-dependent.\r
242 # For example:\r
243 # - disposed tuples are not freed, but reused\r
244 # - the call to assertEqual somehow avoids building its args tuple\r
245 def test_get_count(self):\r
246 # Avoid future allocation of method object\r
247 assertEqual = self._baseAssertEqual\r
248 gc.collect()\r
249 assertEqual(gc.get_count(), (0, 0, 0))\r
250 a = dict()\r
251 # since gc.collect(), we created two objects:\r
252 # the dict, and the tuple returned by get_count()\r
253 assertEqual(gc.get_count(), (2, 0, 0))\r
254\r
255 def test_collect_generations(self):\r
256 # Avoid future allocation of method object\r
257 assertEqual = self.assertEqual\r
258 gc.collect()\r
259 a = dict()\r
260 gc.collect(0)\r
261 assertEqual(gc.get_count(), (0, 1, 0))\r
262 gc.collect(1)\r
263 assertEqual(gc.get_count(), (0, 0, 1))\r
264 gc.collect(2)\r
265 assertEqual(gc.get_count(), (0, 0, 0))\r
266\r
267 def test_trashcan(self):\r
268 class Ouch:\r
269 n = 0\r
270 def __del__(self):\r
271 Ouch.n = Ouch.n + 1\r
272 if Ouch.n % 17 == 0:\r
273 gc.collect()\r
274\r
275 # "trashcan" is a hack to prevent stack overflow when deallocating\r
276 # very deeply nested tuples etc. It works in part by abusing the\r
277 # type pointer and refcount fields, and that can yield horrible\r
278 # problems when gc tries to traverse the structures.\r
279 # If this test fails (as it does in 2.0, 2.1 and 2.2), it will\r
280 # most likely die via segfault.\r
281\r
282 # Note: In 2.3 the possibility for compiling without cyclic gc was\r
283 # removed, and that in turn allows the trashcan mechanism to work\r
284 # via much simpler means (e.g., it never abuses the type pointer or\r
285 # refcount fields anymore). Since it's much less likely to cause a\r
286 # problem now, the various constants in this expensive (we force a lot\r
287 # of full collections) test are cut back from the 2.2 version.\r
288 gc.enable()\r
289 N = 150\r
290 for count in range(2):\r
291 t = []\r
292 for i in range(N):\r
293 t = [t, Ouch()]\r
294 u = []\r
295 for i in range(N):\r
296 u = [u, Ouch()]\r
297 v = {}\r
298 for i in range(N):\r
299 v = {1: v, 2: Ouch()}\r
300 gc.disable()\r
301\r
302 def test_boom(self):\r
303 class Boom:\r
304 def __getattr__(self, someattribute):\r
305 del self.attr\r
306 raise AttributeError\r
307\r
308 a = Boom()\r
309 b = Boom()\r
310 a.attr = b\r
311 b.attr = a\r
312\r
313 gc.collect()\r
314 garbagelen = len(gc.garbage)\r
315 del a, b\r
316 # a<->b are in a trash cycle now. Collection will invoke\r
317 # Boom.__getattr__ (to see whether a and b have __del__ methods), and\r
318 # __getattr__ deletes the internal "attr" attributes as a side effect.\r
319 # That causes the trash cycle to get reclaimed via refcounts falling to\r
320 # 0, thus mutating the trash graph as a side effect of merely asking\r
321 # whether __del__ exists. This used to (before 2.3b1) crash Python.\r
322 # Now __getattr__ isn't called.\r
323 self.assertEqual(gc.collect(), 4)\r
324 self.assertEqual(len(gc.garbage), garbagelen)\r
325\r
326 def test_boom2(self):\r
327 class Boom2:\r
328 def __init__(self):\r
329 self.x = 0\r
330\r
331 def __getattr__(self, someattribute):\r
332 self.x += 1\r
333 if self.x > 1:\r
334 del self.attr\r
335 raise AttributeError\r
336\r
337 a = Boom2()\r
338 b = Boom2()\r
339 a.attr = b\r
340 b.attr = a\r
341\r
342 gc.collect()\r
343 garbagelen = len(gc.garbage)\r
344 del a, b\r
345 # Much like test_boom(), except that __getattr__ doesn't break the\r
346 # cycle until the second time gc checks for __del__. As of 2.3b1,\r
347 # there isn't a second time, so this simply cleans up the trash cycle.\r
348 # We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get\r
349 # reclaimed this way.\r
350 self.assertEqual(gc.collect(), 4)\r
351 self.assertEqual(len(gc.garbage), garbagelen)\r
352\r
353 def test_boom_new(self):\r
354 # boom__new and boom2_new are exactly like boom and boom2, except use\r
355 # new-style classes.\r
356\r
357 class Boom_New(object):\r
358 def __getattr__(self, someattribute):\r
359 del self.attr\r
360 raise AttributeError\r
361\r
362 a = Boom_New()\r
363 b = Boom_New()\r
364 a.attr = b\r
365 b.attr = a\r
366\r
367 gc.collect()\r
368 garbagelen = len(gc.garbage)\r
369 del a, b\r
370 self.assertEqual(gc.collect(), 4)\r
371 self.assertEqual(len(gc.garbage), garbagelen)\r
372\r
373 def test_boom2_new(self):\r
374 class Boom2_New(object):\r
375 def __init__(self):\r
376 self.x = 0\r
377\r
378 def __getattr__(self, someattribute):\r
379 self.x += 1\r
380 if self.x > 1:\r
381 del self.attr\r
382 raise AttributeError\r
383\r
384 a = Boom2_New()\r
385 b = Boom2_New()\r
386 a.attr = b\r
387 b.attr = a\r
388\r
389 gc.collect()\r
390 garbagelen = len(gc.garbage)\r
391 del a, b\r
392 self.assertEqual(gc.collect(), 4)\r
393 self.assertEqual(len(gc.garbage), garbagelen)\r
394\r
395 def test_get_referents(self):\r
396 alist = [1, 3, 5]\r
397 got = gc.get_referents(alist)\r
398 got.sort()\r
399 self.assertEqual(got, alist)\r
400\r
401 atuple = tuple(alist)\r
402 got = gc.get_referents(atuple)\r
403 got.sort()\r
404 self.assertEqual(got, alist)\r
405\r
406 adict = {1: 3, 5: 7}\r
407 expected = [1, 3, 5, 7]\r
408 got = gc.get_referents(adict)\r
409 got.sort()\r
410 self.assertEqual(got, expected)\r
411\r
412 got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0))\r
413 got.sort()\r
414 self.assertEqual(got, [0, 0] + range(5))\r
415\r
416 self.assertEqual(gc.get_referents(1, 'a', 4j), [])\r
417\r
418 def test_is_tracked(self):\r
419 # Atomic built-in types are not tracked, user-defined objects and\r
420 # mutable containers are.\r
421 # NOTE: types with special optimizations (e.g. tuple) have tests\r
422 # in their own test files instead.\r
423 self.assertFalse(gc.is_tracked(None))\r
424 self.assertFalse(gc.is_tracked(1))\r
425 self.assertFalse(gc.is_tracked(1.0))\r
426 self.assertFalse(gc.is_tracked(1.0 + 5.0j))\r
427 self.assertFalse(gc.is_tracked(True))\r
428 self.assertFalse(gc.is_tracked(False))\r
429 self.assertFalse(gc.is_tracked("a"))\r
430 self.assertFalse(gc.is_tracked(u"a"))\r
431 self.assertFalse(gc.is_tracked(bytearray("a")))\r
432 self.assertFalse(gc.is_tracked(type))\r
433 self.assertFalse(gc.is_tracked(int))\r
434 self.assertFalse(gc.is_tracked(object))\r
435 self.assertFalse(gc.is_tracked(object()))\r
436\r
437 class OldStyle:\r
438 pass\r
439 class NewStyle(object):\r
440 pass\r
441 self.assertTrue(gc.is_tracked(gc))\r
442 self.assertTrue(gc.is_tracked(OldStyle))\r
443 self.assertTrue(gc.is_tracked(OldStyle()))\r
444 self.assertTrue(gc.is_tracked(NewStyle))\r
445 self.assertTrue(gc.is_tracked(NewStyle()))\r
446 self.assertTrue(gc.is_tracked([]))\r
447 self.assertTrue(gc.is_tracked(set()))\r
448\r
449 def test_bug1055820b(self):\r
450 # Corresponds to temp2b.py in the bug report.\r
451\r
452 ouch = []\r
453 def callback(ignored):\r
454 ouch[:] = [wr() for wr in WRs]\r
455\r
456 Cs = [C1055820(i) for i in range(2)]\r
457 WRs = [weakref.ref(c, callback) for c in Cs]\r
458 c = None\r
459\r
460 gc.collect()\r
461 self.assertEqual(len(ouch), 0)\r
462 # Make the two instances trash, and collect again. The bug was that\r
463 # the callback materialized a strong reference to an instance, but gc\r
464 # cleared the instance's dict anyway.\r
465 Cs = None\r
466 gc.collect()\r
467 self.assertEqual(len(ouch), 2) # else the callbacks didn't run\r
468 for x in ouch:\r
469 # If the callback resurrected one of these guys, the instance\r
470 # would be damaged, with an empty __dict__.\r
471 self.assertEqual(x, None)\r
472\r
473class GCTogglingTests(unittest.TestCase):\r
474 def setUp(self):\r
475 gc.enable()\r
476\r
477 def tearDown(self):\r
478 gc.disable()\r
479\r
480 def test_bug1055820c(self):\r
481 # Corresponds to temp2c.py in the bug report. This is pretty\r
482 # elaborate.\r
483\r
484 c0 = C1055820(0)\r
485 # Move c0 into generation 2.\r
486 gc.collect()\r
487\r
488 c1 = C1055820(1)\r
489 c1.keep_c0_alive = c0\r
490 del c0.loop # now only c1 keeps c0 alive\r
491\r
492 c2 = C1055820(2)\r
493 c2wr = weakref.ref(c2) # no callback!\r
494\r
495 ouch = []\r
496 def callback(ignored):\r
497 ouch[:] = [c2wr()]\r
498\r
499 # The callback gets associated with a wr on an object in generation 2.\r
500 c0wr = weakref.ref(c0, callback)\r
501\r
502 c0 = c1 = c2 = None\r
503\r
504 # What we've set up: c0, c1, and c2 are all trash now. c0 is in\r
505 # generation 2. The only thing keeping it alive is that c1 points to\r
506 # it. c1 and c2 are in generation 0, and are in self-loops. There's a\r
507 # global weakref to c2 (c2wr), but that weakref has no callback.\r
508 # There's also a global weakref to c0 (c0wr), and that does have a\r
509 # callback, and that callback references c2 via c2wr().\r
510 #\r
511 # c0 has a wr with callback, which references c2wr\r
512 # ^\r
513 # |\r
514 # | Generation 2 above dots\r
515 #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .\r
516 # | Generation 0 below dots\r
517 # |\r
518 # |\r
519 # ^->c1 ^->c2 has a wr but no callback\r
520 # | | | |\r
521 # <--v <--v\r
522 #\r
523 # So this is the nightmare: when generation 0 gets collected, we see\r
524 # that c2 has a callback-free weakref, and c1 doesn't even have a\r
525 # weakref. Collecting generation 0 doesn't see c0 at all, and c0 is\r
526 # the only object that has a weakref with a callback. gc clears c1\r
527 # and c2. Clearing c1 has the side effect of dropping the refcount on\r
528 # c0 to 0, so c0 goes away (despite that it's in an older generation)\r
529 # and c0's wr callback triggers. That in turn materializes a reference\r
530 # to c2 via c2wr(), but c2 gets cleared anyway by gc.\r
531\r
532 # We want to let gc happen "naturally", to preserve the distinction\r
533 # between generations.\r
534 junk = []\r
535 i = 0\r
536 detector = GC_Detector()\r
537 while not detector.gc_happened:\r
538 i += 1\r
539 if i > 10000:\r
540 self.fail("gc didn't happen after 10000 iterations")\r
541 self.assertEqual(len(ouch), 0)\r
542 junk.append([]) # this will eventually trigger gc\r
543\r
544 self.assertEqual(len(ouch), 1) # else the callback wasn't invoked\r
545 for x in ouch:\r
546 # If the callback resurrected c2, the instance would be damaged,\r
547 # with an empty __dict__.\r
548 self.assertEqual(x, None)\r
549\r
550 def test_bug1055820d(self):\r
551 # Corresponds to temp2d.py in the bug report. This is very much like\r
552 # test_bug1055820c, but uses a __del__ method instead of a weakref\r
553 # callback to sneak in a resurrection of cyclic trash.\r
554\r
555 ouch = []\r
556 class D(C1055820):\r
557 def __del__(self):\r
558 ouch[:] = [c2wr()]\r
559\r
560 d0 = D(0)\r
561 # Move all the above into generation 2.\r
562 gc.collect()\r
563\r
564 c1 = C1055820(1)\r
565 c1.keep_d0_alive = d0\r
566 del d0.loop # now only c1 keeps d0 alive\r
567\r
568 c2 = C1055820(2)\r
569 c2wr = weakref.ref(c2) # no callback!\r
570\r
571 d0 = c1 = c2 = None\r
572\r
573 # What we've set up: d0, c1, and c2 are all trash now. d0 is in\r
574 # generation 2. The only thing keeping it alive is that c1 points to\r
575 # it. c1 and c2 are in generation 0, and are in self-loops. There's\r
576 # a global weakref to c2 (c2wr), but that weakref has no callback.\r
577 # There are no other weakrefs.\r
578 #\r
579 # d0 has a __del__ method that references c2wr\r
580 # ^\r
581 # |\r
582 # | Generation 2 above dots\r
583 #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .\r
584 # | Generation 0 below dots\r
585 # |\r
586 # |\r
587 # ^->c1 ^->c2 has a wr but no callback\r
588 # | | | |\r
589 # <--v <--v\r
590 #\r
591 # So this is the nightmare: when generation 0 gets collected, we see\r
592 # that c2 has a callback-free weakref, and c1 doesn't even have a\r
593 # weakref. Collecting generation 0 doesn't see d0 at all. gc clears\r
594 # c1 and c2. Clearing c1 has the side effect of dropping the refcount\r
595 # on d0 to 0, so d0 goes away (despite that it's in an older\r
596 # generation) and d0's __del__ triggers. That in turn materializes\r
597 # a reference to c2 via c2wr(), but c2 gets cleared anyway by gc.\r
598\r
599 # We want to let gc happen "naturally", to preserve the distinction\r
600 # between generations.\r
601 detector = GC_Detector()\r
602 junk = []\r
603 i = 0\r
604 while not detector.gc_happened:\r
605 i += 1\r
606 if i > 10000:\r
607 self.fail("gc didn't happen after 10000 iterations")\r
608 self.assertEqual(len(ouch), 0)\r
609 junk.append([]) # this will eventually trigger gc\r
610\r
611 self.assertEqual(len(ouch), 1) # else __del__ wasn't invoked\r
612 for x in ouch:\r
613 # If __del__ resurrected c2, the instance would be damaged, with an\r
614 # empty __dict__.\r
615 self.assertEqual(x, None)\r
616\r
617def test_main():\r
618 enabled = gc.isenabled()\r
619 gc.disable()\r
620 assert not gc.isenabled()\r
621 debug = gc.get_debug()\r
622 gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak\r
623\r
624 try:\r
625 gc.collect() # Delete 2nd generation garbage\r
626 run_unittest(GCTests, GCTogglingTests)\r
627 finally:\r
628 gc.set_debug(debug)\r
629 # test gc.enable() even if GC is disabled by default\r
630 if verbose:\r
631 print "restoring automatic collection"\r
632 # make sure to always test gc.enable()\r
633 gc.enable()\r
634 assert gc.isenabled()\r
635 if not enabled:\r
636 gc.disable()\r
637\r
638if __name__ == "__main__":\r
639 test_main()\r