fix(clinical): 정렬 화면에서 위치 이름을 없애고 지시문 하나만
시험하는 분이 "최적 위치"와 "부착 위치"를 계속 헷갈렸다(2026-09-09 현장). 당연하다 —
둘 다 "위치"라는 이름의 숫자인데 화면이 단계마다 다른 쪽을 크게 보여 줬다.
측정할 위치 2 cm ← 탐색 중
부착 위치 확정 3 cm ← 확정 뒤
여기로 옮기고 (최적 2cm + 1cm) 고정하세요
숫자 셋(2·3·+1)이 한 화면에 있었다. 프로브를 잡은 사람에게 필요한 건 **지금 할 동작
하나**다. 절대 cm 은 기록의 몫이고 사람이 셀 일이 아니다.
치골 바로 위에 붙이고 측정
↑ 1cm 올리고 측정
↓ 2cm 내리고 측정
● 여기서 한 번 더 측정 (옮겨 온 자리를 확인하는 마지막 측정입니다)
↑ 1cm 올리세요 (여기서는 측정하지 않습니다. 올린 뒤 좌우로.) ← 초록
↓ 더 아래에 다시 붙이세요 (재부착)
화살표 + 한 줄 지시 + 이유. 전부 **상대 이동**이라 사람이 cm 을 누적해 셀 필요가 없다.
측정 버튼도 "2cm 측정"→"여기서 측정" 으로 바꿨다 — 카드가 "1cm 올리고 측정"이라고 말하는데
버튼이 다른 숫자를 달고 있으면 둘을 맞춰 보려다 또 헷갈린다.
"여기서 한 번 더 측정"에 이유를 붙인 것은 이 화면에서 제일 많이 나오는 질문이 "왜 방금
잰 자리를 또 재냐"이기 때문이다. 확정 판정의 입력이 그 측정이다.
판정 근거(cm · 채널 n/4 · CH3 · cap)는 카드 아래 작은 글씨로 남긴다. 왜 그 지시가
나왔는지 물어보게 되므로 숨기지 않는다. 기록에 남는 cm 도 진행 버튼 아래 한 줄로 남긴다.
좌우 카드 안내도 고쳤다: "부착 위치 3cm 로 옮기세요" → "앞에서 1cm 올린 자리 그대로
둡니다. 아직 안 올렸으면 지금 올리세요."
테스트 129개 통과(신규 8). AlignInstructionTest 가 지시문에 "최적"·"부착 위치" 가
들어가지 않는지까지 본다 — 여기서는 헷갈림 자체가 고치려던 결함이라 문구가 요구사항이다.
FINAL_OFFSET_CM 을 바꾸면 문구도 따라가는지도 고정했다(박아 두면 거짓이 된다).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,12 +13,18 @@
|
||||
* cycle 수가 곧 trace 수를 정하는데(20 → 11 trace), trace 가 줄면 CH3 검출률의 해상도가
|
||||
* 거칠어져 0.8 임계가 불안정해진다.
|
||||
*
|
||||
* ## 화면 흐름
|
||||
* [0cm 에서 측정] → 안내(1cm 올리세요) → [1cm 에서 측정] → ... →
|
||||
* 정렬 완료: "최적 2cm · 여기서 1cm 더 올려 3cm 에 부착하세요"
|
||||
* ## 화면은 지시문 하나만 보여준다
|
||||
* 위치 이름(최적·부착)을 쓰지 않는다. 시험하는 사람이 두 숫자를 계속 헷갈렸다
|
||||
* (2026-09-09 현장) — 둘 다 "위치"라는 이름의 숫자이고 단계마다 다른 쪽이 커졌기
|
||||
* 때문이다. 프로브를 잡은 사람에게 필요한 것은 **지금 할 동작 하나**다([instructionOf]).
|
||||
*
|
||||
* 오프셋 위치(+1cm)는 **측정하지 않는다**. 설계상 그 자리는 지표가 나쁘므로, 재확인하면
|
||||
* 반드시 "미달"이 떠서 anchor 에 도달하기 전에 멈추게 된다.
|
||||
* 치골 바로 위에 붙이고 측정 → 1cm 올리고 측정 → ... → 2cm 내리고 측정 →
|
||||
* 여기서 한 번 더 측정 → **1cm 올리세요 (재지 말고 좌우로)**
|
||||
*
|
||||
* 절대 cm 은 기록의 몫이다 — 진행 버튼 아래 작은 한 줄과 파일에만 남는다.
|
||||
*
|
||||
* 마지막 +1cm 자리는 **측정하지 않는다**. 설계상 그 자리는 지표가 나쁘므로, 재확인하면
|
||||
* 반드시 "미달"이 떠서 도달 전에 멈추게 된다.
|
||||
*/
|
||||
package com.medithings.vesiscan.ui.views.clinical
|
||||
|
||||
@@ -471,65 +477,46 @@ fun AnchorAlignView(appState: AppState) {
|
||||
}
|
||||
Spacer(Modifier.height(16.dp))
|
||||
|
||||
// 현재 위치 — 크게. 간호사가 프로브를 잡은 채 멀리서 봐야 한다.
|
||||
// 지금 무엇을 할지 — 크게. **위치 이름을 쓰지 않는다.**
|
||||
//
|
||||
// **확정된 순간에는 카드를 통째로 초록으로 칠한다.** 탐색 중에는 흰 카드에
|
||||
// 숫자만 바뀌므로, 확정도 흰 카드에 글자색만 다르면 "또 한 번 바뀐 숫자"로
|
||||
// 읽힌다. 여기서 프로브를 옮기고 더 이상 재지 않는다는 것이 이 화면에서
|
||||
// 제일 중요한 전환점이라, 색이 면으로 바뀌어야 곁눈으로도 걸린다.
|
||||
val fixed = last?.action == AnchorAction.STOP && last?.anchorCm != null
|
||||
// 종전에는 "지금 측정할 위치 2cm" 과 "부착 위치 3cm" 을 번갈아 보여 줬다.
|
||||
// 두 숫자가 다 "위치"라는 이름을 달고 나오니, 시험하는 사람이 어느 자리에
|
||||
// 붙여야 하는지 매번 헷갈렸다(2026-09-09 현장 피드백). 실제로 필요한 것은
|
||||
// **지금 할 동작 하나**다 — 1cm 올려서 재라, 2cm 내려서 재라, 1cm 올리고
|
||||
// 좌우로 넘어가라. 절대 위치는 기록의 몫이고 사람이 셀 일이 아니다.
|
||||
val instr = instructionOf(last, cm, AnchorConfig.FINAL_OFFSET_CM)
|
||||
Box(
|
||||
Modifier.fillMaxWidth()
|
||||
.background(if (fixed) MlSuccess else Color.White, RoundedCornerShape(14.dp))
|
||||
.background(
|
||||
if (instr.done) MlSuccess else Color.White, RoundedCornerShape(14.dp))
|
||||
.padding(vertical = 20.dp, horizontal = 16.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
val fg = if (instr.done) Color.White else MlPrimary
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
if (fixed) {
|
||||
// 판정이 아니라 **지시**로 읽히게 쓴다.
|
||||
Text("부착 위치 확정", fontSize = 13.sp, color = Color.White,
|
||||
fontWeight = FontWeight.Bold)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text("${last?.anchorCm} cm", fontSize = 44.sp,
|
||||
fontWeight = FontWeight.Bold, color = Color.White)
|
||||
Text(instr.arrow, fontSize = 40.sp, fontWeight = FontWeight.Bold, color = fg)
|
||||
Spacer(Modifier.height(2.dp))
|
||||
Text(instr.headline, fontSize = 24.sp, fontWeight = FontWeight.Bold,
|
||||
color = fg, textAlign = TextAlign.Center)
|
||||
instr.sub?.let {
|
||||
Spacer(Modifier.height(6.dp))
|
||||
// 간호사가 제일 많이 틀리는 지점이다 — 최적 위치(best)와 부착
|
||||
// 위치(best+1)를 헷갈려 측정했던 자리에 그대로 붙인다.
|
||||
Text(
|
||||
"여기로 옮기고 " + (last?.bestCm?.let { "(최적 ${it}cm + 1cm) " } ?: "") +
|
||||
"고정하세요. 이 자리는 측정하지 않습니다.",
|
||||
fontSize = 12.sp, color = Color.White.copy(alpha = 0.92f),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
} else {
|
||||
Text(if (done) "부착 위치" else "지금 측정할 위치",
|
||||
fontSize = 13.sp, color = MlSecondaryText)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
if (done) "${last?.anchorCm ?: "-"} cm" else "$cm cm",
|
||||
fontSize = 40.sp, fontWeight = FontWeight.Bold,
|
||||
color = if (done) MlSuccess else MlPrimary,
|
||||
)
|
||||
Text(it, fontSize = 13.sp, textAlign = TextAlign.Center,
|
||||
color = if (instr.done) Color.White.copy(alpha = 0.92f)
|
||||
else MlSecondaryText)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(12.dp))
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
last?.let { s ->
|
||||
val tone = when (s.action) {
|
||||
AnchorAction.STOP -> MlSuccess
|
||||
AnchorAction.REATTACH -> MlCritical
|
||||
else -> MlPrimary
|
||||
}
|
||||
// 확정 카드가 이미 "여기로 옮기고 고정하세요"를 말했다. 같은 말을 두 번
|
||||
// 쓰면 둘 다 안 읽힌다 — 여기서는 근거(nch·cap)만 남긴다.
|
||||
val body = if (fixed) s.message.lineSequence().first() else s.message
|
||||
Box(
|
||||
Modifier.fillMaxWidth()
|
||||
.border(1.5.dp, tone, RoundedCornerShape(12.dp))
|
||||
.padding(12.dp)
|
||||
) { Text(body, fontSize = 14.sp, color = tone) }
|
||||
Spacer(Modifier.height(12.dp))
|
||||
// 판정 근거는 작게. 왜 그 지시가 나왔는지 물어보게 되므로 숨기지는 않는다.
|
||||
last?.record?.let { r ->
|
||||
Text(
|
||||
"${r.cm}cm · 채널 ${r.nch}/4 · CH3 ${if (r.ch3Mean) "O" else "X"} " +
|
||||
"${(r.ch3Rate * 100).toInt()}% · cap " +
|
||||
(if (r.capFrac.isFinite()) "%.3f".format(r.capFrac) else "-"),
|
||||
fontSize = 11.sp, color = MlSecondaryText,
|
||||
)
|
||||
Spacer(Modifier.height(10.dp))
|
||||
}
|
||||
|
||||
error?.let {
|
||||
@@ -610,7 +597,9 @@ fun AnchorAlignView(appState: AppState) {
|
||||
enabled = isConnected && !measuring,
|
||||
modifier = Modifier.fillMaxWidth().height(52.dp),
|
||||
shape = RoundedCornerShape(14.dp),
|
||||
) { Text("${cm}cm 측정", fontSize = 16.sp, fontWeight = FontWeight.Bold) }
|
||||
// 버튼에도 절대 cm 을 쓰지 않는다. 큰 카드가 "1cm 올리고 측정"
|
||||
// 이라고 말하는데 버튼이 "2cm 측정"이면 둘을 맞춰 보려다 헷갈린다.
|
||||
) { Text("여기서 측정", fontSize = 16.sp, fontWeight = FontWeight.Bold) }
|
||||
|
||||
// 탐색 상한(4cm)까지 갔는데도 종료가 안 걸릴 수 있다 — 그때 쓰는 문이다.
|
||||
if (guide.records.isNotEmpty() && !guide.settled) {
|
||||
@@ -712,11 +701,14 @@ fun AnchorAlignView(appState: AppState) {
|
||||
containerColor = if (basis.isAlgorithm) MlSuccess else MlWarning,
|
||||
),
|
||||
) {
|
||||
Text("이 위치(${proceedCm}cm)로 임상 측정 진행",
|
||||
fontSize = 16.sp, fontWeight = FontWeight.Bold)
|
||||
Text("이대로 임상 측정 진행", fontSize = 16.sp, fontWeight = FontWeight.Bold)
|
||||
}
|
||||
// 기록에 남는 cm 은 작게 한 줄. 사람이 셀 숫자가 아니라 기록의 몫이다.
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text("기록에 남는 부착 위치 ${proceedCm}cm · ${basis.label}",
|
||||
fontSize = 11.sp, color = MlSecondaryText)
|
||||
proceedNote(basis, lrConfirmed)?.let { note ->
|
||||
Spacer(Modifier.height(6.dp))
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(note, fontSize = 12.sp, color = MlSecondaryText)
|
||||
}
|
||||
|
||||
@@ -791,24 +783,21 @@ private fun LateralSection(
|
||||
// 위다(오프셋 자리는 측정하지 않는다). 좌우 균형은 그 높이의 단면에서 정해지는
|
||||
// 값이라 높이를 바꾸면 u4·u5 가 달라진다 — 옮기기 **전에** 맞추면 헛일이 된다.
|
||||
// 화면이 이걸 말해 주지 않으면 간호사마다 순서가 갈린다.
|
||||
anchorCm?.let { anchor ->
|
||||
Box(
|
||||
Modifier.fillMaxWidth()
|
||||
.background(MlPrimary.copy(alpha = 0.10f), RoundedCornerShape(10.dp))
|
||||
.padding(10.dp)
|
||||
) {
|
||||
Text(
|
||||
"먼저 프로브를 부착 위치 ${anchor}cm 로 옮기세요. " +
|
||||
"좌우는 그 높이에서 맞춥니다.",
|
||||
fontSize = 13.sp, fontWeight = FontWeight.SemiBold, color = MlPrimary,
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.height(10.dp))
|
||||
Box(
|
||||
Modifier.fillMaxWidth()
|
||||
.background(MlPrimary.copy(alpha = 0.10f), RoundedCornerShape(10.dp))
|
||||
.padding(10.dp)
|
||||
) {
|
||||
Text(
|
||||
"앞에서 1cm 올린 자리 그대로 둡니다. 아직 안 올렸으면 지금 올리세요.",
|
||||
fontSize = 13.sp, fontWeight = FontWeight.SemiBold, color = MlPrimary,
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.height(10.dp))
|
||||
|
||||
Text(
|
||||
"높이는 그대로 두고 프로브를 좌우로만 옮기며 맞춥니다. " +
|
||||
"|ch4−ch5| ≤ ${AlignmentConstants.LAT_TOL} 이면 완료입니다.",
|
||||
"높이는 건드리지 말고 좌우로만 조금씩 움직입니다. " +
|
||||
"화살표가 ■ 로 바뀌면 맞은 것입니다 (|ch4−ch5| ≤ ${AlignmentConstants.LAT_TOL}).",
|
||||
fontSize = 12.sp, color = MlSecondaryText,
|
||||
)
|
||||
Spacer(Modifier.height(12.dp))
|
||||
@@ -926,6 +915,65 @@ private fun RecordRow(r: AnchorPosRecord, isBest: Boolean) {
|
||||
Spacer(Modifier.height(4.dp))
|
||||
}
|
||||
|
||||
/**
|
||||
* 화면에 띄울 **지시문 하나**. 위치 이름을 쓰지 않는다.
|
||||
*
|
||||
* 시험하는 사람이 "최적 위치"와 "부착 위치"를 계속 헷갈렸다(2026-09-09 현장). 당연하다 —
|
||||
* 둘 다 "위치"라는 이름의 숫자이고, 화면이 단계마다 다른 쪽을 크게 보여 줬다. 그런데
|
||||
* 프로브를 잡은 사람에게 필요한 것은 **지금 할 동작 하나**다. 절대 cm 은 기록의 몫이다.
|
||||
*
|
||||
* | 상태 | 지시 |
|
||||
* |---|---|
|
||||
* | 첫 측정 전 | 치골 바로 위에 붙이고 측정 |
|
||||
* | 탐색 중 | N cm 올리고 측정 |
|
||||
* | best 를 지나침 | N cm 내리고 측정 |
|
||||
* | 확정 | 1cm 올리고 **재지 말고** 좌우로 |
|
||||
* | 재부착 | 더 아래(치골 쪽)에 다시 붙이기 |
|
||||
*
|
||||
* @param nextCm 화면이 다음에 재려는 위치. 호출부가 판정 뒤 이미 갱신해 둔다.
|
||||
*/
|
||||
internal data class AlignInstruction(
|
||||
val arrow: String,
|
||||
val headline: String,
|
||||
val sub: String?,
|
||||
/** 확정 상태 — 카드를 통째로 초록으로 칠한다. */
|
||||
val done: Boolean = false,
|
||||
)
|
||||
|
||||
internal fun instructionOf(
|
||||
step: AnchorStep?,
|
||||
nextCm: Int,
|
||||
offsetCm: Int,
|
||||
): AlignInstruction = when {
|
||||
step == null -> AlignInstruction(
|
||||
"●", "치골 바로 위에 붙이고 측정",
|
||||
"젤을 바르고, 프로브를 치골 바로 위에 밀착시킵니다.")
|
||||
|
||||
step.action == AnchorAction.REATTACH -> AlignInstruction(
|
||||
"↓", "더 아래에 다시 붙이세요",
|
||||
"치골 쪽으로 내려 다시 부착한 뒤 처음부터 측정합니다. " +
|
||||
(step.stopReason ?: "CH3 가 잡히는 위치가 없었습니다."))
|
||||
|
||||
step.action == AnchorAction.STOP -> AlignInstruction(
|
||||
"↑", "${offsetCm}cm 올리세요",
|
||||
"여기서는 측정하지 않습니다. 올린 뒤 좌우 맞추기로 넘어갑니다.",
|
||||
done = true)
|
||||
|
||||
else -> {
|
||||
// 지금 있는 자리는 방금 잰 위치다. 다음 자리는 호출부가 이미 정해 두었다.
|
||||
val from = step.record?.cm ?: nextCm
|
||||
val delta = nextCm - from
|
||||
when {
|
||||
delta > 0 -> AlignInstruction("↑", "${delta}cm 올리고 측정", null)
|
||||
delta < 0 -> AlignInstruction("↓", "${-delta}cm 내리고 측정", null)
|
||||
// 이미 그 자리다 — 확인 측정 한 번이 남았다. 이 화면에서 제일 많이 나오는
|
||||
// 질문이 "왜 방금 잰 자리를 또 재냐"라, 이유를 같이 적는다.
|
||||
else -> AlignInstruction("●", "여기서 한 번 더 측정",
|
||||
"옮겨 온 자리의 상태를 확인하는 마지막 측정입니다.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 진행 버튼 아래 한 줄 - **무엇을 모르고 넘어가는지** 말한다.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.medithings.vesiscan.ui.views.clinical
|
||||
|
||||
import com.medithings.vesiscan.managers.AnchorAction
|
||||
import com.medithings.vesiscan.managers.AnchorPosRecord
|
||||
import com.medithings.vesiscan.managers.AnchorStep
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* 정렬 화면 지시문 — **위치 이름이 화면에 나오면 안 된다.**
|
||||
*
|
||||
* 시험하는 사람이 "최적 위치"와 "부착 위치"를 계속 헷갈렸다(2026-09-09 현장). 둘 다
|
||||
* "위치"라는 이름의 숫자이고, 화면이 단계마다 다른 쪽을 크게 보여 줬기 때문이다.
|
||||
* 프로브를 잡은 사람에게 필요한 것은 지금 할 동작 하나다.
|
||||
*
|
||||
* 그래서 이 파일은 문구를 고정한다. 화면 문구 시험은 보통 과하지만, 여기서는 **헷갈림
|
||||
* 자체가 고치려던 결함**이라 문구가 요구사항이다.
|
||||
*/
|
||||
class AlignInstructionTest {
|
||||
|
||||
private fun rec(cm: Int) = AnchorPosRecord(
|
||||
cm = cm, nTrace = 11, nch = 2, ch3Mean = true, ch3Rate = 1.0,
|
||||
capFrac = 0.64, nchTrace = 2.0, nchSd = 0.0,
|
||||
)
|
||||
|
||||
private fun step(
|
||||
action: AnchorAction,
|
||||
atCm: Int,
|
||||
bestCm: Int? = null,
|
||||
searching: Boolean = true,
|
||||
stopReason: String? = null,
|
||||
) = AnchorStep(
|
||||
action = action, message = "", record = rec(atCm), bestCm = bestCm,
|
||||
anchorCm = bestCm?.let { it + 1 }, maxNch = 2.0, searching = searching,
|
||||
stopReason = stopReason,
|
||||
)
|
||||
|
||||
/** 화면에 절대 나오면 안 되는 말. */
|
||||
private val banned = listOf("최적", "부착 위치", "anchor")
|
||||
|
||||
private fun assertNoPositionWords(i: AlignInstruction) {
|
||||
val all = listOf(i.headline, i.sub ?: "").joinToString(" ")
|
||||
banned.forEach { w ->
|
||||
assertFalse("\"$w\" 가 지시문에 있으면 안 된다 — $all", all.contains(w))
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun `첫 측정 전에는 붙일 자리를 말한다`() {
|
||||
val i = instructionOf(null, nextCm = 0, offsetCm = 1)
|
||||
assertEquals("●", i.arrow)
|
||||
assertTrue(i.headline.contains("치골"))
|
||||
assertFalse(i.done)
|
||||
assertNoPositionWords(i)
|
||||
}
|
||||
|
||||
@Test fun `탐색 중에는 올리고 측정하라고만 한다`() {
|
||||
// 0cm 을 재고 다음은 1cm. 절대 숫자가 아니라 **차이**를 말해야 한다.
|
||||
val i = instructionOf(step(AnchorAction.MOVE_UP, atCm = 0), nextCm = 1, offsetCm = 1)
|
||||
assertEquals("↑", i.arrow)
|
||||
assertEquals("1cm 올리고 측정", i.headline)
|
||||
assertFalse(i.done)
|
||||
assertNoPositionWords(i)
|
||||
}
|
||||
|
||||
@Test fun `지나쳤으면 내리고 측정하라고 한다`() {
|
||||
// 3cm 에서 종료가 걸리고 best=1 이면 2cm 내려야 한다.
|
||||
val i = instructionOf(
|
||||
step(AnchorAction.MOVE_DOWN, atCm = 3, bestCm = 1, searching = false),
|
||||
nextCm = 1, offsetCm = 1,
|
||||
)
|
||||
assertEquals("↓", i.arrow)
|
||||
assertEquals("2cm 내리고 측정", i.headline)
|
||||
assertNoPositionWords(i)
|
||||
}
|
||||
|
||||
@Test fun `같은 자리면 한 번 더 재라고 하고 이유를 댄다`() {
|
||||
// 이 화면에서 제일 많이 나오는 질문이 "왜 방금 잰 자리를 또 재냐"다.
|
||||
val i = instructionOf(
|
||||
step(AnchorAction.MOVE_DOWN, atCm = 2, bestCm = 2, searching = false),
|
||||
nextCm = 2, offsetCm = 1,
|
||||
)
|
||||
assertEquals("●", i.arrow)
|
||||
assertTrue(i.headline.contains("한 번 더"))
|
||||
assertNotNull("이유가 없으면 또 물어본다", i.sub)
|
||||
assertNoPositionWords(i)
|
||||
}
|
||||
|
||||
@Test fun `확정되면 올리고 재지 말라고 한다`() {
|
||||
// 여기가 제일 중요하다. 올린 자리를 측정하면 설계상 "미달"이 떠서 되돌아간다.
|
||||
val i = instructionOf(
|
||||
step(AnchorAction.STOP, atCm = 2, bestCm = 2, searching = false),
|
||||
nextCm = 2, offsetCm = 1,
|
||||
)
|
||||
assertEquals("↑", i.arrow)
|
||||
assertEquals("1cm 올리세요", i.headline)
|
||||
assertTrue("측정하지 말라는 말이 빠지면 안 된다",
|
||||
i.sub!!.contains("측정하지 않습니다"))
|
||||
assertTrue("다음 단계를 말해야 멈추지 않는다", i.sub!!.contains("좌우"))
|
||||
assertTrue("확정은 초록이다", i.done)
|
||||
assertNoPositionWords(i)
|
||||
}
|
||||
|
||||
@Test fun `오프셋이 바뀌면 문구도 따라간다`() {
|
||||
// FINAL_OFFSET_CM 을 2 로 바꾸면 화면도 2cm 이라고 말해야 한다. 박아 두면 거짓이 된다.
|
||||
val i = instructionOf(
|
||||
step(AnchorAction.STOP, atCm = 1, bestCm = 1, searching = false),
|
||||
nextCm = 1, offsetCm = 2,
|
||||
)
|
||||
assertEquals("2cm 올리세요", i.headline)
|
||||
}
|
||||
|
||||
@Test fun `재부착은 아래로 내리라고 하고 사유를 붙인다`() {
|
||||
val i = instructionOf(
|
||||
step(AnchorAction.REATTACH, atCm = 3, searching = false,
|
||||
stopReason = "탐색 상한 4cm"),
|
||||
nextCm = 3, offsetCm = 1,
|
||||
)
|
||||
assertEquals("↓", i.arrow)
|
||||
assertTrue(i.headline.contains("아래"))
|
||||
assertTrue("사유가 없으면 같은 실수를 반복한다", i.sub!!.contains("탐색 상한"))
|
||||
assertFalse(i.done)
|
||||
assertNoPositionWords(i)
|
||||
}
|
||||
|
||||
@Test fun `모든 동작에 지시문이 있다`() {
|
||||
// action 이 늘어났는데 여기를 안 고치면 조용히 "올리고 측정"으로 떨어진다.
|
||||
assertEquals(4, AnchorAction.entries.size)
|
||||
AnchorAction.entries.forEach { a ->
|
||||
val i = instructionOf(step(a, atCm = 1, bestCm = 1, searching = false),
|
||||
nextCm = 2, offsetCm = 1)
|
||||
assertTrue("$a 에 문구가 없다", i.headline.isNotBlank())
|
||||
assertNoPositionWords(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user