embedded-host-platform

01 用真实字节流验证,而不是只信模拟

问题

开发前期没有硬件,所有代码都在模拟模式下跑通:模拟设备产生数据,经协议编码, 写进一个内存回环通道,再读出来解码。几百个测试全绿。

回环通道与串口实现同一个接口(connect / send / receive),按理说换成串口上层不必改。 但接口只约定了方法,没有约定行为。回环通道有三个串口没有的性质:

性质 回环通道 真实串口
消息边界 一次 send() 对应一次 receive() 没有边界:几帧可能一次读出,一帧可能分两次读出
应答时机 写进去立即能读回 设备要过一段时间才回应
失败方式 不失败 丢字节、插入干扰字节、比特翻转

上层代码在模拟下正确,只是因为无意中依赖了这些性质。

暴露出来的三个缺陷

第一次接上真实字节流(当时用 WSL + socat 造的一对虚拟串口,另一端是用 Python 写的虚拟设备):

  1. 订阅后收不到数据,也没有任何报错。 接收端把每次 receive() 的返回值整个交给解码器, 隐含假设”一次读取恰好一帧”。真实字节流里常常一次读出多帧,解码器报长度错误, 上层的容错逻辑把它计入错误数后继续——界面表现为”订阅成功、一直没数据”,现象离根因很远
  2. 下发命令时程序崩溃。 设备管理器发送命令后立即读应答,这在回环里成立; 真实设备不会瞬间回应,读到空字节,解码器抛出未捕获的异常
  3. 虚拟设备自己的重同步把整个缓冲区删了。 为了让虚拟设备”像真的固件一样不共享上位机代码”, 它的拼帧逻辑是另写的一份,写的时候没有对照已测过的实现

决定

模拟能验证功能逻辑,不能验证对传输介质的假设。 凡是与介质性质相关的代码路径—— 帧边界、时序、延迟、失败方式——必须在真实字节流上验证过,模拟通过不构成证据。

落到代码上:

结果

代价与后续

模拟模式没有被废弃:界面开发、业务逻辑、大部分测试仍然在它上面跑,因为它快、确定、不依赖任何东西。 变化在于不再把它当作介质相关代码的证据。

同一类问题后来又出现过一次,形式不同:硬件模式的装配代码绕过了统一注册入口, 入口里顺带做的事(订阅报警、设置连接状态)因此丢失,只在硬件模式下表现出来。 根因同样是”两条路径以为自己等价、其实不等价”。处理方式是补一组同时对两条装配路径断言的测试。