diff --git a/app/src/test/java/com/medithings/vesiscan/managers/Cm3PerTraceDumpTest.kt b/app/src/test/java/com/medithings/vesiscan/managers/Cm3PerTraceDumpTest.kt index b068d8d..452aad4 100644 --- a/app/src/test/java/com/medithings/vesiscan/managers/Cm3PerTraceDumpTest.kt +++ b/app/src/test/java/com/medithings/vesiscan/managers/Cm3PerTraceDumpTest.kt @@ -11,11 +11,12 @@ import kotlin.math.sqrt /** * cm=3 세션의 11 trace 각각 BV + 중간값을 dump 해서 Python (cm3_python.json) 과 대조. * divergent trace 특정용. + * + * 입·출력 위치는 [DumpEnv] — 환경 변수가 없으면 건너뛴다(개발 도구이지 회귀 시험이 아니다). */ class Cm3PerTraceDumpTest { - private val session = "c:/Projects/vesiscan-design-archive/data123/dwjang_HUMAN-kai_VBT26040302_SUPINE_ALIGN_3CM_2026-07-06_164842.json" - private val outPath = "C:/Users/장동우/AppData/Local/Temp/claude/c--Projects-medilightv2android/76311e3b-2bb5-4c52-ab8a-8d89cb1052c2/scratchpad/cm3_kotlin.json" + private val sessionName = "dwjang_HUMAN-kai_VBT26040302_SUPINE_ALIGN_3CM_2026-07-06_164842.json" private fun loadCycles(path: String, maxN: Int = 20): List> { val root = JsonParser.parseReader(File(path).bufferedReader()).asJsonObject @@ -54,6 +55,8 @@ class Cm3PerTraceDumpTest { @Test fun `dump cm=3 per-trace BV for python comparison`() { + val session = DumpEnv.dataFileOrSkip(sessionName).path + val outPath = DumpEnv.outFileOrSkip("cm3_kotlin.json").path PiezoHW.activePreset = PiezoHW.DevicePreset.V1 val cycles = loadCycles(session, 20) val params = MethodDParams.DEFAULT diff --git a/app/src/test/java/com/medithings/vesiscan/managers/DumpEnv.kt b/app/src/test/java/com/medithings/vesiscan/managers/DumpEnv.kt new file mode 100644 index 0000000..f512129 --- /dev/null +++ b/app/src/test/java/com/medithings/vesiscan/managers/DumpEnv.kt @@ -0,0 +1,53 @@ +/* + * DumpEnv — Python 대조용 덤프 시험 3개(Cm3PerTraceDump · GoldenDump · PrecisionDump)의 + * 입·출력 위치. + * + * 이 셋은 "시험"이라기보다 **개발 도구**다: 실측 세션 JSON 을 파이프라인에 흘려 중간값을 + * JSON 으로 떨구고 Python 결과와 diff 한다. 2026-09-29 까지는 입력이 이 PC 의 절대 경로, + * 출력이 옛 Claude 세션의 임시 폴더로 박혀 있어, 그 폴더가 사라진 뒤로 전체 단위시험이 + * 늘 3건 실패였다 — 다른 PC·CI 에서는 입력부터 없다. + * + * 그래서 둘 다 환경 변수로 받고, 없으면 **건너뛴다**(JUnit Assume → skipped). 일부러 돌릴 때만: + * + * VESISCAN_DATA123_DIR=c:/Projects/vesiscan-design-archive/data123 \ + * VESISCAN_DUMP_DIR=<출력 폴더> \ + * ./gradlew :app:testDemoDebugUnitTest --tests '*DumpTest*' + * + * 건너뛴 것은 gradle 보고서에 skipped 로 남는다 — "통과"로 위장하지 않는다. + */ +package com.medithings.vesiscan.managers + +import org.junit.Assume.assumeTrue +import java.io.File + +object DumpEnv { + + const val DATA_ENV = "VESISCAN_DATA123_DIR" + const val OUT_ENV = "VESISCAN_DUMP_DIR" + + /** 실측 세션 JSON 폴더. 환경 변수가 없거나 폴더가 없으면 건너뛴다. */ + fun dataDirOrSkip(): File { + val v = System.getenv(DATA_ENV) + assumeTrue("$DATA_ENV 가 없어 덤프 시험을 건너뛴다", !v.isNullOrBlank()) + val dir = File(v!!) + assumeTrue("$DATA_ENV=$v 폴더가 없어 덤프 시험을 건너뛴다", dir.isDirectory) + return dir + } + + /** [dataDirOrSkip] 안의 세션 파일. 그 파일이 없어도 건너뛴다. */ + fun dataFileOrSkip(name: String): File { + val f = File(dataDirOrSkip(), name) + assumeTrue("세션 파일이 없어 덤프 시험을 건너뛴다: ${f.path}", f.isFile) + return f + } + + /** 덤프 출력 파일. 환경 변수가 없으면 건너뛴다. 폴더는 만들어 둔다. */ + fun outFileOrSkip(name: String): File { + val v = System.getenv(OUT_ENV) + assumeTrue("$OUT_ENV 가 없어 덤프 시험을 건너뛴다", !v.isNullOrBlank()) + val dir = File(v!!) + dir.mkdirs() + assumeTrue("$OUT_ENV=$v 폴더를 만들 수 없어 덤프 시험을 건너뛴다", dir.isDirectory) + return File(dir, name) + } +} diff --git a/app/src/test/java/com/medithings/vesiscan/managers/GoldenDumpTest.kt b/app/src/test/java/com/medithings/vesiscan/managers/GoldenDumpTest.kt index d666b30..80addba 100644 --- a/app/src/test/java/com/medithings/vesiscan/managers/GoldenDumpTest.kt +++ b/app/src/test/java/com/medithings/vesiscan/managers/GoldenDumpTest.kt @@ -13,17 +13,20 @@ import java.io.File * * 실행: * 1. Python golden 생성: python scratchpad/golden_dump_python.py - * 2. Kotlin dump: ./gradlew testDevDebugUnitTest --tests "*GoldenDumpTest*" + * 2. Kotlin dump: VESISCAN_DATA123_DIR=… VESISCAN_DUMP_DIR=… \ + * ./gradlew :app:testDemoDebugUnitTest --tests "*GoldenDumpTest*" * 3. diff : python scratchpad/golden_diff.py + * + * 입·출력 위치는 [DumpEnv] — 환경 변수가 없으면 건너뛴다(개발 도구이지 회귀 시험이 아니다). */ class GoldenDumpTest { + /** [DumpEnv.dataDirOrSkip] 안의 파일명. */ private val sessions = mapOf( - "cm0" to "c:/Projects/vesiscan-design-archive/data123/dwjang_HUMAN-kai_VBT26050202_SUPINE_ALIGN_0CM_2026-07-06_164150.json", - "cm1" to "c:/Projects/vesiscan-design-archive/data123/dwjang_HUMAN-kai_VBT26050202_SUPINE_ALIGN_1CM_2026-07-06_164231.json", - "cm3" to "c:/Projects/vesiscan-design-archive/data123/dwjang_HUMAN-kai_VBT26040302_SUPINE_ALIGN_3CM_2026-07-06_164842.json", + "cm0" to "dwjang_HUMAN-kai_VBT26050202_SUPINE_ALIGN_0CM_2026-07-06_164150.json", + "cm1" to "dwjang_HUMAN-kai_VBT26050202_SUPINE_ALIGN_1CM_2026-07-06_164231.json", + "cm3" to "dwjang_HUMAN-kai_VBT26040302_SUPINE_ALIGN_3CM_2026-07-06_164842.json", ) - private val outPath = "C:/Users/장동우/AppData/Local/Temp/claude/c--Projects-medilightv2android/76311e3b-2bb5-4c52-ab8a-8d89cb1052c2/scratchpad/golden_kotlin.json" private fun loadCycles(path: String, maxN: Int = 20): List> { val root = JsonParser.parseReader(File(path).bufferedReader()).asJsonObject @@ -115,11 +118,13 @@ class GoldenDumpTest { @Test fun `golden dump all sessions`() { + val dataDir = DumpEnv.dataDirOrSkip() + val outPath = DumpEnv.outFileOrSkip("golden_kotlin.json").path PiezoHW.activePreset = PiezoHW.DevicePreset.V1 val cases = mutableListOf>() val win = 10 - for ((sid, path) in sessions) { - val cycles = loadCycles(path, 20) + for ((sid, name) in sessions) { + val cycles = loadCycles(File(dataDir, name).path, 20) if (cycles.size < win) { println(" skip $sid: only ${cycles.size} cycles") continue diff --git a/app/src/test/java/com/medithings/vesiscan/managers/PrecisionDumpTest.kt b/app/src/test/java/com/medithings/vesiscan/managers/PrecisionDumpTest.kt index faf790e..190c731 100644 --- a/app/src/test/java/com/medithings/vesiscan/managers/PrecisionDumpTest.kt +++ b/app/src/test/java/com/medithings/vesiscan/managers/PrecisionDumpTest.kt @@ -15,11 +15,12 @@ import java.io.File * * cm=1 세션 첫 10 cycle 평균 (trace 0) 을 pipeline 각 단계에 흘리면서 결과를 JSON 으로 저장. * scratchpad/precision_python.json 과 비교해 divergence 지점 특정. + * + * 입·출력 위치는 [DumpEnv] — 환경 변수가 없으면 건너뛴다(개발 도구이지 회귀 시험이 아니다). */ class PrecisionDumpTest { - private val session = "c:/Projects/vesiscan-design-archive/data123/dwjang_HUMAN-kai_VBT26050202_SUPINE_ALIGN_1CM_2026-07-06_164231.json" - private val outPath = "C:/Users/장동우/AppData/Local/Temp/claude/c--Projects-medilightv2android/76311e3b-2bb5-4c52-ab8a-8d89cb1052c2/scratchpad/precision_kotlin.json" + private val sessionName = "dwjang_HUMAN-kai_VBT26050202_SUPINE_ALIGN_1CM_2026-07-06_164231.json" private fun loadFirstTrace(path: String, win: Int = 10): List { val root = JsonParser.parseReader(File(path).bufferedReader()).asJsonObject @@ -55,6 +56,8 @@ class PrecisionDumpTest { @Test fun `dump pipeline intermediates for cm=1 trace 0`() { + val session = DumpEnv.dataFileOrSkip(sessionName).path + val outPath = DumpEnv.outFileOrSkip("precision_kotlin.json").path PiezoHW.activePreset = PiezoHW.DevicePreset.V1 EllipseFitSpecific.debugForceEigenvector = null val trace = loadFirstTrace(session, 10)