userver
C++ Async Framework
Toggle main menu visibility
Loading...
Searching...
No Matches
base.py
1
"""
2
Configure the service in testsuite.
3
"""
4
5
from
collections.abc
import
Callable
6
import
pathlib
7
import
socket
8
9
import
pytest
10
11
import
testsuite.plugins.network
12
13
USERVER_CONFIG_HOOKS = [
'userver_base_prepare_service_config'
]
14
15
16
def
pytest_addoption(parser) -> None:
17
group = parser.getgroup(
'userver'
)
18
group.addoption(
19
'--build-dir'
,
20
type=pathlib.Path,
21
help=
'Path to service build directory.'
,
22
)
23
24
group = parser.getgroup(
'Test service'
)
25
group.addoption(
26
'--service-binary'
,
27
type=pathlib.Path,
28
help=
'Path to service binary.'
,
29
)
30
group.addoption(
31
'--service-port'
,
32
help=(
'Main HTTP port of the service (default: use the port from the static config)'
),
33
default=
None
,
34
type=int,
35
)
36
group.addoption(
37
'--monitor-port'
,
38
help=(
'Monitor HTTP port of the service (default: use the port from the static config)'
),
39
default=
None
,
40
type=int,
41
)
42
group.addoption(
43
'--service-source-dir'
,
44
type=pathlib.Path,
45
help=
'Path to service source directory.'
,
46
default=pathlib.Path(),
# Current directory by default.
47
)
48
49
50
def
pytest_configure(config):
51
config.option.asyncio_mode =
'auto'
52
53
54
@pytest.fixture(scope='session')
55
def
service_source_dir
(pytestconfig) -> pathlib.Path:
56
"""
57
Returns the path to the service source directory that is set by command
58
line `--service-source-dir` option.
59
60
Override this fixture to change the way the path to the service
61
source directory is detected by testsuite.
62
63
@ingroup userver_testsuite_fixtures
64
"""
65
return
pytestconfig.option.service_source_dir
66
67
68
@pytest.fixture(scope='session')
69
def
build_dir
(pytestconfig) -> pathlib.Path:
70
"""
71
Returns the build directory set by command line `--build-dir` option.
72
73
Override this fixture to change the way the build directory is
74
detected by the testsuite.
75
76
@ingroup userver_testsuite_fixtures
77
"""
78
return
pytestconfig.option.build_dir
79
80
81
@pytest.fixture(scope='session')
82
def
service_binary
(pytestconfig) -> pathlib.Path:
83
"""
84
Returns the path to service binary set by command line `--service-binary`
85
option.
86
87
Override this fixture to change the way the path to service binary is
88
detected by the testsuite.
89
90
@ingroup userver_testsuite_fixtures
91
"""
92
return
pytestconfig.option.service_binary
93
94
95
@pytest.fixture(scope='session')
96
def
service_port
(pytestconfig, _original_service_config, choose_free_port) -> int:
97
"""
98
Returns the main listener port number of the service set by command line
99
`--service-port` option.
100
If no port is specified in the command line option, keeps the original port
101
specified in the static config.
102
103
Override this fixture to change the way the main listener port number is
104
detected by the testsuite.
105
106
@ingroup userver_testsuite_fixtures
107
"""
108
return
pytestconfig.option.service_port
or
_get_port(
109
_original_service_config,
110
choose_free_port,
111
'listener'
,
112
service_port,
113
'--service-port'
,
114
)
115
116
117
@pytest.fixture(scope='session')
118
def
monitor_port
(pytestconfig, _original_service_config, choose_free_port) -> int:
119
"""
120
Returns the monitor listener port number of the service set by command line
121
`--monitor-port` option.
122
If no port is specified in the command line option, keeps the original port
123
specified in the static config.
124
125
Override this fixture to change the way the monitor listener port number
126
is detected by testsuite.
127
128
@ingroup userver_testsuite_fixtures
129
"""
130
return
pytestconfig.option.monitor_port
or
_get_port(
131
_original_service_config,
132
choose_free_port,
133
'listener-monitor'
,
134
monitor_port,
135
'--service-port'
,
136
)
137
138
139
@pytest.fixture(scope='session')
140
def
congestion_control_fake_mode
() -> bool:
141
"""
142
Returns congestion control fake-mode value.
143
144
@ingroup userver_testsuite_fixtures
145
"""
146
return
True
147
148
149
def
_get_port(
150
original_service_config,
151
choose_free_port,
152
listener_name,
153
port_fixture,
154
option_name,
155
) -> int:
156
config_yaml = original_service_config.config_yaml
157
config_vars = original_service_config.config_vars
158
components = config_yaml[
'components_manager'
][
'components'
]
159
listener = components.get(
'server'
, {}).get(listener_name, {})
160
if
not
listener:
161
return
-1
162
163
port = listener.get(
'port'
,
None
)
or
listener.get(
'ports'
, [
None
])[0]
164
if
isinstance(port, str)
and
port.startswith(
'$'
):
165
port = config_vars.get(port[1:],
None
)
or
listener.get(
166
'port#fallback'
,
167
None
,
168
)
169
assert
port, (
170
f
'Please specify '
171
f
'components_manager.components.server.{listener_name}.port '
172
f
'in the static config, or pass {option_name} pytest option, '
173
f
'or override the {port_fixture.__name__} fixture'
174
)
175
return
choose_free_port
(port)
176
177
178
# Beware: global variable
179
_allocated_ports = set()
180
181
182
@pytest.fixture(scope='session')
183
def
choose_free_port
(
184
pytestconfig,
185
get_free_port,
186
_testsuite_socket_cleanup,
187
_testsuite_default_af,
188
) -> Callable[[int | None], int]:
189
"""
190
A function that chooses a free port based on the optional hint given in the parameter.
191
192
@ingroup userver_testsuite_fixtures
193
"""
194
195
family, address = _testsuite_default_af
196
197
def
choose(port_hint: int |
None
=
None
, /) -> int:
198
should_not_randomize_ports = pytestconfig.option.service_runner_mode
199
if
should_not_randomize_ports
and
port_hint
is
not
None
and
port_hint != 0:
200
if
_is_port_free(port_hint, family, address):
201
_allocated_ports.add(port_hint)
202
return
port_hint
203
port = _get_free_port_not_allocated(get_free_port)
204
_allocated_ports.add(port)
205
return
port
206
207
return
choose
208
209
210
def
_get_free_port_not_allocated(get_free_port) -> int:
211
for
_
in
range(100):
212
port = get_free_port()
213
if
port
not
in
_allocated_ports:
214
return
port
215
raise
testsuite.plugins.network.NoEnabledPorts()
216
217
218
def
_is_port_free(port_num: int, family: int, address: str) -> bool:
219
try
:
220
with
socket.socket(family, socket.SOCK_STREAM)
as
sock:
221
sock.bind((address, port_num))
222
except
OSError:
223
return
False
224
else
:
225
return
True
226
227
228
@pytest.fixture(scope='session')
229
def
userver_base_prepare_service_config(congestion_control_fake_mode):
230
def
patch_config(config, config_vars):
231
components = config[
'components_manager'
][
'components'
]
232
if
'congestion-control'
in
components:
233
if
components[
'congestion-control'
]
is
None
:
234
components[
'congestion-control'
] = {}
235
236
components[
'congestion-control'
][
'fake-mode'
] = congestion_control_fake_mode
237
238
return
patch_config
testsuite
pytest_plugins
pytest_userver
plugins
base.py
Generated on
for userver by
Doxygen
1.17.0