青雲的博客

Article

我让 Codex 在 Windows 虚拟机里替我测了一晚上

· 10 分钟阅读

10 月 20 日下午,留白发了 0.6.0,这是它的第一个 Windows 版。发布说明里我写的是:自动化测试已经在 Windows 上通过,还没有经过充分的实机测试。验证记录写得更直白:Windows 真机,发版时未测试。

我手上没有 Windows 电脑。当晚 9 点,我让 Codex 用它的内置浏览器连上一台 Windows 11 云桌面,照着清单去点。机器是 Windows 11 Pro,10.0.26200,x64,装的是 0.6.0。48 项用例,一直跑到后半夜。

结果是 36 项通过,3 项失败,6 项部分验证,3 项未测。

给 Codex 定的规矩

开跑之前我给它定了几条。

先核对环境:系统版本、About 里的版本号、安装路径、进程。新版本从 GitHub 下载安装包,在 Windows 这边校验 SHA-256,再覆盖安装。

测重启、单实例这类会动数据的项之前,先备份用户数据;测完,把改过的设置改回去。

每一项只记四种结论:pass、fail、partial、not_tested。每个结论都要配截图。最后出一份报告,写清复现步骤,再标出源码里可疑的位置。

Mac 上的单元测试、模拟 Win32 的浏览器测试、以前的 CI 记录,都不算 Windows 通过。这条是专门写给它的。留白有一个 check-windows 检查,在 Chrome 里把 navigator.platform 改成 Win32,查快捷键提示和 F8。Windows 专属的 Go 测试在 Mac 上只能确认能编译。这些都过了,可它们证明不了装好的程序在 Windows 里怎么跑。

最后一条:它只测和诊断,不改代码。修不修、怎么修,我来定。

Codex 的测试报告和 Windows 11 云桌面里的留白:左边是报告摘要,48 项里 36 通过、3 失败、6 部分验证、3 未测,列出 P1 打开文稿可能产生两个独立实例;右边是正在测的测试文稿

报告最后一句是:代码未修改,唯一的云桌面连接已保留。

中文界面下,双击会多开一个英文留白

报告里排第一的 P1 是这个:界面切成中文以后,从资源管理器双击一个 .md,会再开一个英文的留白,两个进程同时跑。

原因在应用名。MyGo 用应用名给三样东西命名:单实例锁、PathUserData、WebView2 的配置目录。留白把应用名设成了界面语言里的名字,中文叫「留白」,英文叫「Liubai」。切了语言再打开文稿,新进程算出另一个名字,拿到的是另一把锁。

另一个失败项是同一个原因:重启后语言变回系统语言。Windows 上 MyGo 把选好的语言存在 PathUserData 的 locale 文件里。启动时先在 %AppData%\Liubai 读到简体中文,应用名就变成了「留白」,再去 %AppData%\留白 下面找,找不到了。

修法是 macOS 以外,应用名固定为 Liubai,只有窗口标题跟着语言变:

func appNameFor(goos string, msgs locales.Messages) string {
	if goos == "darwin" {
		return msgs.BMenuAppName()
	}
	return "Liubai"
}

第二次启动交出文件之前,先调用 AllowSetForegroundWindow,不然已有的窗口到不了前台,只会在任务栏闪一下。

这么改有代价,我认了:用中文界面跑过 0.6.0 的人,升级后窗口位置和「最近打开」会重置一次。文稿不受影响,工作区一直在 %AppData%\Liubai。

第 3 个失败是夜墨主题下右上角的窗口按钮还是黑的,按钮颜色跟随 MyGo 的 Theme.SetSource,没设置就跟随系统。三处一起修进了 0.6.1。

0.6.1 发出来以后,我让 Codex 在同一台机器上定向复测 16 项,全部通过。升级前后 6 份文稿的 SHA-256、18 份草稿的正文都没有变。

点「退出」,LibreOffice 崩了

