Merge remote-tracking branch 'origin/sub_laptop_3' into main_laptop_1
This commit is contained in:
@@ -38,7 +38,7 @@ const R2 = 8;
|
||||
const raw = (name, route) => m.readState(name, P, route) ?? {};
|
||||
const keysOf = (name, route) =>
|
||||
Object.keys(raw(name, route))
|
||||
.filter((k) => k !== "@at")
|
||||
.filter((k) => !k.startsWith("@")) // 「@at」 · 「@seat」 = 쓴 때 자리 칸(475-3)
|
||||
.sort();
|
||||
const section = (chainage_m, kind, extra = {}) => ({
|
||||
station_id: `s${chainage_m}`,
|
||||
|
||||
@@ -173,7 +173,7 @@ const NEW = (dy) => [
|
||||
.sort((a, b) => a[0] - b[0])
|
||||
.map(([at, c]) => [at, c.ground_type, c.section_mode ?? null]),
|
||||
rawKeys: Object.keys(m.readState("crossdesign", P, R) ?? {})
|
||||
.filter((k) => k !== "@at")
|
||||
.filter((k) => !k.startsWith("@"))
|
||||
.sort(),
|
||||
widths: m.readState("crossw", P, R),
|
||||
redraws,
|
||||
|
||||
@@ -97,10 +97,10 @@ const snap = () => ({
|
||||
.sort((a, b) => a[0] - b[0])
|
||||
.map(([at, c]) => [at, c.ground_type ?? null, c.section_mode ?? null]),
|
||||
shoulder: Object.entries(m.readState("shoulderext", P, R) ?? {})
|
||||
.filter(([k]) => k !== "@at")
|
||||
.filter(([k]) => !k.startsWith("@"))
|
||||
.map(([k, v]) => [k, v]),
|
||||
berm: Object.entries(m.readState("bermdepth", P, R) ?? {})
|
||||
.filter(([k]) => k !== "@at")
|
||||
.filter(([k]) => !k.startsWith("@"))
|
||||
.map(([k, v]) => [k, v]),
|
||||
});
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ const R2 = 8;
|
||||
const raw = (name, route) => m.readState(name, P, route) ?? {};
|
||||
const keysOf = (name, route) =>
|
||||
Object.keys(raw(name, route))
|
||||
.filter((k) => k !== "@at")
|
||||
.filter((k) => !k.startsWith("@")) // 「@at」 · 「@seat」 = 쓴 때 자리 칸(475-3)
|
||||
.sort();
|
||||
const section = (chainage_m, kind, extra = {}) => ({
|
||||
station_id: `s${chainage_m}`,
|
||||
|
||||
@@ -6,7 +6,7 @@ const ROOT = path.resolve(__dirname, "..", "..");
|
||||
const ts = require(path.join(ROOT, "config", "node_modules", "typescript"));
|
||||
const idOf = (m) => `station_${String(Math.round(m * 1000)).padStart(12, "0")}`;
|
||||
const REAL =
|
||||
/(B05_Profile_Pipeline_Detail|B06_Section_Station_Swap|design_pipeline_cross|common_util_\w+)$/;
|
||||
/(B05_Profile_Pipeline_Detail|B06_Section_Station_Swap|B06_Section_Route_Carry|design_pipeline_cross|common_util_\w+)$/; // Route_Carry = 자리 칸 「@seat」(475-3)
|
||||
const CATALOG = require("./helper_322_catalog_module.cjs");
|
||||
const STUBS = {
|
||||
B05_Profile_Pipeline_Grade: { stationIdOf: idOf },
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
/* 475-3 시험 묶음 진입점 — 자리 칸(암 경계 · 2단 경사 · 손 상단측) 넘김 규칙을 화면 · 서버 없이 잰다. */
|
||||
export { sameSeat } from "../../common_util/common_util_station_key";
|
||||
export { carryKeyedStates } from "../../B06_Section/logic/B06_Section_Route_Carry";
|
||||
export { crossAtSeat } from "../../B05_Profile/logic/B05_Profile_Pipeline_Detail";
|
||||
export { swapCrossSections } from "../../B06_Section/logic/B06_Section_Station_Swap";
|
||||
@@ -0,0 +1,135 @@
|
||||
/* 475-3 헬퍼 — vite ssr 로 묶은 진짜 모듈(helper_475_3_entry.ts)을 부르고 결과를 JSON 한 벌로.
|
||||
* node helper_475_3_seat_carry.mjs <묶음.mjs> <입력.json>
|
||||
* 입력 = { seats: [[a, b], …] }(자리 판정 거울 사례).
|
||||
*/
|
||||
import { readFileSync } from "node:fs";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
const [bundle, inputPath] = process.argv.slice(2);
|
||||
const m = await import(pathToFileURL(bundle).href);
|
||||
const input = JSON.parse(readFileSync(inputPath, "utf-8"));
|
||||
const out = {};
|
||||
|
||||
out.sameSeat = input.seats.map(([a, b]) => m.sameSeat(a, b));
|
||||
|
||||
/* 노선 바뀜 넘김 — grid:1 같은 자리 · grid:2 옮김 · grid:3 옮겼지만 값은 새 자리에서 씀(되돌리기로 되살린 값) */
|
||||
const A = { x: 0, y: 20, left: [-1, 0] };
|
||||
const B = { x: 0, y: 40, left: [-1, 0] };
|
||||
const C = { x: 0, y: 60, left: [-1, 0] };
|
||||
const C2 = { x: 3, y: 60, left: [-1, 0] };
|
||||
const from = [
|
||||
{ key: "grid:1", chainageM: 20, seat: A },
|
||||
{ key: "grid:2", chainageM: 40, seat: B },
|
||||
{ key: "grid:3", chainageM: 60, seat: C },
|
||||
];
|
||||
const to = [
|
||||
{ key: "grid:1", chainageM: 20, seat: A },
|
||||
{ key: "grid:2", chainageM: 40.5, seat: { x: 5, y: 40, left: [-1, 0] } },
|
||||
{ key: "grid:3", chainageM: 61, seat: C2 },
|
||||
];
|
||||
const choice = {
|
||||
ground_type: "soil",
|
||||
ditch_type: "l_type",
|
||||
two_stage_slope: true,
|
||||
section_mode: "right_cut",
|
||||
ditch_side: "left",
|
||||
paved: true,
|
||||
};
|
||||
const raw = {
|
||||
rockb: { "grid:1": 1.2, "grid:2": 1.5, "grid:3": 0.9, "@seat": { "grid:3": C2 } },
|
||||
cutslope: { "grid:1": 0.8, "grid:2": 0.7 },
|
||||
crossdesign: { "grid:1": choice, "grid:2": choice },
|
||||
shoulderext: { "grid:2": { left: 0.5 } },
|
||||
bermdepth: {},
|
||||
};
|
||||
const strip = (states) =>
|
||||
Object.fromEntries(
|
||||
Object.entries(states).map(([name, cells]) => [
|
||||
name,
|
||||
Object.fromEntries(Object.entries(cells).filter(([key]) => !key.startsWith("@"))),
|
||||
]),
|
||||
);
|
||||
const carried = m.carryKeyedStates((name) => raw[name] ?? {}, from, to);
|
||||
out.carried = strip(carried.states);
|
||||
out.carriedSeats = carried.states.rockb["@seat"];
|
||||
out.outside = carried.outside.size;
|
||||
/* 자리 모름(옛 목록) = 옛 동작 그대로 */
|
||||
const bare = (list) => list.map(({ key, chainageM }) => ({ key, chainageM }));
|
||||
out.unknown = strip(m.carryKeyedStates((name) => raw[name] ?? {}, bare(from), bare(to)).states);
|
||||
|
||||
/* 미리보기 입력 — 저장분 · 세션 자리 칸 묶음(`seats`) */
|
||||
const station = (key, x, y) => ({ key, chainageM: y, x, y, left: [-1, 0] });
|
||||
const input2 = {
|
||||
standard: {},
|
||||
byKey: {
|
||||
"grid:1": {
|
||||
groundType: "soil",
|
||||
rockBoundaryOffsetM: 1,
|
||||
sectionMode: "right_cut",
|
||||
seats: [{ ...A, fields: ["rockBoundaryOffsetM", "sectionMode"] }],
|
||||
},
|
||||
"grid:2": {
|
||||
groundType: "soil",
|
||||
twoStageSlope: true,
|
||||
cutSlopeRatio: 0.7,
|
||||
seats: [
|
||||
{ ...B, fields: ["twoStageSlope"] },
|
||||
{ x: 5, y: 40, left: [-1, 0], fields: ["cutSlopeRatio"] },
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
out.atSeat = m.crossAtSeat(input2, [station("grid:1", 0, 20), station("grid:2", 5, 40)]).byKey;
|
||||
|
||||
/* B06 갈아끼움(노선 초안) — 앞 횡단 설계를 이어 받되 자리가 바뀐 측점은 자리 칸 기본값 */
|
||||
const hand = {
|
||||
ground_type: "soil",
|
||||
section_mode: "both_cut",
|
||||
ditch_side: "right",
|
||||
two_stage_slope: true,
|
||||
rock_boundary_offset_m: -1.2,
|
||||
cut_slope_ratio_user: 0.8,
|
||||
shoulder_extend_left_m: 0.5,
|
||||
};
|
||||
const section = (chainage, x) => ({
|
||||
station_id: `s${chainage}`,
|
||||
chainage_m: chainage,
|
||||
kind: "regular",
|
||||
center_x: x,
|
||||
center_y: chainage,
|
||||
frame: { left_xy: [-1, 0] },
|
||||
samples: [],
|
||||
uphill_side: "left",
|
||||
design: { ...hand },
|
||||
});
|
||||
const pipelineStation = (key, chainage, x) => ({
|
||||
key,
|
||||
kind: "grid",
|
||||
chainageM: chainage,
|
||||
x,
|
||||
y: chainage,
|
||||
left: [-1, 0],
|
||||
azimuthDeg: 0,
|
||||
planRadiusM: null,
|
||||
curveOuterSide: null,
|
||||
curveWideningM: 0,
|
||||
ground: null,
|
||||
});
|
||||
const detail = {
|
||||
longitudinal: { stations: [], length_m: 40 },
|
||||
cross_sections: [section(20, 0), section(40, 0)],
|
||||
};
|
||||
const swapped = (draft) =>
|
||||
m
|
||||
.swapCrossSections(
|
||||
detail,
|
||||
[pipelineStation("grid:1", 20, 0), pipelineStation("grid:2", 40, 5)],
|
||||
["grid:1", "grid:2"],
|
||||
20,
|
||||
draft,
|
||||
)
|
||||
.detail.cross_sections.map((s) => s.design);
|
||||
out.swapDraft = swapped(true);
|
||||
out.swapNoDraft = swapped(false);
|
||||
|
||||
process.stdout.write(JSON.stringify(out));
|
||||
@@ -97,11 +97,15 @@ def test_노선_바뀜_측점_열쇠로_넘김(out):
|
||||
assert out["outside"] == 2 # 60 m(노선이 짧아짐) · 99 m(옛 노선에도 없던 옛 초안)
|
||||
assert out["carriedKeys"] == ["grid:1", "grid:2", "pipe:pipe-s1", "structure:st-1"]
|
||||
assert out["carriedRead"] == [20, 40, 47, 55] # 관 · 구조물은 옮긴 자리로
|
||||
# 자리 칸(2단 경사 · 손 상단측) = 같은 자리만(475-3) — 이 측점들은 좌표가 없어(자리 모름)
|
||||
# 같은 자리로 봄 · 옛 단언(단면 유형 · 측구 방향은 늘 새 지형)을 고침 · 포장은 안 넘김
|
||||
assert out["carriedChoice"] == {
|
||||
"ground_type": "soil",
|
||||
"ditch_type": "l_type",
|
||||
"ditch_choice": False,
|
||||
"two_stage_slope": True,
|
||||
"section_mode": "right_cut",
|
||||
"ditch_side": "right",
|
||||
}
|
||||
assert out["carriedRock"] == {"20.00": -0.8}
|
||||
assert out["carriedShoulder"] == ["grid:2"] # 사면 쉬프트도 넘김(417)
|
||||
|
||||
@@ -97,7 +97,9 @@ def test_넘김_값_사용자_칸(out, carried) -> None:
|
||||
by = _by_chainage(carried["patches"])
|
||||
assert by["20.00"]["ground_type"] == "soil" and by["20.00"]["two_stage_slope"] is True
|
||||
assert by["20.00"]["ditch_type"] == "u_flume"
|
||||
assert "section_mode" not in by["20.00"] # 단면 유형 = 새 지형이 정함
|
||||
# 손 단면 유형 = 자리 칸(475-3) — 이 목록은 자리를 몰라(옛 목록) 같은 자리로 봄 · 넘김
|
||||
# (옛 단언 = 늘 새 지형)
|
||||
assert by["20.00"]["section_mode"] == "left_cut"
|
||||
assert by["20.00"]["structure_note"] == "앞 메모"
|
||||
assert by["40.00"]["shoulder_extend_left_m"] == 0.5
|
||||
assert "revet_adjust" not in by["40.00"] # 일반 측점 옹벽 조절 = 안 넘김(관 · 구조물 열쇠만)
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""475-3(요구 275-3) — 노선이 바뀌면 자리 칸은 같은 자리 측점만 들고 감.
|
||||
|
||||
자리 칸 = 암 경계 · 2단 경사 · 손 상단측(TS `SEAT_KEYS` 하나 · 파이썬은 그 글을 읽음).
|
||||
|
||||
못박는 것
|
||||
- 자리 판정 거울 — TS `sameSeat` ↔ 파이썬 `same_seat`(중심 0.01 m · 왼쪽 축 1e-3 ·
|
||||
모르면 같은 자리).
|
||||
- 브라우저 · Node 넘김(`carryKeyedStates`) — 같은 자리 = 자리 칸도 · 옮긴 자리 = 자리 칸
|
||||
버림(새 자리 기본값) · 지반 · 측구 종류 · 사면 쉬프트는 그대로 · 값의 쓴 때 자리(`@seat` ·
|
||||
되돌리기로 되살린 값)가 새 자리와 같으면 유지 · 자리 모르는 옛 목록 = 옛 동작.
|
||||
- 미리보기 입력(`crossAtSeat`) — 자리 칸 묶음이 측점 자리와 다르면 뺌 · 같으면 그대로.
|
||||
- 서버 노선 저장 넘김(`carry_cross_inputs` · `seats`) — 같은 규칙 · `seats` 없으면 옛 동작.
|
||||
- 기본 설계가 넘긴 손 단면 유형으로 셈(`compute_default_designs` · 없으면 상단측 깎기).
|
||||
|
||||
./venv/Scripts/python.exe -m pytest resources/tester/test_475_3_seat_carry.py -q
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
ROOT = HERE.parents[1]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from B05_Profile.B05_Profile_Engine_Sections import find_cross_file # noqa: E402
|
||||
from B05_Profile.B05_Profile_Route_Save_Parts import carry_cross_inputs, station_seats # noqa: E402
|
||||
from B06_Section.B06_Section_Router_Design import ( # noqa: E402
|
||||
CARRY_INPUT_KEYS,
|
||||
SEAT_KEYS,
|
||||
compute_default_designs,
|
||||
)
|
||||
from common_util.common_util_station_key import same_seat, station_seat # noqa: E402
|
||||
|
||||
SEATS = [
|
||||
[{"x": 0, "y": 0, "left": [1, 0]}, {"x": 0.009, "y": 0, "left": [1, 0]}],
|
||||
[{"x": 0, "y": 0, "left": [1, 0]}, {"x": 0.011, "y": 0, "left": [1, 0]}],
|
||||
[{"x": 0, "y": 0, "left": [1, 0]}, {"x": 0, "y": 0, "left": [0.998, 0.002]}],
|
||||
[{"x": 0, "y": 0, "left": [1, 0]}, {"x": 0, "y": 0, "left": [0.9995, 0.0009]}],
|
||||
[{"x": 0, "y": 0, "left": None}, {"x": 0, "y": 0.005, "left": [0, 1]}],
|
||||
[None, {"x": 9, "y": 9, "left": [0, 1]}],
|
||||
]
|
||||
|
||||
|
||||
def _py_seat(seat):
|
||||
if seat is None:
|
||||
return None
|
||||
left = seat.get("left")
|
||||
return seat["x"], seat["y"], tuple(left) if left else None
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def out(tmp_path_factory):
|
||||
work = tmp_path_factory.mktemp("b475_3")
|
||||
build = subprocess.run(
|
||||
[
|
||||
"node",
|
||||
str(ROOT / "config" / "node_modules" / "vite" / "bin" / "vite.js"),
|
||||
"build",
|
||||
"--configLoader",
|
||||
"runner",
|
||||
"--ssr",
|
||||
"../resources/tester/helper_475_3_entry.ts",
|
||||
"--outDir",
|
||||
str(work),
|
||||
"--logLevel",
|
||||
"error",
|
||||
],
|
||||
cwd=ROOT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
timeout=300,
|
||||
)
|
||||
assert build.returncode == 0, build.stderr
|
||||
bundle = next(work.glob("*.js"), None) or next(work.glob("*.mjs"))
|
||||
inputs = work / "input.json"
|
||||
inputs.write_text(json.dumps({"seats": SEATS}), "utf-8")
|
||||
proc = subprocess.run(
|
||||
["node", str(HERE / "helper_475_3_seat_carry.mjs"), str(bundle), str(inputs)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
timeout=60,
|
||||
)
|
||||
assert proc.returncode == 0, proc.stderr
|
||||
return json.loads(proc.stdout)
|
||||
|
||||
|
||||
def test_자리_칸_목록_정본은_TS_하나():
|
||||
swap = (ROOT / "B06_Section" / "logic" / "B06_Section_Station_Swap.ts").read_text("utf-8")
|
||||
body = re.search(r"export const SEAT_KEYS = \[(.*?)\] as const;", swap, re.S)
|
||||
assert tuple(re.findall(r'"([a-z_0-9]+)"', body.group(1))) == SEAT_KEYS
|
||||
assert SEAT_KEYS == (
|
||||
"two_stage_slope",
|
||||
"rock_boundary_offset_m",
|
||||
"cut_slope_ratio_user",
|
||||
"section_mode",
|
||||
"ditch_side",
|
||||
)
|
||||
|
||||
|
||||
def test_자리_판정_거울(out):
|
||||
assert out["sameSeat"] == [same_seat(_py_seat(a), _py_seat(b)) for a, b in SEATS]
|
||||
assert out["sameSeat"] == [True, False, False, True, True, True]
|
||||
|
||||
|
||||
def test_같은_자리는_자리_칸도_옮긴_자리는_기본값(out):
|
||||
carried = out["carried"]
|
||||
assert carried["rockb"] == {
|
||||
"grid:1": 1.2,
|
||||
"grid:3": 0.9,
|
||||
} # grid:2 옮김 = 버림 · grid:3 = 쓴 때 자리
|
||||
assert carried["cutslope"] == {"grid:1": 0.8}
|
||||
assert carried["crossdesign"]["grid:1"] == { # 포장은 안 넘김(138-1)
|
||||
"ground_type": "soil",
|
||||
"ditch_type": "l_type",
|
||||
"two_stage_slope": True,
|
||||
"section_mode": "right_cut",
|
||||
"ditch_side": "left",
|
||||
}
|
||||
assert carried["crossdesign"]["grid:2"] == {"ground_type": "soil", "ditch_type": "l_type"}
|
||||
assert carried["shoulderext"] == {"grid:2": {"left": 0.5}} # 자리 칸 아님 = 그대로
|
||||
assert out["carriedSeats"]["grid:3"] == {"x": 3, "y": 60, "left": [-1, 0]}
|
||||
assert out["outside"] == 0
|
||||
|
||||
|
||||
def test_자리_모르는_옛_목록은_옛_동작(out):
|
||||
unknown = out["unknown"]
|
||||
assert unknown["rockb"] == {"grid:1": 1.2, "grid:2": 1.5, "grid:3": 0.9}
|
||||
assert unknown["crossdesign"]["grid:2"]["two_stage_slope"] is True
|
||||
|
||||
|
||||
def test_미리보기_입력_자리_칸(out):
|
||||
at = out["atSeat"]
|
||||
assert at["grid:1"] == {
|
||||
"groundType": "soil",
|
||||
"rockBoundaryOffsetM": 1,
|
||||
"sectionMode": "right_cut",
|
||||
}
|
||||
# grid:2 = 저장 자리(0, 40)에서 온 2단 경사는 뺌 · 새 자리(5, 40)에서 쓴 암 경사는 그대로
|
||||
assert at["grid:2"] == {"groundType": "soil", "cutSlopeRatio": 0.7}
|
||||
|
||||
|
||||
def test_B06_갈아끼움_자리_바뀐_측점은_자리_칸_기본값(out):
|
||||
same, moved = out["swapDraft"]
|
||||
assert same["rock_boundary_offset_m"] == -1.2 and same["section_mode"] == "both_cut"
|
||||
assert moved == {
|
||||
"ground_type": "soil",
|
||||
"section_mode": "left_cut",
|
||||
"shoulder_extend_left_m": 0.5,
|
||||
}
|
||||
# 노선 초안 아닌 차례(관 · 구조물 · 추가 측점만) = 이어 받은 설계 그대로
|
||||
assert out["swapNoDraft"][1]["two_stage_slope"] is True
|
||||
|
||||
|
||||
def _row(chainage, x, left=(-1.0, 0.0)):
|
||||
return {
|
||||
"chainage_m": chainage,
|
||||
"center_x": x,
|
||||
"center_y": chainage,
|
||||
"frame": {"left_xy": list(left)},
|
||||
}
|
||||
|
||||
|
||||
def test_서버_노선_저장_넘김_같은_규칙():
|
||||
old_keys = {"grid:1": 20.0, "grid:2": 40.0}
|
||||
new_keys = {"grid:1": 20.0, "grid:2": 40.5}
|
||||
old_rows = [_row(20.0, 0.0), _row(40.0, 0.0)]
|
||||
new_rows = [_row(20.0, 0.0), _row(40.5, 5.0)]
|
||||
seats = (station_seats(old_rows, old_keys), station_seats(new_rows, new_keys))
|
||||
hand = {
|
||||
"ground_type": "soil",
|
||||
"two_stage_slope": True,
|
||||
"rock_boundary_offset_m": 1.5,
|
||||
"cut_slope_ratio_user": 0.8,
|
||||
"section_mode": "right_cut",
|
||||
"ditch_side": "left",
|
||||
"shoulder_extend_left_m": 0.5,
|
||||
}
|
||||
designs = {20.0: dict(hand), 40.0: dict(hand)}
|
||||
keys = (*CARRY_INPUT_KEYS, "shoulder_extend_left_m")
|
||||
inputs, _, dropped = carry_cross_inputs(old_keys, new_keys, designs, keys, (), SEAT_KEYS, seats)
|
||||
assert inputs[20.0] == hand
|
||||
assert inputs[40.5] == {"ground_type": "soil", "shoulder_extend_left_m": 0.5}
|
||||
assert dropped == 0
|
||||
# seats 없음 = 옛 동작(손 상단측은 안 넘김 · 자리 칸 셋은 넘김)
|
||||
old, _, _ = carry_cross_inputs(old_keys, new_keys, designs, keys, ())
|
||||
assert old[40.5] == {k: v for k, v in hand.items() if k not in ("section_mode", "ditch_side")}
|
||||
assert station_seat({"chainage_m": 1}) is None
|
||||
|
||||
|
||||
def test_기본_설계가_넘긴_손_단면_유형으로_셈(tmp_path, monkeypatch):
|
||||
"""엔진 입력 = 넘긴 단면 유형 · 측구 방향(결과 단면 유형은 엔진이 지형으로 다시 판정)."""
|
||||
import B06_Section.B06_Section_Router_Design as design_module
|
||||
|
||||
seen: list[tuple[str, object]] = []
|
||||
real = design_module.compute_cross_design
|
||||
|
||||
def spy(*args, **kwargs):
|
||||
seen.append((kwargs["section_mode"], kwargs.get("ditch_side")))
|
||||
return real(*args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(design_module, "compute_cross_design", spy)
|
||||
route = tmp_path / "routes" / "r1"
|
||||
(route / "longitudinal").mkdir(parents=True)
|
||||
(route / "cross_sections").mkdir()
|
||||
profile = [
|
||||
{"chainage_m": 0.0, "elevation_m": 100.0},
|
||||
{"chainage_m": 40.0, "elevation_m": 100.0},
|
||||
]
|
||||
relative = "routes/r1/longitudinal/longitudinal.json"
|
||||
stations = [{"chainage_m": 20.0, "uphill_side": "left"}]
|
||||
(tmp_path / relative).write_text(
|
||||
json.dumps({"stations": stations, "design_profiles": [{"samples": profile}]}), "utf-8"
|
||||
)
|
||||
samples = [
|
||||
{"offset_m": float(o), "elevation_m": 100.0 + 0.3 * o, "valid": True}
|
||||
for o in range(-15, 16)
|
||||
]
|
||||
cross = {"chainage_m": 20.0, "samples": samples, "uphill_side": "left"}
|
||||
find_cross_file(route / "cross_sections", 20.0).write_text(json.dumps(cross), "utf-8")
|
||||
plain = compute_default_designs(tmp_path, relative, [20.0])
|
||||
hand = compute_default_designs(
|
||||
tmp_path,
|
||||
relative,
|
||||
[20.0],
|
||||
carried={20.0: {"section_mode": "both_cut", "ditch_side": "right"}},
|
||||
)
|
||||
assert plain and hand
|
||||
assert seen == [("left_cut", None), ("both_cut", "right")]
|
||||
Reference in New Issue
Block a user