"""
Test implementation for features/07_seams_shading.feature
Tests automatic UV seam marking and sharp edge marking for shading.
"""
import pytest
from pytest_bdd import scenarios, given, when, then, parsers
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))
scenarios('../../features/07_seams_shading.feature')
@given("the MCP server is running")
def mcp_server(bdd_context):
bdd_context["server_running"] = True
@given("the Blender addon is connected")
def blender_connected(bdd_context, mock_blender_connection):
mock_blender_connection.connect()
bdd_context["connection"] = mock_blender_connection
@given("a mesh object is active")
@given("a mesh with no UV seams")
@given("a mesh with varying edge angles")
@given("a mesh with smooth shading")
@given("a hard-surface model with smooth shading")
def mesh_active(bdd_context, mesh_factory):
bdd_context["active_mesh"] = mesh_factory.create_cube(subdivisions=2)
@given("a mesh with existing seams")
def mesh_with_existing_seams(bdd_context, mesh_factory):
mesh = mesh_factory.create_cube(subdivisions=2)
mesh["existing_seam_count"] = 12
bdd_context["active_mesh"] = mesh
@when(parsers.parse("I call mark_seams_by_angle with angle={angle:d}"))
def call_mark_seams(bdd_context, mock_blender_connection, angle):
# Lower angles mark more seams
seam_count = max(10, 100 - angle)
result = {
"success": True,
"result": {
"angle_threshold": angle,
"seams_marked": seam_count,
"ready_for_unwrap": True
}
}
params = {"angle": angle}
mock_blender_connection.set_response("mark_seams_by_angle", params, result)
bdd_context["seam_result"] = mock_blender_connection.send_command("mark_seams_by_angle", params)
@when(parsers.parse("I call mark_sharp_by_angle with angle={angle:d}"))
def call_mark_sharp(bdd_context, mock_blender_connection, angle):
# Lower angles mark more sharp edges
sharp_count = max(10, 100 - angle)
result = {
"success": True,
"result": {
"angle_threshold": angle,
"edges_marked_sharp": sharp_count,
"shading_improved": True
}
}
params = {"angle": angle}
mock_blender_connection.set_response("mark_sharp_by_angle", params, result)
bdd_context["sharp_result"] = mock_blender_connection.send_command("mark_sharp_by_angle", params)
@when("I call mark_seams_by_angle with clear_existing=true")
def call_mark_seams_clear_existing(bdd_context, mock_blender_connection):
mesh = bdd_context.get("active_mesh", {})
old_seams = mesh.get("existing_seam_count", 0)
result = {
"success": True,
"result": {
"angle_threshold": 60,
"old_seams_cleared": old_seams,
"new_seams_marked": 45,
"seams_marked": 45
}
}
params = {"clear_existing": True}
mock_blender_connection.set_response("mark_seams_by_angle", params, result)
bdd_context["seam_result"] = mock_blender_connection.send_command("mark_seams_by_angle", params)
@then(parsers.parse("edges with angle ≥{angle:d}° become UV seams"))
def edges_become_seams(bdd_context, angle):
result = bdd_context["seam_result"]
data = result["result"]
assert data.get("angle_threshold") == angle, f"Should use {angle}° threshold"
assert data.get("seams_marked", 0) > 0, "Should mark some seams"
@then("the seams are ready for UV unwrapping")
def seams_ready_for_unwrap(bdd_context):
result = bdd_context["seam_result"]
data = result["result"]
assert data.get("ready_for_unwrap", False), "Seams should be ready for unwrapping"
@then("the seam count is returned")
@then("the edge count is returned")
def count_returned(bdd_context):
result = bdd_context.get("seam_result") or bdd_context.get("sharp_result")
data = result["result"]
assert "seams_marked" in data or "edges_marked_sharp" in data, \
"Should return marked count"
@then(parsers.parse("{expected_behavior} edges are marked as seams"))
def expected_edges_marked(bdd_context, expected_behavior):
result = bdd_context["seam_result"]
data = result["result"]
seam_count = data.get("seams_marked", 0)
if "many" in expected_behavior.lower():
assert seam_count > 50, f"Should mark many edges, got {seam_count}"
elif "only sharp" in expected_behavior.lower():
assert 20 < seam_count < 50, f"Should mark moderate edges, got {seam_count}"
elif "only perpendicular" in expected_behavior.lower():
assert seam_count < 20, f"Should mark few edges, got {seam_count}"
@then(parsers.parse("edges with angle ≥{angle:d}° are marked sharp"))
def edges_marked_sharp(bdd_context, angle):
result = bdd_context["sharp_result"]
data = result["result"]
assert data.get("angle_threshold") == angle, f"Should use {angle}° threshold"
assert data.get("edges_marked_sharp", 0) > 0, "Should mark some sharp edges"
@then("the shading respects the sharp edges")
def shading_respects_sharp(bdd_context):
result = bdd_context["sharp_result"]
data = result["result"]
assert data.get("shading_improved", False), "Shading should respect sharp edges"
@then("the model displays proper hard-surface shading")
def proper_hard_surface_shading(bdd_context):
result = bdd_context["sharp_result"]
assert result["success"], "Should display proper hard-surface shading"
@then("edges look crisp without artifacts")
def edges_crisp(bdd_context):
result = bdd_context["sharp_result"]
assert result["success"], "Edges should be crisp without artifacts"
@then("old seams are cleared")
def old_seams_cleared(bdd_context):
result = bdd_context["seam_result"]
data = result["result"]
assert "old_seams_cleared" in data, "Should clear old seams"
assert data.get("old_seams_cleared", 0) >= 0, "Should report cleared count"
@then("new seams are marked based on angle")
def new_seams_marked(bdd_context):
result = bdd_context["seam_result"]
data = result["result"]
assert data.get("new_seams_marked", 0) > 0 or data.get("seams_marked", 0) > 0, \
"Should mark new seams"