- 맞춤 규칙 한 벌 — common_util_structure_station(파이썬 · TS 짝 · 거울 시험) - 입력 — 좌측 폼 칸(1 m 단계 · 친 값만) · 끌기 · 우클릭 · 방향키(1 m 걸음) · 배수유역도 관 · B06 관 옮김 · 추가 - 구간형 시작 · 끝도 정수 m · 일반 측점 = 횡단 상세를 읽을 때 등록 - 서버 자동 배치 · 노선 재투영 자리도 정수 m(확정본은 단추로만) - [정수 맞춤] — 구조물 목록 머리 · 확인 창 · 관은 하나씩 폼 [수정] 길 · 계획고 편집 따라감 · 결과 n건 알림 - 찾는 폭 한 값 — 손으로 적은 0.51 을 PIPE_MATCH_M 으로 - 시험 — test_234_1 새로 · 1 m 걸음 · 정수 입력에 맞춘 기대값 고침(219_1 · station_roundtrip) · 헬퍼 대역 보탬 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F3mdb91125mPoC6tpsQx4P
131 lines
5.4 KiB
Python
131 lines
5.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""PLAN 219-1 — 횡단 보기 방향키가 관을 몰래 옮기던 흠 · 측점 없는 구조물 판정 폭.
|
|
|
|
까닭: 횡단 보기에서 관 · 도수로/돌붙임 띠를 고르면 조정창이 없어 방향키를 받는 쪽이 없고,
|
|
종단도 창 듣개(`_Profile_Panel_Tools`)가 받아 고른 관 측점을 0.1 m 씩 옮김 → [저장] 때
|
|
pipe_points +0.1(엄상준 263.21 → 263.31 · 226fc805 769.28 → 769.38) → 192 · 213 숨은 측점 씨앗.
|
|
고침: ① 좌우 이동은 마지막 누름이 종단도 안일 때만(`arrowsBelongToProfile` 한 벌) ② 관 · 도수로
|
|
띠 고름 동안 방향키 삼킴(`swallowsArrows`) ③ 판정 폭 = 같은 측점 폭 0.006 · 노선 밖 뺌.
|
|
TS 는 `helper_219_1_arrow_owner.cjs` 가 Node 로 돌린다.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
ROOT = os.path.abspath(os.path.join(HERE, "..", ".."))
|
|
if ROOT not in sys.path:
|
|
sys.path.insert(0, ROOT)
|
|
|
|
import B05_Profile.B05_Profile_Engine_Sections as sections # noqa: E402
|
|
from B05_Profile.B05_Profile_Engine_Grade_Alignment import SAME_STATION_SNAP_M # noqa: E402
|
|
from B06_Section.B06_Section_Router_Stations import ( # noqa: E402
|
|
STATION_MATCH_TOLERANCE_M,
|
|
_missing_marks,
|
|
)
|
|
|
|
needs_node = pytest.mark.skipif(
|
|
not os.path.isdir(os.path.join(ROOT, "config", "node_modules")),
|
|
reason="프론트엔드 의존성 미설치",
|
|
)
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def out() -> dict:
|
|
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
["node", os.path.join(HERE, "helper_219_1_arrow_owner.cjs")],
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
timeout=60,
|
|
)
|
|
assert proc.returncode == 0, proc.stderr
|
|
return json.loads(proc.stdout)
|
|
|
|
|
|
def _read(*parts: str) -> str:
|
|
with open(os.path.join(ROOT, *parts), encoding="utf-8") as handle:
|
|
return handle.read()
|
|
|
|
|
|
# ── ① · ② 방향키 소속 ─────────────────────────────────────────────────
|
|
@needs_node
|
|
def test_횡단_카드를_누른_뒤_좌우키는_관을_안_옮김(out) -> None:
|
|
assert out["cardThenArrows"] == {"move": [], "edits": 0}
|
|
|
|
|
|
@needs_node
|
|
def test_종단도를_누른_뒤_좌우키는_지금처럼_이동(out) -> None:
|
|
# 좌우 한 걸음 = 1 m · 정수 m(234-1 · 옛 0.1 m)
|
|
assert out["profileThenArrows"]["move"] == [[263.21, 264]]
|
|
assert out["popoutThenArrows"]["move"] == [[263.21, 262]] # 새 창 종단도도
|
|
assert out["chuteThenProfile"]["move"] == [[263.21, 264]]
|
|
|
|
|
|
@needs_node
|
|
def test_도수로_관_고름_동안_방향키는_아무것도_안_옮김(out) -> None:
|
|
assert out["chute"] == {"move": [], "edits": 0} # 계획고(▲▼)도
|
|
assert out["pipeSide"] == {"move": [], "edits": 0}
|
|
assert out["swallow"] == {"wall": False, "none": False, "letter": False}
|
|
assert out["cardUpArrow"] == {"move": [], "edits": 1} # 고름 없는 카드 ▲ = 계획고 그대로
|
|
|
|
|
|
def test_배선() -> None:
|
|
tools = _read("B05_Profile", "B05_Profile_UI_Profile_Panel_Tools.ts")
|
|
assert "if (!arrowsBelongToProfile()) return false;" in tools
|
|
assert 'from "../B06_Section/B06_Section_UI_Cross_Panel_Base"' in tools
|
|
panel = _read("B06_Section", "B06_Section_UI_Cross_Structure_Panel.ts")
|
|
assert "swallowsArrows(root.dataset.side, event.key)" in panel
|
|
assert (
|
|
'if (!opts?.dock && root.dataset.side) document.addEventListener("keydown", onArrowKey);'
|
|
in panel
|
|
)
|
|
base = _read("B06_Section", "B06_Section_UI_Cross_Panel_Base.ts")
|
|
assert 'typeof target?.closest === "function"' in base
|
|
|
|
|
|
# ── ③ 측점 없는 구조물 판정 ───────────────────────────────────────────
|
|
def test_판정_폭은_같은_측점_폭_한_벌() -> None:
|
|
assert STATION_MATCH_TOLERANCE_M == SAME_STATION_SNAP_M == 0.006
|
|
|
|
|
|
def test_판정_폭과_노선_밖(tmp_path: Path, monkeypatch) -> None:
|
|
target = tmp_path / "B06_Section" / "longitudinal" / "longitudinal.json"
|
|
target.parent.mkdir(parents=True)
|
|
target.write_text(
|
|
json.dumps(
|
|
{"stations": [{"chainage_m": 0.0}, {"chainage_m": 50.0}, {"chainage_m": 100.0}]}
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
# 노선 = 수평 길이 100m
|
|
monkeypatch.setattr(
|
|
sections, "_load_route_polyline", lambda *_: [[0, 0, 5], [60, 0, 9], [100, 0, 7]]
|
|
)
|
|
monkeypatch.setattr(sections, "_load_pipe_points", lambda *_: [])
|
|
extras = [
|
|
(-0.5, "시작 앞"),
|
|
(50.0055, "반올림 차이"), # 옛 0.005 폭은 「측점 없음」으로 셌음
|
|
(70.0, "관"),
|
|
(100.004, "끝 반올림"),
|
|
(100.3, "끝 넘음"),
|
|
]
|
|
monkeypatch.setattr(sections, "resolve_extra_stations", lambda *_: extras)
|
|
assert _missing_marks(tmp_path, "route.csv") == [{"chainage_m": 70.0, "label": "관"}]
|
|
|
|
|
|
def test_노선_길이를_모르면_끝은_안_봄(tmp_path: Path, monkeypatch) -> None:
|
|
monkeypatch.setattr(sections, "_load_route_polyline", lambda *_: [])
|
|
monkeypatch.setattr(sections, "_load_pipe_points", lambda *_: [])
|
|
monkeypatch.setattr(
|
|
sections, "resolve_extra_stations", lambda *_: [(500.0, "관"), (-1.0, "앞")]
|
|
)
|
|
assert _missing_marks(tmp_path, "route.csv") == [{"chainage_m": 500.0, "label": "관"}]
|