Files
Aislo/resources/tester/test_508_3_b07_fingerprint.py
T

222 lines
9.5 KiB
Python

# -*- coding: utf-8 -*-
"""508-3b · c B07 도면 지문 · 평면도 파생 저장본(임시 폴더 · DB · 실제 마스터 안 씀).
- 지문 = 입력이 바뀔 때만 바뀜(같은 내용으로 다시 쓴 종단 = 그대로)
- 횡단 측점 하나 바꾸면 그 장만 · 그 장 측점 지문도 그 측점만
- B07 설정 · 도각 · 그 도면 manifest 칸 = 그 범위만 바뀜
- 평면도 저장본 열쇠 = 지문 · 읽기는 안 지움 · 최근 2벌 · 해상도(종이 0.1 mm 솎음 · 1 µm 끊음)
./venv/Scripts/python.exe -m pytest resources/tester/test_508_3_b07_fingerprint.py -q
"""
from __future__ import annotations
import json
import os
import sys
import time
from pathlib import Path
from types import SimpleNamespace
import pytest
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
from B07_DesignDetail import B07_DesignDetail_Router_Support_Fingerprint as fp # noqa: E402
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Basin import _thinned # noqa: E402
SHEETS = [
{"id": "cross_s00000m", "number": 1, "chainages": [0, 20], "grid": [4, 4]},
{"id": "cross_s00040m", "number": 2, "chainages": [40, 60], "grid": [4, 4]},
]
def _item(drawing_id: str, kind: str) -> SimpleNamespace:
return SimpleNamespace(id=drawing_id, kind=kind)
ITEMS = [
_item("cover", "cover"),
_item("longitudinal_1", "longitudinal"),
_item("cross_s00000m", "cross"),
_item("cross_s00040m", "cross"),
_item("mass_haul", "mass_haul"),
]
@pytest.fixture()
def project(tmp_path: Path) -> dict:
root = tmp_path / "proj"
longitudinal = root / "B06_Section" / "longitudinal" / "longitudinal.json"
longitudinal.parent.mkdir(parents=True)
longitudinal.write_text(json.dumps({"stations": [{"chainage_m": 0}]}), encoding="utf-8")
(root / "B07_DesignDetail").mkdir()
(root / "B07_DesignDetail" / "drawing_scales.json").write_text("{}", encoding="utf-8")
(root / "templates" / "drawing").mkdir(parents=True)
(root / "templates" / "drawing" / "00_template_A1.json").write_text("{}", encoding="utf-8")
designs = {c: {"cut_slope_ratio": 0.4, "at": c} for c in (0, 20, 40, 60)}
sources = {
"title": {"공사명": "시험"},
"designs": designs,
"detail": {c: {"chainage_m": c, "design": designs[c]} for c in designs},
"overlays": {},
"elevations": {str(c): 100.0 for c in designs},
"sheets": SHEETS,
"mass_haul": {"points": [1, 2]},
"cross_inputs": lambda c: {"chainage": c, "design": sources["designs"].get(c)},
}
return {"root": root, "longitudinal": longitudinal, "sources": sources}
def _prints(project: dict) -> dict:
return fp.compute_fingerprints(
project["root"], project["longitudinal"], ITEMS, project["sources"]
)
def _changed(before: dict, after: dict) -> set[str]:
return {key for key in before if before[key]["fingerprint"] != after[key]["fingerprint"]}
def test_같은_입력_같은_지문(project):
first = _prints(project)
assert first == _prints(project)
assert all(len(value["fingerprint"]) == 16 for value in first.values())
assert set(first["cross_s00000m"]["stations"]) == {"0", "20"}
def test_같은_내용으로_다시_쓴_종단은_그대로(project):
before = _prints(project)
path: Path = project["longitudinal"]
text = path.read_text(encoding="utf-8")
time.sleep(0.02)
path.write_text(text, encoding="utf-8") # B06 [저장]이 같은 내용으로 다시 씀
os.utime(path, None)
assert _changed(before, _prints(project)) == set()
path.write_text(json.dumps({"stations": [{"chainage_m": 0}, {"chainage_m": 20}]}))
assert _changed(before, _prints(project)) == {"longitudinal_1", "mass_haul"}
def test_횡단_측점_하나_바꾸면_그_장만(project):
before = _prints(project)
sources = project["sources"]
sources["designs"] = {**sources["designs"], 40: {"cut_slope_ratio": 0.5, "at": 40}}
sources["detail"] = {**sources["detail"], 40: {"chainage_m": 40, "design": {"x": 1}}}
after = _prints(project)
assert _changed(before, after) == {"cross_s00040m"}
stations_before = before["cross_s00040m"]["stations"]
stations_after = after["cross_s00040m"]["stations"]
assert stations_before["60"] == stations_after["60"]
assert stations_before["40"] != stations_after["40"]
def test_덧그림_범위만_바뀌어도_그_장(project):
before = _prints(project)
project["sources"]["overlays"] = {20: (-5.0, 5.0, 90.0, 110.0)}
assert _changed(before, _prints(project)) == {"cross_s00000m"}
def test_설정_도각_manifest_범위(project):
root: Path = project["root"]
before = _prints(project)
(root / "B07_DesignDetail" / "drawing_scales.json").write_text('{"cross": {"x": 200}}')
after = _prints(project)
assert _changed(before, after) == {item.id for item in ITEMS} # 설정 = 모든 도면
(root / "templates" / "drawing" / "00_template_A1.json").write_text('{"a": 1}')
again = _prints(project)
assert _changed(after, again) == {item.id for item in ITEMS} # 도각 = 모든 도면
manifest = {"drawings": {"cover": {"number": 1}}}
(root / "B07_DesignDetail" / "manifest.json").write_text(json.dumps(manifest))
last = _prints(project)
assert _changed(again, last) == {"cover"} # 그 도면 칸만 · manifest 파일은 설정이 아님
# 그린 결과 폴더(plan_pieces)는 입력 아님
(root / "B07_DesignDetail" / "plan_pieces" / "1").mkdir(parents=True)
(root / "B07_DesignDetail" / "plan_pieces" / "1" / "contour.json").write_text("[]")
assert _changed(last, _prints(project)) == set()
def test_DB_유토만_바뀌면_토적도만(project):
before = _prints(project)
project["sources"]["mass_haul"] = {"points": [1, 3]}
assert _changed(before, _prints(project)) == {"mass_haul"}
def test_파일_도장_내용_대_시각(tmp_path, monkeypatch):
small = tmp_path / "a.json"
small.write_text("1")
first = fp.stamp(small)
os.utime(small, (1, 1))
assert fp.stamp(small) == first # 작은 파일 = 내용
monkeypatch.setattr(fp, "_CONTENT_MAX", 0)
assert fp.stamp(small) == "1000000000:1" # 큰 파일 = 시각:크기
assert fp.stamp(tmp_path / "없음") is None
def test_평면도_저장본_열쇠_읽기는_안_지움(tmp_path):
root = tmp_path / "proj"
assert fp.read_plan_copy(root, "plan_terrain", "k1") is None
fp.write_plan_copy(root, "plan_terrain", "k1", b'{"a":1}')
fp.write_plan_copy(root, "plan_route", "r1", b'{"r":1}')
folder = root / "_derived" / "b07_plan_plan_terrain"
assert fp.read_plan_copy(root, "plan_terrain", "k1") == b'{"a":1}'
assert fp.read_plan_copy(root, "plan_terrain", "다른") is None # 지문 다름 = 없음
assert (folder / "k1.json").is_file() # 읽기 · 빗나감 모두 안 지움
time.sleep(0.02)
fp.write_plan_copy(root, "plan_terrain", "k2", b'{"a":2}')
time.sleep(0.02)
fp.read_plan_copy(root, "plan_terrain", "k1") # 꺼낸 것 = 최근
time.sleep(0.02)
fp.write_plan_copy(root, "plan_terrain", "k3", b'{"a":3}')
assert sorted(p.name for p in folder.glob("*.json")) == ["k1.json", "k3.json"] # 최근 2벌
assert (root / "_derived" / "b07_plan_plan_route" / "r1.json").is_file() # 다른 도면 그대로
assert not list(folder.glob("*.tmp"))
def test_해상도_종이_솎음(tmp_path):
# 종이 mm 직선 위 점 100 개 + 0.05 mm 흔들림 = 양 끝만 · 좌표 1 µm
points = [(i * 0.5 + 0.0000123, (0.05 if i % 2 else 0.0)) for i in range(100)]
thinned = _thinned(points)
assert len(thinned) == 2
assert all(round(v, 3) == v for xy in thinned for v in xy)
bend = [(0.0, 0.0), (10.0, 0.5), (20.0, 0.0)] # 0.5 mm 꺾임은 남김
assert len(_thinned(bend)) == 3
def test_평면도_등고선_솎음_적용():
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_PlanKinds import build_plan_kind_drawing
scale = 1200
line = [(i * 0.6, 0.00001 * (i % 2)) for i in range(200)] # 실좌표 m — 종이에선 곧은 선
box = (0.0, -10.0, 120.0, 10.0)
pieces = {"contour": {"lines": [line]}, "centerline": {"lines": [line]}}
drawing = build_plan_kind_drawing("plan_t", "지형", box, pieces, scale)
lines = {
entity["layerId"]: entity
for entity in drawing["entities"]
if entity.get("type") == "PolyLine"
}
contour = next(value for key, value in lines.items() if "contour" in key)
center = next(value for key, value in lines.items() if "center" in key)
assert len(contour["children"]) == 1 # 배경선 = 솎음
assert len(center["children"]) == 199 # 주제선(중심선)은 그대로
def test_값_꼴_경고_글만_바뀐_저장은_그대로(project):
# 옛 판 자료의 첫 B06 [저장] = 4.0 → 4 · 경고 글 꼴만 바뀜(2026-10-22 실측) — 그림은 같음
path: Path = project["longitudinal"]
path.write_text(json.dumps({"stations": [{"chainage_m": 0.0}], "warnings": ["a"]}))
sources = project["sources"]
sources["designs"] = {c: {**d, "width": 4.0} for c, d in sources["designs"].items()}
sources["detail"] = {c: {"chainage_m": c, "design": d} for c, d in sources["designs"].items()}
before = _prints(project)
path.write_text(json.dumps({"stations": [{"chainage_m": 0}], "warnings": ["a", "b"]}))
sources["designs"] = {c: {**d, "width": 4} for c, d in sources["designs"].items()}
sources["detail"] = {
c: {"chainage_m": float(c), "design": d} for c, d in sources["designs"].items()
}
assert _changed(before, _prints(project)) == set()