@@ -110,6 +110,8 @@ def wrap_session(config, doit):
110
110
session .exitstatus = doit (config , session ) or 0
111
111
except UsageError :
112
112
raise
113
+ except Failed :
114
+ session .exitstatus = EXIT_TESTSFAILED
113
115
except KeyboardInterrupt :
114
116
excinfo = _pytest ._code .ExceptionInfo ()
115
117
if initstate < 2 and isinstance (excinfo .value , exit .Exception ):
@@ -169,6 +171,8 @@ def pytest_runtestloop(session):
169
171
item .config .hook .pytest_runtest_protocol (item = item , nextitem = nextitem )
170
172
if session .shouldstop :
171
173
raise session .Interrupted (session .shouldstop )
174
+ if session .shouldfail :
175
+ raise session .Failed (session .shouldfail )
172
176
return True
173
177
174
178
@@ -590,15 +594,21 @@ class Interrupted(KeyboardInterrupt):
590
594
__module__ = 'builtins' # for py3
591
595
592
596
597
+ class Failed (Exception ):
598
+ """ signals an stop as failed test run. """
599
+
600
+
593
601
class Session (FSCollector ):
594
602
Interrupted = Interrupted
603
+ Failed = Failed
595
604
596
605
def __init__ (self , config ):
597
606
FSCollector .__init__ (self , config .rootdir , parent = None ,
598
607
config = config , session = self )
599
608
self .testsfailed = 0
600
609
self .testscollected = 0
601
610
self .shouldstop = False
611
+ self .shouldfail = False
602
612
self .trace = config .trace .root .get ("collection" )
603
613
self ._norecursepatterns = config .getini ("norecursedirs" )
604
614
self .startdir = py .path .local ()
@@ -618,7 +628,7 @@ def pytest_runtest_logreport(self, report):
618
628
self .testsfailed += 1
619
629
maxfail = self .config .getvalue ("maxfail" )
620
630
if maxfail and self .testsfailed >= maxfail :
621
- self .shouldstop = "stopping after %d failures" % (
631
+ self .shouldfail = "stopping after %d failures" % (
622
632
self .testsfailed )
623
633
pytest_collectreport = pytest_runtest_logreport
624
634
0 commit comments