Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015uTVHEAjkLLn21sdLiKAkn
161 lines
6.3 KiB
Python
161 lines
6.3 KiB
Python
"""급경사 범위 수평 바(PLAN 631 · 요구 537) — `A00_Common/map/logic/map_slope_bands.ts`.
|
|
|
|
경계 ↔ 범위(기본 · 빈 곳 = 칠 안 함 칸 · 되돌리면 같음) · 칸 나눔(가운데 · 새 색 · 좁음 · 8 단계) ·
|
|
합침(왼쪽 · 첫 칸은 오른쪽 · 하나면 막음) · 경계 검사(빈 값 · 0 · 250 · 같은 값 · 거꾸로) ·
|
|
창 = 부품 `ui_template_range_bar`(날 input · button 없음) · 부품 모음 줄.
|
|
TS 를 tsc 로 옮겨 node 로 돌림(서버 · 화면 없이).
|
|
|
|
./venv/Scripts/python.exe -m pytest resources/tester/test_631_slope_bar.py -q
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
TS_FILE = ROOT / "A00_Common" / "map" / "logic" / "map_slope_bands.ts"
|
|
UI_FILE = ROOT / "A00_Common" / "map" / "map_slope_bands.ts"
|
|
TSC = ROOT / "config" / "node_modules" / "typescript" / "bin" / "tsc"
|
|
|
|
_RUNNER = """
|
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
import * as bands from "./A00_Common/map/logic/map_slope_bands.js";
|
|
const cases = JSON.parse(readFileSync(process.argv[2], "utf8"));
|
|
const D = bands.DEFAULT_SLOPE_BANDS;
|
|
const out = { DEFAULT: D, COLORS: bands.SLOPE_COLORS.map((c) => c.color) };
|
|
// 인자 "D" = 기본 범위 · "DBAR" = 기본 바
|
|
const arg = (a) => (a === "D" ? D : a === "DBAR" ? bands.bandsToBar(D) : a);
|
|
for (const [key, [name, ...args]] of Object.entries(cases)) out[key] = bands[name](...args.map(arg));
|
|
out.roundtrip = bands.barToBands(bands.bandsToBar(D));
|
|
writeFileSync(process.argv[3], JSON.stringify(out));
|
|
"""
|
|
|
|
C3, C2, C1 = "--draw-map-landslide-3", "--draw-map-landslide-2", "--draw-map-landslide-1"
|
|
|
|
|
|
def _bar(edges: list, colors: list) -> dict:
|
|
return {"edges": edges, "colors": colors}
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def r(tmp_path_factory: pytest.TempPathFactory) -> dict:
|
|
tmp = tmp_path_factory.mktemp("slope_bar")
|
|
out = tmp / "js"
|
|
subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
[
|
|
"node",
|
|
str(TSC),
|
|
str(TS_FILE),
|
|
"--outDir",
|
|
str(out),
|
|
"--rootDir",
|
|
str(ROOT),
|
|
"--module",
|
|
"esnext",
|
|
"--target",
|
|
"es2022",
|
|
"--moduleResolution",
|
|
"bundler",
|
|
"--ignoreConfig",
|
|
],
|
|
cwd=str(ROOT),
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
(out / "runner.mjs").write_text(_RUNNER, encoding="utf-8")
|
|
eight = _bar(list(range(0, 80, 10)) + [250], [C3] * 8)
|
|
cases = {
|
|
"default_bar": ["bandsToBar", "D"],
|
|
"gap_bar": [
|
|
"bandsToBar",
|
|
[
|
|
{"low": 30, "high": 250, "color": C1},
|
|
{"low": 10, "high": 20, "color": C3},
|
|
],
|
|
],
|
|
"full_bar": ["bandsToBar", [{"low": 0, "high": 250, "color": C2}]],
|
|
"gap_bands": ["barToBands", _bar([0, 10, 20, 30, 250], ["", C3, "", C1])],
|
|
"split_top": ["splitBar", "DBAR", 3],
|
|
"split_gap": ["splitBar", "DBAR", 0],
|
|
"split_narrow": ["splitBar", _bar([0, 0.1, 250], [C3, C2]), 0],
|
|
"split_eight": ["splitBar", eight, 7],
|
|
"split_bad": ["splitBar", _bar([0, 30, 20, 250], [C3, C2, C1]), 0],
|
|
"merge_top": ["mergeBar", "DBAR", 3],
|
|
"merge_first": ["mergeBar", "DBAR", 0],
|
|
"merge_mid": ["mergeBar", "DBAR", 2],
|
|
"merge_one": ["mergeBar", _bar([0, 250], [C3]), 0],
|
|
"edges_ok": ["checkEdges", [0, 26.8, 70, 83.9, 250]],
|
|
"edges_blank": ["checkEdges", [0, None, 250]],
|
|
"edges_zero": ["checkEdges", [0, 0, 250]],
|
|
"edges_top": ["checkEdges", [0, 20, 250, 250]],
|
|
"edges_same": ["checkEdges", [0, 20, 20, 250]],
|
|
"edges_reverse": ["checkEdges", [0, 30, 20, 250]],
|
|
}
|
|
job, result = tmp / "job.json", tmp / "out.json"
|
|
job.write_text(json.dumps(cases), encoding="utf-8")
|
|
subprocess.run( # noqa: S603
|
|
["node", str(out / "runner.mjs"), str(job), str(result)],
|
|
cwd=str(ROOT),
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
return json.loads(result.read_text(encoding="utf-8"))
|
|
|
|
|
|
def test_기본_범위는_바로_가도_같음(r):
|
|
assert r["default_bar"] == _bar([0, 26.8, 70.0, 83.9, 250], ["", C3, C2, C1])
|
|
assert r["roundtrip"] == r["DEFAULT"] # 범위 같음 = 그림 · 범례 같음
|
|
|
|
|
|
def test_빈_곳은_칠_안_함_칸(r):
|
|
assert r["gap_bar"] == _bar([0, 10, 20, 30, 250], ["", C3, "", C1])
|
|
assert r["gap_bands"] == [
|
|
{"low": 10, "high": 20, "color": C3},
|
|
{"low": 30, "high": 250, "color": C1},
|
|
]
|
|
assert r["full_bar"] == _bar([0, 250], [C2])
|
|
|
|
|
|
def test_칸_나눔(r):
|
|
# 83.9 ~ 250 → 가운데 167.0 · 위 반 = 안 쓴 첫 색
|
|
assert r["split_top"]["edges"] == [0, 26.8, 70.0, 83.9, 167.0, 250]
|
|
new = next(c for c in r["COLORS"] if c not in (C3, C2, C1))
|
|
assert r["split_top"]["colors"] == ["", C3, C2, C1, new]
|
|
assert r["split_gap"]["edges"] == [0, 13.4, 26.8, 70.0, 83.9, 250]
|
|
assert r["split_gap"]["colors"][:2] == ["", new]
|
|
assert r["split_narrow"] is None
|
|
assert r["split_eight"] is None # 8 단계까지
|
|
assert r["split_bad"] is None
|
|
|
|
|
|
def test_칸_합침(r):
|
|
assert r["merge_top"] == _bar([0, 26.8, 70.0, 250], ["", C3, C2])
|
|
assert r["merge_first"] == _bar([0, 70.0, 83.9, 250], [C3, C2, C1])
|
|
assert r["merge_mid"] == _bar([0, 26.8, 83.9, 250], ["", C3, C1])
|
|
assert r["merge_one"] is None
|
|
|
|
|
|
def test_경계_검사(r):
|
|
assert r["edges_ok"] is None
|
|
assert "1번째 경계" in r["edges_blank"] and "비었습니다" in r["edges_blank"]
|
|
assert "0 보다 크고" in r["edges_zero"]
|
|
assert "2번째 경계" in r["edges_top"] and "250" in r["edges_top"]
|
|
assert "같은 값" in r["edges_same"]
|
|
assert "왼쪽 경계보다" in r["edges_reverse"]
|
|
|
|
|
|
def test_창은_부품만():
|
|
src = UI_FILE.read_text(encoding="utf-8")
|
|
assert "createRangeBar(" in src
|
|
assert not re.search(r'createElement\("(input|button|select)"\)', src)
|
|
kit = (ROOT / "ui_template" / "ui_template_ui_kit_parts.ts").read_text(encoding="utf-8")
|
|
assert '{ key: "range-bar", name: "범위 바", group: "compound",' in kit
|
|
assert 'file: "ui_template_range_bar.ts"' in kit
|
|
css = (ROOT / "ui_template" / "ui_template_range_bar.css").read_text(encoding="utf-8")
|
|
assert not re.search(r"#[0-9a-fA-F]{3,8}\b|rgba?\(|--draw-", css) # 색 = 공통 값만
|