9740bc64c9
Continuous Deployment / Deploy to Production (push) Blocked by required conditions
Continuous Deployment / Rollback Deployment (push) Blocked by required conditions
Continuous Deployment / Post-deployment Monitoring (push) Blocked by required conditions
Continuous Deployment / Notify Deployment Status (push) Blocked by required conditions
Firmware QEMU Tests (ADR-061) / QEMU Test (edge-tier1) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (full-adr060) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (tdm-3node) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / Swarm Test (ADR-062) (push) Has been skipped
npm packages / tools/ruview-mcp (node 22) (push) Failing after 1s
nvsim-server → ghcr.io / build-and-publish (push) Failing after 1s
ruview-swarm CI guard / tests (full+train) (push) Failing after 2s
Bench Regression Guard / bench compile-verify (--no-run) (push) Failing after 0s
Bench Regression Guard / bench fast-run (informational, non-gating) (push) Has been skipped
Firmware CI / Verify version.txt matches release tag (push) Has been skipped
Dashboard a11y + cross-browser / a11y (push) Failing after 0s
nvsim Dashboard → GitHub Pages / build-and-deploy (push) Failing after 2s
Firmware CI / Build firmware (esp32s3 / 4mb) (push) Failing after 15s
Firmware QEMU Tests (ADR-061) / Build Espressif QEMU (push) Failing after 1s
Firmware QEMU Tests (ADR-061) / Fuzz Testing (ADR-061 Layer 6) (push) Failing after 1s
Continuous Deployment / Pre-deployment Checks (push) Has been skipped
Continuous Deployment / Deploy to Staging (push) Waiting to run
Firmware CI / Build firmware (esp32c6 / c6-4mb) (push) Failing after 15s
Firmware CI / Build firmware (esp32s3 / 8mb) (push) Failing after 15s
Firmware QEMU Tests (ADR-061) / QEMU Test (boundary-max) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (boundary-min) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (default) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / QEMU Test (edge-tier0) (push) Has been skipped
Firmware QEMU Tests (ADR-061) / NVS Matrix Generation (push) Failing after 1s
Security Scanning / Security Policy Compliance (push) Failing after 0s
Security Scanning / Security Report (push) Waiting to run
Security Scanning / Dependency Vulnerability Scan (push) Failing after 0s
Security Scanning / Static Application Security Testing (push) Failing after 1s
Security Scanning / Infrastructure Security Scan (push) Failing after 1s
Security Scanning / Secret Scanning (push) Failing after 1s
npm packages / harness/ruview (node 22) (push) Failing after 17s
Security Scanning / License Compliance Scan (push) Failing after 1s
Security Scanning / Container Security Scan (push) Failing after 4s
three.js demos → GitHub Pages / build-and-deploy (push) Failing after 1s
Verify Pipeline Determinism / Verify Pipeline Determinism (3.11) (push) Failing after 1s
Fix-Marker Regression Guard / Verify fix markers (push) Failing after 1s
ADR-115 MQTT integration tests / mqtt-integration (push) Failing after 1s
npm packages / harness/ruview (node 20) (push) Failing after 1s
npm packages / tools/ruview-mcp (node 20) (push) Failing after 1s
npm packages / tools/ruview-cli (node 20) (push) Failing after 1s
npm packages / tools/ruview-cli (node 22) (push) Failing after 1s
BFLD MQTT Integration / cargo test --features mqtt (live mosquitto) (push) Failing after 29s
ruview-swarm CI guard / build train_marl bin (push) Failing after 2s
ruview-swarm CI guard / clippy (-D warnings, --no-deps) (push) Failing after 3s
ruview-swarm CI guard / tests (ruflo) (push) Failing after 1s
ruview-swarm CI guard / tests (train) (push) Failing after 2s
ruview-swarm CI guard / tests (default) (push) Failing after 2s
Point Cloud Viewer → GitHub Pages / build-and-deploy (push) Failing after 8s
ruview-swarm CI guard / ITAR / publish guard (push) Failing after 0s
wifi-densepose sensing-server → Docker Hub + ghcr.io / build · push · smoke-test (push) Failing after 1s
249 lines
7.4 KiB
Python
249 lines
7.4 KiB
Python
"""ADR-117 P2 tests — BoundingBox + PersonPose + PoseEstimate bindings.
|
|
|
|
Run with: cd python && .venv/Scripts/python -m pytest tests/test_pose.py -v
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from wifi_densepose import (
|
|
BoundingBox,
|
|
Keypoint,
|
|
KeypointType,
|
|
PersonPose,
|
|
PoseEstimate,
|
|
)
|
|
|
|
|
|
# ─── BoundingBox ─────────────────────────────────────────────────────
|
|
|
|
|
|
def test_bounding_box_construct() -> None:
|
|
bb = BoundingBox(0.1, 0.2, 0.5, 0.7)
|
|
assert bb.x_min == pytest.approx(0.1)
|
|
assert bb.y_min == pytest.approx(0.2)
|
|
assert bb.x_max == pytest.approx(0.5)
|
|
assert bb.y_max == pytest.approx(0.7)
|
|
|
|
|
|
def test_bounding_box_dimensions() -> None:
|
|
bb = BoundingBox(0.0, 0.0, 4.0, 3.0)
|
|
assert bb.width == pytest.approx(4.0)
|
|
assert bb.height == pytest.approx(3.0)
|
|
assert bb.area == pytest.approx(12.0)
|
|
assert bb.center == pytest.approx((2.0, 1.5))
|
|
|
|
|
|
def test_bounding_box_from_center() -> None:
|
|
bb = BoundingBox.from_center(2.0, 3.0, 4.0, 6.0)
|
|
assert bb.x_min == pytest.approx(0.0)
|
|
assert bb.y_min == pytest.approx(0.0)
|
|
assert bb.x_max == pytest.approx(4.0)
|
|
assert bb.y_max == pytest.approx(6.0)
|
|
|
|
|
|
def test_bounding_box_iou_no_overlap() -> None:
|
|
a = BoundingBox(0.0, 0.0, 1.0, 1.0)
|
|
b = BoundingBox(2.0, 2.0, 3.0, 3.0)
|
|
assert a.iou(b) == pytest.approx(0.0)
|
|
|
|
|
|
def test_bounding_box_iou_full_overlap() -> None:
|
|
a = BoundingBox(0.0, 0.0, 1.0, 1.0)
|
|
b = BoundingBox(0.0, 0.0, 1.0, 1.0)
|
|
assert a.iou(b) == pytest.approx(1.0)
|
|
|
|
|
|
def test_bounding_box_iou_partial() -> None:
|
|
a = BoundingBox(0.0, 0.0, 10.0, 10.0)
|
|
b = BoundingBox(5.0, 5.0, 15.0, 15.0)
|
|
# intersection 25, union 175 → 1/7
|
|
assert a.iou(b) == pytest.approx(25.0 / 175.0)
|
|
|
|
|
|
def test_bounding_box_eq() -> None:
|
|
assert BoundingBox(1, 2, 3, 4) == BoundingBox(1, 2, 3, 4)
|
|
assert BoundingBox(1, 2, 3, 4) != BoundingBox(1, 2, 3, 5)
|
|
|
|
|
|
def test_bounding_box_repr() -> None:
|
|
bb = BoundingBox(0.1, 0.2, 0.5, 0.7)
|
|
assert "BoundingBox" in repr(bb)
|
|
assert "x_min=0.1" in repr(bb)
|
|
|
|
|
|
# ─── PersonPose ──────────────────────────────────────────────────────
|
|
|
|
|
|
def test_person_pose_empty() -> None:
|
|
p = PersonPose()
|
|
assert p.id is None
|
|
assert p.visible_keypoint_count == 0
|
|
assert p.bounding_box is None
|
|
assert p.confidence == 0.0
|
|
|
|
|
|
def test_person_pose_set_get_keypoint() -> None:
|
|
p = PersonPose()
|
|
kp = Keypoint(KeypointType.Nose, 0.5, 0.3, 0.95)
|
|
p.set_keypoint(kp)
|
|
got = p.get_keypoint(KeypointType.Nose)
|
|
assert got is not None
|
|
assert got.x == pytest.approx(0.5)
|
|
assert got.confidence == pytest.approx(0.95)
|
|
|
|
|
|
def test_person_pose_get_missing_returns_none() -> None:
|
|
p = PersonPose()
|
|
p.set_keypoint(Keypoint(KeypointType.Nose, 0.5, 0.3, 0.95))
|
|
assert p.get_keypoint(KeypointType.LeftWrist) is None
|
|
|
|
|
|
def test_person_pose_visible_count() -> None:
|
|
p = PersonPose()
|
|
p.set_keypoint(Keypoint(KeypointType.Nose, 0.0, 0.0, 0.9)) # visible
|
|
p.set_keypoint(Keypoint(KeypointType.LeftEar, 0.0, 0.0, 0.2)) # invisible
|
|
p.set_keypoint(Keypoint(KeypointType.RightEar, 0.0, 0.0, 0.8)) # visible
|
|
assert p.visible_keypoint_count == 2
|
|
|
|
|
|
def test_person_pose_visible_keypoints_list() -> None:
|
|
p = PersonPose()
|
|
p.set_keypoint(Keypoint(KeypointType.Nose, 0.0, 0.0, 0.9))
|
|
p.set_keypoint(Keypoint(KeypointType.LeftEar, 0.0, 0.0, 0.2))
|
|
vis = p.visible_keypoints()
|
|
assert len(vis) == 1
|
|
assert vis[0].keypoint_type == KeypointType.Nose
|
|
|
|
|
|
def test_person_pose_keypoints_dict_excludes_missing() -> None:
|
|
p = PersonPose()
|
|
p.set_keypoint(Keypoint(KeypointType.Nose, 0.0, 0.0, 0.9))
|
|
p.set_keypoint(Keypoint(KeypointType.LeftWrist, 0.5, 0.5, 0.6))
|
|
d = p.keypoints()
|
|
assert KeypointType.Nose in d
|
|
assert KeypointType.LeftWrist in d
|
|
assert KeypointType.RightAnkle not in d
|
|
assert len(d) == 2
|
|
|
|
|
|
def test_person_pose_set_id() -> None:
|
|
p = PersonPose()
|
|
p.set_id(7)
|
|
assert p.id == 7
|
|
|
|
|
|
def test_person_pose_set_bounding_box() -> None:
|
|
p = PersonPose()
|
|
bb = BoundingBox(0.1, 0.1, 0.5, 0.9)
|
|
p.set_bounding_box(bb)
|
|
assert p.bounding_box == bb
|
|
|
|
|
|
def test_person_pose_compute_bbox_returns_none_when_empty() -> None:
|
|
p = PersonPose()
|
|
assert p.compute_bounding_box() is None
|
|
|
|
|
|
def test_person_pose_compute_bbox_from_keypoints() -> None:
|
|
p = PersonPose()
|
|
p.set_keypoint(Keypoint(KeypointType.Nose, 0.0, 0.0, 0.95))
|
|
p.set_keypoint(Keypoint(KeypointType.RightAnkle, 1.0, 2.0, 0.95))
|
|
bb = p.compute_bounding_box()
|
|
assert bb is not None
|
|
# bbox should span both keypoints
|
|
assert bb.x_min <= 0.0
|
|
assert bb.y_min <= 0.0
|
|
assert bb.x_max >= 1.0
|
|
assert bb.y_max >= 2.0
|
|
# also stored
|
|
assert p.bounding_box is not None
|
|
|
|
|
|
def test_person_pose_set_confidence_validation() -> None:
|
|
p = PersonPose()
|
|
p.set_confidence(0.85)
|
|
assert p.confidence == pytest.approx(0.85)
|
|
with pytest.raises(ValueError):
|
|
p.set_confidence(1.5)
|
|
|
|
|
|
def test_person_pose_repr() -> None:
|
|
p = PersonPose()
|
|
p.set_id(3)
|
|
p.set_keypoint(Keypoint(KeypointType.Nose, 0.0, 0.0, 0.9))
|
|
r = repr(p)
|
|
assert "PersonPose" in r
|
|
assert "id=Some(3)" in r or "id=3" in r
|
|
|
|
|
|
# ─── PoseEstimate ────────────────────────────────────────────────────
|
|
|
|
|
|
def test_pose_estimate_construct_empty() -> None:
|
|
e = PoseEstimate([], 0.5, 1.0, "test-v0")
|
|
assert e.person_count == 0
|
|
assert not e.has_detections
|
|
assert e.confidence == pytest.approx(0.5)
|
|
assert e.latency_ms == pytest.approx(1.0)
|
|
assert e.model_version == "test-v0"
|
|
|
|
|
|
def test_pose_estimate_construct_with_persons() -> None:
|
|
p1 = PersonPose()
|
|
p1.set_id(1)
|
|
p1.set_confidence(0.8)
|
|
p2 = PersonPose()
|
|
p2.set_id(2)
|
|
p2.set_confidence(0.9)
|
|
e = PoseEstimate([p1, p2], 0.85, 5.2, "v0.7.0")
|
|
assert e.person_count == 2
|
|
assert e.has_detections
|
|
assert e.confidence == pytest.approx(0.85)
|
|
|
|
|
|
def test_pose_estimate_highest_confidence_person() -> None:
|
|
p1 = PersonPose()
|
|
p1.set_confidence(0.5)
|
|
p2 = PersonPose()
|
|
p2.set_confidence(0.95)
|
|
p3 = PersonPose()
|
|
p3.set_confidence(0.7)
|
|
e = PoseEstimate([p1, p2, p3], 0.85, 5.2, "v0.7.0")
|
|
best = e.highest_confidence_person()
|
|
assert best is not None
|
|
assert best.confidence == pytest.approx(0.95)
|
|
|
|
|
|
def test_pose_estimate_highest_confidence_returns_none_when_empty() -> None:
|
|
e = PoseEstimate([], 0.5, 1.0, "test")
|
|
assert e.highest_confidence_person() is None
|
|
|
|
|
|
def test_pose_estimate_metadata_strings_nonempty() -> None:
|
|
e = PoseEstimate([], 0.5, 1.0, "test")
|
|
assert isinstance(e.id, str)
|
|
assert isinstance(e.timestamp, str)
|
|
assert e.id # non-empty
|
|
assert e.timestamp # non-empty
|
|
|
|
|
|
def test_pose_estimate_confidence_validation() -> None:
|
|
with pytest.raises(ValueError):
|
|
PoseEstimate([], 1.5, 0.0, "test")
|
|
|
|
|
|
def test_pose_estimate_repr_contains_counts() -> None:
|
|
e = PoseEstimate([], 0.5, 2.3, "v0.7.0")
|
|
r = repr(e)
|
|
assert "PoseEstimate" in r
|
|
assert "v0.7.0" in r
|
|
|
|
|
|
def test_build_features_marks_p2_complete() -> None:
|
|
import wifi_densepose
|
|
|
|
assert "p2-keypoint-bindings" in wifi_densepose.__build_features__
|
|
assert "p2-pose-bindings" in wifi_densepose.__build_features__
|