鸿蒙 PC Markdown 编辑器测试设计:字节语料覆盖文本保真
鸿蒙 PC Markdown 编辑器测试设计:字节语料覆盖文本保真
Markdown编辑器最危险的回归往往肉眼看不出来。BOM消失、CRLF变成 LF、末尾换行增加、非法 UTF-8被替换字符吞掉,预览可能完全相同,Git却显示整文件变化。测试语料必须同时覆盖 Markdown语义和底层字节,而不是只准备几篇“看起来复杂”的文章。
本文基于 OhMarkdown,说明如何组织 CommonMark、GFM、大纲、安全与字节格式语料,如何区分文件 fixture和运行时构造,以及怎样用十六进制比较验证未编辑保存。代码位于 https://gitcode.com/VON-/codex_md_oh,对应提交3a9146e。
语料按风险分类
项目保留四份可读 Markdown基线:
test-fixtures/markdown/ commonmark-baseline.md gfm-baseline.md outline-baseline.md security-baseline.mdCommonMark覆盖标题、强调、代码、链接、引用和列表;GFM覆盖表格、任务项、删除线和裸链接;大纲覆盖 ATX、Setext、普通正文井号和围栏伪标题;安全语料包含 script、javascript协议、iframe和允许的相对图片。
分类让失败可定位。若表格消失,先检查 GFM插件;若围栏标题进入大纲,检查解析状态机;若脚本进入 DOM,检查净化链。把全部语法塞进一个大文件,测试失败只知道“页面不同”。
可读 fixture 与字节 fixture 分工
普通.md文件适合代码审查、模拟器打开和截图,但 BOM、CRLF和 Mixed可能被开发者编辑器自动转换。关键字节语料由 ohosTest在运行时构造:
constoriginal='\uFEFF# 鸿蒙 PC\r\n\r\n'+'第一行\r\n第二行\r\n';awaitwriteRawText(testPath,original);字符串明确写出 U+FEFF与\r\n,测试文件每次在应用沙箱生成,不依赖 Git checkout的 autocrlf。Mixed语料同样显式:
awaitwriteRawText(testPath,'第一行\r\n第二行\n第三行\r');可读 fixture验证语法内容,运行时 fixture验证编码字节,两者不能互相替代。
CommonMark 基线保持最小
# CommonMark 基线 普通文本、**粗体**、*斜体*、`行内代码` 与链接。 > 引用内容 1. 有序列表 - 无序列表 ```ts const platform: string = 'HarmonyOS PC';最小基线不追求覆盖整个规范,而是锁住产品承诺的高频结构。每次遇到解析缺陷,新增一个最小复现。语料太大时,依赖升级产生差异难以判断是预期还是回归。 代码围栏本身要包含反引号和语言标记,验证预览不会把内部内容当 Markdown。链接目标使用稳定示例域名,但自动化不发网络请求。 ## GFM 基线验证扩展边界 ```md | 能力 | 状态 | | --- | --- | | 表格 | 完成 | | 任务列表 | 完成 | - [ ] 待处理任务 - [x] 已完成任务 ~~已废弃内容~~ https://example.com/gfm同一文件包含四项,可在模拟器一次观察。Playwright不只比较截图,而是断言 table、s、a和 checkbox结构,任务复选框 disabled与 checked状态同时验证。
后续扩充应加入对齐列、转义管道、嵌套任务和带括号 URL,但每个边缘项最好有独立断言。语料负责复现,测试负责解释预期。
大纲语料故意制造伪标题
正文中的 # 不是标题。 Setext 一级标题 ============== ```md # 代码围栏中的标题必须忽略只放正常 `# A`无法证明解析器会排除错误输入。正文内井号、Setext与围栏标题同时出现,才能覆盖行首规则、前瞻和围栏状态。 设备测试再加入中文并比较 UTF-16 offset: ```ts expect(headings[2].offset).assertEqual( content.indexOf('### 目标标题') );中文让 UTF-8字节与字符串索引分离,能发现坐标单位错误。
安全语料同时包含允许项
<script>window.fixtureScriptExecuted = true</script> [危险链接](javascript:alert('blocked')) <iframe src="https://example.com"></iframe> 安全测试不能只输入危险内容。相对图片是允许能力,净化后应保留安全 src和 alt。如果修复通过把所有标签属性删除,危险消失但功能也坏了。拒绝列表和允许列表必须同场验证。
还应增加 style、form、object、embed、事件属性、data URI与编码协议变体。每个新增渲染插件都要重新跑安全语料。
字节读取辅助函数
测试使用 Core File Kit读取全部字节转十六进制:
asyncfunctionreadBytesAsHex(path:string):Promise<string>{constfile=awaitfileIo.open(path,fileIo.OpenMode.READ_ONLY);try{conststat=awaitfileIo.stat(file.fd);constdata=newArrayBuffer(stat.size);constbytesRead=awaitfileIo.read(file.fd,data,{length:stat.size});constbytes=newUint8Array(data,0,bytesRead);letresult='';for(letindex=0;index<bytes.length;index+=1){result+=bytes[index].toString(16).padStart(2,'0');}returnresult;}finally{awaitfileIo.close(file);}}十六进制字符串便于 Hypium比较,不受文本解码影响。测试规模很小,字符串拼接成本可接受;大文件可比较哈希而不是生成两倍长度文本。
未编辑保存的黄金断言
constbeforeHex=awaitreadBytesAsHex(testPath);constopened=awaitreadUtf8Document(testPath);awaitwriteUtf8Document(testPath,opened.content,opened.format);expect(awaitreadBytesAsHex(testPath)).assertEqual(beforeHex);这是文本保真的核心:打开后不修改,以检测格式保存,字节完全一致。正文字符串比较无法证明 BOM和换行。
测试还断言内存正文不含 BOM、format标记 true、lineEnding为 CRLF。字节与语义双重断言能定位是读取模型还是序列化出错。
Mixed 语料验证明确转换
Mixed不要求未编辑后自动统一。测试指定目标 LF,保存后正文必须全部使用\n,再检测为 LF。另一个对称用例应指定 CRLF,断言没有\r\r\n。
UI层还需测试 Mixed修改后弹出策略对话框,取消不保存,LF/CRLF分别更新状态栏。服务测试验证转换函数,模拟器验证用户决策。
非 ASCII 与块边界
读取器64 KiB分块并使用 streaming TextDecoder。语料应让三字节中文或四字节emoji跨块边界:用填充字符把多字节序列首字节放在块末。fatal: true下若 stream配置错误会抛出,正确实现能跨块组合。
还应覆盖组合字符、代理对、空文件、只有 BOM、无末尾换行、多个末尾换行和超长单行。文本编辑器不能只测试中文常用字和英文。
非法 UTF-8 应拒绝
严格读取不应把非法字节替换为 U+FFFD后允许保存,因为原字节将不可恢复。测试应直接写入非法序列,断言readUtf8Document失败、当前编辑会话不被替换、文件未修改。
这类语料无法可靠保存为普通 Markdown源码,应以 Uint8Array在测试中构造。错误信息不应打印文件正文。
文件变化和短读语料
读取器比较实际总字节与 stat.size。要验证“读取期间变化”,需要测试替身或并发修改文件,断言不返回半文档。写入则可注入目标目录删除,验证备份保留。
故障语料不是内容文件,而是文件系统状态:不存在目录、无权限、短写、备份清理失败。测试计划应把内容矩阵与故障矩阵交叉,而不是只列语法。
鸿蒙 PC 应用中的格式语料
下图来自模拟器,格式测试文档进入真实编辑器,状态栏显示 UTF-8与换行。应用画面证明 fixture可由用户路径打开;字节断言由设备测试完成。
文章和报告中的截图应使用无敏感内容的专用语料,不用真实用户文件。图片只记录应用状态,不作为字节证明。
版本管理规则
可读 fixtures同步仓库,成为代码评审的一部分;技术文章目录本地忽略,截图不影响测试。字节语料生成逻辑同步测试代码。修改 fixture必须说明预期变化,不能在依赖升级时直接接受全部新快照。
如果未来引入视觉快照,应对字体和平台差异设置容忍,结构断言仍是主要契约。测试文件名称表达语义,不用test1.md。
当前边界
现有四份可读语料仍较小,非法 UTF-8、块边界、CRLF目标归一和两千项目录规模需要继续补充。设备字节测试当前三项,尚未形成完整参数化矩阵。
结语
Markdown测试语料必须覆盖看得见的语法和看不见的字节。OhMarkdown用分类文件锁定 CommonMark、GFM、大纲与安全,用运行时构造锁定 BOM、CRLF、Mixed和故障状态,再以十六进制比较验证保真。
语料的价值不在数量,而在每一份都对应明确风险、可重复预期和真实运行层。这样鸿蒙 PC编辑器升级解析器或 SDK时,才能知道改变的是排版,还是用户文件本身。
每次线上或内部试用发现新的格式异常,都应先把最小字节复现固化,再修改实现;没有进入语料库的修复,很容易在下一次工具链升级时重新出现。
