From 36ad813b948f964933a3266e46f4173000499d3b Mon Sep 17 00:00:00 2001 From: Andrei Chekun Date: Mon, 3 Feb 2025 11:56:06 +0100 Subject: [PATCH] test.py: Add the possibility to run unit tests from pytest Add the possibility to run unit tests from pytest --- test/pylib/cpp/unit/__init__.py | 0 test/pylib/cpp/unit/unit_facade.py | 61 ++++++++++++++++++++++++++++++ test/unit/README.md | 17 +++++++++ test/unit/__init__.py | 0 test/unit/conftest.py | 20 ++++++++++ 5 files changed, 98 insertions(+) create mode 100644 test/pylib/cpp/unit/__init__.py create mode 100644 test/pylib/cpp/unit/unit_facade.py create mode 100644 test/unit/README.md create mode 100644 test/unit/__init__.py create mode 100644 test/unit/conftest.py diff --git a/test/pylib/cpp/unit/__init__.py b/test/pylib/cpp/unit/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/pylib/cpp/unit/unit_facade.py b/test/pylib/cpp/unit/unit_facade.py new file mode 100644 index 0000000000..4f21da5c64 --- /dev/null +++ b/test/pylib/cpp/unit/unit_facade.py @@ -0,0 +1,61 @@ +# +# Copyright (C) 2025-present ScyllaDB +# +# SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0 +# +from __future__ import annotations + +import os +from pathlib import Path +from typing import Sequence + +from test.pylib.cpp.facade import CppTestFacade, CppTestFailure, run_process + +TIMEOUT = 30 # seconds + +class UnitTestFacade(CppTestFacade): + + def list_tests( + self, + executable: Path, + no_parallel_run: bool + ) -> tuple[bool, list[str]]: + return False, [os.path.basename(os.path.splitext(executable)[0])] + + def run_test( + self, + executable: Path, + original_name: str, + test_name: str, + mode: str, + file_name: Path, + test_args: Sequence[str] = (), + ) -> tuple[list[CppTestFailure], str] | tuple[None, str]: + args = [str(executable), *test_args] + os.chdir(self.temp_dir.parent) + p, stderr, stdout = run_process(args, TIMEOUT) + + if p.returncode != 0: + msg = ( + 'working_dir: {working_dir}\n' + 'Internal Error: calling {executable} ' + 'for test {test_id} failed (returncode={returncode}):\n' + 'output:{stdout}\n' + 'std error:{stderr}\n' + 'command to repeat:{command}' + ) + failure = CppTestFailure( + file_name.name, + line_num=0, + contents=msg.format( + working_dir=os.getcwd(), + executable=executable, + test_id=test_name, + stdout=stdout, + stderr=stderr, + command=' '.join(p.args), + returncode=p.returncode, + ), + ) + return [failure], stdout + return None, stdout diff --git a/test/unit/README.md b/test/unit/README.md new file mode 100644 index 0000000000..f3ed0c4c2a --- /dev/null +++ b/test/unit/README.md @@ -0,0 +1,17 @@ +# Running tests with pytest + +To run test with pytest execute +```bash +pytest test/unit +``` +To execute only one file, provide the path filename +```bash +pytest test/unit/lsa_async_eviction_test.cc +``` +Since it's a normal path, autocompletion works in the terminal out of the box. + +To provide a specific mode, use the next parameter `--mode dev`, +if parameter isn't provided pytest tries to use `ninja mode_list` to find out the compiled modes. + +Parallel execution is controlled by `pytest-xdist` and the parameter `-n auto`. +This command starts tests with the number of workers equal to CPU cores. diff --git a/test/unit/__init__.py b/test/unit/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/unit/conftest.py b/test/unit/conftest.py new file mode 100644 index 0000000000..22a94e6964 --- /dev/null +++ b/test/unit/conftest.py @@ -0,0 +1,20 @@ +# +# Copyright (C) 2025-present ScyllaDB +# +# SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0 +# +from pathlib import PosixPath + +from pytest import Collector + +from test.pylib.cpp.common_cpp_conftest import collect_items +from test.pylib.cpp.unit.unit_facade import UnitTestFacade + + +def pytest_collect_file(file_path: PosixPath, parent: Collector): + """ + Method triggered automatically by pytest to collect files from a directory. Boost and unit have the same logic for + collection, the only difference in execution, and it's covered by facade + """ + if file_path.suffix == '.cc': + return collect_items(file_path, parent, facade=UnitTestFacade(parent.config))