Files
Aislo/resources/tester/test_b06_structure_pavement_mirror.py
T

132 lines
4.8 KiB
Python

"""노면공 포장 제원(88) — 파이썬·TS 짝 거울 시험.
`common_util/common_util_structure_pavement.py`(서버 · 저장분) 와 `.ts`(브라우저 · 초안)가
같은 입력에 같은 `section.pavement` 를 내는지 대조. TS 는 프로젝트 tsc 로 옮겨 실제 코드 그대로 돌림.
"""
import json
import subprocess
import sys
import tempfile
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(PROJECT_ROOT))
TSC = PROJECT_ROOT / "config" / "node_modules" / "typescript" / "bin" / "tsc"
from common_util.common_util_structure_pavement import ( # noqa: E402
attach_pavement_specs,
pavement_specs_from,
)
STRUCTURES = [
{
"type_id": "pavement_concrete",
"placement": "interval",
"chainage_m": None,
"start_m": 100.0,
"end_m": 160.0,
"options": {"pave_spec": "콘크리트", "height_m": 0.25, "widening_off": "120-140, x-1,,"},
},
{
# 뒤집힌 구간 · 두께 빔 · 사양 빔 — 두 벌 다 작은 값을 시작으로 · null 로.
"type_id": "gravel_surfacing",
"placement": "interval",
"start_m": 150.0,
"end_m": 130.0,
"options": {"pave_spec": "", "height_m": "0.3"},
},
{"type_id": "pavement_concrete", "start_m": None, "end_m": None, "options": {}},
{"type_id": "masonry_wet", "start_m": 0.0, "end_m": 500.0, "options": {}},
{
"type_id": "pavement_concrete",
"chainage_m": 300.0,
"options": {"pave_spec": "아스콘", "height_m": 0.05, "widening_off": "290-310"},
},
]
TYPE_IDS = ["pavement_concrete", "gravel_surfacing"]
CHAINAGES = [99.97, 100.0, 119.99, 130.0, 145.0, 160.02, 160.05, 300.0, 400.0]
_RUNNER = """
import { readFileSync, writeFileSync } from "node:fs";
import { attachPavementSpecs, pavementSpecsFrom } from "./common_util_structure_pavement.js";
const input = JSON.parse(readFileSync(process.argv[2], "utf8"));
const sections = input.chainages.map((chainage_m) => ({ chainage_m }));
attachPavementSpecs(sections, pavementSpecsFrom(input.structures, new Set(input.type_ids)));
writeFileSync(process.argv[3], JSON.stringify(sections));
"""
def _python_result() -> list[dict]:
sections = [{"chainage_m": value} for value in CHAINAGES]
attach_pavement_specs(sections, pavement_specs_from(STRUCTURES, set(TYPE_IDS)))
return sections
def _ts_result(tmp_path: Path) -> list[dict]:
out = tmp_path / "js"
subprocess.run( # noqa: S603 — 고정 실행 파일
[
"node",
str(TSC),
str(PROJECT_ROOT / "common_util" / "common_util_structure_pavement.ts"),
"--outDir",
str(out),
"--module",
"esnext",
"--target",
"es2022",
"--moduleResolution",
"bundler",
"--ignoreConfig",
],
cwd=str(PROJECT_ROOT),
check=True,
capture_output=True,
)
(out / "runner.mjs").write_text(_RUNNER, encoding="utf-8")
payload = tmp_path / "input.json"
result = tmp_path / "output.json"
payload.write_text(
json.dumps({"structures": STRUCTURES, "type_ids": TYPE_IDS, "chainages": CHAINAGES}),
encoding="utf-8",
)
subprocess.run( # noqa: S603
["node", str(out / "runner.mjs"), str(payload), str(result)],
cwd=str(PROJECT_ROOT),
check=True,
capture_output=True,
)
return json.loads(result.read_text(encoding="utf-8"))
def test_pavement_specs_mirror():
with tempfile.TemporaryDirectory() as workdir:
ts = _ts_result(Path(workdir))
py = _python_result()
assert ts == py
by = {section["chainage_m"]: section.get("pavement") for section in py}
# 범위 밖 · 경계 오차(0.02 m) 안
assert by[99.97] is None and by[160.05] is None and by[400.0] is None
assert [entry["type_id"] for entry in by[100.0]] == ["pavement_concrete"]
assert by[160.02][0]["thickness_m"] == 0.25
# 확폭 끈 구간 120~140 (±0.02) — 끔 · 못 읽는 조각은 버림
assert by[119.99][0]["widening_on"] is False
assert by[100.0][0]["widening_on"] is True
# 겹친 범위 = 둘 다 · 뒤집힌 구간 정렬 · 문자 두께 · 빈 사양 = null
assert [entry["type_id"] for entry in by[145.0]] == ["pavement_concrete", "gravel_surfacing"]
gravel = by[145.0][1]
assert (gravel["start_m"], gravel["end_m"]) == (130.0, 150.0)
assert gravel["thickness_m"] is None and gravel["spec"] is None
# 기준 측점만 있는 옛 항목 = 그 측점 한 점
assert by[300.0] == [
{
"type_id": "pavement_concrete",
"spec": "아스콘",
"thickness_m": 0.05,
"start_m": 300.0,
"end_m": 300.0,
"widening_on": False,
}
]