Skip to content

Commit f27c9ca

Browse files
authored
fix: save coverage data on SIGTERM (#1600)
* Add test that reproduces the issue * Suggested fix - always save data in sigterm exit flow * Address test failures on MacOS due to lack of 'Terminated' output on SIGTERM
1 parent 3bc7d2c commit f27c9ca

File tree

2 files changed

+27
-2
lines changed

2 files changed

+27
-2
lines changed

coverage/control.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ def _atexit(self, event: str = "atexit") -> None:
653653
self._debug.write(f"{event}: pid: {os.getpid()}, instance: {self!r}")
654654
if self._started:
655655
self.stop()
656-
if self._auto_save:
656+
if self._auto_save or event == "sigterm":
657657
self.save()
658658

659659
def _on_sigterm(self, signum_unused: int, frame_unused: Optional[FrameType]) -> None:

tests/test_concurrency.py

+26-1
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,7 @@ class SigtermTest(CoverageTest):
705705
"""Tests of our handling of SIGTERM."""
706706

707707
@pytest.mark.parametrize("sigterm", [False, True])
708-
def test_sigterm_saves_data(self, sigterm: bool) -> None:
708+
def test_sigterm_multiprocessing_saves_data(self, sigterm: bool) -> None:
709709
# A terminated process should save its coverage data.
710710
self.make_file("clobbered.py", """\
711711
import multiprocessing
@@ -751,6 +751,31 @@ def subproc(x):
751751
expected = "clobbered.py 17 5 71% 5-10"
752752
assert self.squeezed_lines(out)[2] == expected
753753

754+
def test_sigterm_threading_saves_data(self) -> None:
755+
# A terminated process should save its coverage data.
756+
self.make_file("handler.py", """\
757+
import os, signal
758+
759+
print("START", flush=True)
760+
print("SIGTERM", flush=True)
761+
os.kill(os.getpid(), signal.SIGTERM)
762+
print("NOT HERE", flush=True)
763+
""")
764+
self.make_file(".coveragerc", """\
765+
[run]
766+
# The default concurrency option.
767+
concurrency = thread
768+
sigterm = true
769+
""")
770+
out = self.run_command("coverage run handler.py")
771+
if env.LINUX:
772+
assert out == "START\nSIGTERM\nTerminated\n"
773+
else:
774+
assert out == "START\nSIGTERM\n"
775+
out = self.run_command("coverage report -m")
776+
expected = "handler.py 5 1 80% 6"
777+
assert self.squeezed_lines(out)[2] == expected
778+
754779
def test_sigterm_still_runs(self) -> None:
755780
# A terminated process still runs its own SIGTERM handler.
756781
self.make_file("handler.py", """\

0 commit comments

Comments
 (0)