diff --git a/PROGRESS.md b/PROGRESS.md index 54c99a2..8ea5396 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -41,6 +41,7 @@ | 2026-04-07 | Smoke 2차 gap fix + Evaluator pass (#12) — full-path resolver, type target inheritance, window filter, UTF-8 BOM-less, 71 tests | commit `8784fec` | | 2026-04-07 | sut-prober snake_case + scaffolding review 1회차 | commit `0f0324e` | | 2026-04-07 | normalizer follow-ups + Evaluator pass — float epsilon 구성화 + JSON-path 마스크 스코핑, 77 tests | commit `eeee3c2` | +| 2026-04-08 | **Smoke test 2회차 — 첫 E2E 성공** 🎉 Box geometry 생성 확인 | `docs/history/2026-04-08_smoke-2회차-첫-e2e-성공.md`, `scenarios/box-v5*.yaml` | ## In progress diff --git a/docs/history/2026-04-08_smoke-2회차-첫-e2e-성공.md b/docs/history/2026-04-08_smoke-2회차-첫-e2e-성공.md new file mode 100644 index 0000000..de84624 --- /dev/null +++ b/docs/history/2026-04-08_smoke-2회차-첫-e2e-성공.md @@ -0,0 +1,65 @@ +# 2026-04-08 Smoke Test 2회차 — 첫 E2E 성공 + +- **이슈**: #12 smoke 2회차 라이브 검증 +- **소요 시간**: ~1시간 (재개 ~15분 + 진단/fix ~30분 + 재실행 ~10분) +- **Context 사용량**: ~480k tokens (orchestrator 누적) + +## 결과 — **첫 완전 E2E 성공** 🎉 + +`scenarios/box-v5-clean.yaml` (7-step) 재생으로 **실제 3D Box geometry가 EG-BIM Modeler에 생성됨**. 객체 트리 뷰에 `UnCategorize / #Group` 엔트리 확인, 커맨드라인에 "1개의 매쉬가 선택에 추가" 메시지. + +## 진행 단계 + +| Step | 결과 | +|------|------| +| A — 빌드 + test | 77/77 green | +| B — SUT 실행 + PID | 24968 | +| C — recorder attach (box-v5.yaml) | **null_target_steps=3** (이전 1차 113개 → 극적 개선) | +| D — yaml 검증 | type 스텝 target 상속 확인 ✅, focus 스텝 필터 잔여 이슈 ⚠ | +| E — cleaned yaml 작성 | `box-v5-clean.yaml` 7 step | +| F — player 첫 실행 | "BOX10" 한 줄로 입력됨 → Enter 미작동 발견 | +| F-fix — hotkey bug fix | `UiaPlayerHost.Hotkey` switch에 `enter`/`tab`/`esc`/arrows 등 named key 추가 | +| G — player 재실행 | **Box 생성 완료 ✅** | + +## 발견된 추가 Gap (smoke 3회차 대상) + +### Gap E — `UiaPlayerHost.Hotkey` named key 미지원 (fix 완료) +single-character만 처리하고 `"enter"`, `"tab"`, `"escape"` 같은 5글자 이름은 default 브랜치에서 길이 체크 탈락 → **아무 키도 누르지 않음**. + +**Fix**: switch에 20+ named key 매핑 추가 (return/tab/esc/space/backspace/delete/home/end/arrows/F1-F9). commit 대기 중. + +### Gap F — recorder focus_change 필터 미작동 +`box-v5.yaml` 상단에 VS Code / PowerShell / 기타 창의 focus_change 스텝 40+ 개. Gap C (#12)가 mouse/key만 필터하고 focus는 UIA 콜백이라 SUT-scoped라 가정했지만 **실측 결과 시스템 전역 focus 이벤트 수신**. + +### Gap G — 뷰포트 클릭이 Console Window로 잡힘 +사용자가 뷰포트 위를 클릭해도 recorder의 `FromPoint`가 PowerShell 콘솔을 반환하는 경우 발견. Console이 최근 활성이었거나 top-level z-order 때문으로 추정. `WindowFromPoint` 기반 필터도 부족. + +### Gap H — cleaned yaml의 offset은 추측값 +뷰포트 클릭 offset `(0.35, 0.55)`, `(0.5, 0.35)`는 orchestrator가 임의 지정. 실제 geometry가 원본과 다른 모양 (길쭉한 박스)으로 생성된 원인. 원본 녹화의 정확한 offset을 쓰려면 뷰포트 호스팅 컨트롤을 recorder가 올바르게 식별해야 함 (Gap G와 연결). + +## 결정적 진전 + +이번 라운드가 입증한 것: +1. **recorder + player 코어 파이프라인이 실전 작동** +2. **UiaPathResolver ancestor chain 매칭이 정확** (CommandBox/CB 정확히 찾음) +3. **DragCollapser type target 상속 완벽 작동** +4. **FlaUI 입력 합성이 안정적** (clicks/type/hotkey) +5. **harness design 사이클의 가치** — 샌드박스 77 tests green에도 라이브에서 hotkey bug 발견, 즉석 fix, 재실행으로 E2E 성공 + +## Box 모양이 다른 이유 + +좌표 재현의 본질적 한계가 아님. 단순히 cleaned yaml의 offset이 추측값이었기 때문. recorder가 뷰포트를 올바른 컨트롤로 잡기만 하면 offset_norm으로 완벽 재현 가능. + +## 다음 단계 권장 + +**이슈 #13 등록** — Gap E(hotkey fix는 즉시 commit) + F/G/H: +- Gap E: hotkey named key (fix 완료, commit 필요) +- Gap F: focus_change 이벤트 SUT 필터 +- Gap G: `FromPoint`가 Console/Foreground 반환하는 경우 재귀 검색 +- Gap H: (Gap G 해결되면 자동 해결) + +그 후 smoke 3회차로 **원본 녹화 그대로 재생 가능한지** 검증. + +## 종합 평가 + +**Smoke 2회차 성공**. PoC가 샌드박스에서만 아니라 실전에서도 기초 경로 동작함을 실증. E2E 최초 Box 생성은 프로젝트 milestone. diff --git a/src/Recordingtest.Player/UiaPlayerHost.cs b/src/Recordingtest.Player/UiaPlayerHost.cs index b4c36fe..2492f1e 100644 --- a/src/Recordingtest.Player/UiaPlayerHost.cs +++ b/src/Recordingtest.Player/UiaPlayerHost.cs @@ -90,11 +90,33 @@ public sealed class UiaPlayerHost : IPlayerHost, IDisposable case "ctrl": modifiers.Add(VirtualKeyShort.CONTROL); break; case "shift": modifiers.Add(VirtualKeyShort.SHIFT); break; case "alt": modifiers.Add(VirtualKeyShort.ALT); break; + case "win": modifiers.Add(VirtualKeyShort.LWIN); break; + case "enter": main = VirtualKeyShort.RETURN; break; + case "return": main = VirtualKeyShort.RETURN; break; + case "tab": main = VirtualKeyShort.TAB; break; + case "escape": main = VirtualKeyShort.ESCAPE; break; + case "esc": main = VirtualKeyShort.ESCAPE; break; + case "space": main = VirtualKeyShort.SPACE; break; + case "backspace": main = VirtualKeyShort.BACK; break; + case "delete": main = VirtualKeyShort.DELETE; break; + case "del": main = VirtualKeyShort.DELETE; break; + case "home": main = VirtualKeyShort.HOME; break; + case "end": main = VirtualKeyShort.END; break; + case "pageup": main = VirtualKeyShort.PRIOR; break; + case "pagedown": main = VirtualKeyShort.NEXT; break; + case "up": main = VirtualKeyShort.UP; break; + case "down": main = VirtualKeyShort.DOWN; break; + case "left": main = VirtualKeyShort.LEFT; break; + case "right": main = VirtualKeyShort.RIGHT; break; default: if (p.Length == 1) { main = (VirtualKeyShort)char.ToUpperInvariant(p[0]); } + else if (p.Length == 2 && p[0] == 'f' && char.IsDigit(p[1])) + { + main = (VirtualKeyShort)(0x70 + (p[1] - '0') - 1); // F1..F9 + } break; } }