Files

539 lines
17 KiB
Python

from unittest.mock import patch
import pytest
_FACE_CFG = {
"pupils": ["--", "round pupils"],
"eye_state": ["--", "half closed"],
"gaze": ["--", "looking at viewer"],
"blush": ["--", "light blush"],
"mouth_state": ["--", "closed mouth"],
"mouth_expression": ["--", "smile"],
"emotion": ["--", "neutral"],
"head_angle": ["--", "head tilt"],
}
_POSE_CFG = {
"stance": ["--", "standing"],
"posture": ["--", "contrapposto"],
"arms": ["--", "arms at sides"],
"arm": ["--", "arm raised"],
"hands": ["--", "hands on hips"],
"hand": ["--", "hand raised"],
"holding": ["--", "sword"],
"legs": ["--", "legs together"],
"feet": ["--", "bare feet"],
}
_SCENE_CFG = {
"focus": ["--", "subject"],
"view": ["--", "from front"],
"angle": ["--", "eye level"],
"framing": ["--", "full body"],
"time": ["--", "daytime"],
"sky": ["--", "clear sky"],
"location": ["--", "indoors"],
"effect": ["--", "bokeh"],
}
with patch("src.nodes.prompt_face_builder.load_builder_config", return_value=_FACE_CFG):
from src.nodes.prompt_face_builder import PromptFaceBuilder
with patch("src.nodes.prompt_pose_builder.load_builder_config", return_value=_POSE_CFG):
from src.nodes.prompt_pose_builder import PromptPoseBuilder
with patch(
"src.nodes.prompt_scene_builder.load_builder_config", return_value=_SCENE_CFG
):
from src.nodes.prompt_scene_builder import PromptSceneBuilder
# --- Fixtures ---
@pytest.fixture
def face():
return PromptFaceBuilder()
@pytest.fixture
def pose():
return PromptPoseBuilder()
@pytest.fixture
def scene():
return PromptSceneBuilder()
# --- Helpers ---
def run_face(node, **kwargs):
defaults = dict(
show_eyes=True,
pupils="--",
eye_state="--",
gaze="--",
show_eyewear=True,
eyewear_override="",
blush="--",
mouth_state="--",
mouth_expression="--",
emotion="--",
head_angle="--",
show_body_details=True,
show_nails=True,
nail_color_override="",
show_makeup=True,
preset_data="{}",
eyes="",
)
defaults.update(kwargs)
return node.build(**defaults)
def run_pose(node, **kwargs):
defaults = dict(
stance="--",
posture="--",
split_arms=False,
arms="--",
first_arm="--",
second_arm="--",
split_hands=False,
hands="--",
first_hand="--",
second_hand="--",
holding="--",
legs="--",
feet="--",
show_body_details=True,
show_upper_body=True,
show_lower_body=True,
action_options="",
action_selected="",
preset_data="{}",
)
defaults.update(kwargs)
return node.build(**defaults)
def run_scene(node, **kwargs):
defaults = dict(
focus="--",
view="--",
angle="--",
framing="--",
time="--",
sky="--",
location="--",
effect="--",
preset_data="{}",
)
defaults.update(kwargs)
return node.build(**defaults)
# --- FaceBuilder ---
class TestFaceBuilder:
# --- Eyes group ---
def test_eyes_included_when_show_eyes(self, face):
(text,) = run_face(face, eyes="blue eyes", show_eyes=True)
assert "blue eyes" in text
def test_eyes_excluded_when_show_eyes_false(self, face):
(text,) = run_face(face, eyes="blue eyes", show_eyes=False)
assert "blue eyes" not in text
def test_pupils_included_when_show_eyes(self, face):
(text,) = run_face(face, pupils="round pupils", show_eyes=True)
assert "round pupils" in text
def test_pupils_excluded_when_show_eyes_false(self, face):
(text,) = run_face(face, pupils="round pupils", show_eyes=False)
assert "round pupils" not in text
def test_eye_type_included_when_show_eyes(self, face):
(text,) = run_face(face, eye_type="almond eyes", show_eyes=True)
assert "almond eyes" in text
def test_eye_type_excluded_when_show_eyes_false(self, face):
(text,) = run_face(face, eye_type="almond eyes", show_eyes=False)
assert "almond eyes" not in text
def test_eye_state_always_included(self, face):
(text,) = run_face(face, eye_state="half closed", show_eyes=False)
assert "half closed" in text
def test_eyewear_included_when_show_eyewear(self, face):
(text,) = run_face(face, eyewear="sunglasses", show_eyewear=True)
assert "sunglasses" in text
def test_eyewear_excluded_when_show_eyewear_false(self, face):
(text,) = run_face(face, eyewear="sunglasses", show_eyewear=False)
assert "sunglasses" not in text
def test_eyewear_override_takes_priority(self, face):
(text,) = run_face(
face, eyewear="sunglasses", eyewear_override="glasses", show_eyewear=True
)
assert "glasses" in text
assert "sunglasses" not in text
def test_eye_modifiers_always_included(self, face):
(text,) = run_face(face, eye_modifiers="sparkle", show_eyes=False)
assert "sparkle" in text
def test_eye_group_in_parentheses(self, face):
(text,) = run_face(
face, eyes="blue eyes", pupils="round pupils", show_eyes=True
)
assert "(blue eyes, round pupils)" in text
# --- Hair group ---
def test_hair_color_in_hair_group(self, face):
(text,) = run_face(face, hair_color="black hair")
assert "black hair" in text
def test_hair_style_in_hair_group(self, face):
(text,) = run_face(face, hair_style="long hair")
assert "long hair" in text
def test_hair_group_in_parentheses(self, face):
(text,) = run_face(face, hair_color="black hair", hair_style="long hair")
assert "(black hair, long hair)" in text
# --- Face group ---
def test_gaze_included_when_show_eyes(self, face):
(text,) = run_face(face, gaze="looking at viewer", show_eyes=True)
assert "looking at viewer" in text
def test_gaze_excluded_when_show_eyes_false(self, face):
(text,) = run_face(face, gaze="looking at viewer", show_eyes=False)
assert "looking at viewer" not in text
def test_blush_included(self, face):
(text,) = run_face(face, blush="light blush")
assert "light blush" in text
def test_teeth_included(self, face):
(text,) = run_face(face, teeth="teeth")
assert "teeth" in text
def test_mouth_state_included(self, face):
(text,) = run_face(face, mouth_state="closed mouth")
assert "closed mouth" in text
def test_mouth_expression_included(self, face):
(text,) = run_face(face, mouth_expression="smile")
assert "smile" in text
def test_mouth_modifiers_included(self, face):
(text,) = run_face(face, mouth_modifiers="tongue out")
assert "tongue out" in text
def test_emotion_included(self, face):
(text,) = run_face(face, emotion="neutral")
assert "neutral" in text
def test_head_angle_included(self, face):
(text,) = run_face(face, head_angle="head tilt")
assert "head tilt" in text
# --- Makeup group ---
def test_piercings_included_when_show_body_details(self, face):
(text,) = run_face(face, facial_piercings="nose ring", show_body_details=True)
assert "nose ring" in text
def test_piercings_excluded_when_show_body_details_false(self, face):
(text,) = run_face(face, facial_piercings="nose ring", show_body_details=False)
assert "nose ring" not in text
def test_makeup_included_when_show_makeup(self, face):
(text,) = run_face(face, makeup="red lipstick", show_makeup=True)
assert "red lipstick" in text
def test_makeup_excluded_when_show_makeup_false(self, face):
(text,) = run_face(face, makeup="red lipstick", show_makeup=False)
assert "red lipstick" not in text
def test_nail_type_included_when_show_nails(self, face):
(text,) = run_face(face, nail_type="long nails", show_nails=True)
assert "long nails" in text
def test_nail_type_excluded_when_show_nails_false(self, face):
(text,) = run_face(face, nail_type="long nails", show_nails=False)
assert "long nails" not in text
def test_nail_color_included_when_show_makeup_and_nails(self, face):
(text,) = run_face(
face, nail_color="red nails", show_makeup=True, show_nails=True
)
assert "red nails" in text
def test_nail_color_excluded_when_show_makeup_false(self, face):
(text,) = run_face(
face, nail_color="red nails", show_makeup=False, show_nails=True
)
assert "red nails" not in text
def test_nail_color_override_takes_priority(self, face):
(text,) = run_face(
face,
nail_color="red nails",
nail_color_override="blue nails",
show_makeup=True,
show_nails=True,
)
assert "blue nails" in text
assert "red nails" not in text
# --- Extra group ---
def test_face_modifiers_in_extra_group(self, face):
(text,) = run_face(face, face_modifiers="freckles")
assert "freckles" in text
def test_face_modifiers_in_parentheses(self, face):
(text,) = run_face(face, face_modifiers="freckles")
assert "(freckles)" in text
# --- General ---
def test_dash_dash_ignored(self, face):
(text,) = run_face(face, blush="--", emotion="--")
assert "--" not in text
def test_all_empty_returns_empty(self, face):
(text,) = run_face(face, eyes="", show_eyes=True)
assert text == ""
def test_cleanup_applied(self, face):
(text,) = run_face(face, eyes="blue eyes,", show_eyes=True)
assert not text.endswith(",")
# --- PoseBuilder ---
class TestPoseBuilder:
# --- Body group ---
def test_base_body_included(self, pose):
(text,) = run_pose(pose, base_body="slim")
assert "slim" in text
def test_body_type_included(self, pose):
(text,) = run_pose(pose, body_type="tall")
assert "tall" in text
def test_body_piercings_included_when_show_body_details(self, pose):
(text,) = run_pose(
pose, body_piercings="navel piercing", show_body_details=True
)
assert "navel piercing" in text
def test_body_piercings_excluded_when_show_body_details_false(self, pose):
(text,) = run_pose(
pose, body_piercings="navel piercing", show_body_details=False
)
assert "navel piercing" not in text
def test_upper_body_included_when_show_upper_body(self, pose):
(text,) = run_pose(pose, upper_body="large chest", show_upper_body=True)
assert "large chest" in text
def test_upper_body_excluded_when_show_upper_body_false(self, pose):
(text,) = run_pose(pose, upper_body="large chest", show_upper_body=False)
assert "large chest" not in text
def test_lower_body_included_when_show_lower_body(self, pose):
(text,) = run_pose(pose, lower_body="wide hips", show_lower_body=True)
assert "wide hips" in text
def test_lower_body_excluded_when_show_lower_body_false(self, pose):
(text,) = run_pose(pose, lower_body="wide hips", show_lower_body=False)
assert "wide hips" not in text
def test_body_modifiers_always_included(self, pose):
(text,) = run_pose(pose, body_modifiers="athletic")
assert "athletic" in text
def test_body_group_in_parentheses(self, pose):
(text,) = run_pose(pose, base_body="slim", body_type="tall")
assert "(slim, tall)" in text
# --- Pose group ---
def test_stance_included(self, pose):
(text,) = run_pose(pose, stance="standing")
assert "standing" in text
def test_posture_included(self, pose):
(text,) = run_pose(pose, posture="contrapposto")
assert "contrapposto" in text
def test_arms_when_not_split(self, pose):
(text,) = run_pose(pose, arms="arms at sides", split_arms=False)
assert "arms at sides" in text
def test_first_arm_when_split(self, pose):
(text,) = run_pose(pose, first_arm="arm raised", split_arms=True)
assert "arm raised" in text
def test_second_arm_when_split(self, pose):
(text,) = run_pose(pose, second_arm="arm at side", split_arms=True)
assert "arm at side" in text
def test_arms_excluded_when_split(self, pose):
(text,) = run_pose(pose, arms="arms at sides", split_arms=True)
assert "arms at sides" not in text
def test_hands_when_not_split(self, pose):
(text,) = run_pose(pose, hands="hands on hips", split_hands=False)
assert "hands on hips" in text
def test_first_hand_when_split(self, pose):
(text,) = run_pose(pose, first_hand="hand raised", split_hands=True)
assert "hand raised" in text
def test_second_hand_when_split(self, pose):
(text,) = run_pose(pose, second_hand="hand at side", split_hands=True)
assert "hand at side" in text
def test_holding_prepends_holding(self, pose):
(text,) = run_pose(pose, holding="sword")
assert "holding sword" in text
def test_legs_included(self, pose):
(text,) = run_pose(pose, legs="legs together")
assert "legs together" in text
def test_feet_included(self, pose):
(text,) = run_pose(pose, feet="bare feet")
assert "bare feet" in text
def test_pose_group_in_parentheses(self, pose):
(text,) = run_pose(pose, stance="standing", posture="contrapposto")
assert "(standing, contrapposto)" in text
# --- Action group ---
def test_action_included(self, pose):
(text,) = run_pose(pose, action_selected="walking")
assert "walking" in text
def test_action_group_in_parentheses(self, pose):
(text,) = run_pose(pose, action_selected="walking")
assert "(walking)" in text
# --- Extra group ---
def test_extra_included(self, pose):
(text,) = run_pose(pose, extra="from behind")
assert "from behind" in text
def test_extra_group_in_parentheses(self, pose):
(text,) = run_pose(pose, extra="from behind")
assert "(from behind)" in text
# --- General ---
def test_dash_dash_ignored(self, pose):
(text,) = run_pose(pose, stance="--", posture="--")
assert "--" not in text
def test_all_empty_returns_empty(self, pose):
(text,) = run_pose(pose)
assert text == ""
def test_cleanup_applied(self, pose):
(text,) = run_pose(pose, stance="standing,")
assert not text.endswith(",")
# --- SceneBuilder ---
class TestSceneBuilder:
def test_focus_included(self, scene):
(text,) = run_scene(scene, focus="subject")
assert "subject" in text
def test_view_included(self, scene):
(text,) = run_scene(scene, view="from front")
assert "from front" in text
def test_angle_included(self, scene):
(text,) = run_scene(scene, angle="eye level")
assert "eye level" in text
def test_framing_included(self, scene):
(text,) = run_scene(scene, framing="full body")
assert "full body" in text
def test_time_included(self, scene):
(text,) = run_scene(scene, time="daytime")
assert "daytime" in text
def test_sky_included(self, scene):
(text,) = run_scene(scene, sky="clear sky")
assert "clear sky" in text
def test_location_included(self, scene):
(text,) = run_scene(scene, location="indoors")
assert "indoors" in text
def test_effect_included(self, scene):
(text,) = run_scene(scene, effect="bokeh")
assert "bokeh" in text
def test_dash_dash_ignored(self, scene):
(text,) = run_scene(scene, view="--", framing="--")
assert "--" not in text
def test_camera_group_in_parentheses(self, scene):
(text,) = run_scene(
scene, view="from front", angle="from above", framing="full body"
)
assert "(from front, from above, full body)" in text
def test_scene_group_in_parentheses(self, scene):
(text,) = run_scene(scene, time="daytime", sky="clear sky", location="indoors")
assert "(daytime, clear sky, indoors)" in text
def test_effect_group_in_parentheses(self, scene):
(text,) = run_scene(scene, effect="bokeh")
assert "(bokeh)" in text
def test_all_empty_returns_empty(self, scene):
(text,) = run_scene(scene)
assert text == ""
def test_cleanup_applied(self, scene):
(text,) = run_scene(scene, view="from front,")
assert not text.endswith(",")
def test_full(self, scene):
(text,) = run_scene(
scene,
focus="subject",
view="from front",
angle="eye level",
framing="full body",
time="daytime",
sky="clear sky",
location="indoors",
effect="bokeh",
)
assert "(subject, from front, eye level, full body)" in text
assert "(daytime, clear sky, indoors)" in text
assert "(bokeh)" in text