commit:     43e2ea97f397a796d3a4fa9e35bbf1cd5336ceff
Author:     Michał Górny <mgorny <AT> gentoo <DOT> org>
AuthorDate: Sat Dec 13 15:13:25 2025 +0000
Commit:     Michał Górny <mgorny <AT> gentoo <DOT> org>
CommitDate: Sat Dec 13 15:13:25 2025 +0000
URL:        https://gitweb.gentoo.org/proj/steve.git/commit/?id=43e2ea97

Add initial tests

Signed-off-by: Michał Górny <mgorny <AT> gentoo.org>

 .gitignore         |  1 +
 meson.build        | 30 +++++++++++++++++++++++-------
 meson.options      |  4 ++++
 test/conftest.py   | 42 ++++++++++++++++++++++++++++++++++++++++++
 test/test_steve.py | 19 +++++++++++++++++++
 5 files changed, 89 insertions(+), 7 deletions(-)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..04a41a2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+**.pyc

diff --git a/meson.build b/meson.build
index c00d515..db20496 100644
--- a/meson.build
+++ b/meson.build
@@ -7,17 +7,33 @@ project('steve', 'cpp',
 version = meson.project_version()
 
 if get_option('client')
-  executable('stevie', ['stevie.cxx'],
-             cpp_args : [f'-DSTEVE_VERSION="@version@"'],
-             install : true)
+  executable(
+    'stevie',
+    ['stevie.cxx'],
+    cpp_args : [f'-DSTEVE_VERSION="@version@"'],
+    install : true)
 endif
 
 if get_option('server')
   fuse3 = dependency('fuse3')
   libevent = dependency('libevent', version: '>= 2')
 
-  executable('steve', ['steve.cxx'],
-             cpp_args : [f'-DSTEVE_VERSION="@version@"'],
-             dependencies: [fuse3, libevent],
-             install : true)
+  steve = executable(
+    'steve',
+    ['steve.cxx'],
+    cpp_args : [f'-DSTEVE_VERSION="@version@"'],
+    dependencies: [fuse3, libevent],
+    install : true)
+
+  if get_option('test')
+    pytest = find_program('pytest')
+    source_root = meson.source_root()
+    test('pytest',
+      pytest,
+      args : [f'@source_root@/test'],
+      env : {
+        'PYTEST_DISABLE_PLUGIN_AUTOLOAD': '1',
+        'STEVE': steve.path(),
+      })
+  endif
 endif

diff --git a/meson.options b/meson.options
index 8a312bf..af22b97 100644
--- a/meson.options
+++ b/meson.options
@@ -6,3 +6,7 @@ option('server',
        type: 'boolean',
        value: true,
        description: 'Build the server')
+option('test',
+       type: 'boolean',
+       value: false,
+       description: 'Enable testing (requires pytest)')

diff --git a/test/conftest.py b/test/conftest.py
new file mode 100644
index 0000000..894f265
--- /dev/null
+++ b/test/conftest.py
@@ -0,0 +1,42 @@
+from __future__ import annotations
+
+import os
+import subprocess
+import time
+from pathlib import Path
+
+import pytest
+
+
+class Steve:
+    dev_name: str | None = None
+    dev_path: str | None = None
+    subprocess: subprocess.Popen | None = None
+    retval: int | None = None
+
+    def start(self, args: list[str]) -> None:
+        self.dev_name = f"steve.test.{os.getpid()}"
+        self.dev_path = Path(f"/dev/{self.dev_name}")
+        self.subprocess = subprocess.Popen(
+            [os.environ.get("STEVE", "steve"), "--dev-name", self.dev_name, 
*args]
+        )
+
+        # wait for device to appear
+        for attempt in range(15):
+            if self.dev_path.exists():
+                break
+            time.sleep(0.1 * attempt)
+        else:
+            raise RuntimeError("Steve failed to start")
+
+    def terminate(self) -> None:
+        if self.subprocess is not None:
+            self.subprocess.terminate()
+            self.retval = self.subprocess.wait()
+
+
[email protected]
+def steve() -> Steve:
+    steve = Steve()
+    yield steve
+    steve.terminate()

diff --git a/test/test_steve.py b/test/test_steve.py
new file mode 100644
index 0000000..3612f4c
--- /dev/null
+++ b/test/test_steve.py
@@ -0,0 +1,19 @@
+import os
+
+
+def test_basic(steve):
+    steve.start(["-j", "5"])
+    with steve.dev_path.open("r+b") as f:
+        tokens = f.read(5)
+        assert len(tokens) == 5
+        f.write(tokens)
+
+
+def test_too_many(steve):
+    steve.start(["-j", "5"])
+    with steve.dev_path.open("r+b") as f:
+        os.set_blocking(f.fileno(), False)
+        tokens = f.read(6)
+        assert len(tokens) == 5
+        assert f.read(1) is None
+        f.write(tokens)

Reply via email to