diff --git a/.gitignore b/.gitignore index ea8c4bf..3005ab5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /target +/tests/component/__pycache__ +/tests/component/harness/__pycache__ diff --git a/Makefile b/Makefile index af4fc30..116f15a 100644 --- a/Makefile +++ b/Makefile @@ -48,6 +48,12 @@ clean: unit-test: RUST_BACKTRACE=1 cargo test --all-features +.PHONY: component-test +component-test: + cargo build --locked --no-default-features \ + --features threshold-engine + python3 -m pytest tests/component + .PHONY: test test: unit-test diff --git a/tests/component/harness/__init__.py b/tests/component/harness/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/component/harness/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/component/harness/common.py b/tests/component/harness/common.py new file mode 100644 index 0000000..bec6955 --- /dev/null +++ b/tests/component/harness/common.py @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (c) 2026 Nutanix, Inc. All rights reserved. +# +# Author: Leonardo Forchini + +import json +import subprocess +import time +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[3] +POLL_S = 0.2 + + +def wait_for(predicate, timeout, description): + """Poll ``predicate`` until it is true or ``timeout`` seconds elapse.""" + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + if predicate(): + return + time.sleep(0.05) + raise AssertionError("timed out waiting for %s" % (description,)) + + +def physical(root, logical): + """Prefix an absolute path with the test-specific root.""" + return root / logical.lstrip("/") + + +def write_json(path, payload): + """Write ``payload`` as JSON to ``path``, creating parent directories.""" + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(payload)) + + +def target_dir(): + """Return cargo's target directory for this workspace.""" + raw = subprocess.check_output( + ["cargo", "metadata", "--format-version", "1", "--no-deps", "--offline"], + cwd=REPO_ROOT, + text=True, + ) + return Path(json.loads(raw)["target_directory"]) + + +def built_binary(name): + """Return the path of a debug binary, failing if it has not been built.""" + binary = target_dir() / "debug" / name + if not binary.exists(): + raise RuntimeError("%s is not built at %s" % (name, binary)) + return binary