系列說明 >> 本系列大部分 PR 來自私人或公司專案,部分程式碼、環境設定與實作細節不便公開。
我只能保證文中提到的 PR 與問題皆為真實案例,但會經過必要的匿名化與內容調整。
本系列主要分享問題如何被發現、背後的思考方式,以及解法如何形成,不會深入討論完整實作與部署細節,敬請見諒。
Change Ref: PR #7-feat: serialwrap RAW log integration,第一階段 Commit 07129e
Issue: TestPilot 已經可以自動執行 Test Case、產生 Verdict 與 Report,但每條 Case 對應的 DUT/STA UART Log 區間,仍然需要人工整理。
Root Cause: 原始 UART 資料、清理後的 Command Result 與最終測試判定,被混在同一份 Terminal Transcript 裡;人類可以忽略其中的控制字元與畫面效果,程式卻無法穩定地靠文字 Offset、行號或 Regex 建立可追溯關係。
Solution: 將 UART 資料拆成 RAW、Result、Decision 三層;TestPilot 在每條 Case 執行前後記錄原始 UART Record 的 Sequence,再產生 DUT/STA 可讀 Log 與逐 Case Line Reference。
Evidence: 完成 D009/D014 兩條 Test Case 的實機 Run;產生 15 KB/464 行 DUT Log、6.5 KB/247 行 STA Log 與逐 Case Log Reference;新增 32 個 Unit Tests,Full Suite 共 1513 Tests Pass。
上一篇最後,我們把 Command、Output 與 Result 的責任還給 serialwrap。
TestPilot 不需要再理解 BEGIN/END Marker,也不用替 serialwrap 清 Prompt、找 RC、猜 Command 到底跑完了沒有。
它只要提交 Command,再拿回結構化 Result。
看起來,這次總算可以專心跑測試了。
但當時的 TestPilot 不只是拿來執行幾條 Command。
我正在用它校正四百多條 LLAPI Test Case。
每一條 Case 都不是跑完、看到 PASS,然後雙手一攤:
好,下一條。
真正的校正流程還要確認:
Workbook 裡原本寫的預期是否正確。
待測設備 DUT 的 Northbound API 有沒有真的接受設定。
Getter、Runtime Config 與 Driver 狀態是否一致。
陪測設備 STA 有沒有在正確的 Band 上完成連線。
Verdict 到底是 Pass、Fail、Not Supported,還是只是目前測不到。
當時 DUT 與 STA 的 UART 上,究竟發生了什麼。
所以每做完一條 Case,我還要把:
Case ID
Workbook Row
Command
Verdict
DUT Log 區間
STA Log 區間
判定用的 Log 摘錄
整理進 Audit Report。
一條一條校正時,這件事還做得下去。
我打開 minicom Log,找到剛才的操作,複製幾段 Output,再把行號填回 Report。
等到 TestPilot 開始可以自動執行整批 Case、自己產生 Markdown 與 JSON Report 後,事情突然變得有點奇怪。
Agent 像保姆一樣地護著 TestPilot 跑測試。
TestPilot 負責依法判刑。
我負責在兩份 minicom Log 裡比對證據,所以我是書記官?
欸,等等,這不對,為什麼又是我受傷害?
自動化非常精準地把最無聊的部分保留給了我!!!
既然人工流程是:
Case 開始
→ 看一下 DUT/STA Log 現在到哪裡
→ 執行 Case
→ 再看一次 Log
→ 把中間那段填進 Report
那就讓 TestPilot 自己做。
Case 開始前,記住 minicom Log 的 Offset 或行號。
Case 結束後,再記一次。
中間那一段,不就是這條 Case 的 UART 證據嗎?
這個方法不用改 serialwrap,也不用新增什麼複雜 Protocol。
TestPilot 本來就知道 Case 何時開始、何時結束。
minicom 也已經有 -C Log。
兩邊接起來,看起來就結束了。
嗯嗯……這套做法看起來非常眼熟,好像在哪見過?
Day 1 的 serialwrap,正是靠記住 minicom Log Offset,再從持續成長的檔案裡切出 Command Output。
先把 Terminal Log 當成資料來源,再從裡面找出我要的部分。
現在只是把 Command Boundary 放大成 Case Boundary。
同一套思路。
規模升級。
看起來也很合理。
然後我開始認真看那份 Log。
minicom 畫面上的 Command、Output 與 Prompt 都很正常,人工核對也沒有問題。
但寫進 Log 的內容,可能還夾著 ANSI Escape Sequence、CR、Backspace、BEL 或 Bracketed Paste Code。
Terminal 會替人類處理這些顯示效果,程式拿到的卻只是原始 Byte。
所以同一份 Log,人工找行號可以,程式拿來算 Offset、切行或跑 Regex,就可能因為多一個控制字元而失準。
後來 serialwrap 的 file pull 也真的踩過同一類問題:畫面看起來沒什麼異常,Base64 中間卻混入 Terminal Control Code,最後直接 BASE64_DECODE_FAILED。
這不是再多補一支 Sanitizer 就能完全解決。
因為我要保存原始資料時,不能先把控制字元清掉;要讓程式解析時,卻又必須清理它們。
同一份 Log,同時被要求忠於現場、方便閱讀,最後還要負責 Pass/Fail。
我等於把三種互相衝突的責任,全塞回同一個檔案。
我好難啊~
把需求拆開後,其實只有三件事:
RAW
→ 當時真正經過 UART 的資料是什麼?
Result
→ 這條 Command 整理後得到什麼答案?
Decision
→ 目前的證據足不足以支持 Verdict?
新的 serialwrapd 已經直接持有實體 UART,所有經過 Broker 的 RX/TX 都會被逐筆記錄,包含 Sequence、COM、方向、來源、Command ID 與完整性資訊。
這份不做顯示清理的 UART 流水帳,就是 WAL。
RAW Layer 不需要理解畫面,只負責把現場留下來。
Result Layer 再依 Command Lifecycle 整理 Output,處理 Prompt、Command Echo 與 Terminal Noise,回傳 TestPilot 真正能使用的結構化 Result。
最後才由 TestPilot 的 pass_criteria 產生 Decision。
這樣即使 Result 看起來可以解析,原始紀錄若已經標記遺失或截斷,Decision Layer 也不應該裝作沒看到。
D2 已經證明三條 Command 只跑一條也能 PASS。
我不想再把它進化成:
三段資料只收到一段,也能 PASS。
serialwrap 知道每筆 UART Record 的 Sequence。
TestPilot 則知道一條 Case 何時進入 execute_with_retry(),又在什麼時候離開。
所以不需要再把 Case ID 寫進 DUT,也不用重新發明另一組 BEGIN/END Marker。
只要在 Case 前後各記一次目前的 Sequence:
seq_before
→ execute_with_retry()
→ seq_after
這段 Sequence Range 就是該 Case 對應的原始 UART 區間。
Run 結束後,再依 COM 把 Record 分成 DUT/STA,解碼成可閱讀的 Log,並建立 Sequence 到行號的 Mapping。
程式用 Sequence 綁定原始紀錄;Report 裡的 Line Reference 則讓我可以直接跳到對應位置查看。
這次先用 D009 與 D014 兩條 LLAPI Test Case 做實機驗證,最後產生:
DUT.log:15 KB,464 行
STA.log:6.5 KB,247 行
Markdown Report 開始帶有逐 Case 的 DUT/STA Log Reference,JSON Result 也加入:
dut_log_lines
sta_log_lines
另外新增 32 個 Log Capture Unit Tests,完整 Test Suite 共 1513 Tests Pass。
TestPilot 從這裡開始,不再只留下:
我判斷這條 Case 是 PASS。
它還能指回當時的 UART 原始紀錄,讓人重新檢查這個結論是怎麼來的。
看來我離成功當薪水小偷的日子,又更靠近了一些……(揮汗)
既然每次 Run 都要記錄自己的 Sequence Range,第一版當然希望從一份乾淨的 WAL 開始。
於是流程做得很直接:
Stop daemon
→ Clean WAL
→ Start daemon
→ Bind sessions
→ Run cases
→ Export logs
→ Stop daemon
舊資料不會混進來,Sequence 每次也都從頭開始。
我一跑 TestPilot,旁邊正在看的 minicom 就斷線了。
欸!!!重新打開再跑一次。
喂~~又斷!!!
見鬼,又有 Bug 了喔?
嘶~~~喔喔喔喔~我想到了!!!
現在 minicom 連的是 serialwrapd 提供的 PTY。
我只是想清掉這次 Test Run 的帳,卻順便把負責 UART、Session 與 Human Console 的 daemon 一起重啟了。
資料被拆成 RAW、Result 與 Decision 三層之後,下一個混在一起的東西也出現了:
重設這次 Run 的資料
≠
重設整套 UART Runtime
下一篇,清帳歸清帳,不要再拉著全家人一起連坐。
Have a nice day.