UT覆盖率

📅 2026/8/25 13:07:43
UT覆盖率
行覆盖 48.8%(6734/13800)- 分母 13800:整个项目里 lcov 认为可执行的代码行(不含空行、注释、纯声明)。- 分子 6734:测试跑起来的时候,实际被执行过至少一次的行。- 结论:一半多的可执行代码,你的测试从来没碰过。函数覆盖 54.2%(1379/2543)- 分母 2543:项目里的函数总数。- 分子 1379:被调用过至少一次的函数(哪怕只进去执行了第一行,也算覆盖)。- 结论:有 1164 个函数在整轮测试里一次都没被调用过。两个数的关系:函数覆盖率通常比行覆盖率高,因为函数被调过一次很容易达成,但函数内部的分支、错误处理路径往往没走到。所以 54.2% 的函数覆盖 48.8%的行覆盖,典型含义是:主流程调通了,异常分支和边界处理基本没测。分支覆盖率分支覆盖率问的是:每个判断的每条岔路,都走过了吗?行覆盖只要求这行被执行过,分支覆盖要求这个 if 的成立和不成立两种情况都发生过。举个例子:int divide(int a, int b) {if (b ! 0) {return a / b;}return -1;}return -1;}}return -1;}假设测试只调了 divide(10, 2):- 行覆盖:第 2、3 行执行了,第 5 行没执行 → 3/4 行,75%- 分支覆盖:b ! 0 这个判断只走过 true 一侧,false 一侧从没发生 → 1/2 分支,50%这就是分支覆盖的价值:它专门暴露没测的错误处理路径。这里 b 0 的除零保护根本没被验证过,而行覆盖率 75% 看起来还挺体面。Bazel 是 Google 开发的开源构建和测试工具。 它旨在管理具有复杂依赖关系的大型代码库支持多种语言和平台。 Bazel 自动化了编译代码、链接依赖项、运行测试和部署工件的过程。Bazel是Google在2015年开源的一款构建工具。一、gtest 最小例子假设我写了个加法函数想测它对不对。完整代码就这么几行#include gtest/gtest.hint add(int a, int b) // 被测的函数{return a b;}TEST(MyMathTest, one_plus_one) // 一个测试用例{EXPECT_EQ(add(1, 1), 2); // 我期望 add(1,1) 的结果是 2}跑起来输出是[] Running 1 test from 1 test suite.[ RUN ] MyMathTest.one_plus_one[ OK ] MyMathTest.one_plus_one (0 ms)[ PASSED ] 1 test.如果我把 add 写错成 return a - b;输出变成[ RUN ] MyMathTest.one_plus_onedemo.cpp:11: FailureExpected equality of these values:add(1, 1)Which is: 0 ← 实际算出来 02 ← 但我期望 2[ FAILED ] MyMathTest.one_plus_one (0 ms)gtest 的用法就这三条1. TEST(套名, 用例名) { ... } —— 大括号里放你的测试代码2. EXPECT_EQ(实际, 期望) —— 写你的期望不符合就报错3. 不用写 main() —— gtest 自动把所有 TEST 收集起来一个个跑会了这三条你已经会用 gtest 了。test_time.cpp 比这个例子只多两样东西- TEST_F 代替了 TEST —— 就是跑之前先做点准备工作的版本- 多了一套内存监控 —— 跟测时间无关是额外查有没有偷偷申请内存二、回到 test_time.cpp从头往下15-24 行引入需要的东西#include gtest/gtest.h // 15 gtest 本身必须有#include chrono // 17 C 标准库的时间工具#include cinttypes // 18 int64_t 这类固定宽度整数#include thread // 19 为了后面的 sleep#include osrf_.../memory_tools.hpp // 21 内存监控工具#include rcutils/error_handling.h // 23 为了拿错误信息#include rcutils/time.h // 24 ★ 被测对象就在这里第 24 行是重点这个文件测的就是 rcutils/time.h 里的东西。看一个测试文件先看它 include 了哪个被测头文件就知道它测谁。第 17 行的 chrono 也值得注意 —— 后面要拿 rcutils 的时间跟 C标准库的时间对比这是个常用招数用一个你信得过的东西来验证你要测的东西。26-31 行省字using osrf_testing_tools_cpp::memory_tools::on_unexpected_malloc;纯粹为了偷懒。有了这行下面写 on_unexpected_malloc 就行不用每次都写 osrf_testing_tools_cpp::memory_tools::on_unexpected_malloc那么长一串。没有任何功能跳过就行。33-51 行准备工作fixtureclass TestTimeFixture : public ::testing::Test{public:void SetUp() { 装内存监控 }void TearDown() { 拆内存监控 }};这段的作用只有一句话每个用例开跑前自动执行 SetUp() 里的东西跑完自动执行 TearDown() 里的东西。这里放的是内存监控的开关。所以整个效果是这个文件里 6 个用例每个跑的时候都开着内存监控。SetUp 里那 4 行 on_unexpected_malloc(...) 的意思是「要是抓到不该有的内存申请就让这个用例失败」。跟测时间无关先当它不存在往下看。54 行第一个真正的用例TEST_F(TestTimeFixture, test_rcutils_time_conversion_macros)SetUp()和TearDown()是框架自动调用不需要你手动调用但只针对 TEST_F 测试夹具TEST 普通测试用例不会执行它们。1. TEST_F测试夹具 TestFixtureclass MyTest : public ::testing::Test { protected: // 每个测试用例运行前自动执行 virtual void SetUp() override { // 初始化 } // 每个测试用例运行结束后自动执行 virtual void TearDown() override { // 清理资源 } }; TEST_F(MyTest, Case1) { // 测试逻辑 } TEST_F(MyTest, Case2) { // 测试逻辑 }export TMPDIR/zeekr_map/xindi/tmp export LOCK_DIR/zeekr_map/xindi/tmp/ut.lock export COVERAGE1 export INSTALL_ROOT/zeekr_map/xindi/install/aarch64le export LD_LIBRARY_PATH/zeekr_map/xindi/install/aarch64le/lib:/usr/lib:/lib mkdir -p $TMPDIR REPORT_DIR$PWD/reports TEST_TIMEOUT15 sh ./run-target-tests.sh rcutils 21 | tee run.log 跑之前先确认这个,省一趟 ls /zeekr_map/xindi/install/aarch64le/test/rcutils/ | head ls /zeekr_map/xindi/install/aarch64le/lib/ | grep dummy 第一条应该看到一堆 test_*;第二条应该看到 3 个 .so(不是 .a): libdummy_shared_library_in_load_paths.so libdummy_shared_library_in_run_paths.so libdummy_shared_library_preloaded.so