From 30aaf310e35a7ba745e73aa9ae39bd81f0a8c689 Mon Sep 17 00:00:00 2001 From: jjangddu Date: Mon, 12 Oct 2026 14:16:02 +0900 Subject: [PATCH] =?UTF-8?q?fix(align):=20=EC=A7=84=ED=96=89=20=EA=B8=B0?= =?UTF-8?q?=EB=A1=9D=EC=9D=84=20=EB=B0=94=EB=A1=9C=EC=9E=A1=EB=8A=94?= =?UTF-8?q?=EB=8B=A4=20=E2=80=94=20=EB=B6=80=EC=B0=A9=20=EC=9C=84=EC=B9=98?= =?UTF-8?q?=EB=8A=94=20=EB=A7=88=EC=A7=80=EB=A7=89=20=EC=9E=B0=20=EC=9E=90?= =?UTF-8?q?=EB=A6=AC,=20=EC=88=98=EC=A7=91=20=EC=A4=91=20=ED=9B=84?= =?UTF-8?q?=EB=B3=B4=20=EC=97=86=EC=9D=8C=EC=9D=80=20=ED=83=90=EC=83=89=20?= =?UTF-8?q?=EB=AF=B8=EC=99=84=EB=A3=8C,=20=ED=99=95=EC=9D=B8=20=EB=92=A4?= =?UTF-8?q?=20best=20=EA=B0=80=20=EC=98=AE=EA=B8=B0=EB=A9=B4=20=ED=99=95?= =?UTF-8?q?=EC=A0=95=20=ED=95=B4=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2026-10-08 P7(Supine) 두 세션과 2026-10-12 보고(Supine·Sitting 모두 CH3 미검출 상태에서 "이대로 진행")를 대조하다 찾은 세 가지다. 셋 다 [이대로 임상 측정 진행]이 남기는 기록 (proceed_anchor_cm · anchor_basis)과 그 뒤 임상 600 의 anchor_cm 에 바로 들어간다. ① 부착 위치가 프로브가 있던 자리가 아니었다. 수집 중에는 한 자리를 재면 화면 위치(cm)가 이미 다음 빈 자리로 넘어가고, 다 잰 뒤에는 best 자리로 "가서 재라"로 바뀐다 — 그 화면 위치를 그대로 기록했다. P7 1404: 0cm 를 재고 바로 진행했는데 요약·재부착 확인 파일·600 의 anchor_cm 이 모두 1cm 로 남았다. 간호사는 0cm 에서 쟀다고 했고 그 말이 맞았다. 이제 확정 전에는 마지막 측정 레코드의 cm 를, 확정 뒤에는 best 자리를 기록한다(recordedAnchorCm). ② 수집 중 후보가 없으면 "재부착 권고 무시"로 적었다. 재부착 권고는 0~4cm 를 다 잰 뒤의 판정인데 지시 카드는 sweeping 을 보고 버튼 쪽만 안 봤다. P7 1404 는 0cm 하나 재고 진행한 세션인데 reattach_override 로 남았다 — 실제는 "수동 지정 (탐색 미완료)"다. sweeping 을 컴포저블 최상위로 올려 카드·버튼·[처음부터 다시 정렬] 이 같은 값을 보게 했다(proceedBasis). ③ 확인 측정 뒤 best 가 다른 자리로 옮겨가도 confirmDone 이 풀리지 않았다 (`if (!isConfirm) confirmDone = false`). P7 1358 은 확인 측정마다 CH3 가 안 잡혀 best 가 1 → 2 → 4 → 3 으로 옮겨갔다 — 중간에 한 번 확정이 떴으면 3cm 를 한 번도 안 쟀는데 초록 "3cm 그대로 — 좌우 맞추기"가 보이고 근거는 알고리즘 확정으로 남는다. 후보가 사라진 경우 (target null)도 같이 푼다(afterMeasure). 분석 시: 이 커밋 전 빌드로 받은 세션은 anchor_cm 을 "그 직전에 잰 자리"로, 수집 중 진행한 reattach_override 는 manual 로 읽어야 한다. P7 1404 와 2026-10-12 세 자세 세션이 해당한다. RecordedAnchorCmTest 5 · ProceedBasisTest 4 · AfterMeasureTest 5 가 잠근다. Co-Authored-By: Claude Fable 5.1 --- .../ui/views/clinical/AnchorAlignView.kt | 115 ++++++++++++++---- .../ui/views/clinical/AfterMeasureTest.kt | 50 ++++++++ .../ui/views/clinical/ProceedBasisTest.kt | 43 +++++++ .../ui/views/clinical/RecordedAnchorCmTest.kt | 45 +++++++ 4 files changed, 231 insertions(+), 22 deletions(-) create mode 100644 app/src/test/java/com/medithings/vesiscan/ui/views/clinical/AfterMeasureTest.kt create mode 100644 app/src/test/java/com/medithings/vesiscan/ui/views/clinical/ProceedBasisTest.kt create mode 100644 app/src/test/java/com/medithings/vesiscan/ui/views/clinical/RecordedAnchorCmTest.kt diff --git a/app/src/main/java/com/medithings/vesiscan/ui/views/clinical/AnchorAlignView.kt b/app/src/main/java/com/medithings/vesiscan/ui/views/clinical/AnchorAlignView.kt index a5c573b..44e31e4 100644 --- a/app/src/main/java/com/medithings/vesiscan/ui/views/clinical/AnchorAlignView.kt +++ b/app/src/main/java/com/medithings/vesiscan/ui/views/clinical/AnchorAlignView.kt @@ -268,8 +268,18 @@ fun AnchorAlignView(appState: AppState) { val finalBest = AnchorSelection.selectBest(selectable).bestCm /** 단일 pass 가 냈을 답. **비교·감사용**이고 판정에는 쓰지 않는다. */ val singlePassBest = last?.bestCm - /** 잰 위치가 하나도 후보가 못 되면 재부착이다. */ + /** + * 잰 위치 중 후보가 하나도 없다. **재부착 권고는 다 잰 뒤의 판정**이라, 쓰는 쪽은 모두 + * `sweeping` 과 같이 본다 — 지시 카드([instructionOf])·진행 근거([proceedBasis])· + * [처음부터 다시 정렬] 버튼. 수집 중에 후보가 없는 것은 "탐색 미완료"다. + */ val needReattach = records.isNotEmpty() && finalBest == null + // 수집 단계인가 — 판정이 났어도 0~SEARCH_MAX_CM 에 빈 자리가 있으면 계속 잰다. + // (2026-10-01 까지는 "최대 cm 까지 올렸나"였다. 칩으로 중간 위치를 다시 재도 + // 빈 자리 기준이라야 흐름이 안 꼬인다.) 지시 카드와 진행 버튼이 **같은 값**을 봐야 + // 한다 — 2026-10-08 P7 은 카드가 "1cm 에 붙이고 측정"인데 버튼은 재부착 권고 무시로 적었다. + val sweeping = !sweepStopped && + (0..AnchorConfig.SEARCH_MAX_CM).any { c -> records.none { it.cm == c } } val anchorTarget = finalBest?.let { it + AnchorConfig.FINAL_OFFSET_CM } // ── 연결이 끊기면 기기에서 온 값을 비운다 ─────────────────────────────── @@ -491,11 +501,11 @@ fun AnchorAlignView(appState: AppState) { // 있으므로 확정은 다시 판단한다. val target = AnchorSelection.selectBest( guide.records.filter { it.capFrac.isFinite() }).bestCm - if (target != null) { - // 방금 잰 자리가 곧 판정된 자리면 그 측정이 확인 측정이다. - if (cm == target && isConfirm) confirmDone = true - else { cm = target; if (!isConfirm) confirmDone = false } - } + // 방금 잰 자리가 곧 판정된 자리면 그 측정이 확인 측정이다. best 가 옮겨갔으면 + // 확정도 같이 풀린다([afterMeasure]) — 옮겨간 자리는 아직 아무도 안 쟀다. + val next = afterMeasure(cm, target, isConfirm) + cm = next.cm + confirmDone = next.confirmDone } } } finally { @@ -791,11 +801,6 @@ fun AnchorAlignView(appState: AppState) { val singlePassDisagrees = finalBest != null && singlePassBest != null && finalBest != singlePassBest - // 수집 단계인가 — 판정이 났어도 0~SEARCH_MAX_CM 에 빈 자리가 있으면 계속 잰다. - // (2026-10-01 까지는 "최대 cm 까지 올렸나"였다. 칩으로 중간 위치를 다시 재도 - // 빈 자리 기준이라야 흐름이 안 꼬인다.) - val sweeping = !sweepStopped && - (0..AnchorConfig.SEARCH_MAX_CM).any { c -> records.none { it.cm == c } } val instr = instructionOf( last, cm, AnchorConfig.FINAL_OFFSET_CM, sweeping = sweeping, decidedAtCm = decidedAtCm, @@ -1030,15 +1035,14 @@ fun AnchorAlignView(appState: AppState) { // 대신 **근거를 같이 들고 간다**([AnchorBasis]). 사람이 고른 위치와 알고리즘이 // 확정한 위치가 기록에서 구분되지 않으면, 정확도 검증의 모집단을 나중에 // 가려낼 수 없어 데이터 전체가 주장을 받치지 못한다. - // 근거. 확인 측정까지 마쳤으면 앱 규칙이 정한 값이다. - val basis = when { - confirmDone -> AnchorBasis.ALGORITHM - needReattach -> AnchorBasis.REATTACH_OVERRIDE - else -> AnchorBasis.MANUAL - } - // 확정됐으면 그 값(best+offset), 아니면 지금 화면의 위치. 둘 다 "프로브가 - // 지금 있는 자리"라는 뜻이라 간호사가 보는 숫자와 기록이 어긋나지 않는다. - val proceedCm = if (confirmDone) anchorTarget ?: cm else cm + // 근거. 확인 측정까지 마쳤으면 앱 규칙이 정한 값이다. 수집 중이면 재부착 권고가 + // 아니라 탐색 미완료다([proceedBasis]). + val basis = proceedBasis(confirmDone, needReattach, sweeping) + // 프로브가 **지금 있는 자리**를 기록한다([recordedAnchorCm]). 확정됐으면 best 자리 + // (확인 측정을 거기서 했다), 아니면 마지막으로 잰 자리다. 화면 위치 `cm` 은 수집 중엔 + // 한 자리를 재고 나면 이미 다음 빈 자리로 넘어가 있어 프로브가 있는 곳이 아니다 — + // 2026-10-08 P7 은 0cm 를 재고 바로 진행했는데 1cm 로 기록됐다. + val proceedCm = recordedAnchorCm(confirmDone, anchorTarget, last?.record?.cm, cm) Button( onClick = { appState.anchorCm = proceedCm @@ -1090,7 +1094,7 @@ fun AnchorAlignView(appState: AppState) { Text(note, fontSize = 12.sp, color = MlSecondaryText) } - if (confirmDone || needReattach) { + if (confirmDone || (needReattach && !sweeping)) { Spacer(Modifier.height(8.dp)) TextButton( onClick = { @@ -1596,6 +1600,70 @@ internal fun instructionOf( else -> AlignInstruction("치골 위 ${nextCm}cm 에 붙이고 측정", null) } +/** + * 수집이 끝난 뒤 한 위치를 잰 결과 → 다음에 잴 자리와 확정 여부. + * + * 확정은 **이번 측정이 확인 측정이었고, 그 결과까지 넣어 다시 고른 best 가 그 자리 그대로일 + * 때**뿐이다. 확인 측정에서 CH3 가 안 잡혀 best 가 다른 자리로 옮겨가면 그 자리는 아직 아무도 + * 재지 않은 자리다 — 옛 확정이 남아 있으면 안 된다. 후보가 아예 사라져도(null) 마찬가지다. + * + * 2026-10-08 P7(Supine) 1358 세션: 확인 측정마다 best 가 1 → 2 → 4 → 3 으로 옮겨갔다. 종전 + * 코드는 `if (!isConfirm) confirmDone = false` 라 **확인 측정** 뒤 best 가 옮겨가면 확정을 + * 되돌리지 않았다. 중간에 한 번이라도 확정이 떴으면, 3cm 는 한 번도 안 쟀는데 초록 + * "치골 위 3cm 그대로 — 좌우 맞추기" 와 [처음부터 다시 정렬] 이 보이고 진행 근거는 알고리즘 + * 확정으로 기록된다. + * + * @param cm 방금 잰 자리. + * @param target 이번 측정까지 넣어 전 위치로 다시 고른 best. null = 후보 없음(재부착). + * @param isConfirm 이번 측정이 확인 측정이었는가(재기 전 best 가 [cm] 이었다). + */ +internal data class AfterMeasure(val cm: Int, val confirmDone: Boolean) + +internal fun afterMeasure(cm: Int, target: Int?, isConfirm: Boolean): AfterMeasure = AfterMeasure( + cm = target ?: cm, + confirmDone = target != null && cm == target && isConfirm, +) + +/** + * [이대로 임상 측정 진행] 이 기록하는 부착 위치 = **프로브가 지금 있는 자리.** + * + * - 확정됐으면 best 자리(`anchorTarget`) — 확인 측정을 거기서 했으니 프로브도 거기 있다. + * - 아니면 **마지막으로 잰 자리**다. 간호사는 방금 잰 자리의 파형·채널을 보고 "이대로"를 + * 누른다(2026-10-08 P7 · 2026-10-12 Supine/Sitting 모두 그랬다). + * - 한 번도 안 쟀으면 화면 위치다 — 그 외에 아는 것이 없다. + * + * 화면 위치(`screenCm`)를 그대로 쓰면 안 된다. 수집 중에는 한 자리를 재고 나면 화면이 이미 + * **다음 빈 자리**로 넘어가 있고, 다 잰 뒤에는 best 자리로 "가서 재라"고 바뀌어 있다 — 둘 다 + * 프로브가 아직 가지 않은 자리다. 2026-10-08 P7 1404: 0cm 를 재고 바로 진행했는데 화면은 + * 1cm 였고, 요약·재부착 확인 파일·임상(600)의 anchor_cm 이 모두 1cm 로 남았다. 간호사는 + * 0cm 에서 쟀다고 했고 그 말이 맞았다. + * + * 칩으로 자리를 고른 뒤 재지 않고 진행하면 마지막 잰 자리가 기록된다 — 재지 않은 자리를 + * 기록하는 쪽보다 낫다. 기록된 자리는 "그 자리에서 받은 신호가 있다"는 뜻이어야 한다. + */ +internal fun recordedAnchorCm( + confirmDone: Boolean, + anchorTarget: Int?, + lastMeasuredCm: Int?, + screenCm: Int, +): Int = + if (confirmDone) anchorTarget ?: lastMeasuredCm ?: screenCm + else lastMeasuredCm ?: screenCm + +/** + * 진행 버튼에 붙는 근거 — 기록(`anchor_basis`)과 병원 화면 라벨이 이 값을 쓴다. + * + * 재부착 권고는 **0~4cm 를 다 잰 뒤의 판정**이다. 수집 중에 후보가 없는 것은 아직 판정이 + * 아니라 "탐색 미완료"다. 2026-10-08 P7(Supine) 1404 세션: 0cm 하나 재고(CH3 X) 진행한 것이 + * `reattach_override` 로 기록됐다 — 지시 카드([instructionOf])는 `sweeping` 을 보는데 버튼 + * 쪽만 안 봤다. 이 값으로 정확도 검증 모집단을 가르므로 틀리면 분석이 오염된다. + */ +internal fun proceedBasis(confirmDone: Boolean, needReattach: Boolean, sweeping: Boolean): AnchorBasis = when { + confirmDone -> AnchorBasis.ALGORITHM + needReattach && !sweeping -> AnchorBasis.REATTACH_OVERRIDE + else -> AnchorBasis.MANUAL +} + /** * 진행 버튼 아래 한 줄 - **무엇을 모르고 넘어가는지** 말한다. * @@ -1638,7 +1706,10 @@ private fun buildAlignSummary( * 진행 버튼을 누르는 순간 덮어쓰므로, 남은 null 은 "정렬만 하고 나갔다"는 뜻이다. */ basis: AnchorBasis? = null, - /** 실제로 임상에 넘긴 cm. [AnchorStep.anchorCm] 이 null 인 채 진행할 수 있다. */ + /** + * 실제로 임상에 넘긴 cm = 프로브가 있던 자리([recordedAnchorCm]). [AnchorStep.anchorCm] 이 + * null 인 채 진행할 수 있다. + */ proceedCm: Int? = null, /** * 알고리즘이 판정을 내린 위치. **이 cm 까지가 판정의 후보다.** diff --git a/app/src/test/java/com/medithings/vesiscan/ui/views/clinical/AfterMeasureTest.kt b/app/src/test/java/com/medithings/vesiscan/ui/views/clinical/AfterMeasureTest.kt new file mode 100644 index 0000000..3a6bb46 --- /dev/null +++ b/app/src/test/java/com/medithings/vesiscan/ui/views/clinical/AfterMeasureTest.kt @@ -0,0 +1,50 @@ +package com.medithings.vesiscan.ui.views.clinical + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +/** + * 수집이 끝난 뒤 한 위치를 잰 결과로 다음 자리와 확정 여부를 정하는 규칙([afterMeasure]). + * + * 2026-10-08 P7(Supine) 1358 세션: 0~4cm 를 다 잰 뒤 확인 측정마다 CH3 가 안 잡혀 best 가 + * 1 → 2 → 4 → 3 으로 옮겨갔다. 종전 코드는 **확인 측정** 뒤 best 가 옮겨가면 `confirmDone` + * 을 되돌리지 않았다(`if (!isConfirm) confirmDone = false`). 중간에 한 번이라도 확정이 떴으면 + * 3cm 는 한 번도 안 쟀는데 초록 "3cm 그대로 — 좌우 맞추기" 와 [처음부터 다시 정렬] 이 보인다. + */ +class AfterMeasureTest { + + @Test fun `확인 측정에서 best 가 그대로면 확정이다`() { + val r = afterMeasure(cm = 3, target = 3, isConfirm = true) + assertEquals(3, r.cm) + assertTrue(r.confirmDone) + } + + @Test fun `확인 측정에서 best 가 옮겨가면 확정이 풀리고 새 자리로 간다`() { + // P7 1358: 1cm 확인 측정 → best 2cm. 2cm 는 아직 아무도 확인하지 않았다. + val r = afterMeasure(cm = 1, target = 2, isConfirm = true) + assertEquals(2, r.cm) + assertFalse(r.confirmDone) + } + + @Test fun `탐색을 끝낸 측정이 best 자리여도 확인 측정은 아니다`() { + // 4cm 까지 재서 수집이 끝났는데 best 가 마침 4cm. 그 측정은 탐색이라 한 번 더 재야 한다. + val r = afterMeasure(cm = 4, target = 4, isConfirm = false) + assertEquals(4, r.cm) + assertFalse(r.confirmDone) + } + + @Test fun `칩으로 다른 자리를 다시 재면 확정이 풀리고 best 자리로 돌아간다`() { + val r = afterMeasure(cm = 1, target = 3, isConfirm = false) + assertEquals(3, r.cm) + assertFalse(r.confirmDone) + } + + @Test fun `후보가 하나도 없어지면 자리는 그대로, 확정도 풀린다`() { + // 재측정으로 유일한 후보가 사라진 경우. 종전에는 아무것도 안 바꿔 옛 확정이 남았다. + val r = afterMeasure(cm = 2, target = null, isConfirm = true) + assertEquals(2, r.cm) + assertFalse(r.confirmDone) + } +} diff --git a/app/src/test/java/com/medithings/vesiscan/ui/views/clinical/ProceedBasisTest.kt b/app/src/test/java/com/medithings/vesiscan/ui/views/clinical/ProceedBasisTest.kt new file mode 100644 index 0000000..52ae417 --- /dev/null +++ b/app/src/test/java/com/medithings/vesiscan/ui/views/clinical/ProceedBasisTest.kt @@ -0,0 +1,43 @@ +package com.medithings.vesiscan.ui.views.clinical + +import com.medithings.vesiscan.managers.AnchorBasis +import org.junit.Assert.assertEquals +import org.junit.Test + +/** + * 진행 버튼의 근거(`anchor_basis`) — 기록과 병원 화면 라벨이 이 값을 쓴다. + * + * 2026-10-08 P7(Supine) 1404 세션: 0cm 하나만 재고(CH3 X, 후보 없음) [이대로 임상 측정 진행] + * 을 눌렀는데 `anchor_basis = reattach_override` 로 남았다. 재부착 권고는 0~4cm 를 **다 잰 + * 뒤**의 판정인데, 지시 카드([instructionOf])는 `sweeping` 을 보고 버튼 쪽만 안 봤다. 그 + * 세션의 실제 상태는 "탐색 미완료 · 사람이 고른 자리"다. 이 값으로 정확도 검증 모집단을 + * 가르므로 틀리면 두 집단이 섞인다. + */ +class ProceedBasisTest { + + @Test fun `확인 측정까지 마쳤으면 알고리즘 확정이다`() { + assertEquals(AnchorBasis.ALGORITHM, + proceedBasis(confirmDone = true, needReattach = false, sweeping = false)) + // 확정이 다른 어떤 상태보다 앞선다 — 확정이 났다는 것은 다 재고 후보도 있었다는 뜻이다. + assertEquals(AnchorBasis.ALGORITHM, + proceedBasis(confirmDone = true, needReattach = true, sweeping = true)) + } + + @Test fun `다 쟀는데 후보가 없으면 재부착 권고 무시다`() { + assertEquals(AnchorBasis.REATTACH_OVERRIDE, + proceedBasis(confirmDone = false, needReattach = true, sweeping = false)) + } + + @Test fun `수집 중에 후보가 없는 것은 재부착 권고가 아니라 탐색 미완료다`() { + // 2026-10-08 P7 1404: 0cm 한 번 재고 진행. 1~4cm 는 아직 안 쟀다. + assertEquals(AnchorBasis.MANUAL, + proceedBasis(confirmDone = false, needReattach = true, sweeping = true)) + } + + @Test fun `후보가 있어도 확인 측정을 안 했으면 수동이다`() { + assertEquals(AnchorBasis.MANUAL, + proceedBasis(confirmDone = false, needReattach = false, sweeping = false)) + assertEquals(AnchorBasis.MANUAL, + proceedBasis(confirmDone = false, needReattach = false, sweeping = true)) + } +} diff --git a/app/src/test/java/com/medithings/vesiscan/ui/views/clinical/RecordedAnchorCmTest.kt b/app/src/test/java/com/medithings/vesiscan/ui/views/clinical/RecordedAnchorCmTest.kt new file mode 100644 index 0000000..b24534e --- /dev/null +++ b/app/src/test/java/com/medithings/vesiscan/ui/views/clinical/RecordedAnchorCmTest.kt @@ -0,0 +1,45 @@ +package com.medithings.vesiscan.ui.views.clinical + +import org.junit.Assert.assertEquals +import org.junit.Test + +/** + * [이대로 임상 측정 진행] 이 기록하는 부착 위치([recordedAnchorCm]). + * + * 2026-10-08 P7(Supine) 1404: 0cm 한 번 재고 바로 진행. 화면은 이미 "치골 위 1cm 에 붙이고 + * 측정"으로 넘어가 있었고, 종전 코드는 그 화면 위치를 기록해 anchor_cm=1 이 됐다. 재부착 확인 + * 파일도 `align_1cm_reattach.csv`, 임상(600) params 의 anchor_cm 도 1. 간호사는 0cm 에서 쟀다고 + * 했고 그 말이 맞았다 — 프로브는 0cm 에 그대로 있었다. + */ +class RecordedAnchorCmTest { + + @Test fun `확정 전에는 마지막으로 잰 자리다 - 화면이 다음 자리로 넘어가 있어도`() { + // P7 1404: 0cm 재고 → 화면 1cm → 진행. + assertEquals(0, recordedAnchorCm( + confirmDone = false, anchorTarget = null, lastMeasuredCm = 0, screenCm = 1)) + } + + @Test fun `다 재고 best 자리로 가라는 상태에서 확인 없이 진행하면 마지막 잰 자리다`() { + // P7 1358 끝: 4cm 를 마지막으로 재고 best 는 3cm. 화면은 "3cm 에 다시 붙이고 측정". + // 프로브는 아직 4cm 에 있다. + assertEquals(4, recordedAnchorCm( + confirmDone = false, anchorTarget = 3, lastMeasuredCm = 4, screenCm = 3)) + } + + @Test fun `확정이면 best 자리다`() { + assertEquals(3, recordedAnchorCm( + confirmDone = true, anchorTarget = 3, lastMeasuredCm = 3, screenCm = 3)) + } + + @Test fun `한 번도 안 쟀으면 화면 위치다`() { + assertEquals(0, recordedAnchorCm( + confirmDone = false, anchorTarget = null, lastMeasuredCm = null, screenCm = 0)) + assertEquals(2, recordedAnchorCm( + confirmDone = false, anchorTarget = null, lastMeasuredCm = null, screenCm = 2)) + } + + @Test fun `확정인데 best 가 없는 어긋난 상태면 마지막 잰 자리로 떨어진다`() { + assertEquals(2, recordedAnchorCm( + confirmDone = true, anchorTarget = null, lastMeasuredCm = 2, screenCm = 4)) + } +}