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
port = listener.get(
'port'
,
None
)
163
if
isinstance(port, str)
and
port.startswith(
'$'
):
164
port = config_vars.get(port[1:],
None
)
or
listener.get(
165
'port#fallback'
,
166
None
,
167
)
168
assert
port, (
169
f
'Please specify '
170
f
'components_manager.components.server.{listener_name}.port '
171
f
'in the static config, or pass {option_name} pytest option, '
172
f
'or override the {port_fixture.__name__} fixture'
173
)
174
return
choose_free_port
(port)
175
176
177
# Beware: global variable
178
_allocated_ports = set()
179
180
181
@pytest.fixture(scope='session')
182
def
choose_free_port
(
183
pytestconfig,
184
get_free_port,
185
_testsuite_socket_cleanup,
186
_testsuite_default_af,
187
) -> Callable[[int | None], int]:
188
"""
189
A function that chooses a free port based on the optional hint given in the parameter.
190
191
@ingroup userver_testsuite_fixtures
192
"""
193
194
family, address = _testsuite_default_af
195
196
def
choose(port_hint: int |
None
=
None
, /) -> int:
197
should_not_randomize_ports = pytestconfig.option.service_runner_mode
198
if
should_not_randomize_ports
and
port_hint
is
not
None
and
port_hint != 0:
199
if
_is_port_free(port_hint, family, address):
200
_allocated_ports.add(port_hint)
201
return
port_hint
202
port = _get_free_port_not_allocated(get_free_port)
203
_allocated_ports.add(port)
204
return
port
205
206
return
choose
207
208
209
def
_get_free_port_not_allocated(get_free_port) -> int:
210
for
_
in
range(100):
211
port = get_free_port()
212
if
port
not
in
_allocated_ports:
213
return
port
214
raise
testsuite.plugins.network.NoEnabledPorts()
215
216
217
def
_is_port_free(port_num: int, family: int, address: str) -> bool:
218
try
:
219
with
socket.socket(family, socket.SOCK_STREAM)
as
sock:
220
sock.bind((address, port_num))
221
except
OSError:
222
return
False
223
else
:
224
return
True
225
226
227
@pytest.fixture(scope='session')
228
def
userver_base_prepare_service_config(congestion_control_fake_mode):
229
def
patch_config(config, config_vars):
230
components = config[
'components_manager'
][
'components'
]
231
if
'congestion-control'
in
components:
232
if
components[
'congestion-control'
]
is
None
:
233
components[
'congestion-control'
] = {}
234
235
components[
'congestion-control'
][
'fake-mode'
] = congestion_control_fake_mode
236
237
return
patch_config
testsuite
pytest_plugins
pytest_userver
plugins
base.py
Generated on
for userver by
Doxygen
1.17.0