diff --git a/README.md b/README.md index 1345fdb..aa34b42 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,12 @@ tox -e integration .venv/bin/pytest tests/integration ``` +Some tests are marked as ``slow`` and skipped unless you pass ``--runslow``: + +```bash +tox -e integration -- --runslow +``` + Compare write/read throughput of openvixdisklib and native VDDK (`64KiB`, 129-sector, and `32MiB` transfers; `nbdssl` and `nbd`; plain and FastLZ): diff --git a/tests/conftest.py b/tests/conftest.py index 65a9866..a972d1e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -35,3 +35,32 @@ def lab() -> Iterator[LabEnv]: def vddk() -> None: """Skip VDDK-backed tests when ``libvixDiskLib`` cannot be loaded.""" require_vddk() + + +def pytest_addoption(parser: pytest.Parser) -> None: + """Add ``--runslow`` to opt in to long-running tests.""" + parser.addoption( + "--runslow", + action="store_true", + default=False, + help="run tests marked as slow", + ) + + +def pytest_configure(config: pytest.Config) -> None: + """Register the ``slow`` marker.""" + config.addinivalue_line( + "markers", "slow: long-running tests; enable with --runslow" + ) + + +def pytest_collection_modifyitems( + config: pytest.Config, items: list[pytest.Item] +) -> None: + """Skip ``slow`` tests unless ``--runslow`` was given.""" + if config.getoption("--runslow"): + return + skip_slow = pytest.mark.skip(reason="need --runslow to run") + for item in items: + if "slow" in item.keywords: + item.add_marker(skip_slow) diff --git a/tests/integration/base.py b/tests/integration/base.py index a5ce4f0..57157fc 100644 --- a/tests/integration/base.py +++ b/tests/integration/base.py @@ -42,6 +42,7 @@ _LAB_VM_PREFIX = "ovdl-test-" VDDK_DIR = _VDDK_DIR SECTOR_SIZE = 512 SECTOR_AT_1GB = (1024 * 1024 * 1024) // SECTOR_SIZE +SECTOR_AT_5GB = (5 * 1024 * 1024 * 1024) // SECTOR_SIZE @dataclass diff --git a/tests/integration/test_crosscheck.py b/tests/integration/test_crosscheck.py index d4ff621..9942699 100644 --- a/tests/integration/test_crosscheck.py +++ b/tests/integration/test_crosscheck.py @@ -3,13 +3,25 @@ """Compare writes and reads from VDDK with openvixdisklib.""" +from __future__ import annotations + +import os from typing import Any import pytest from openvixdisklib import openvixdisklib as open_vix from tests.integration import vixdisklib -from tests.integration.base import SECTOR_AT_1GB, SECTOR_SIZE, LabEnv, pattern_bytes +from tests.integration.base import ( + SECTOR_AT_1GB, + SECTOR_AT_5GB, + SECTOR_SIZE, + LabEnv, + pattern_bytes, +) + +_64MIB = 64 * 1024 * 1024 +_5GIB = SECTOR_AT_5GB * SECTOR_SIZE def _connect_extra(lab: LabEnv, module: Any) -> dict[str, Any] | None: @@ -70,6 +82,52 @@ def _assert_both_read( ) +def _write_bytes( + lab: LabEnv, module: Any, start_byte: int, data: bytes, flags: int = 0 +) -> None: + """Write ``data`` starting at ``start_byte`` using ``module``.""" + handle = module.VixDiskLibHandle( + vixdisklib_compatibility_version="8.0", config_path=None + ) + buf = module.get_buffer(len(data)) + buf[: len(data)] = data + kwargs = lab.vixdisklib_connect_kwargs(_connect_extra(lab, module)) + with ( + handle.connect(**kwargs) as conn, + handle.open(conn, lab.disk_path, flags=flags) as disk, + ): + handle.write(disk, start_byte // SECTOR_SIZE, len(data) // SECTOR_SIZE, buf) + + +def _read_bytes( + lab: LabEnv, module: Any, start_byte: int, nbytes: int, flags: int = 0 +) -> bytes: + """Read ``nbytes`` starting at ``start_byte`` using ``module``.""" + handle = module.VixDiskLibHandle( + vixdisklib_compatibility_version="8.0", config_path=None + ) + buf = module.get_buffer(nbytes) + buf[:nbytes] = b"\xa5" * nbytes + kwargs = lab.vixdisklib_connect_kwargs(_connect_extra(lab, module)) + with ( + handle.connect(**kwargs) as conn, + handle.open(conn, lab.disk_path, flags=flags) as disk, + ): + handle.read(disk, start_byte // SECTOR_SIZE, nbytes // SECTOR_SIZE, buf) + return buf.raw[:nbytes] + + +def _assert_both_read_bytes( + lab: LabEnv, start_byte: int, expected: bytes, flags: int = 0 +) -> None: + vddk_data = _read_bytes(lab, vixdisklib, start_byte, len(expected), flags=flags) + replacement = _read_bytes(lab, open_vix, start_byte, len(expected), flags=flags) + assert vddk_data == expected, f"VDDK mismatch at byte offset {start_byte}" + assert replacement == expected, ( + f"openvixdisklib mismatch at byte offset {start_byte}" + ) + + class TestCrosscheck: @pytest.mark.parametrize( "open_flags", @@ -80,11 +138,12 @@ class TestCrosscheck: self, lab: LabEnv, vddk: None, open_flags: int ) -> None: """Writes from either library must be visible to both readers.""" - sectors = (0, 1, SECTOR_AT_1GB) + sectors = (0, 1, SECTOR_AT_1GB, SECTOR_AT_5GB) vddk_payloads = { 0: pattern_bytes(SECTOR_SIZE, b"XCHK-VDDK-S0"), 1: pattern_bytes(SECTOR_SIZE, b"XCHK-VDDK-S1"), SECTOR_AT_1GB: pattern_bytes(SECTOR_SIZE, b"XCHK-VDDK-1G"), + SECTOR_AT_5GB: pattern_bytes(SECTOR_SIZE, b"XCHK-VDDK-5G"), } _write_sectors(lab, vixdisklib, vddk_payloads, flags=open_flags) _assert_both_read(lab, sectors, vddk_payloads, flags=open_flags) @@ -93,6 +152,29 @@ class TestCrosscheck: 0: pattern_bytes(SECTOR_SIZE, b"XCHK-OVDL-S0"), 1: pattern_bytes(SECTOR_SIZE, b"XCHK-OVDL-S1"), SECTOR_AT_1GB: pattern_bytes(SECTOR_SIZE, b"XCHK-OVDL-1G"), + SECTOR_AT_5GB: pattern_bytes(SECTOR_SIZE, b"XCHK-OVDL-5G"), } _write_sectors(lab, open_vix, ovdl_payloads, flags=open_flags) _assert_both_read(lab, sectors, ovdl_payloads, flags=open_flags) + + @pytest.mark.slow + @pytest.mark.parametrize( + "open_flags", + [0, vixdisklib.VIXDISKLIB_FLAG_OPEN_COMPRESSION_FASTLZ], + ids=["plain", "fastlz"], + ) + def test_openvixdisklib_matches_vddk_64mib_at_5gib( + self, lab: LabEnv, vddk: None, open_flags: int + ) -> None: + """Write 64 MiB of random data at a 5 GiB offset and compare both libraries. + + The 5 GiB start is past the 4 GiB (2**32) byte boundary, so a + 32-bit disk offset would wrap and fail the comparison. + """ + vddk_payload = os.urandom(_64MIB) + _write_bytes(lab, vixdisklib, _5GIB, vddk_payload, flags=open_flags) + _assert_both_read_bytes(lab, _5GIB, vddk_payload, flags=open_flags) + + ovdl_payload = os.urandom(_64MIB) + _write_bytes(lab, open_vix, _5GIB, ovdl_payload, flags=open_flags) + _assert_both_read_bytes(lab, _5GIB, ovdl_payload, flags=open_flags)