Spaces:
Running on Zero
Running on Zero
| """Tests for the FMS session accumulator — no GPU, no model downloads.""" | |
| import numpy as np | |
| from formscout.types import ( | |
| IngestResult, Pose2DResult, BiomechFeatures, ScoreResult, JudgeResult, | |
| MovementResult, SessionEntry, | |
| ) | |
| def test_session_entry_holds_typed_objects(): | |
| movement = MovementResult(test_name="deep_squat", side="na", confidence=1.0) | |
| features = BiomechFeatures( | |
| test_name="deep_squat", view="2d", side="na", | |
| angles={"left_knee_flexion_deg": 95.0}, alignments={"knees_tracking_over_feet": True}, | |
| symmetry_delta=None, timing={"deepest_frame": 2}, confidence=0.9, | |
| ) | |
| rubric = ScoreResult(score=2, rationale="ok", confidence=0.8) | |
| judge = JudgeResult(score=2, rationale="ok", compensation_tags=["heels elevated"], | |
| corrective_hint="ankle mobility", confidence=0.85) | |
| entry = SessionEntry( | |
| test_name="deep_squat", side="na", score=2, needs_human=False, | |
| rationale="ok", compensation_tags=["heels elevated"], corrective_hint="ankle mobility", | |
| measurements={"left_knee_flexion_deg": 95.0}, confidence=0.85, view="2d", | |
| keyframe_path=None, movement=movement, features=features, | |
| rubric_score=rubric, judge=judge, | |
| ) | |
| assert entry.score == 2 | |
| assert entry.movement.test_name == "deep_squat" | |
| assert entry.rubric_score.score == 2 | |
| assert entry.judge.compensation_tags == ["heels elevated"] | |
| def _ingest(n=5, h=480, w=640): | |
| frames = [np.zeros((h, w, 3), dtype=np.uint8) for _ in range(n)] | |
| return IngestResult(frames=frames, fps=30.0, duration=n / 30.0, n_people=1, width=w, height=h) | |
| def _pose(n=5): | |
| kps = [] | |
| for i in range(n): | |
| kps.append({j: {"x": float(50 + j * 25), "y": float(80 + j * 18), "conf": 0.9} | |
| for j in range(17)}) | |
| return Pose2DResult(keypoints=kps, fps=30.0, confidence=0.9) | |
| def _features(test_name="deep_squat", side="na", frame_key="deepest_frame"): | |
| return BiomechFeatures( | |
| test_name=test_name, view="2d", side=side, | |
| angles={"left_knee_flexion_deg": 95.0}, | |
| alignments={"knees_tracking_over_feet": False}, | |
| symmetry_delta=None, timing={frame_key: 2}, confidence=0.9, | |
| ) | |
| def _judge(score=2, needs_human=False): | |
| return JudgeResult( | |
| score=None if needs_human else score, rationale="r", | |
| compensation_tags=["heels elevated"], corrective_hint="ankle mobility", | |
| confidence=0.85, needs_human=needs_human, | |
| ) | |
| def test_add_analysis_appends_entry_and_writes_files(): | |
| import os | |
| from formscout import session as S | |
| sess = S.new_session() | |
| entry = S.add_analysis(sess, ingest=_ingest(), pose2d=_pose(), | |
| features=_features(), judge=_judge(), test_name="deep_squat", side="na") | |
| assert len(sess.entries) == 1 | |
| assert entry.score == 2 | |
| assert os.path.exists(os.path.join(sess.session_dir, "session.json")) | |
| assert os.path.exists(os.path.join(sess.session_dir, "analysis.md")) | |
| # key-frame still written (deepest_frame=2 is valid) | |
| assert entry.keyframe_path and os.path.exists(entry.keyframe_path) | |
| def test_finish_composite_null_when_needs_human(): | |
| from formscout import session as S | |
| sess = S.new_session() | |
| S.add_analysis(sess, ingest=_ingest(), pose2d=_pose(), features=_features(), | |
| judge=_judge(score=3), test_name="deep_squat", side="na") | |
| S.add_analysis(sess, ingest=_ingest(), pose2d=_pose(), | |
| features=_features("trunk_stability_pushup", frame_key="max_sag_frame"), | |
| judge=_judge(needs_human=True), test_name="trunk_stability_pushup", side="na") | |
| report, pdf_path = S.finish_session(sess) | |
| assert report is not None | |
| assert report.composite is None # one test needs_human | |
| def test_finish_empty_session_returns_none(): | |
| from formscout import session as S | |
| sess = S.new_session() | |
| report, pdf_path = S.finish_session(sess) | |
| assert report is None and pdf_path is None | |