一行从左到右分别万代表:
1.代码行号(空白代表分支显示不过来产生换行)
2.分支覆盖情况 3.该行调用次数 4.该行源代码以及行覆盖情况
中括号代表生成的一对子分支,+代表该子分支被覆盖,-代表该子分支未覆盖,但对应的另一分支被覆盖,#代表两个子分支均未被覆盖。
以if(condition)为例,如果该condition没有子条件,即不是其他条件"与"、"或"产生,那么会产生两个分支,即condition == true 和 condition == false,若只能满足condition == true 或false,则分支覆盖结果为[+ -]或[- +],如果多次调用时condition == true 或false都能满足,则分支覆盖结果为[+ +]。如果condition == true或false都不能发生,那么覆盖结果为[# #](虽然在最简单的条件下这个结果并不会发生)。
如果该行代码被覆盖到,则其底色为蓝色,没有被覆盖到,则底色为蓝色,若该行是上一行代码的续行,或为return 语句、class声明等,其底色为白色,代表不会进行检测。注意,没有被覆盖的行不会产生分支。
除了常见的逻辑判断外,还有很多产生分支的情况,并且难以完全覆盖,总结如下:
1. if (condition1) - elseif (condition2) - else [if(true)和if(false)除外]
2. for(init;condition/incre)/while(condition) [while(true)、while(false)和for(init;;incre)等没有条件判断除外]
3.条件运算符 condition ? exp1 : exp2
4.switch - case
5.多条件的与或。常见的为return (mulit-condition) ,例如return (x==0 || x==1)。
解析:可能存在操作没有权限、内存分配失败等情况。
1. new /delete 操作符
2. 文件操作:打开、关闭、输出等
例如
cv::FileStorage cameraSettings{std::string(camera_file), cv::FileStorage::READ};cameraSettings.isOpened()
cameraSettings.close()
以及fprintf等。
3.memcpy
如果宏/内联函数声明中自带判断,那么预处理时会在调用处原地展开,最好的做法是用普通函数代替宏/内联函数,这样不会在每个调用的代码段都产生分支。
1.char *到String类
String类的构造函数如下:(加粗部分含有判断)
basic_string(
const
_CharT* __s,
const
_Alloc& __a = _Alloc())
: _M_dataplus(_M_local_data(), __a)
{ _M_construct(__s, __s ? __s + traits_type::length(__s) : __s+npos); }
char *转String时会判断char *是否为nullptr,即自带一个判断。
2.String类到char *
与char *到String类相似。
即使成员函数内部没有判断,也没有返回值。
但是一般情况下只能使用测试样例实现对常见逻辑判断的覆盖,以及使用普通函数代替宏或者内联函数来提高分支覆盖率,其他情况下不能覆盖,只能给予说明。
if ((std::abs(p.arr[2]) < 1e-10)
&& (std::abs(p.arr[3]) < 1e-10)
&& (std::abs(p.arr[4]) < 1e-10)
&&(std::abs(p.arr[5]) < 1e-10))
该判断共四个子条件,但lcov显示共10个分支,包括四个子条件分别成立和不成立,共八个分支,外加总条件,即这些子条件的与的成立和不成立两个条件,共十个分支:
if ((!load_with_general_format) &&
loadCameraModelParametersWithSVFormat(
std::string(camera_file), camera_param))
{return camera;}
看起来平平无奇只有两个子条件,其实蕴藏杀机,衍生出了14个分支,分别为:
以及最后6个分支为函数loadCameraModelParametersWithSVFormat内部有三个if实现,即6个分支,共14个。