|
13 | 13 | from ..testgres import NodeStatus
|
14 | 14 | from ..testgres import IsolationLevel
|
15 | 15 |
|
| 16 | +import testgres |
| 17 | + |
16 | 18 | # New name prevents to collect test-functions in TestgresException and fixes
|
17 | 19 | # the problem with pytest warning.
|
18 | 20 | from ..testgres import TestgresException as testgres_TestgresException
|
|
39 | 41 | import os
|
40 | 42 | import re
|
41 | 43 | import subprocess
|
| 44 | +import typing |
42 | 45 |
|
43 | 46 |
|
44 | 47 | @contextmanager
|
@@ -1169,17 +1172,177 @@ def test_the_same_port(self, node_svc: PostgresNodeService):
|
1169 | 1172 | r = node.safe_psql("SELECT 3;")
|
1170 | 1173 | assert (__class__.helper__rm_carriage_returns(r) == b'3\n')
|
1171 | 1174 |
|
| 1175 | + class tagPortManagerProxy(PortManager): |
| 1176 | + m_PrevPortManager: PortManager |
| 1177 | + |
| 1178 | + m_DummyPortNumber: int |
| 1179 | + m_DummyPortMaxUsage: int |
| 1180 | + |
| 1181 | + m_DummyPortCurrentUsage: int |
| 1182 | + m_DummyPortTotalUsage: int |
| 1183 | + |
| 1184 | + def __init__(self, prevPortManager: PortManager, dummyPortNumber: int, dummyPortMaxUsage: int): |
| 1185 | + assert isinstance(prevPortManager, PortManager) |
| 1186 | + assert type(dummyPortNumber) == int # noqa: E721 |
| 1187 | + assert type(dummyPortMaxUsage) == int # noqa: E721 |
| 1188 | + assert dummyPortNumber >= 0 |
| 1189 | + assert dummyPortMaxUsage >= 0 |
| 1190 | + |
| 1191 | + super().__init__() |
| 1192 | + |
| 1193 | + self.m_PrevPortManager = prevPortManager |
| 1194 | + |
| 1195 | + self.m_DummyPortNumber = dummyPortNumber |
| 1196 | + self.m_DummyPortMaxUsage = dummyPortMaxUsage |
| 1197 | + |
| 1198 | + self.m_DummyPortCurrentUsage = 0 |
| 1199 | + self.m_DummyPortTotalUsage = 0 |
8000
| 1200 | + |
| 1201 | + def __enter__(self): |
| 1202 | + return self |
| 1203 | + |
| 1204 | + def __exit__(self, type, value, traceback): |
| 1205 | + assert self.m_DummyPortCurrentUsage == 0 |
| 1206 | + |
| 1207 | + assert self.m_PrevPortManager is not None |
| 1208 | + |
| 1209 | + def reserve_port(self) -> int: |
| 1210 | + assert type(self.m_DummyPortMaxUsage) == int # noqa: E721 |
| 1211 | + assert type(self.m_DummyPortTotalUsage) == int # noqa: E721 |
| 1212 | + assert type(self.m_DummyPortCurrentUsage) == int # noqa: E721 |
| 1213 | + assert self.m_DummyPortTotalUsage >= 0 |
| 1214 | + assert self.m_DummyPortCurrentUsage >= 0 |
| 1215 | + |
| 1216 | + assert self.m_DummyPortTotalUsage <= self.m_DummyPortMaxUsage |
| 1217 | + assert self.m_DummyPortCurrentUsage <= self.m_DummyPortTotalUsage |
| 1218 | + |
| 1219 | + assert self.m_PrevPortManager is not None |
| 1220 | + assert isinstance(self.m_PrevPortManager, PortManager) |
| 1221 | + |
| 1222 | + if self.m_DummyPortTotalUsage == self.m_DummyPortMaxUsage: |
| 1223 | + return self.m_PrevPortManager.reserve_port() |
| 1224 | + |
| 1225 | + self.m_DummyPortTotalUsage += 1 |
| 1226 | + self.m_DummyPortCurrentUsage += 1 |
| 1227 | + return self.m_DummyPortNumber |
| 1228 | + |
| 1229 | + def release_port(self, dummyPortNumber: int): |
| 1230 | + assert type(dummyPortNumber) == int # noqa: E721 |
| 1231 | + |
| 1232 | + assert type(self.m_DummyPortMaxUsage) == int # noqa: E721 |
| 1233 | + assert type(self.m_DummyPortTotalUsage) == int # noqa: E721 |
| 1234 | + assert type(self.m_DummyPortCurrentUsage) == int # noqa: E721 |
| 1235 | + assert self.m_DummyPortTotalUsage >= 0 |
| 1236 | + assert self.m_DummyPortCurrentUsage >= 0 |
| 1237 | + |
| 1238 | + assert self.m_DummyPortTotalUsage <= self.m_DummyPortMaxUsage |
| 1239 | + assert self.m_DummyPortCurrentUsage <= self.m_DummyPortTotalUsage |
| 1240 | + |
| 1241 | + assert self.m_PrevPortManager is not None |
| 1242 | + assert isinstance(self.m_PrevPortManager, PortManager) |
| 1243 | + |
| 1244 | + if self.m_DummyPortCurrentUsage > 0 and dummyPortNumber == self.m_DummyPortNumber: |
| 1245 | + assert self.m_DummyPortTotalUsage > 0 |
| 1246 | + self.m_DummyPortCurrentUsage -= 1 |
| 1247 | + return |
| 1248 | + |
| 1249 | + return self.m_PrevPortManager.release_port(dummyPortNumber) |
| 1250 | + |
| 1251 | + def test_port_rereserve_during_node_start(self, node_svc: PostgresNodeService): |
| 1252 | + assert type(node_svc) == PostgresNodeService # noqa: E721 |
| 1253 | + assert testgres.PostgresNode._C_MAX_START_ATEMPTS == 5 |
| 1254 | + |
| 1255 | + C_COUNT_OF_BAD_PORT_USAGE = 3 |
| 1256 | + |
| 1257 | + with __class__.helper__get_node(node_svc) as node1: |
| 1258 | + node1.init().start() |
| 1259 | + assert node1._should_free_port |
| 1260 | + assert type(node1.port) == int # noqa: E721 |
| 1261 | + node1_port_copy = node1.port |
| 1262 | + assert __class__.helper__rm_carriage_returns(node1.safe_psql("SELECT 1;")) == b'1\n' |
| 1263 | + |
| 1264 | + with __class__.tagPortManagerProxy(node_svc.port_manager, node1.port, C_COUNT_OF_BAD_PORT_USAGE) as proxy: |
| 1265 | + assert proxy.m_DummyPortNumber == node1.port |
| 1266 | + with __class__.helper__get_node(node_svc, port_manager=proxy) as node2: |
| 1267 | + assert node2._should_free_port |
| 1268 | + assert node2.port == node1.port |
| 1269 | + |
| 1270 | + node2.init().start() |
| 1271 | + |
| 1272 | + assert node2.port != node1.port |
| 1273 | + assert node2._should_free_port |
| 1274 | + assert proxy.m_DummyPortCurrentUsage == 0 |
| 1275 | + assert proxy.m_DummyPortTotalUsage == C_COUNT_OF_BAD_PORT_USAGE |
| 1276 | + assert node2.is_started |
| 1277 | + r = node2.safe_psql("SELECT 2;") |
| 1278 | + assert __class__.helper__rm_carriage_returns(r) == b'2\n' |
| 1279 | + |
| 1280 | + # node1 is still working |
| 1281 | + assert node1.port == node1_port_copy |
| 1282 | + assert node1._should_free_port |
| 1283 | + r = node1.safe_psql("SELECT 3;") |
| 1284 | + assert __class__.helper__rm_carriage_returns(r) == b'3\n' |
| 1285 | + |
| 1286 | + def test_port_conflict(self, node_svc: PostgresNodeService): |
| 1287 | + assert type(node_svc) == PostgresNodeService # noqa: E721 |
| 1288 | + assert testgres.PostgresNode._C_MAX_START_ATEMPTS > 1 |
| 1289 | + |
| 1290 | + C_COUNT_OF_BAD_PORT_USAGE = testgres.PostgresNode._C_MAX_START_ATEMPTS |
| 1291 | + |
| 1292 | + with __class__.helper__get_node(node_svc) as node1: |
| 1293 | + node1.init().start() |
| 1294 | + assert node1._should_free_port |
| 1295 | + assert type(node1.port) == int # noqa: E721 |
| 1296 | + node1_port_copy = node1.port |
| 1297 | + assert __class__.helper__rm_carriage_returns(node1.safe_psql("SELECT 1;")) == b'1\n' |
| 1298 | + |
| 1299 | + with __class__.tagPortManagerProxy(node_svc.port_manager, node1.port, C_COUNT_OF_BAD_PORT_USAGE) as proxy: |
| 1300 | + assert proxy.m_DummyPortNumber == node1.port |
| 1301 | + with __class__.helper__get_node(node_svc, port_manager=proxy) as node2: |
| 1302 | + assert node2._should_free_port |
| 1303 | + assert node2.port == node1.port |
| 1304 | + |
| 1305 | + with pytest.raises( |
| 1306 | + expected_exception=StartNodeException, |
| 1307 | + match=re.escape("Cannot start node after multiple attempts.") |
| 1308 | + ): |
| 1309 | + node2.init().start() |
| 1310 | + |
| 1311 | + assert node2.port == node1.port |
| 1312 | + assert node2._should_free_port |
| 1313 | + assert proxy.m_DummyPortCurrentUsage == 1 |
| 1314 | + assert proxy.m_DummyPortTotalUsage == C_COUNT_OF_BAD_PORT_USAGE |
| 1315 | + assert not node2.is_started |
| 1316 | + |
| 1317 | + # node2 must release our dummyPort (node1.port) |
| 1318 | + assert (proxy.m_DummyPortCurrentUsage == 0) |
| 1319 | + |
| 1320 | + # node1 is still working |
| 1321 | + assert node1.port == node1_port_copy |
| 1322 | + assert node1._should_free_port |
| 1323 | + r = node1.safe_psql("SELECT 3;") |
| 1324 | + assert __class__.helper__rm_carriage_returns(r) == b'3\n' |
| 1325 | + |
1172 | 1326 | @staticmethod
|
1173 |
| - def helper__get_node(node_svc: PostgresNodeService, name=None, port=None): |
| 1327 | + def helper__get_node( |
| 1328 | + node_svc: PostgresNodeService, |
| 1329 | + name: typing.Optional[str] = None, |
| 1330 | + port: typing.Optional[int] = None, |
| 1331 | + port_manager: typing.Optional[PortManager] = None |
| 1332 | + ) -> PostgresNode: |
1174 | 1333 | assert isinstance(node_svc, PostgresNodeService)
|
1175 | 1334 | assert isinstance(node_svc.os_ops, OsOperations)
|
1176 | 1335 | assert isinstance(node_svc.port_manager, PortManager)
|
| 1336 | + |
| 1337 | + if port_manager is None: |
| 1338 | + port_manager = node_svc.port_manager |
| 1339 | + |
1177 | 1340 | return PostgresNode(
|
1178 | 1341 | name,
|
1179 | 1342 | port=port,
|
1180 | 1343 | conn_params=None,
|
1181 | 1344 | os_ops=node_svc.os_ops,
|
1182 |
| - port_manager=node_svc.port_manager if port is None else None |
| 1345 | + port_manager=port_manager if port is None else None |
1183 | 1346 | )
|
1184 | 1347 |
|
1185 | 1348 | @staticmethod
|
|
0 commit comments