Add stress tests
This commit is contained in:
@@ -48,7 +48,9 @@
|
|||||||
run and destroys it at session end. Run them with
|
run and destroys it at session end. Run them with
|
||||||
`tox -e integration` or
|
`tox -e integration` or
|
||||||
`.venv/bin/pytest tests/integration`. Throughput comparison against
|
`.venv/bin/pytest tests/integration`. Throughput comparison against
|
||||||
native VDDK lives under `tests/perf/` (`tox -e perf`).
|
native VDDK lives under `tests/perf/` (`tox -e perf`). Repeated
|
||||||
|
connect/open/close leak checks live under `tests/stress/`
|
||||||
|
(`tox -e stress`).
|
||||||
|
|
||||||
|
|
||||||
## Other rules
|
## Other rules
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ VDDK-shaped handle.
|
|||||||
| `openvixdisklib/fastlz.py` | FastLZ NFC adapter (pip `pyfastlz`) |
|
| `openvixdisklib/fastlz.py` | FastLZ NFC adapter (pip `pyfastlz`) |
|
||||||
| `tests/integration/` | Live pytest suite against a lab vCenter |
|
| `tests/integration/` | Live pytest suite against a lab vCenter |
|
||||||
| `tests/perf/` | Throughput comparison of openvixdisklib vs VDDK |
|
| `tests/perf/` | Throughput comparison of openvixdisklib vs VDDK |
|
||||||
|
| `tests/stress/` | Repeated connect/open/close leak check |
|
||||||
| `tests/integration/vixdisklib.py` | Native VDDK wrapper used only to cross-check |
|
| `tests/integration/vixdisklib.py` | Native VDDK wrapper used only to cross-check |
|
||||||
| `docs/` | Protocol notes and reverse-engineering steps |
|
| `docs/` | Protocol notes and reverse-engineering steps |
|
||||||
|
|
||||||
@@ -114,6 +115,14 @@ plain and FastLZ):
|
|||||||
tox -e perf
|
tox -e perf
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Repeat ``connect`` / ``open`` / write-read one sector / ``close`` /
|
||||||
|
``disconnect`` 200 times (one process, sequential, like many VMs) and
|
||||||
|
assert the fd count does not grow:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
tox -e stress
|
||||||
|
```
|
||||||
|
|
||||||
VDDK cross-check tests skip when `libvixDiskLib` is not loadable from
|
VDDK cross-check tests skip when `libvixDiskLib` is not loadable from
|
||||||
`.vddk`. `tox -e integration` sets `LD_LIBRARY_PATH` to that directory
|
`.vddk`. `tox -e integration` sets `LD_LIBRARY_PATH` to that directory
|
||||||
and clears `LD_PRELOAD`. For a direct pytest run, do the same.
|
and clears `LD_PRELOAD`. For a direct pytest run, do the same.
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# Copyright 2026 Cloudbase Solutions Srl
|
||||||
|
# All Rights Reserved.
|
||||||
|
|
||||||
|
"""Repeat connect/open/close against the lab to catch leaked sockets."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import gc
|
||||||
|
import os
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from openvixdisklib import openvixdisklib as vixdisklib
|
||||||
|
from tests.integration.base import SECTOR_SIZE, LabEnv, pattern_bytes
|
||||||
|
|
||||||
|
# Sequential ConnectEx + Open, as when migrating many VMs in one process.
|
||||||
|
_ITERATIONS = 200
|
||||||
|
# Noise from pytest/OpenSSL; one leaked socket per iteration is 200.
|
||||||
|
_FD_SLACK = 8
|
||||||
|
|
||||||
|
|
||||||
|
def _fd_count() -> int:
|
||||||
|
"""Return the number of open file descriptors in this process."""
|
||||||
|
return len(os.listdir("/proc/self/fd"))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
not os.path.isdir("/proc/self/fd"),
|
||||||
|
reason="fd leak check needs /proc/self/fd",
|
||||||
|
)
|
||||||
|
class TestConnectOpenReadWriteClose:
|
||||||
|
def test_repeated_connect_open_read_write_close_does_not_leak_fds(
|
||||||
|
self,
|
||||||
|
lab: LabEnv,
|
||||||
|
) -> None:
|
||||||
|
"""Connect, open, write/read one sector, and close ``_ITERATIONS`` times.
|
||||||
|
|
||||||
|
Each cycle is a full ``VixDiskLib_ConnectEx`` / ``Open`` /
|
||||||
|
``Close`` / ``Disconnect`` for the lab VM (ticket, authd, NFC).
|
||||||
|
After the loop the process fd count must not have grown by more
|
||||||
|
than ``_FD_SLACK``.
|
||||||
|
"""
|
||||||
|
handle = vixdisklib.VixDiskLibHandle(
|
||||||
|
vixdisklib_compatibility_version="8.0", config_path=None
|
||||||
|
)
|
||||||
|
write_buf = vixdisklib.get_buffer(SECTOR_SIZE)
|
||||||
|
read_buf = vixdisklib.get_buffer(SECTOR_SIZE)
|
||||||
|
kwargs = lab.vixdisklib_connect_kwargs(
|
||||||
|
{
|
||||||
|
"allow_untrusted": lab.allow_untrusted,
|
||||||
|
"transport_modes": "nbdssl",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
gc.collect()
|
||||||
|
fds_before = _fd_count()
|
||||||
|
for i in range(_ITERATIONS):
|
||||||
|
expected = pattern_bytes(SECTOR_SIZE, f"STRESS-{i}-".encode())
|
||||||
|
write_buf[:SECTOR_SIZE] = expected
|
||||||
|
with (
|
||||||
|
handle.connect(**kwargs) as conn,
|
||||||
|
handle.open(conn, lab.disk_path, flags=0) as disk,
|
||||||
|
):
|
||||||
|
handle.write(disk, 0, 1, write_buf)
|
||||||
|
read_buf[:SECTOR_SIZE] = b"\xa5" * SECTOR_SIZE
|
||||||
|
handle.read(disk, 0, 1, read_buf)
|
||||||
|
assert read_buf.raw[:SECTOR_SIZE] == expected
|
||||||
|
gc.collect()
|
||||||
|
fds_after = _fd_count()
|
||||||
|
assert fds_after <= fds_before + _FD_SLACK, (
|
||||||
|
f"open fds grew from {fds_before} to {fds_after} after "
|
||||||
|
f"{_ITERATIONS} connect/open/close cycles"
|
||||||
|
)
|
||||||
@@ -60,6 +60,15 @@ setenv =
|
|||||||
commands =
|
commands =
|
||||||
python -m pytest -v -s tests/perf/ {posargs}
|
python -m pytest -v -s tests/perf/ {posargs}
|
||||||
|
|
||||||
|
[testenv:stress]
|
||||||
|
description = Repeat connect/open/close against the lab to detect socket leaks.
|
||||||
|
sitepackages = True
|
||||||
|
usedevelop = True
|
||||||
|
setenv =
|
||||||
|
{[testenv:integration]setenv}
|
||||||
|
commands =
|
||||||
|
python -m pytest -v tests/stress/ {posargs}
|
||||||
|
|
||||||
[testenv:fmt]
|
[testenv:fmt]
|
||||||
description = Format the code based on the coding style standards.
|
description = Format the code based on the coding style standards.
|
||||||
deps =
|
deps =
|
||||||
|
|||||||
Reference in New Issue
Block a user