Files
ParaWiki/cimery/crates/ifc/tests/snapshot_tests.rs
minsung e32c09df2d 품질 강화 — ADR-004 + IFC snapshot 테스트 + helper 유닛 + clippy 경고 정리
## ADR-004 (Output/reports/ADR-004-sprint-25-39-decisions.md)
Sprint 25~39 기간의 **15개 아키텍처 결정** 정리:
- D1~D9: 거더교 MVP 확장 (단면 분기·다경간·Skew 관례·방호벽·격벽·Camber·헌치·UI)
- D10~D13: IFC4X3 Add2 익스포터 4 결정 (크레이트 분리·형상 전략 3단계·GUID·Camber 근사)
- D14: proc-macro 스캐폴딩 (전면 #[param] 는 Feature 10+ 안정 후)
- D15: 변단면 거더 알고리즘 (소핏 lift + Y 선형보간)
- 미결 6항목 (Pset 확장·LinearPlacement·ElementAssembly·IfcPile·#[param] 전면·변단면 IFC)
- 테스트 커버리지 101개 현황표

## IFC 스냅샷 테스트 (crates/ifc/tests/snapshot_tests.rs)
insta 기반 회귀 방지, 8개 baseline:
- mask_guids(): 22자 IFC GUID 를 'GUID' 로 정규화 (결정적 비교 가능)
- 시나리오: 기본 단경간 PSC-I / 2경간 π형 / skew 15° / camber 50mm /
  Rectangle 단면 / parapets off
- mask_guids 자체 유닛 테스트 2개

## Mesh helper 유닛 테스트 (crates/viewer/src/bridge_scene.rs helper_tests)
순수 함수 9개 검증:
- apply_camber_mesh: zero 항등·midspan 도달값·경간 밖 미영향
- rotate_y_around_z: 0 회전 항등·90° 피봇 회전·정점 개수 보존
- apply_variable_depth: zero 항등·소핏 lift · 지점 0 lift

## clippy lib 경고 15+ → 0
- map_identity (kernel/expansion_joint.rs)
- unnecessary_lazy_evaluations ×4 (dsl/abutment·pier·csv_template — auto-fix)
- too_many_arguments (usd save_scene — allow with justification)
- clamp-like 패턴 ×7 (viewer bridge_scene/incremental_scene 의 .max(1).min(N) → .clamp(1, N))
- redundant_closure ×2 (project_file 의 `|e| Error::other(e)` → `Error::other`)
- redundant_guard ×1 (viewer KeyboardInput match guard → 패턴 내 직접 매치)

cargo clippy --workspace --lib: 0 경고.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 08:37:11 +09:00

119 lines
3.2 KiB
Rust

//! IFC 출력 스냅샷 테스트 (insta) — 회귀 방지용.
//!
//! IfcGUID 는 UUIDv4 무작위라 매 실행마다 달라지므로 `mask_guids()` 로
//! `GUID-XXX` 형태로 정규화한 뒤 스냅샷 비교.
//!
//! 실행:
//! ```bash
//! cargo test -p cimery-ifc --test snapshot_tests
//! # 새 baseline 채택: INSTA_UPDATE=always cargo test ...
//! ```
use cimery_ifc::{BridgeExportParams, IfcSectionKind, export_bridge};
/// GUID 정규화 — `'...'` 22자 IFC GUID 를 `'GUID'` 로 치환.
/// (IFC GUID charset: 0-9A-Za-z_$)
fn mask_guids(ifc: &str) -> String {
// 22자 = [0-9A-Za-z_$]{22} 로 정확 매치.
let mut out = String::with_capacity(ifc.len());
let chars: Vec<char> = ifc.chars().collect();
let mut i = 0;
while i < chars.len() {
if chars[i] == '\'' && i + 23 < chars.len() && chars[i + 23] == '\'' {
// i+1 ~ i+22 검사.
let all_guid_chars = (1..=22).all(|k| is_guid_char(chars[i + k]));
if all_guid_chars {
out.push_str("'GUID'");
i += 24;
continue;
}
}
out.push(chars[i]);
i += 1;
}
out
}
fn is_guid_char(c: char) -> bool {
c.is_ascii_alphanumeric() || c == '_' || c == '$'
}
#[test]
fn single_span_psc_i_default() {
let p = BridgeExportParams::default();
let ifc = export_bridge(&p);
insta::assert_snapshot!(mask_guids(&ifc));
}
#[test]
fn two_span_multi_column() {
let p = BridgeExportParams {
span_count: 2,
girder_count: 3,
..Default::default()
};
let ifc = export_bridge(&p);
insta::assert_snapshot!(mask_guids(&ifc));
}
#[test]
fn skewed_15_deg() {
let p = BridgeExportParams {
skew_deg: 15.0,
girder_count: 2,
..Default::default()
};
let ifc = export_bridge(&p);
insta::assert_snapshot!(mask_guids(&ifc));
}
#[test]
fn camber_50mm_subdivides() {
let p = BridgeExportParams {
camber_mid_mm: 50.0,
girder_count: 2,
..Default::default()
};
let ifc = export_bridge(&p);
insta::assert_snapshot!(mask_guids(&ifc));
}
#[test]
fn rectangle_section_no_profile_poly() {
let p = BridgeExportParams {
section_kind: IfcSectionKind::Rectangle,
girder_count: 2,
..Default::default()
};
let ifc = export_bridge(&p);
insta::assert_snapshot!(mask_guids(&ifc));
}
#[test]
fn parapets_off() {
let p = BridgeExportParams {
show_parapets: false,
girder_count: 2,
..Default::default()
};
let ifc = export_bridge(&p);
insta::assert_snapshot!(mask_guids(&ifc));
}
// ─── mask_guids 유닛 테스트 ───────────────────────────────────────────────
#[test]
fn mask_guids_replaces_22_char_token() {
let before = "IFCPROJECT('2JBHBMFGp5w3sCd6sd_Oz$',$,...)";
let after = mask_guids(before);
assert!(after.contains("'GUID'"));
assert!(!after.contains("2JBHBMFGp5w3sCd6sd_Oz$"));
}
#[test]
fn mask_guids_leaves_short_strings_alone() {
let before = "lit('Bridge Alignment')";
let after = mask_guids(before);
assert_eq!(before, after);
}