Kaydet (Commit) 31f5929c authored tarafından Brett Cannon's avatar Brett Cannon

Issue #10990: Prevent tests from clobbering a set trace function.

Many tests simply didn't care if they unset a pre-existing trace function. This
made test coverage impossible. This patch fixes various tests to put back any
pre-existing trace function. It also introduces test.support.no_tracing as a
decorator which will temporarily unset the trace function for tests which
simply fail otherwise.

Thanks to Kristian Vlaardingerbroek for helping to find the cause of various
trace function unsets.
üst 4709ec06
......@@ -1373,6 +1373,7 @@ class DocTestRunner:
# Note that the interactive output will go to *our*
# save_stdout, even if that's not the real sys.stdout; this
# allows us to write test cases for the set_trace behavior.
save_trace = sys.gettrace()
save_set_trace = pdb.set_trace
self.debugger = _OutputRedirectingPdb(save_stdout)
self.debugger.reset()
......@@ -1392,6 +1393,7 @@ class DocTestRunner:
finally:
sys.stdout = save_stdout
pdb.set_trace = save_set_trace
sys.settrace(save_trace)
linecache.getlines = self.save_linecache_getlines
sys.displayhook = save_displayhook
if clear_globs:
......
......@@ -5,7 +5,7 @@ import pickletools
import copyreg
from http.cookies import SimpleCookie
from test.support import TestFailed, TESTFN, run_with_locale
from test.support import TestFailed, TESTFN, run_with_locale, no_tracing
from pickle import bytes_types
......@@ -1002,6 +1002,7 @@ class AbstractPickleTests(unittest.TestCase):
y = self.loads(s)
self.assertEqual(y._reduce_called, 1)
@no_tracing
def test_bad_getattr(self):
x = BadGetattr()
for proto in 0, 1:
......
......@@ -827,7 +827,7 @@ class saved_test_environment:
resources = ('sys.argv', 'cwd', 'sys.stdin', 'sys.stdout', 'sys.stderr',
'os.environ', 'sys.path', 'sys.path_hooks', '__import__',
'warnings.filters', 'asyncore.socket_map',
'logging._handlers', 'logging._handlerList')
'logging._handlers', 'logging._handlerList', 'sys.gettrace')
def get_sys_argv(self):
return id(sys.argv), sys.argv, sys.argv[:]
......@@ -874,6 +874,11 @@ class saved_test_environment:
sys.path_hooks = saved_hooks[1]
sys.path_hooks[:] = saved_hooks[2]
def get_sys_gettrace(self):
return sys.gettrace()
def restore_sys_gettrace(self, trace_fxn):
sys.settrace(trace_fxn)
def get___import__(self):
return builtins.__import__
def restore___import__(self, import_):
......
......@@ -1108,6 +1108,21 @@ def check_impl_detail(**guards):
return guards.get(platform.python_implementation().lower(), default)
def no_tracing(func):
"""Decorator to temporarily turn off tracing for the duration of a test."""
if not hasattr(sys, 'gettrace'):
return func
else:
@functools.wraps(func)
def wrapper(*args, **kwargs):
original_trace = sys.gettrace()
try:
sys.settrace(None)
return func(*args, **kwargs)
finally:
sys.settrace(original_trace)
return wrapper
def _run_suite(suite):
"""Run tests from a unittest.TestSuite-derived class."""
......
This diff is collapsed.
......@@ -7,7 +7,7 @@ import pickle
import weakref
from test.support import (TESTFN, unlink, run_unittest, captured_output,
gc_collect, cpython_only)
gc_collect, cpython_only, no_tracing)
# XXX This is not really enough, each *operation* should be tested!
......@@ -388,6 +388,7 @@ class ExceptionTests(unittest.TestCase):
x = DerivedException(fancy_arg=42)
self.assertEqual(x.fancy_arg, 42)
@no_tracing
def testInfiniteRecursion(self):
def f():
return f()
......@@ -631,6 +632,7 @@ class ExceptionTests(unittest.TestCase):
u.start = 1000
self.assertEqual(str(u), "can't translate characters in position 1000-4: 965230951443685724997")
@no_tracing
def test_badisinstance(self):
# Bug #2542: if issubclass(e, MyException) raises an exception,
# it should be ignored
......@@ -741,6 +743,7 @@ class ExceptionTests(unittest.TestCase):
self.fail("MemoryError not raised")
self.assertEqual(wr(), None)
@no_tracing
def test_recursion_error_cleanup(self):
# Same test as above, but with "recursion exceeded" errors
class C:
......
......@@ -2214,6 +2214,7 @@ class TextIOWrapperTest(unittest.TestCase):
with self.open(support.TESTFN, "w", errors="replace") as f:
self.assertEqual(f.errors, "replace")
@support.no_tracing
@unittest.skipUnless(threading, 'Threading required for this test.')
def test_threads_write(self):
# Issue6750: concurrent writes could duplicate data
......@@ -2669,6 +2670,7 @@ class SignalsTest(unittest.TestCase):
def test_interrupted_write_text(self):
self.check_interrupted_write("xy", b"xy", mode="w", encoding="ascii")
@support.no_tracing
def check_reentrant_write(self, data, **fdopen_kwargs):
def on_alarm(*args):
# Will be called reentrantly from the same thread
......
......@@ -20,9 +20,12 @@ class PdbTestInput(object):
def __enter__(self):
self.real_stdin = sys.stdin
sys.stdin = _FakeInput(self.input)
self.orig_trace = sys.gettrace() if hasattr(sys, 'gettrace') else None
def __exit__(self, *exc):
sys.stdin = self.real_stdin
if self.orig_trace:
sys.settrace(self.orig_trace)
def test_pdb_displayhook():
......
......@@ -220,6 +220,7 @@ class MiscTest(unittest.TestCase):
for func in (do, operator.not_):
self.assertRaises(Exc, func, Bad())
@support.no_tracing
def test_recursion(self):
# Check that comparison for recursive objects fails gracefully
from collections import UserList
......
......@@ -6,7 +6,8 @@ import sys
import re
import tempfile
import py_compile
from test.support import forget, make_legacy_pyc, run_unittest, unload, verbose
from test.support import (
forget, make_legacy_pyc, run_unittest, unload, verbose, no_tracing)
from test.script_helper import (
make_pkg, make_script, make_zip_pkg, make_zip_script, temp_dir)
......@@ -395,6 +396,7 @@ argv0 = sys.argv[0]
msg = "can't find '__main__' module in %r" % zip_name
self._check_import_error(zip_name, msg)
@no_tracing
def test_main_recursion_error(self):
with temp_dir() as script_dir, temp_dir() as dummy_dir:
mod_name = '__main__'
......
import unittest
from test.support import check_syntax_error, run_unittest
from test.support import check_syntax_error, cpython_only, run_unittest
class ScopeTests(unittest.TestCase):
......@@ -496,23 +496,22 @@ class ScopeTests(unittest.TestCase):
self.assertNotIn("x", varnames)
self.assertIn("y", varnames)
@cpython_only
def testLocalsClass_WithTrace(self):
# Issue23728: after the trace function returns, the locals()
# dictionary is used to update all variables, this used to
# include free variables. But in class statements, free
# variables are not inserted...
import sys
self.addCleanup(sys.settrace, sys.gettrace())
sys.settrace(lambda a,b,c:None)
try:
x = 12
x = 12
class C:
def f(self):
return x
class C:
def f(self):
return x
self.assertEqual(x, 12) # Used to raise UnboundLocalError
finally:
sys.settrace(None)
self.assertEqual(x, 12) # Used to raise UnboundLocalError
def testBoundAndFree(self):
# var is bound and free in class
......@@ -527,6 +526,7 @@ class ScopeTests(unittest.TestCase):
inst = f(3)()
self.assertEqual(inst.a, inst.m())
@cpython_only
def testInteractionWithTraceFunc(self):
import sys
......@@ -543,6 +543,7 @@ class ScopeTests(unittest.TestCase):
class TestClass:
pass
self.addCleanup(sys.settrace, sys.gettrace())
sys.settrace(tracer)
adaptgetter("foo", TestClass, (1, ""))
sys.settrace(None)
......
......@@ -251,6 +251,7 @@ class TraceTestCase(unittest.TestCase):
def setUp(self):
self.using_gc = gc.isenabled()
gc.disable()
self.addCleanup(sys.settrace, sys.gettrace())
def tearDown(self):
if self.using_gc:
......@@ -389,6 +390,9 @@ class TraceTestCase(unittest.TestCase):
class RaisingTraceFuncTestCase(unittest.TestCase):
def setUp(self):
self.addCleanup(sys.settrace, sys.gettrace())
def trace(self, frame, event, arg):
"""A trace function that raises an exception in response to a
specific trace event."""
......@@ -688,6 +692,10 @@ def no_jump_without_trace_function():
class JumpTestCase(unittest.TestCase):
def setUp(self):
self.addCleanup(sys.settrace, sys.gettrace())
sys.settrace(None)
def compare_jump_output(self, expected, received):
if received != expected:
self.fail( "Outputs don't match:\n" +
......@@ -739,6 +747,8 @@ class JumpTestCase(unittest.TestCase):
def test_18_no_jump_to_non_integers(self):
self.run_test(no_jump_to_non_integers)
def test_19_no_jump_without_trace_function(self):
# Must set sys.settrace(None) in setUp(), else condition is not
# triggered.
no_jump_without_trace_function()
def test_20_large_function(self):
......
......@@ -102,6 +102,7 @@ class TracedClass(object):
class TestLineCounts(unittest.TestCase):
"""White-box testing of line-counting, via runfunc"""
def setUp(self):
self.addCleanup(sys.settrace, sys.gettrace())
self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
self.my_py_filename = fix_ext_py(__file__)
......@@ -192,6 +193,7 @@ class TestRunExecCounts(unittest.TestCase):
"""A simple sanity test of line-counting, via runctx (exec)"""
def setUp(self):
self.my_py_filename = fix_ext_py(__file__)
self.addCleanup(sys.settrace, sys.gettrace())
def test_exec_counts(self):
self.tracer = Trace(count=1, trace=0, countfuncs=0, countcallers=0)
......@@ -218,6 +220,7 @@ class TestRunExecCounts(unittest.TestCase):
class TestFuncs(unittest.TestCase):
"""White-box testing of funcs tracing"""
def setUp(self):
self.addCleanup(sys.settrace, sys.gettrace())
self.tracer = Trace(count=0, trace=0, countfuncs=1)
self.filemod = my_file_and_modname()
......@@ -257,6 +260,7 @@ class TestFuncs(unittest.TestCase):
class TestCallers(unittest.TestCase):
"""White-box testing of callers tracing"""
def setUp(self):
self.addCleanup(sys.settrace, sys.gettrace())
self.tracer = Trace(count=0, trace=0, countcallers=1)
self.filemod = my_file_and_modname()
......@@ -280,6 +284,9 @@ class TestCallers(unittest.TestCase):
# Created separately for issue #3821
class TestCoverage(unittest.TestCase):
def setUp(self):
self.addCleanup(sys.settrace, sys.gettrace())
def tearDown(self):
rmtree(TESTFN)
unlink(TESTFN)
......
......@@ -163,20 +163,24 @@ class ZipSupportTests(unittest.TestCase):
test_zipped_doctest.test_DocTestRunner.verbose_flag,
test_zipped_doctest.test_Example,
test_zipped_doctest.test_debug,
test_zipped_doctest.test_pdb_set_trace,
test_zipped_doctest.test_pdb_set_trace_nested,
test_zipped_doctest.test_testsource,
test_zipped_doctest.test_trailing_space_in_test,
test_zipped_doctest.test_DocTestSuite,
test_zipped_doctest.test_DocTestFinder,
]
# These remaining tests are the ones which need access
# These tests are the ones which need access
# to the data files, so we don't run them
fail_due_to_missing_data_files = [
test_zipped_doctest.test_DocFileSuite,
test_zipped_doctest.test_testfile,
test_zipped_doctest.test_unittest_reportflags,
]
# These tests are skipped when a trace funciton is set
can_fail_due_to_tracing = [
test_zipped_doctest.test_pdb_set_trace,
test_zipped_doctest.test_pdb_set_trace_nested,
]
for obj in known_good_tests:
_run_object_doctest(obj, test_zipped_doctest)
finally:
......
......@@ -18,6 +18,10 @@ Library
- Issue #10276: Fix the results of zlib.crc32() and zlib.adler32() on buffers
larger than 4GB. Patch by Nadeem Vawda.
Tests
- Issue #10990: Prevent tests from clobbering a set trace function.
What's New in Python 3.2?
=========================
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment