aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/test/test_gc.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_gc.py')
-rw-r--r--Lib/test/test_gc.py118
1 files changed, 86 insertions, 32 deletions
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
index fd874c3a2c3..e1c124d160f 100644
--- a/Lib/test/test_gc.py
+++ b/Lib/test/test_gc.py
@@ -1,5 +1,5 @@
import unittest
-from test.test_support import verbose, run_unittest
+from test.support import verbose, run_unittest, strip_python_stderr
import sys
import time
import gc
@@ -176,7 +176,7 @@ class GCTests(unittest.TestCase):
# Tricky: f -> d -> f, code should call d.clear() after the exec to
# break the cycle.
d = {}
- exec("def f(): pass\n") in d
+ exec("def f(): pass\n", d)
gc.collect()
del d
self.assertEqual(gc.collect(), 2)
@@ -245,30 +245,41 @@ class GCTests(unittest.TestCase):
# The following two tests are fragile:
# They precisely count the number of allocations,
# which is highly implementation-dependent.
- # For example:
- # - disposed tuples are not freed, but reused
- # - the call to assertEqual somehow avoids building its args tuple
+ # For example, disposed tuples are not freed, but reused.
+ # To minimize variations, though, we first store the get_count() results
+ # and check them at the end.
def test_get_count(self):
- # Avoid future allocation of method object
- assertEqual = self._baseAssertEqual
gc.collect()
- assertEqual(gc.get_count(), (0, 0, 0))
- a = dict()
- # since gc.collect(), we created two objects:
- # the dict, and the tuple returned by get_count()
- assertEqual(gc.get_count(), (2, 0, 0))
+ a, b, c = gc.get_count()
+ x = []
+ d, e, f = gc.get_count()
+ self.assertEqual((b, c), (0, 0))
+ self.assertEqual((e, f), (0, 0))
+ # This is less fragile than asserting that a equals 0.
+ self.assertLess(a, 5)
+ # Between the two calls to get_count(), at least one object was
+ # created (the list).
+ self.assertGreater(d, a)
def test_collect_generations(self):
- # Avoid future allocation of method object
- assertEqual = self.assertEqual
gc.collect()
- a = dict()
+ # This object will "trickle" into generation N + 1 after
+ # each call to collect(N)
+ x = []
gc.collect(0)
- assertEqual(gc.get_count(), (0, 1, 0))
+ # x is now in gen 1
+ a, b, c = gc.get_count()
gc.collect(1)
- assertEqual(gc.get_count(), (0, 0, 1))
+ # x is now in gen 2
+ d, e, f = gc.get_count()
gc.collect(2)
- assertEqual(gc.get_count(), (0, 0, 0))
+ # x is now in gen 3
+ g, h, i = gc.get_count()
+ # We don't check a, d, g since their exact values depends on
+ # internal implementation details of the interpreter.
+ self.assertEqual((b, c), (1, 0))
+ self.assertEqual((e, f), (0, 1))
+ self.assertEqual((h, i), (0, 0))
def test_trashcan(self):
class Ouch:
@@ -349,8 +360,8 @@ class GCTests(unittest.TestCase):
while not exit:
make_nested()
- old_checkinterval = sys.getcheckinterval()
- sys.setcheckinterval(3)
+ old_switchinterval = sys.getswitchinterval()
+ sys.setswitchinterval(1e-5)
try:
exit = False
threads = []
@@ -364,7 +375,7 @@ class GCTests(unittest.TestCase):
for t in threads:
t.join()
finally:
- sys.setcheckinterval(old_checkinterval)
+ sys.setswitchinterval(old_switchinterval)
gc.collect()
self.assertEqual(len(C.inits), len(C.dels))
@@ -480,7 +491,7 @@ class GCTests(unittest.TestCase):
got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0))
got.sort()
- self.assertEqual(got, [0, 0] + range(5))
+ self.assertEqual(got, [0, 0] + list(range(5)))
self.assertEqual(gc.get_referents(1, 'a', 4j), [])
@@ -495,23 +506,19 @@ class GCTests(unittest.TestCase):
self.assertFalse(gc.is_tracked(1.0 + 5.0j))
self.assertFalse(gc.is_tracked(True))
self.assertFalse(gc.is_tracked(False))
+ self.assertFalse(gc.is_tracked(b"a"))
self.assertFalse(gc.is_tracked("a"))
- self.assertFalse(gc.is_tracked(u"a"))
- self.assertFalse(gc.is_tracked(bytearray("a")))
+ self.assertFalse(gc.is_tracked(bytearray(b"a")))
self.assertFalse(gc.is_tracked(type))
self.assertFalse(gc.is_tracked(int))
self.assertFalse(gc.is_tracked(object))
self.assertFalse(gc.is_tracked(object()))
- class OldStyle:
- pass
- class NewStyle(object):
+ class UserClass:
pass
self.assertTrue(gc.is_tracked(gc))
- self.assertTrue(gc.is_tracked(OldStyle))
- self.assertTrue(gc.is_tracked(OldStyle()))
- self.assertTrue(gc.is_tracked(NewStyle))
- self.assertTrue(gc.is_tracked(NewStyle()))
+ self.assertTrue(gc.is_tracked(UserClass))
+ self.assertTrue(gc.is_tracked(UserClass()))
self.assertTrue(gc.is_tracked([]))
self.assertTrue(gc.is_tracked(set()))
@@ -539,6 +546,53 @@ class GCTests(unittest.TestCase):
# would be damaged, with an empty __dict__.
self.assertEqual(x, None)
+ def test_garbage_at_shutdown(self):
+ import subprocess
+ code = """if 1:
+ import gc
+ class X:
+ def __init__(self, name):
+ self.name = name
+ def __repr__(self):
+ return "<X %%r>" %% self.name
+ def __del__(self):
+ pass
+
+ x = X('first')
+ x.x = x
+ x.y = X('second')
+ del x
+ gc.set_debug(%s)
+ """
+ def run_command(code):
+ p = subprocess.Popen([sys.executable, "-Wd", "-c", code],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ p.stdout.close()
+ p.stderr.close()
+ self.assertEqual(p.returncode, 0)
+ self.assertEqual(stdout.strip(), b"")
+ return strip_python_stderr(stderr)
+
+ stderr = run_command(code % "0")
+ self.assertIn(b"ResourceWarning: gc: 2 uncollectable objects at "
+ b"shutdown; use", stderr)
+ self.assertNotIn(b"<X 'first'>", stderr)
+ # With DEBUG_UNCOLLECTABLE, the garbage list gets printed
+ stderr = run_command(code % "gc.DEBUG_UNCOLLECTABLE")
+ self.assertIn(b"ResourceWarning: gc: 2 uncollectable objects at "
+ b"shutdown", stderr)
+ self.assertTrue(
+ (b"[<X 'first'>, <X 'second'>]" in stderr) or
+ (b"[<X 'second'>, <X 'first'>]" in stderr), stderr)
+ # With DEBUG_SAVEALL, no additional message should get printed
+ # (because gc.garbage also contains normally reclaimable cyclic
+ # references, and its elements get printed at runtime anyway).
+ stderr = run_command(code % "gc.DEBUG_SAVEALL")
+ self.assertNotIn(b"uncollectable objects at shutdown", stderr)
+
+
class GCTogglingTests(unittest.TestCase):
def setUp(self):
gc.enable()
@@ -697,7 +751,7 @@ def test_main():
gc.set_debug(debug)
# test gc.enable() even if GC is disabled by default
if verbose:
- print "restoring automatic collection"
+ print("restoring automatic collection")
# make sure to always test gc.enable()
gc.enable()
assert gc.isenabled()