4. 白盒测试方法论:代码覆盖、分支覆盖、路径覆盖、条件覆盖、MC/DC覆盖

好,咱们进入白盒测试的核心部分。说实话,很多做ADAS硬件的工程师,一听到「覆盖」两个字就头大。我刚开始带项目时也这样,觉得这东西是软件测试才玩的。直到有一次,一个雷达信号处理模块在极端场景下出了bug,我们花了整整两周才定位到问题——原来是一段条件判断代码,在特定输入组合下从未被执行过。

从那以后,我彻底明白了:白盒覆盖分析,是硬件逻辑验证的照妖镜。你想想看,代码写得再漂亮,如果有一半的分支从来没跑过,你敢说它可靠吗?

4.1 代码覆盖(Statement Coverage)

这是最基础的一层。说白了,就是看你的测试用例,有没有把每一行代码都执行到。

举个例子,一段简单的Verilog代码:

always @(posedge clk) begin
    if (valid) begin
        data_out <= data_in + 1;  // 语句A
    end else begin
        data_out <= data_in;      // 语句B
    end
    flag <= 1'b1;                 // 语句C
end

如果测试用例只给了 valid = 1,那么语句B就永远没机会执行。代码覆盖就是统计这种「漏网之鱼」。

我的经验:代码覆盖是底线,但别迷信它。我曾经见过一个项目,代码覆盖率达到100%,但核心功能还是出了问题。为什么?因为代码覆盖只关心「执行了没有」,不关心「执行得对不对」。

小技巧:在仿真工具里,代码覆盖通常默认开启。我建议你每次跑回归测试时,都把覆盖率报告导出来看一眼。养成习惯,比事后补测试省事得多。

4.2 分支覆盖(Branch Coverage)

分支覆盖比代码覆盖更进一步。它不光看代码有没有执行,还要看每个判断点的「真」和「假」两个分支,是不是都跑过了。

还是刚才的例子:

if (valid) begin
    // 真分支
end else begin
    // 假分支
end

分支覆盖要求:valid = 1valid = 0 两种情况都要覆盖到。这听起来简单,但在复杂的条件嵌套里,很容易漏掉。

我记得有一次做摄像头ISP模块的测试,一个 if-else if-else 结构嵌套了四层。我们一开始只覆盖了最常见的两条路径,结果在某个罕见场景下,第三条路径里的一个寄存器赋值错了,导致图像出现条纹。嗯,从那以后,我对分支覆盖的要求就严格多了。

覆盖类型 要求 ADAS场景举例
代码覆盖 每行代码至少执行一次 所有赋值语句都跑过
分支覆盖 每个分支点真假都覆盖 if/else、case的每个分支

4.3 路径覆盖(Path Coverage)

路径覆盖,是分支覆盖的「升级版」。它要求覆盖从函数入口到出口的所有可能路径。

你想想看,一个模块里可能有多个判断点,每个判断点有两个分支。组合起来,路径数量是指数级增长的。比如:

if (a) { ... }   // 分支1
if (b) { ... }   // 分支2
if (c) { ... }   // 分支3

这里就有 2³ = 8 条路径。如果代码里有循环,路径数更是爆炸。

注意:路径覆盖在实际项目中很难做到100%。我个人习惯是:对关键模块(比如刹车控制、转向控制)要求路径覆盖达到90%以上,对普通模块则放宽到70%。

为什么会这样?因为ADAS系统里,有些路径是「不可能」出现的。比如,车速为0时,某些碰撞预警路径就不会触发。强行覆盖所有路径,反而会引入无效测试。

4.4 条件覆盖(Condition Coverage)

条件覆盖关注的是每个「原子条件」的真假。注意,它和分支覆盖不一样。

看这个例子:

if (A && B) begin
    // 动作
end

分支覆盖只要求 A && B 整体为真和为假各一次。但条件覆盖要求:A = trueA = falseB = trueB = false 这四种情况都要出现。

我遇到过的一个坑:某个ADAS系统的车道保持模块,条件判断是 (speed > 60) && (lane_detected)。测试时只给了 speed=80, lane_detected=1speed=40, lane_detected=0 两个用例。条件覆盖看起来是100%了?不对!因为 speed=80, lane_detected=0 这个组合从来没测过,而实际场景中,摄像头被遮挡时就会触发这个条件。

避坑指南:我曾经因为条件覆盖没做全,导致一个雷达融合模块在「目标有效但置信度低」的场景下误触发。后来我养成了一个习惯:写测试用例时,把每个条件的真/假单独列出来,逐个检查。

4.5 MC/DC覆盖(Modified Condition/Decision Coverage)

MC/DC,这是白盒测试里的「天花板」了。DO-178C航空标准里,对最高安全等级(Level A)的软件,就要求MC/DC覆盖达到100%。ADAS系统虽然还没到那个级别,但趋势是越来越严。

MC/DC的核心要求是:

  1. 每个条件(原子条件)的真假都要出现
  2. 每个条件都能独立影响决策结果

什么叫「独立影响」?就是改变这个条件,其他条件不变,决策结果会跟着变。

还是用 if (A && B) 举例:

A B 结果 说明
1 1 1 基准
0 1 0 A独立影响
1 0 0 B独立影响

你看,只需要3个用例,就能满足MC/DC。但如果用路径覆盖,需要4个用例。这就是MC/DC的巧妙之处——用最少的用例,达到最高的覆盖质量。

我的建议:对于ADAS系统中的安全关键模块(比如AEB自动紧急制动、LKA车道保持辅助),我强烈建议引入MC/DC覆盖。虽然测试用例会多写一些,但换来的是「每个条件都被独立验证过」的信心。

4.6 五种覆盖的对比与选择

说了这么多,你可能想问:到底该用哪种?

我个人习惯是这样的:

  • 日常回归测试:代码覆盖 + 分支覆盖,够用
  • 关键模块验证:条件覆盖 + 路径覆盖(关键路径)
  • 安全等级要求高的模块:MC/DC覆盖

记住一个原则:覆盖不是目的,质量才是。别为了追求100%的覆盖率,写一堆没意义的测试用例。我见过有人为了凑MC/DC,硬造出一些现实中永远不会出现的输入组合——这反而浪费了时间。

嗯,最后说一句。白盒覆盖分析,工具只是辅助,真正重要的是你对代码逻辑的理解。工具告诉你「这里没覆盖到」,你得能判断出「这个分支是不是真的需要覆盖」。这个判断力,靠的是经验,靠的是对ADAS系统场景的深刻理解。

下一章,咱们聊聊具体的覆盖分析工具和实战案例。到时候我会拿一个真实的雷达信号处理模块出来,手把手带大家做一遍覆盖分析。