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.
This commit is contained in:
Brett Cannon 2011-02-21 19:29:56 +00:00
parent 4709ec0686
commit 31f5929c1e
15 changed files with 283 additions and 221 deletions

View file

@ -1373,6 +1373,7 @@ def out(s):
# 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 @@ def out(s):
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:

View file

@ -5,7 +5,7 @@
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 @@ def test_reduce_calls_base(self):
y = self.loads(s)
self.assertEqual(y._reduce_called, 1)
@no_tracing
def test_bad_getattr(self):
x = BadGetattr()
for proto in 0, 1:

View file

@ -827,7 +827,7 @@ def __init__(self, testname, verbose=0, quiet=False):
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 @@ def restore_sys_path_hooks(self, saved_hooks):
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_):

View file

@ -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."""

View file

@ -5,6 +5,7 @@
from test import support
import doctest
import os
import sys
# NOTE: There are some additional tests relating to interaction with
@ -373,7 +374,7 @@ def test_DocTestFinder(): r"""
>>> tests = finder.find(sample_func)
>>> print(tests) # doctest: +ELLIPSIS
[<DocTest sample_func from ...:17 (1 example)>]
[<DocTest sample_func from ...:18 (1 example)>]
The exact name depends on how test_doctest was invoked, so allow for
leading path components.
@ -1686,6 +1687,7 @@ def test_debug(): r"""
"""
if not hasattr(sys, 'gettrace') or not sys.gettrace():
def test_pdb_set_trace():
"""Using pdb.set_trace from a doctest.

View file

@ -7,7 +7,7 @@
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 @@ def __init__(self, fancy_arg):
x = DerivedException(fancy_arg=42)
self.assertEqual(x.fancy_arg, 42)
@no_tracing
def testInfiniteRecursion(self):
def f():
return f()
@ -631,6 +632,7 @@ def testUnicodeChangeAttributes(self):
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 @@ def inner():
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:

View file

@ -2214,6 +2214,7 @@ def test_errors_property(self):
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 @@ def test_interrupted_write_buffered(self):
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

View file

@ -20,9 +20,12 @@ def __init__(self, input):
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():

View file

@ -220,6 +220,7 @@ def do(bad):
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

View file

@ -6,7 +6,8 @@
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 @@ def test_zipfile_error(self):
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__'

View file

@ -1,5 +1,5 @@
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,14 +496,15 @@ def m(self):
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
class C:
@ -511,8 +512,6 @@ def f(self):
return x
self.assertEqual(x, 12) # Used to raise UnboundLocalError
finally:
sys.settrace(None)
def testBoundAndFree(self):
# var is bound and free in class
@ -527,6 +526,7 @@ def m(self):
inst = f(3)()
self.assertEqual(inst.a, inst.m())
@cpython_only
def testInteractionWithTraceFunc(self):
import sys
@ -543,6 +543,7 @@ def adaptgetter(name, klass, getter):
class TestClass:
pass
self.addCleanup(sys.settrace, sys.gettrace())
sys.settrace(tracer)
adaptgetter("foo", TestClass, (1, ""))
sys.settrace(None)

View file

@ -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 @@ def test_16_blank_lines(self):
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 @@ def test_17_no_jump_out_of_finally_block(self):
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):

View file

@ -102,6 +102,7 @@ def static_method_linear(y):
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 @@ def test_exec_counts(self):
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 @@ def test_inst_method_calling(self):
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 @@ def test_loop_caller_importing(self):
# 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)

View file

@ -163,20 +163,24 @@ def test_doctest_issue4197(self):
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:

View file

@ -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?
=========================