Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/_pytest/stepwise.py : 19%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from typing import List
2from typing import Optional
4import pytest
5from _pytest import nodes
6from _pytest.config import Config
7from _pytest.config.argparsing import Parser
8from _pytest.main import Session
9from _pytest.reports import TestReport
12def pytest_addoption(parser: Parser) -> None:
13 group = parser.getgroup("general")
14 group.addoption(
15 "--sw",
16 "--stepwise",
17 action="store_true",
18 dest="stepwise",
19 help="exit on test failure and continue from last failing test next time",
20 )
21 group.addoption(
22 "--stepwise-skip",
23 action="store_true",
24 dest="stepwise_skip",
25 help="ignore the first failing test but stop on the next failing test",
26 )
29@pytest.hookimpl
30def pytest_configure(config: Config) -> None:
31 config.pluginmanager.register(StepwisePlugin(config), "stepwiseplugin")
34class StepwisePlugin:
35 def __init__(self, config: Config) -> None:
36 self.config = config
37 self.active = config.getvalue("stepwise")
38 self.session = None # type: Optional[Session]
39 self.report_status = ""
41 if self.active:
42 assert config.cache is not None
43 self.lastfailed = config.cache.get("cache/stepwise", None)
44 self.skip = config.getvalue("stepwise_skip")
46 def pytest_sessionstart(self, session: Session) -> None:
47 self.session = session
49 def pytest_collection_modifyitems(
50 self, session: Session, config: Config, items: List[nodes.Item]
51 ) -> None:
52 if not self.active:
53 return
54 if not self.lastfailed:
55 self.report_status = "no previously failed tests, not skipping."
56 return
58 already_passed = []
59 found = False
61 # Make a list of all tests that have been run before the last failing one.
62 for item in items:
63 if item.nodeid == self.lastfailed:
64 found = True
65 break
66 else:
67 already_passed.append(item)
69 # If the previously failed test was not found among the test items,
70 # do not skip any tests.
71 if not found:
72 self.report_status = "previously failed test not found, not skipping."
73 already_passed = []
74 else:
75 self.report_status = "skipping {} already passed items.".format(
76 len(already_passed)
77 )
79 for item in already_passed:
80 items.remove(item)
82 config.hook.pytest_deselected(items=already_passed)
84 def pytest_runtest_logreport(self, report: TestReport) -> None:
85 if not self.active:
86 return
88 if report.failed:
89 if self.skip:
90 # Remove test from the failed ones (if it exists) and unset the skip option
91 # to make sure the following tests will not be skipped.
92 if report.nodeid == self.lastfailed:
93 self.lastfailed = None
95 self.skip = False
96 else:
97 # Mark test as the last failing and interrupt the test session.
98 self.lastfailed = report.nodeid
99 assert self.session is not None
100 self.session.shouldstop = (
101 "Test failed, continuing from this test next run."
102 )
104 else:
105 # If the test was actually run and did pass.
106 if report.when == "call":
107 # Remove test from the failed ones, if exists.
108 if report.nodeid == self.lastfailed:
109 self.lastfailed = None
111 def pytest_report_collectionfinish(self) -> Optional[str]:
112 if self.active and self.config.getoption("verbose") >= 0 and self.report_status:
113 return "stepwise: %s" % self.report_status
114 return None
116 def pytest_sessionfinish(self, session: Session) -> None:
117 assert self.config.cache is not None
118 if self.active:
119 self.config.cache.set("cache/stepwise", self.lastfailed)
120 else:
121 # Clear the list of failing tests if the plugin is not active.
122 self.config.cache.set("cache/stepwise", [])