userver: en/testsuite/plugins/servicetest.py Source File
Loading...
Searching...
No Matches
servicetest.py
1import asyncio
2import traceback
3
4import pytest
5
6OUTPUT_PREFIX = '[service-runner] '
7
8
9class Hookspec:
10 def pytest_servicetest_modifyitem(self, session, item):
11 """Modify items for servicetest runner."""
12
13
14class ServiceTestRunner:
15 def __init__(self, config):
16 self.config = config
17 self.service_started = False
18
19 @property
20 def reporter(self):
21 return self.config.pluginmanager.get_plugin('terminalreporter')
22
23 # Filter out all but servicetest items
24 @pytest.hookimpl(trylast=True)
25 def pytest_collection_modifyitems(self, session, items):
26 service_items = []
27 for item in items:
28 for marker in item.own_markers:
29 if marker.name == 'servicetest':
30 service_items.append(item)
31 break
32 if len(service_items) != 1:
33 self.reporter.write_line(
34 OUTPUT_PREFIX
35 + (
36 f'You have to select only one servicetest, '
37 f'{len(service_items)} selected'
38 ),
39 red=True,
40 )
41 raise session.Failed
42
43 for item in service_items:
44 self.config.hook.pytest_servicetest_modifyitem(
45 session=session,
46 item=item,
47 )
48 items[:] = service_items
49
50 def pytest_sessionstart(self, session):
51 reporter = self.config.pluginmanager.get_plugin('terminalreporter')
52 reporter.write_line(
53 OUTPUT_PREFIX + 'Running in servicetest mode',
54 yellow=True,
55 )
56
57 def pytest_runtestloop(self, session):
58 item = session.items[0]
59 item.config.hook.pytest_runtest_protocol(item=item, nextitem=None)
60 if session.shouldfail:
61 raise session.Failed(session.shouldfail)
62 if session.shouldstop:
63 raise session.Interrupted(session.shouldstop)
64 return True
65
66 @pytest.hookimpl(hookwrapper=True, tryfirst=True)
67 def pytest_runtest_call(self, item):
68 try:
69 result = yield
70 result.get_result()
71 except Exception:
72 self.reporter.write_line(
73 OUTPUT_PREFIX + 'Failed to start service',
74 red=True,
75 )
76 exc_info = traceback.format_exc()
77 self.reporter.write_line(OUTPUT_PREFIX + exc_info, red=True)
78 raise
79 else:
80 self.service_started = True
81
82
83def pytest_addoption(parser):
84 group = parser.getgroup('service runner', 'servicerunner')
85 group.addoption(
86 '--service-runner-mode',
87 action='store_true',
88 help='Run pytest in service runner mode',
89 )
90
91
92def pytest_configure(config):
93 config.addinivalue_line(
94 'markers',
95 'servicetest: mark test as service entrypoint',
96 )
97 if config.option.service_runner_mode:
98 runner = ServiceTestRunner(config)
99 config.pluginmanager.register(runner, 'servicetest_runner')
100
101
102def pytest_addhooks(pluginmanager):
103 pluginmanager.add_hookspecs(Hookspec)
104
105
106def pytest_servicetest_modifyitem(session, item):
107 item.fixturenames.append('_servicetest_endless_sleep')
108
109
110@pytest.fixture
111async def _servicetest_endless_sleep(pytestconfig, _global_daemon_store):
112 yield
113 servicetest_runner = pytestconfig.pluginmanager.get_plugin(
114 'servicetest_runner',
115 )
116 if not servicetest_runner.service_started:
117 return
118 servicetest_runner.reporter.write_line(
119 OUTPUT_PREFIX + 'Service started, use C-c to terminate',
120 yellow=True,
121 )
122 while _global_daemon_store.has_running_daemons():
123 await asyncio.sleep(1)