跳转至

Lecture 10. Path Coverage 路径覆盖(旷课了)

先考虑不包含循环的情况(只有条件判断)。

就是对于 CFG,要从 起始节点(source node)终止节点(sink node) 的所有可能路径,都跑一遍。

也就是说,每一个 testcase,都是一个路径。每一个测试数据,也只包含一个测试点(路径)。然后设计能走这个路径的测试数据就好了。

包含循环的 CFG

对于包含循环的 CFG,只需要考虑两种路径:一次循环都没执行的路径、执行 $n(n>1)$ 次循环的路径。也可以差不多理解成执行零次和执行一次。比如下图中间那个 CFG,可以拆成三条路(一条是不执行循环的,另外两条执行循环的)

image-20221104133436160

与正则表达式结合

控制流可以用正则表达式表达。

正则表达式三种运算

  • 连接(concatenation,.),就是一个点,表示控制流图当中的一个节点(一段连续的代码)
  • 选择(selection,+),就是一个加号,表示控制流图当中做一个决定
  • 迭代(iteration,()*),就是一个括号外面套个星号,表示控制流图当中一段重复的

比如,上面那个 CFG,就可以用正则表达式描述成:1.2.(3.(4+5).6.2)*.7

由于由于可以把循环当作只执行一次或者不执行,于是这个循环就退化成了一个简单的条件选择语句。用 0 表示什么都不执行,那么 ()* 就可以用 (()+0) 代替,于是刚才的正则表达式可以化简成:1.2.((3.(4+5).6.2)+0).7

用正则表达式计算路径总数

神奇的地方来了。把化简后的正则表达式当中的 + 当成加号 $+$,. 当成乘号 $\times$,再把所有节点编号(包括 0)都当成数字 $1$,括号保留不变,然后直接计算这个表达式的值,就是路径总数了。

比如刚才那个正则表达式代表的路径总数就是:$1 \times 1 \times ((1 \times (1 + 1) \times 1 \times 1) + 1) \times 1 = 3$。

从正则表达式当中提取路所有路径

还是要看简化后的正则表达式 1.2.((3.(4+5).6.2)+0).7

对于表达式当中的每一个这种形式 pre.(x+y).suf,把加号 + 左右两边的东西提出来,拆成俩:pre.x.sufpre.y.suf。比如上面那个正则,就可以拆成 1.2.3.(4+5).6.2.71.2.0.7。左边那个由于还是 pre.(x+y).suf 的形式,所以还可以继续拆成 1.2.3.4.6.2.71.2.3.5.6.2.7。右边那个,由于 0 相当于啥都没执行,所以最后可以删去,变成 1.2.7

于是,这一个正则表达式可以提取出来三条不同的路径:1.2.3.4.6.2.71.2.3.5.6.2.71.2.7。跟之前计算出来的路径总数(三条)一致。

还有一个稍微复杂一点的例子。

正则表达式(化简后)是:1.(2+0).3.(4+(5.(6+0))).7,路径提取过程是,先把 pre.(2+0).suf 拆成如下的俩:

  • 1.2.3.(4+(5.(6+0))).7,然后这个拆出俩:1.2.3.4.71.2.3.5.(6+0).7
  • 后面那个又拆成俩:1.2.3.5.6.71.2.3.5.7
  • 1.3.(4+(5.(6+0))).7,然后这个拆出俩:1.3.4.71.3.5.(6+0).7
  • 后面那个又拆成俩:1.3.5.6.71.3.5.7

总计六个路径。

小结

路径覆盖没有去考虑每一个条件或者判定的取值,但是可以覆盖到所有的可能的组合产生的路径。

如果程序比较复杂,那么很难找出所有的路径。就算找出来了,测试的时候也要花好多时间。