Files
autocad-mcp-pro/tests/test_engineering_gear.py
T
Umutcan Edizsalan 0c7f64ceb6 fix(correctness-bundle): I16 correctness fixes + dead code removal + ruff clean
N1: COM hatch non-associative (AddHatch associativity=False)
N2: ezdxf refuses DWG saves with clear error; server.py derives fmt from extension
R19: dimension_aligned dist→distance; dimension_angular correct add_angular_dim_2l args (base/line1/line2/location)
R9: polar array 360° no-duplicate (divisor=n when fill_angle%360<ε)
R13: entity_offset side_x/side_y honored in ezdxf via cross-product (LINE) and distance-to-center (CIRCLE)
R18: drawing_new bootstrap failure surfaces status="degraded" instead of swallowing silently
Dead code: engineering/section.py, gear.generate_tooth_profile, CommandResult, _ACI_NAMES, set_layer_active, ensure_linetypes, ComBackend._batch_mode instance attr, engineering/__init__ build-seam comment
Ruff: 38 auto-fixed + 5 manual → codebase is now ruff-clean; CI lint is now blocking

184 tests pass.
2026-06-18 11:34:30 +03:00

128 lines
4.4 KiB
Python

"""Tests for engineering.gear — pure-math + drawing orchestration."""
from __future__ import annotations
import math
import pytest
from engineering.gear import (
draw_gear_section_aa,
draw_helical_gear_front_view,
generate_full_gear_outline,
generate_involute_flank,
involute_xy,
)
from engineering.layers import (
ensure_engineering_layers,
ensure_standard_linetypes,
)
# ---------------------------------------------------------------------------
# Pure math (sync)
# ---------------------------------------------------------------------------
def test_involute_xy_at_zero_returns_base_point():
x, y = involute_xy(10.0, 0.0)
assert math.isclose(x, 10.0, abs_tol=1e-9)
assert math.isclose(y, 0.0, abs_tol=1e-9)
def test_generate_involute_flank_min_30_points():
flank = generate_involute_flank(base_r=20.0, root_r=18.0, outer_r=25.0, n_points=5)
assert len(flank) >= 30
def test_generate_involute_flank_radius_progression():
base_r, root_r, outer_r = 20.0, 18.0, 25.0
flank = generate_involute_flank(base_r=base_r, root_r=root_r, outer_r=outer_r)
first_r = math.hypot(*flank[0])
last_r = math.hypot(*flank[-1])
assert math.isclose(first_r, root_r, abs_tol=1e-6)
assert math.isclose(last_r, outer_r, rel_tol=1e-3)
def test_generate_full_gear_outline_closed():
pts = generate_full_gear_outline(module=3.0, teeth=24)
assert pts[0] == pts[-1]
def test_generate_full_gear_outline_point_count():
teeth = 24
pts = generate_full_gear_outline(module=3.0, teeth=teeth)
# At minimum: teeth * (40 right + 40 left) = 1920 unique flank points,
# plus tip and root arc samples. Closing duplicate adds 1.
assert len(pts) >= teeth * 40
# ---------------------------------------------------------------------------
# Drawing orchestration (async, ezdxf backend)
# ---------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_helical_gear_front_view_returns_metadata(backend):
await ensure_standard_linetypes(backend)
await ensure_engineering_layers(backend)
res = await draw_helical_gear_front_view(
backend, module=3, teeth=24, helix_angle=20.0, hand="RH",
center=(0.0, 0.0),
)
expected_keys = {
"outline", "pitch_circle", "base_circle", "outer_circle", "root_circle",
"helix_symbol", "helix_label", "bore", "keyway", "metadata",
}
assert expected_keys.issubset(res.keys())
md = res["metadata"]
for k in (
"module", "teeth", "pressure_angle", "helix_angle", "hand", "center",
"bore_diameter", "keyway_width", "keyway_depth",
"pitch_radius", "outer_radius", "base_radius", "root_radius",
):
assert k in md, f"metadata missing key {k!r}"
@pytest.mark.asyncio
async def test_helical_gear_with_bore_and_keyway_handles_present(backend):
await ensure_standard_linetypes(backend)
await ensure_engineering_layers(backend)
res = await draw_helical_gear_front_view(
backend, module=3, teeth=24, helix_angle=20.0,
bore_diameter=25.0, keyway_width=8.0, keyway_depth=4.0,
)
assert res["bore"] is not None
assert res["keyway"] is not None
assert "bore" in res["keyway"]
assert "keyway_polyline" in res["keyway"]
@pytest.mark.asyncio
async def test_helical_gear_metadata_radii_correct(backend):
await ensure_standard_linetypes(backend)
await ensure_engineering_layers(backend)
res = await draw_helical_gear_front_view(
backend, module=3, teeth=24, helix_angle=20.0, pressure_angle=20.0,
)
md = res["metadata"]
assert math.isclose(md["pitch_radius"], 36.0, abs_tol=1e-6)
assert math.isclose(md["outer_radius"], 39.0, abs_tol=1e-6)
assert math.isclose(md["base_radius"], 36.0 * math.cos(math.radians(20.0)), abs_tol=1e-3)
assert math.isclose(md["root_radius"], 32.25, abs_tol=1e-6)
@pytest.mark.asyncio
async def test_section_aa_does_not_draw_helix_lines(backend):
await ensure_standard_linetypes(backend)
await ensure_engineering_layers(backend)
front = await draw_helical_gear_front_view(
backend, module=3, teeth=24, helix_angle=20.0,
bore_diameter=25.0, keyway_width=8.0, keyway_depth=4.0,
)
section = await draw_gear_section_aa(
backend, gear_metadata=front["metadata"],
x_offset=200.0, face_width=40.0,
)
assert "helix" not in section
assert "helix_symbol" not in section