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