Skip to content

Commit fdaa822

Browse files
committed
test: add more tests of run_python_file
The tests in test_process run the exception handling in execfile.py, but only under coverage, so metacov can't see it. These smaller tests exercise the code without coverage on top.
1 parent cedd319 commit fdaa822

File tree

3 files changed

+95
-9
lines changed

3 files changed

+95
-9
lines changed

coverage/execfile.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,7 @@ def run(self):
220220

221221
# Call the excepthook.
222222
try:
223-
if hasattr(err, "__traceback__"):
224-
err.__traceback__ = err.__traceback__.tb_next
223+
err.__traceback__ = err.__traceback__.tb_next
225224
sys.excepthook(typ, err, tb.tb_next)
226225
except SystemExit: # pylint: disable=try-except-raise
227226
raise
@@ -231,8 +230,7 @@ def run(self):
231230
sys.stderr.write("Error in sys.excepthook:\n")
232231
typ2, err2, tb2 = sys.exc_info()
233232
err2.__suppress_context__ = True
234-
if hasattr(err2, "__traceback__"):
235-
err2.__traceback__ = err2.__traceback__.tb_next
233+
err2.__traceback__ = err2.__traceback__.tb_next
236234
sys.__excepthook__(typ2, err2, tb2.tb_next)
237235
sys.stderr.write("\nOriginal exception was:\n")
238236
raise ExceptionDuringRun(typ, err, tb.tb_next) from exc

tests/test_execfile.py

+86-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import pytest
1515

1616
from coverage import env
17-
from coverage.exceptions import NoCode, NoSource
17+
from coverage.exceptions import NoCode, NoSource, ExceptionDuringRun
1818
from coverage.execfile import run_python_file, run_python_module
1919
from coverage.files import python_reported_file
2020

@@ -102,6 +102,91 @@ def test_directory_without_main(self):
102102
with pytest.raises(NoSource, match="Can't find '__main__' module in 'without_main'"):
103103
run_python_file(["without_main"])
104104

105+
def test_code_throws(self):
106+
self.make_file("throw.py", """\
107+
class MyException(Exception):
108+
pass
109+
110+
def f1():
111+
print("about to raise..")
112+
raise MyException("hey!")
113+
114+
def f2():
115+
f1()
116+
117+
f2()
118+
""")
119+
120+
with pytest.raises(SystemExit) as exc_info:
121+
run_python_file(["throw.py"])
122+
assert exc_info.value.args == (1,)
123+
assert self.stdout() == "about to raise..\n"
124+
assert self.stderr() == ""
125+
126+
def test_code_exits(self):
127+
self.make_file("exit.py", """\
128+
import sys
129+
def f1():
130+
print("about to exit..")
131+
sys.exit(17)
132+
133+
def f2():
134+
f1()
135+
136+
f2()
137+
""")
138+
139+
with pytest.raises(SystemExit) as exc_info:
140+
run_python_file(["exit.py"])
141+
assert exc_info.value.args == (17,)
142+
assert self.stdout() == "about to exit..\n"
143+
assert self.stderr() == ""
144+
145+
@pytest.mark.skipif(not env.CPYTHON,
146+
reason="non-CPython handles excepthook exits differently, punt for now."
147+
)
148+
def test_excepthook_exit(self):
149+
self.make_file("excepthook_exit.py", """\
150+
import sys
151+
152+
def excepthook(*args):
153+
print('in excepthook')
154+
sys.exit(0)
155+
156+
sys.excepthook = excepthook
157+
158+
raise RuntimeError('Error Outside')
159+
""")
160+
with pytest.raises(SystemExit):
161+
run_python_file(["excepthook_exit.py"])
162+
cov_out = self.stdout()
163+
assert cov_out == "in excepthook\n"
164+
165+
@pytest.mark.skipif(env.PYPY, reason="PyPy handles excepthook throws differently.")
166+
def test_excepthook_throw(self):
167+
self.make_file("excepthook_throw.py", """\
168+
import sys
169+
170+
def excepthook(*args):
171+
# Write this message to stderr so that we don't have to deal
172+
# with interleaved stdout/stderr comparisons in the assertions
173+
# in the test.
174+
sys.stderr.write('in excepthook\\n')
175+
raise RuntimeError('Error Inside')
176+
177+
sys.excepthook = excepthook
178+
179+
raise RuntimeError('Error Outside')
180+
""")
181+
with pytest.raises(ExceptionDuringRun) as exc_info:
182+
run_python_file(["excepthook_throw.py"])
183+
# The ExceptionDuringRun exception has the RuntimeError as its argument.
184+
assert exc_info.value.args[1].args[0] == "Error Outside"
185+
stderr = self.stderr()
186+
assert "in excepthook\n" in stderr
187+
assert "Error in sys.excepthook:\n" in stderr
188+
assert "RuntimeError: Error Inside" in stderr
189+
105190

106191
class RunPycFileTest(CoverageTest):
107192
"""Test cases for `run_python_file`."""

tests/test_process.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -468,8 +468,11 @@ def test_running_missing_file(self):
468468

469469
def test_code_throws(self):
470470
self.make_file("throw.py", """\
471+
class MyException(Exception):
472+
pass
473+
471474
def f1():
472-
raise Exception("hey!")
475+
raise MyException("hey!")
473476
474477
def f2():
475478
f1()
@@ -488,9 +491,9 @@ def f2():
488491

489492
# But also make sure that the output is what we expect.
490493
path = python_reported_file('throw.py')
491-
msg = f'File "{re.escape(path)}", line 5,? in f2'
494+
msg = f'File "{re.escape(path)}", line 8, in f2'
492495
assert re.search(msg, out)
493-
assert 'raise Exception("hey!")' in out
496+
assert 'raise MyException("hey!")' in out
494497
assert status == 1
495498

496499
def test_code_exits(self):
@@ -1121,7 +1124,7 @@ def excepthook(*args):
11211124
assert cov_st == py_st
11221125
assert cov_st == 0
11231126

1124-
assert "in excepthook" in py_out
1127+
assert py_out == "in excepthook\n"
11251128
assert cov_out == py_out
11261129

11271130
@pytest.mark.skipif(env.PYPY, reason="PyPy handles excepthook throws differently.")

0 commit comments

Comments
 (0)