Cleanzo 的 Windows 版也走这套流程。0.2.0 测出一个 P1:在系统状态页点「退出」关 LibreOffice,LibreOffice 直接崩溃,弹出崩溃报告,没保存的测试文稿丢了。

Cleanzo 0.2.0 的系统状态页,点「退出」后 LibreOffice 弹出崩溃报告

最早的退出只发 WM_CLOSE,不强杀进程,想让应用自己问要不要保存。问题是它发给了这个进程的所有顶层窗口,隐藏的也算。LibreOffice 有一些隐藏的内部窗口,不处理 WM_CLOSE,交给 DefWindowProc 以后就被销毁了。应用还没来得及弹保存询问,先崩了。

修法是换成标题栏 X 和 Alt+F4 发的那条消息,而且只发给用户能关的窗口:

// WM_SYSCOMMAND / SC_CLOSE,和点标题栏的 X 一样
postMessage.Call(uintptr(hwnd), 0x0112, 0xf060, 0)

// 只发给可见、没有 owner、不是工具窗口、没被 DWM 隐藏的顶层窗口
if owner == pid && regularWindow(hwnd) && postWindowClose(hwnd) {
	sent = true
}

找不到这样的窗口就什么也不做,不回退到别的窗口,也不强制结束进程。我还补了一个 Windows 上跑的测试:起一个子进程,带一个像保存询问那样关不掉的主窗口、一个隐藏的辅助窗口、一个工具窗口,断言只有主窗口收到了关闭请求。0.2.1 带着这个修复发出去,0.2.2 复测这条过了。

回收站里是空的

0.2.0 的发布说明里有一句:移到回收站时绝不会悄悄变成永久删除。

0.2.3 我升级到 mygo 0.6.2,改用框架新的 TrashItem。发布说明是这么写的:文件放不进回收站时,比如超过回收站容量,会直接提示「无法移到回收站,已保留原文件」,不再在清理中途弹出「是否永久删除」。

Codex 测这一版时,把回收站容量调到 1 MB,再把一个 128 MiB 的测试样本移进回收站。结果原路径没了,回收站里也是空的。

原因是 TrashItem 只在 Shell 一开始就说「这个不能进回收站」的时候拦。文件比回收站的 MaxCapacity 还大时,Shell 先说可以,后面发现放不下,又因为不允许弹界面,就直接永久删除了。回收站默认容量约为磁盘的 5%,在大文件页移几 GB 的文件就会碰上。

提示是写了,实际没走到。0.2.4 退回 0.2.0 到 0.2.2 用过的 SHFileOperationW,带上 FOF_WANTNUKEWARNING,Windows 要永久删除之前一定先问。移动之前再加一道预检:读这个卷在注册表 BitBucket 下的 NukeOnDelete 和 MaxCapacity,放不下就直接提示「无法移到回收站,已保留原文件」,根本不去调 Shell。0.2.3 撤下了。框架这边我也给 MyGo 提了两个 PR(#629、#630),还在等合并。

跑完一遍,才知道 skill 该写什么

留白第一轮跑完,我把这套流程整理成了一个 skill,叫「Windows 虚拟机测试」。之后留白 0.6.1、0.6.2,Cleanzo 0.2.0、0.2.2、0.2.3 都是用它测的。

里面写的大多是开跑前我一条都想不到的事。远程分辨率选的是 1366×768,实际画面是 1364×768。小写的 ctrl+k 会被当成 Win+K。复制粘贴只能走屏幕键盘。

我还专门写了一句:不要默认测 48 项。后面测 Cleanzo,四轮计划分别是 15、7、12、6 项。

所以现在我只把跑通过的流程写成 skill,没跑过的先不写。

留白这几轮还有不少没覆盖到:全新安装和卸载、版本之间的自动更新、Windows 10、ARM64、多显示器、OneDrive 同步目录、缺少 WebView2 的精简系统。这些在报告里都还标着未测。

留白:https://github.com/echoVic/liubai Cleanzo:https://github.com/echoVic/cleanzo

Keep Reading

相关文章

评论