Added shim, ready to build and test on Pi Zero 2Ws
Some checks are pending
Tests / Detect Changes (push) Waiting to run
Tests / Unit Tests (push) Blocked by required conditions
Tests / Unit Tests-1 (push) Blocked by required conditions
Tests / Unit Tests-2 (push) Blocked by required conditions
Tests / Unit Tests-3 (push) Blocked by required conditions
Tests / Integration Tests (push) Blocked by required conditions
Tests / Integration Tests-1 (push) Blocked by required conditions
Tests / Integration Tests-2 (push) Blocked by required conditions
Tests / Integration Tests-3 (push) Blocked by required conditions
Tests / Installer Test (Fresh System) (push) Blocked by required conditions
Tests / Installer Test (Fresh System)-1 (push) Blocked by required conditions
Tests / Installer Test (Fresh System)-2 (push) Blocked by required conditions
Tests / Installer Test (Fresh System)-3 (push) Blocked by required conditions
Tests / Installer Test (Fresh System)-4 (push) Blocked by required conditions
Tests / Installer Test (Raspberry Pi OS - ARM) (push) Waiting to run
Tests / Installer Test (Raspberry Pi OS - ARM)-1 (push) Waiting to run
Some checks are pending
Tests / Detect Changes (push) Waiting to run
Tests / Unit Tests (push) Blocked by required conditions
Tests / Unit Tests-1 (push) Blocked by required conditions
Tests / Unit Tests-2 (push) Blocked by required conditions
Tests / Unit Tests-3 (push) Blocked by required conditions
Tests / Integration Tests (push) Blocked by required conditions
Tests / Integration Tests-1 (push) Blocked by required conditions
Tests / Integration Tests-2 (push) Blocked by required conditions
Tests / Integration Tests-3 (push) Blocked by required conditions
Tests / Installer Test (Fresh System) (push) Blocked by required conditions
Tests / Installer Test (Fresh System)-1 (push) Blocked by required conditions
Tests / Installer Test (Fresh System)-2 (push) Blocked by required conditions
Tests / Installer Test (Fresh System)-3 (push) Blocked by required conditions
Tests / Installer Test (Fresh System)-4 (push) Blocked by required conditions
Tests / Installer Test (Raspberry Pi OS - ARM) (push) Waiting to run
Tests / Installer Test (Raspberry Pi OS - ARM)-1 (push) Waiting to run
This commit is contained in:
parent
36bc86baa9
commit
65a3b73014
2 changed files with 249 additions and 0 deletions
179
migration/tests/test_fragmentation_backend_shim.py
Normal file
179
migration/tests/test_fragmentation_backend_shim.py
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
|
||||
SRC_DIR = os.path.join(REPO_ROOT, "src")
|
||||
CPP_BUILD_DIR = os.path.join(REPO_ROOT, "migration", "protocol_core")
|
||||
|
||||
|
||||
PROBE = r"""
|
||||
import json
|
||||
from ble_reticulum.BLEFragmentationBackend import (
|
||||
BACKEND,
|
||||
BLEFragmenter,
|
||||
BLEReassembler,
|
||||
HDLCFramer,
|
||||
)
|
||||
|
||||
fragmenter = BLEFragmenter(mtu=20)
|
||||
reassembler = BLEReassembler(timeout=0.1)
|
||||
packet = b"abc"
|
||||
fragments = fragmenter.fragment_packet(packet)
|
||||
result = reassembler.receive_fragment(fragments[0], "device1")
|
||||
framed = HDLCFramer.frame_packet(packet)
|
||||
deframed = HDLCFramer.deframe_packet(framed)
|
||||
|
||||
print(json.dumps({
|
||||
"backend": BACKEND,
|
||||
"fragmenter_module": BLEFragmenter.__module__,
|
||||
"reassembler_module": BLEReassembler.__module__,
|
||||
"hdlc_module": HDLCFramer.__module__,
|
||||
"fragment": fragments[0].hex(),
|
||||
"result": result.hex(),
|
||||
"deframed": deframed.hex(),
|
||||
"stats_keys": sorted(reassembler.get_statistics().keys()),
|
||||
"fragmenter_methods": sorted(
|
||||
name for name in dir(BLEFragmenter) if name in {
|
||||
"fragment_packet",
|
||||
"get_fragment_overhead",
|
||||
}
|
||||
),
|
||||
"reassembler_methods": sorted(
|
||||
name for name in dir(BLEReassembler) if name in {
|
||||
"receive_fragment",
|
||||
"_reassemble",
|
||||
"cleanup_stale_buffers",
|
||||
"get_statistics",
|
||||
"reset_statistics",
|
||||
}
|
||||
),
|
||||
"hdlc_methods": sorted(
|
||||
name for name in dir(HDLCFramer) if name in {
|
||||
"frame_packet",
|
||||
"deframe_packet",
|
||||
}
|
||||
),
|
||||
}))
|
||||
"""
|
||||
|
||||
|
||||
def run_probe(backend, include_cpp):
|
||||
env = os.environ.copy()
|
||||
env["BLE_RETICULUM_FRAGMENTATION_BACKEND"] = backend
|
||||
pythonpath = [SRC_DIR]
|
||||
if include_cpp:
|
||||
pythonpath.append(CPP_BUILD_DIR)
|
||||
if env.get("PYTHONPATH"):
|
||||
pythonpath.append(env["PYTHONPATH"])
|
||||
env["PYTHONPATH"] = os.pathsep.join(pythonpath)
|
||||
|
||||
completed = subprocess.run(
|
||||
[sys.executable, "-c", PROBE],
|
||||
cwd=REPO_ROOT,
|
||||
env=env,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=True,
|
||||
)
|
||||
return json.loads(completed.stdout)
|
||||
|
||||
|
||||
def test_cpp_backend_loads_when_available():
|
||||
result = run_probe("cpp", include_cpp=True)
|
||||
|
||||
assert result["backend"] == "cpp"
|
||||
assert result["fragmenter_module"] == "ble_protocol_core_cpp"
|
||||
assert result["reassembler_module"] == "ble_protocol_core_cpp"
|
||||
assert result["hdlc_module"] == "ble_protocol_core_cpp"
|
||||
assert result["fragment"] == "0100000001616263"
|
||||
assert result["result"] == "616263"
|
||||
assert result["deframed"] == "616263"
|
||||
|
||||
|
||||
def test_python_backend_still_works_when_cpp_backend_is_unavailable():
|
||||
result = run_probe("auto", include_cpp=False)
|
||||
|
||||
assert result["backend"] == "python"
|
||||
assert result["fragmenter_module"] == "ble_reticulum.BLEFragmentation"
|
||||
assert result["fragment"] == "0100000001616263"
|
||||
assert result["result"] == "616263"
|
||||
assert result["deframed"] == "616263"
|
||||
|
||||
|
||||
def test_auto_backend_prefers_cpp_when_available():
|
||||
result = run_probe("auto", include_cpp=True)
|
||||
|
||||
assert result["backend"] == "cpp"
|
||||
assert result["fragmenter_module"] == "ble_protocol_core_cpp"
|
||||
|
||||
|
||||
def test_python_backend_can_be_forced_even_when_cpp_backend_is_available():
|
||||
result = run_probe("python", include_cpp=True)
|
||||
|
||||
assert result["backend"] == "python"
|
||||
assert result["fragmenter_module"] == "ble_reticulum.BLEFragmentation"
|
||||
|
||||
|
||||
def test_cpp_backend_request_fails_clearly_when_unavailable():
|
||||
env = os.environ.copy()
|
||||
env["BLE_RETICULUM_FRAGMENTATION_BACKEND"] = "cpp"
|
||||
env["PYTHONPATH"] = SRC_DIR
|
||||
|
||||
completed = subprocess.run(
|
||||
[sys.executable, "-c", "import ble_reticulum.BLEFragmentationBackend"],
|
||||
cwd=REPO_ROOT,
|
||||
env=env,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
assert completed.returncode != 0
|
||||
assert "C++ BLE fragmentation backend is not available" in completed.stderr
|
||||
|
||||
|
||||
def test_selected_backend_exposes_methods_expected_by_current_python_code():
|
||||
result = run_probe("cpp", include_cpp=True)
|
||||
|
||||
assert result["fragmenter_methods"] == [
|
||||
"fragment_packet",
|
||||
"get_fragment_overhead",
|
||||
]
|
||||
assert result["reassembler_methods"] == [
|
||||
"_reassemble",
|
||||
"cleanup_stale_buffers",
|
||||
"get_statistics",
|
||||
"receive_fragment",
|
||||
"reset_statistics",
|
||||
]
|
||||
assert result["hdlc_methods"] == [
|
||||
"deframe_packet",
|
||||
"frame_packet",
|
||||
]
|
||||
assert result["stats_keys"] == [
|
||||
"fragments_received",
|
||||
"packets_reassembled",
|
||||
"packets_timeout",
|
||||
"pending_packets",
|
||||
]
|
||||
|
||||
|
||||
def test_invalid_backend_setting_fails_clearly():
|
||||
env = os.environ.copy()
|
||||
env["BLE_RETICULUM_FRAGMENTATION_BACKEND"] = "rust"
|
||||
env["PYTHONPATH"] = SRC_DIR
|
||||
|
||||
completed = subprocess.run(
|
||||
[sys.executable, "-c", "import ble_reticulum.BLEFragmentationBackend"],
|
||||
cwd=REPO_ROOT,
|
||||
env=env,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
assert completed.returncode != 0
|
||||
assert "expected 'auto', 'cpp', or 'python'" in completed.stderr
|
||||
70
src/ble_reticulum/BLEFragmentationBackend.py
Normal file
70
src/ble_reticulum/BLEFragmentationBackend.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
"""
|
||||
Selectable backend for BLE fragmentation protocol classes.
|
||||
|
||||
This module is intentionally small. It gives integration code one import location
|
||||
while preserving a Python fallback until the C++ backend is proven in live BLE
|
||||
use.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
|
||||
BACKEND_ENV_VAR = "BLE_RETICULUM_FRAGMENTATION_BACKEND"
|
||||
DEFAULT_BACKEND = "auto"
|
||||
|
||||
|
||||
def _requested_backend() -> str:
|
||||
value = os.environ.get(BACKEND_ENV_VAR, DEFAULT_BACKEND).strip().lower()
|
||||
if value not in ("auto", "cpp", "python"):
|
||||
raise ValueError(
|
||||
f"Invalid {BACKEND_ENV_VAR}={value!r}; expected 'auto', 'cpp', or 'python'"
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
def _load_python_backend():
|
||||
from .BLEFragmentation import BLEFragmenter, BLEReassembler, HDLCFramer
|
||||
|
||||
return "python", BLEFragmenter, BLEReassembler, HDLCFramer
|
||||
|
||||
|
||||
def _load_cpp_backend():
|
||||
# Future packaging should prefer a package-local extension module. The
|
||||
# top-level name is used by the current migration/protocol_core build.
|
||||
try:
|
||||
from ._ble_protocol_core import BLEFragmenter, BLEReassembler, HDLCFramer
|
||||
except ImportError as package_error:
|
||||
try:
|
||||
from ble_protocol_core_cpp import BLEFragmenter, BLEReassembler, HDLCFramer
|
||||
except ImportError as top_level_error:
|
||||
raise ImportError(
|
||||
"C++ BLE fragmentation backend is not available. Build or install "
|
||||
"the pybind11 extension, or set "
|
||||
f"{BACKEND_ENV_VAR}=python."
|
||||
) from top_level_error
|
||||
|
||||
return "cpp", BLEFragmenter, BLEReassembler, HDLCFramer
|
||||
|
||||
|
||||
_backend = _requested_backend()
|
||||
|
||||
if _backend == "python":
|
||||
BACKEND, BLEFragmenter, BLEReassembler, HDLCFramer = _load_python_backend()
|
||||
elif _backend == "cpp":
|
||||
BACKEND, BLEFragmenter, BLEReassembler, HDLCFramer = _load_cpp_backend()
|
||||
else:
|
||||
try:
|
||||
BACKEND, BLEFragmenter, BLEReassembler, HDLCFramer = _load_cpp_backend()
|
||||
except ImportError:
|
||||
BACKEND, BLEFragmenter, BLEReassembler, HDLCFramer = _load_python_backend()
|
||||
|
||||
|
||||
__all__ = [
|
||||
"BACKEND",
|
||||
"BACKEND_ENV_VAR",
|
||||
"BLEFragmenter",
|
||||
"BLEReassembler",
|
||||
"HDLCFramer",
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue