Files
by-aeandCursor Agent 5489ac6bef test: add comprehensive tests for pose_editor module
- Test T-pose creation and structure validation
- Test pose data hashing for caching
- Test skeleton structure (child keypoints, descendants)
- Test pose transformations (flip, space_out_people)
- Test validation of malformed/empty pose data
- Test helper functions (get_closest_person, get_nearest_bone)
- Test fix_missing_keypoints functionality
- 16 passing tests, 1 skipped (interactive pygame test)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-10-23 20:57:58 +00:00

321 lines
10 KiB
Python

"""
Tests for pose_editor module
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import pytest
import torch
import numpy as np
def test_module_imports():
"""Test that pose_editor can be imported"""
import pose_editor
assert hasattr(pose_editor, 'pose_editor')
assert hasattr(pose_editor, 'create_t_pose')
assert hasattr(pose_editor, 'get_pose_hash')
def test_create_t_pose():
"""Test T-pose template creation"""
from pose_editor import create_t_pose
t_pose = create_t_pose()
# Should have 18 keypoints (standard OpenPose format)
assert len(t_pose) == 18
# Each keypoint should have [x, y] coordinates
for point in t_pose:
assert len(point) == 2
assert isinstance(point[0], (int, float))
assert isinstance(point[1], (int, float))
# Verify some expected positions (neck should be below nose)
nose = t_pose[0]
neck = t_pose[1]
assert neck[1] > nose[1], "Neck should be below nose"
# Left and right shoulders should be symmetric
right_shoulder = t_pose[2]
left_shoulder = t_pose[5]
assert abs(right_shoulder[0] - neck[0]) == abs(left_shoulder[0] - neck[0]), "Shoulders should be symmetric"
def test_get_pose_hash():
"""Test pose data hashing for caching"""
from pose_editor import get_pose_hash
pose_data1 = [{"people": [{"pose_keypoints_2d": [100, 200, 1.0, 150, 250, 1.0]}]}]
pose_data2 = [{"people": [{"pose_keypoints_2d": [100, 200, 1.0, 150, 250, 1.0]}]}]
pose_data3 = [{"people": [{"pose_keypoints_2d": [100, 200, 1.0, 150, 251, 1.0]}]}]
hash1 = get_pose_hash(pose_data1)
hash2 = get_pose_hash(pose_data2)
hash3 = get_pose_hash(pose_data3)
# Same data should produce same hash
assert hash1 == hash2
# Different data should produce different hash
assert hash1 != hash3
# Hash should be a string
assert isinstance(hash1, str)
assert len(hash1) > 0
def test_get_child_keypoints():
"""Test getting child keypoints from skeleton structure"""
from pose_editor import get_child_keypoints
# Keypoint 1 (neck) should have multiple children
children = get_child_keypoints(1)
assert len(children) > 0
assert 0 in children # nose is child of neck
# Keypoint 0 (nose) should have children (eyes)
children = get_child_keypoints(0)
assert 14 in children or 15 in children # eyes are children of nose
def test_get_all_descendants():
"""Test recursive descendant finding"""
from pose_editor import get_all_descendants
# Neck (1) should have many descendants (entire body)
descendants = get_all_descendants(1)
assert len(descendants) > 5
assert 0 in descendants # nose
assert 2 in descendants or 5 in descendants # shoulders
# Elbow should have fewer descendants (just wrist)
descendants = get_all_descendants(3) # right elbow
assert 4 in descendants # right wrist
def test_flip_person_horizontal():
"""Test horizontal flipping with left-right swap"""
from pose_editor import flip_person_horizontal, create_t_pose
# Create a simple pose
people_keypoints = [create_t_pose()]
people_valid_keypoints = [list(range(18))]
# Store original positions (store x-coordinates)
original_right_shoulder_x = people_keypoints[0][2][0]
original_left_shoulder_x = people_keypoints[0][5][0]
original_neck_x = people_keypoints[0][1][0]
# Flip with left-right swap
flip_person_horizontal(0, people_keypoints, people_valid_keypoints, swap_left_right=True)
# After flip with swap:
# 1. X-coordinates should be mirrored around center
# 2. Left and right keypoints should be swapped
new_right_shoulder_x = people_keypoints[0][2][0]
new_left_shoulder_x = people_keypoints[0][5][0]
# Right shoulder should now be where left shoulder was (mirrored)
assert abs(new_right_shoulder_x - (2 * original_neck_x - original_left_shoulder_x)) < 1.0
# Left shoulder should now be where right shoulder was (mirrored)
assert abs(new_left_shoulder_x - (2 * original_neck_x - original_right_shoulder_x)) < 1.0
def test_pose_editor_validation_empty_people():
"""Test pose_editor handles empty people array"""
import pose_editor
# Empty people array
invalid_pose = [{"people": []}]
result = pose_editor.pose_editor(invalid_pose, padding=10, reset_cached_window_position=True)
# Should return None for invalid input
assert result[0] is None
assert result[1] is None
def test_pose_editor_validation_missing_field():
"""Test pose_editor handles missing pose_keypoints_2d field"""
import pose_editor
# Missing pose_keypoints_2d field
invalid_pose = [{"people": [{"invalid_field": []}]}]
result = pose_editor.pose_editor(invalid_pose, padding=10, reset_cached_window_position=True)
# Should return None for invalid input
assert result[0] is None
assert result[1] is None
def test_pose_editor_validation_malformed_keypoints():
"""Test pose_editor handles malformed keypoints array"""
import pose_editor
# Length not divisible by 3
invalid_pose = [{"people": [{"pose_keypoints_2d": [100, 200, 1.0, 150]}]}]
result = pose_editor.pose_editor(invalid_pose, padding=10, reset_cached_window_position=True)
# Should return None for invalid input
assert result[0] is None
assert result[1] is None
def test_pose_editor_validation_wrong_type():
"""Test pose_editor handles wrong type for pose_keypoints_2d"""
import pose_editor
# pose_keypoints_2d is not a list
invalid_pose = [{"people": [{"pose_keypoints_2d": "not a list"}]}]
result = pose_editor.pose_editor(invalid_pose, padding=10, reset_cached_window_position=True)
# Should return None for invalid input
assert result[0] is None
assert result[1] is None
def test_pose_editor_validation_empty_keypoints():
"""Test pose_editor handles empty keypoints array"""
import pose_editor
# Empty keypoints array
invalid_pose = [{"people": [{"pose_keypoints_2d": []}]}]
result = pose_editor.pose_editor(invalid_pose, padding=10, reset_cached_window_position=True)
# Should return None for invalid input
assert result[0] is None
assert result[1] is None
def test_pose_editor_with_none_input():
"""Test pose_editor creates T-pose when input is None"""
import pose_editor
# This test would require pygame display, so we'll skip it in CI
# But we can test the T-pose creation logic
pytest.skip("Skipping interactive pygame test in CI")
def test_fix_missing_keypoints():
"""Test fixing missing keypoints using T-pose proportions"""
from pose_editor import fix_missing_keypoints, create_t_pose
# Create a pose with some missing keypoints
t_pose = create_t_pose()
people_keypoints = [t_pose]
people_valid_keypoints = [[1, 2, 5]] # Only neck and shoulders are valid
people_valid_pairs = [[(1, 2), (1, 5)]]
people_valid_colors = [[(255, 0, 0), (0, 255, 0)]]
people_original_confidences = [[0.0] * 18]
people_original_confidences[0][1] = 1.0
people_original_confidences[0][2] = 1.0
people_original_confidences[0][5] = 1.0
fixed_count = fix_missing_keypoints(
0, people_keypoints, people_valid_keypoints,
people_valid_pairs, people_valid_colors, people_original_confidences
)
# Should fix some missing keypoints
assert fixed_count > 0
assert len(people_valid_keypoints[0]) > 3
def test_space_out_people():
"""Test spacing out overlapping people"""
from pose_editor import space_out_people, create_t_pose
# Create two people at the same location
t_pose1 = create_t_pose()
t_pose2 = [point.copy() for point in create_t_pose()] # Same position
people_keypoints = [t_pose1, t_pose2]
people_valid_keypoints = [list(range(18)), list(range(18))]
# Space them out
spaced_keypoints = space_out_people(people_keypoints, people_valid_keypoints)
# Should return same number of people
assert len(spaced_keypoints) == 2
# The newest person (index 1) should have been moved
# Calculate centers to verify spacing
center1_x = sum(p[0] for p in spaced_keypoints[0]) / len(spaced_keypoints[0])
center2_x = sum(p[0] for p in spaced_keypoints[1]) / len(spaced_keypoints[1])
# Centers should be different after spacing
assert center1_x != center2_x
def test_get_closest_person():
"""Test finding closest person to mouse position"""
from pose_editor import get_closest_person, create_t_pose
# Create two people at different locations
t_pose1 = create_t_pose()
t_pose2 = [[p[0] + 500, p[1]] for p in create_t_pose()]
people_keypoints = [t_pose1, t_pose2]
people_valid_keypoints = [list(range(18)), list(range(18))]
# Mouse near first person
mouse_pos = (400, 200)
closest = get_closest_person(mouse_pos, people_keypoints, people_valid_keypoints)
assert closest == 0
# Mouse near second person
mouse_pos = (900, 200)
closest = get_closest_person(mouse_pos, people_keypoints, people_valid_keypoints)
assert closest == 1
def test_get_nearest_bone():
"""Test finding nearest bone to mouse position"""
from pose_editor import get_nearest_bone, create_t_pose
t_pose = create_t_pose()
people_keypoints = [t_pose]
people_valid_keypoints = [list(range(18))]
# Mouse near neck-nose bone
nose = t_pose[0]
neck = t_pose[1]
mouse_pos = ((nose[0] + neck[0]) / 2, (nose[1] + neck[1]) / 2)
nearest = get_nearest_bone(mouse_pos, people_keypoints, people_valid_keypoints, 0)
# Should return a tuple of keypoint indices
assert isinstance(nearest, tuple)
assert len(nearest) == 2
def test_pose_pairs_structure():
"""Test that POSE_PAIRS structure is valid"""
from pose_editor import POSE_PAIRS, POSE_COLORS
# Should have bone connections
assert len(POSE_PAIRS) > 0
# Each pair should be a tuple of two indices
for pair in POSE_PAIRS:
assert isinstance(pair, tuple)
assert len(pair) == 2
assert isinstance(pair[0], int)
assert isinstance(pair[1], int)
# Should have matching colors
assert len(POSE_COLORS) >= len(POSE_PAIRS)
# Each color should be RGB tuple
for color in POSE_COLORS:
assert isinstance(color, tuple)
assert len(color) == 3
assert all(0 <= c <= 255 for c in color)