Skip to content

Commit 4c14f54

Browse files
committed
WIP: Handle interrupted test runs
This uses a instance to store the exitstatus from `pytest_sessionfinish`, although that could probably just also be done on the module?! With `exitstatus` as an argument to the `pytest_terminal_summary` hook it would be easier (pytest-dev/pytest#1809).
1 parent 32ea414 commit 4c14f54

File tree

1 file changed

+47
-22
lines changed

1 file changed

+47
-22
lines changed

pytest_notifier/__init__.py

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
"""pytest-notifier - A pytest plugin to notify test result"""
22
from time import time
33

4+
import pytest
5+
from _pytest.main import EXIT_INTERRUPTED
6+
47
from .notifier import notify
58

69

@@ -33,6 +36,12 @@ def pytest_addoption(parser):
3336
default='py.test',
3437
help='Notifier title when tests fail.',
3538
)
39+
group.addoption(
40+
'--notifier-oninterrupt-title',
41+
dest='notifier_oninterrupt_title',
42+
default='py.test - interrupted',
43+
help='Notifier title when tests are interrupted (e.g. Ctrl-C).',
44+
)
3645

3746

3847
def get_msg_part(count, group):
@@ -47,28 +56,44 @@ def get_msg_part(count, group):
4756
return '{} {}'.format(count, suffix)
4857

4958

50-
def pytest_terminal_summary(terminalreporter):
51-
if not terminalreporter.config.option.notifier:
52-
return
59+
class Notifier:
60+
@pytest.hookimpl(trylast=True)
61+
def pytest_sessionfinish(self, session, exitstatus):
62+
self.exitstatus = exitstatus
63+
64+
@pytest.hookimpl(trylast=True)
65+
def pytest_terminal_summary(self, terminalreporter):
66+
if not terminalreporter.config.option.notifier:
67+
return
68+
69+
tr = terminalreporter
70+
duration = time() - tr._sessionstarttime
71+
keys = ('passed', 'failed', 'error', 'deselected')
72+
counts = {
73+
k: len(list(filter(lambda r: getattr(r, 'when', '') == 'call', tr.stats.get(k, []))))
74+
for k in keys
75+
}
76+
77+
if self.exitstatus == EXIT_INTERRUPTED:
78+
title = terminalreporter.config.option.notifier_oninterrupt_title
79+
# TODO: same as with last "else", refactor?
80+
msg = ' '.join(filter(None, (
81+
get_msg_part(count=counts[k], group=k) for k in keys)))
82+
elif sum(counts.values()) == 0:
83+
title = terminalreporter.config.option.notifier_onzero_title
84+
msg = 'No tests ran'
85+
elif counts['passed'] and not (counts['failed'] or counts['error']):
86+
title = terminalreporter.config.option.notifier_onpass_title
87+
msg = 'Success - {passed} Passed'.format(**counts)
88+
else:
89+
title = terminalreporter.config.option.notifier_onfail_title
90+
msg = ' '.join(filter(None, (
91+
get_msg_part(count=counts[k], group=k) for k in keys)))
5392

54-
tr = terminalreporter
55-
duration = time() - tr._sessionstarttime
56-
keys = ('passed', 'failed', 'error', 'deselected')
57-
counts = {
58-
k: len(list(filter(lambda r: getattr(r, 'when', '') == 'call', tr.stats.get(k, []))))
59-
for k in keys
60-
}
93+
msg += ' in {:.2f}s'.format(duration)
94+
notify(title, msg)
6195

62-
if sum(counts.values()) == 0:
63-
title = terminalreporter.config.option.notifier_onzero_title
64-
msg = 'No tests ran'
65-
elif counts['passed'] and not (counts['failed'] or counts['error']):
66-
title = terminalreporter.config.option.notifier_onpass_title
67-
msg = 'Success - {passed} Passed'.format(**counts)
68-
else:
69-
title = terminalreporter.config.option.notifier_onfail_title
70-
msg = ' '.join(filter(None, (
71-
get_msg_part(count=counts[k], group=k) for k in keys)))
7296

73-
msg += ' in {:.2f}s'.format(duration)
74-
notify(title, msg)
97+
def pytest_configure(config):
98+
notifier = Notifier()
99+
config.pluginmanager.register(notifier, '_notifier')

0 commit comments

Comments
 (0)