diff options
Diffstat (limited to 'Lib/test/test_gc.py')
-rw-r--r-- | Lib/test/test_gc.py | 69 |
1 files changed, 66 insertions, 3 deletions
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py index 8d806db..16b2242 100644 --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -1,14 +1,17 @@ import unittest from test.support import (verbose, refcount_test, run_unittest, strip_python_stderr, cpython_only, start_threads, - temp_dir, requires_type_collecting, TESTFN, unlink) + temp_dir, requires_type_collecting, TESTFN, unlink, + import_module) from test.support.script_helper import assert_python_ok, make_script +import gc import sys +import sysconfig +import textwrap +import threading import time -import gc import weakref -import threading try: from _testcapi import with_tp_del @@ -62,6 +65,14 @@ class Uncollectable(object): def __tp_del__(self): pass +if sysconfig.get_config_vars().get('PY_CFLAGS', ''): + BUILD_WITH_NDEBUG = ('-DNDEBUG' in sysconfig.get_config_vars()['PY_CFLAGS']) +else: + # Usually, sys.gettotalrefcount() is only present if Python has been + # compiled in debug mode. If it's missing, expect that Python has + # been released in release mode: with NDEBUG defined. + BUILD_WITH_NDEBUG = (not hasattr(sys, 'gettotalrefcount')) + ### Tests ############################################################################### @@ -878,6 +889,58 @@ class GCCallbackTests(unittest.TestCase): self.assertEqual(len(gc.garbage), 0) + @unittest.skipIf(BUILD_WITH_NDEBUG, + 'built with -NDEBUG') + def test_refcount_errors(self): + self.preclean() + # Verify the "handling" of objects with broken refcounts + + # Skip the test if ctypes is not available + import_module("ctypes") + + import subprocess + code = textwrap.dedent(''' + from test.support import gc_collect, SuppressCrashReport + + a = [1, 2, 3] + b = [a] + + # Avoid coredump when Py_FatalError() calls abort() + SuppressCrashReport().__enter__() + + # Simulate the refcount of "a" being too low (compared to the + # references held on it by live data), but keeping it above zero + # (to avoid deallocating it): + import ctypes + ctypes.pythonapi.Py_DecRef(ctypes.py_object(a)) + + # The garbage collector should now have a fatal error + # when it reaches the broken object + gc_collect() + ''') + p = subprocess.Popen([sys.executable, "-c", code], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + p.stdout.close() + p.stderr.close() + # Verify that stderr has a useful error message: + self.assertRegex(stderr, + br'gcmodule\.c:[0-9]+: gc_decref: Assertion "gc_get_refs\(g\) > 0" failed.') + self.assertRegex(stderr, + br'refcount is too small') + self.assertRegex(stderr, + br'object : \[1, 2, 3\]') + self.assertRegex(stderr, + br'type : list') + self.assertRegex(stderr, + br'refcount: 1') + # "address : 0x7fb5062efc18" + # "address : 7FB5062EFC18" + self.assertRegex(stderr, + br'address : [0-9a-fA-Fx]+') + + class GCTogglingTests(unittest.TestCase): def setUp(self): gc.enable() |