fix(tui): 单键序列被 [pscustomobject] 拆成字符串(驱动器改 List[string])

根因:`[pscustomobject]@{ Script = $scriptKeys }` 里,**单元素数组会被 PowerShell 拆成字符串**,于是 `$Driver.Script[0]` 取到的是字符串首字母 —— @('Esc') 变成 'E',键名不再匹配任何分支,循环继续往下读,最后"序列用尽"报错。

这个坑只在"恰好一个键"时出现,所以块 ② 当时的断言(两键序列按顺序消费)看不见它;而菜单循环那块因此连着两次"看着对、跑起来不对"。修法是改用 List[string]:不会被拆、可按索引取、两个版本行为一致。

补一条正是为它立的断言:单键序列必须整键返回(不是首字母)。这类"只在边界数量上出现"的错,只能靠把边界数量本身写进断言来防。

验收:test.ps1 9/9 全绿(5.1 与 7)。
This commit is contained in:
Shuery committed 2026-09-27 16:11:17 +08:00
1 parent 347959d677
commit 6315c72f69
2 files changed
+12 -6

No files matched your search

+9 -6
View File
@@ -22,12 +22,15 @@
throw '指定了 -NonInteractive 但没有给出 -Script:没有可用的输入来源。'
}
# 先把数组算好,再放进哈希。
# 两个都踩过的坑:① `if` 是**语句**不是表达式,写在哈希字面量的值位置上会得不到结果;
# ② 不能写 @($Script) —— $Script 为 $null 时 @($null) 会造出**一个含 $null 的单元素数组**,
# 于是"空序列"被当成"有一个键",读者静默返回 $null 而不报错(而且两个 PowerShell 版本表现
# 还不一样:实测 7 静默返回、5.1 抛错)。所以显式分岔,让"没有序列"就是 0 个元素。
$scriptKeys = if ($Script) { @($Script) } else { @() }
# 用 List[string] 而不是数组。三个坑都踩过:
# ① `if` 是**语句**不是表达式,写在哈希字面量的值位置上会得不到结果;
# ② @($null) 会造出**一个含 $null 的单元素数组**,于是"空序列"被当成"有一个键";
# ③ **单元素数组存进 [pscustomobject] 会被拆成字符串** —— 于是 $Driver.Script[0] 取到的是
# 字符串的首字母(@('Esc') 变成 'E'),键名不再匹配、循环继续读、最后序列用尽。
# 这个坑只在"恰好一个键"时出现,所以只跑多键序列的断言看不见它(实测栽过一次)。
# List[string] 不会被拆,也能按索引取,两个版本行为一致。
$scriptKeys = [System.Collections.Generic.List[string]]::new()
foreach ($entry in @($Script)) { if ($entry) { $scriptKeys.Add([string]$entry) } }
return [pscustomobject]@{
Script = $scriptKeys
+3
View File
@@ -1447,6 +1447,7 @@ Test-Case '按键归一化与输入缝:映射可无控制台测,序列用尽
# 驱动:按序列顺序消费(这一条曾经因为 Script 被折进注释而静默变成"没有序列")
$driver = New-BakNRetInputDriver -Script @('Down', 'Enter')
Assert-Equal 2 $driver.Script.Count '驱动里应当有两个键'
# 单键序列:单元素数组会被 [pscustomobject] 拆成字符串,于是 [0] 变成首字母 —— 这条断言就是 # 为那个坑立的(块 ② 当时只覆盖了两键序列,所以它溜过去了)。 $single = New-BakNRetInputDriver -Script @('Esc') Assert-Equal 'Esc' (Read-BakNRetKey -Driver $single) '单键序列必须整键返回(不是首字母)'
Assert-Equal 'Down' (Read-BakNRetKey -Driver $driver) '第一个键'
Assert-Equal 'Enter' (Read-BakNRetKey -Driver $driver) '第二个键'
@@ -1520,6 +1521,8 @@ Test-Case '菜单状态机:绕回、空格切换、确认/取消、空菜单
Assert-Equal 'cancel' (Update-BakNRetMenuState -Items $empty -Key 'Esc').Action '空菜单仍然能退出'
}
# ============================================================================
Remove-Item -LiteralPath $sandbox -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item -LiteralPath $itemSandbox -Recurse -Force -ErrorAction SilentlyContinue