严正声明:本文系作者davidhopper原创,未经许可,不得转载。
说明:本文修改后的代码已上传到GitHub
网站Apollo项目
中。
Apollo使用Google Test框架撰写单元测试用例。如果某个测试用例通不过,仅借助日志信息,很难弄清楚详细的失败原因。本文介绍使用GDB调试单元测试用例的方法,可供各位同学撰写单元测试代码提供一定的帮助。
一、撰写单元测试用例
以RelativeMap中的单元测试用例进行说明。
首先在文件夹modules/map/relative_map/testdata/multi_lane_map
中增加如下测试数据文件:chassis_info.pb.txt
、localization_info.pb.txt
、left.smoothed
、middle.smoothed
、right.smoothed
。
接下来增加单元测试文件modules/map/relative_map/navigation_lane_test.cc
,内部代码如下(涉及到的Google Test框架知识不作介绍,大家自行上网查询):
#include "modules/map/relative_map/navigation_lane.h"
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "third_party/json/json.hpp"
#include "modules/canbus/proto/chassis.pb.h"
#include "modules/common/adapters/adapter_manager.h"
#include "modules/common/vehicle_state/vehicle_state_provider.h"
#include "modules/map/relative_map/common/relative_map_gflags.h"
#include "modules/map/relative_map/proto/navigation.pb.h"
#include "modules/map/relative_map/proto/relative_map_config.pb.h"
namespace apollo {
namespace relative_map {
using apollo::common::VehicleStateProvider;
using apollo::common::adapter::AdapterConfig;
using apollo::common::adapter::AdapterManager;
using apollo::common::adapter::AdapterManagerConfig;
using apollo::relative_map::NavigationInfo;
using apollo::relative_map::NavigationLane;
using apollo::relative_map::NavigationPath;
using nlohmann::json;
namespace {
bool GetNavigationPathFromFile(const std::string& filename,
NavigationPath* navigation_path) {
CHECK_NOTNULL(navigation_path);
std::ifstream ifs(filename, std::ios::in);
if (!ifs.is_open()) {
AERROR << "Failed to open " << filename;
return false;
}
std::string line_str;
while (std::getline(ifs, line_str)) {
try {
auto json_obj = json::parse(line_str);
auto* point = navigation_path->mutable_path()->add_path_point();
point->set_x(json_obj["x"]);
point->set_y(json_obj["y"]);
point->set_s(json_obj["s"]);
point->set_theta(json_obj["theta"]);
point->set_kappa(json_obj["kappa"]);
point->set_dkappa(json_obj["dkappa"]);
} catch (const std::exception& e) {
AERROR << "Failed to parse JSON data: " << e.what();
return false;
}
}
return true;
}
bool GenerateNavigationInfo(
const std::vector<std::string>& navigation_line_filenames,
NavigationInfo* navigation_info) {
CHECK_NOTNULL(navigation_info);
int i = 0;
for (const std::string& filename : navigation_line_filenames) {
auto* navigation_path = navigation_info->add_navigation_path();
if (!GetNavigationPathFromFile(filename, navigation_path)) {
continue;
}
navigation_path->set_path_priority(i);
navigation_path->mutable_path()->set_name("Navigation path " + i);
++i;
}
return navigation_info->navigation_path_size() > 0;
}
} // namespace
class NavigationLaneTest : public testing::Test {
public:
virtual void SetUp() {
common::adapter::AdapterManagerConfig adapter_conf;
RelativeMapConfig config;
EXPECT_TRUE(common::util::GetProtoFromFile(
FLAGS_relative_map_adapter_config_filename, &adapter_conf));
EXPECT_TRUE(common::util::GetProtoFromFile(
FLAGS_relative_map_config_filename, &config));
navigation_lane_.SetConfig(config.navigation_lane());
map_param_ = config.map_param();
navigation_lane_.SetDefaultWidth(map_param_.default_left_width(),
map_param_.default_right_width());
data_file_dir_ = "modules/map/relative_map/testdata/multi_lane_map/";
localization::LocalizationEstimate localization;
canbus::Chassis chassis;
EXPECT_TRUE(common::util::GetProtoFromFile(
data_file_dir_ + "localization_info.pb.txt", &localization));
EXPECT_TRUE(common::util::GetProtoFromFile(
data_file_dir_ + "chassis_info.pb.txt", &chassis));
VehicleStateProvider::instance()->Update(localization, chassis);
}
protected:
NavigationLane navigation_lane_;
NavigationInfo navigation_info_;
MapGenerationParam map_param_;
std::vector<std::string> navigation_line_filenames_;
std::string data_file_dir_;
};
TEST_F(NavigationLaneTest, GenerateOneLaneMap) {
navigation_line_filenames_.clear();
navigation_info_.Clear();
navigation_line_filenames_.emplace_back(data_file_dir_ + "left.smoothed");
EXPECT_TRUE(
GenerateNavigationInfo(navigation_line_filenames_, &navigation_info_));
navigation_lane_.UpdateNavigationInfo(navigation_info_);
EXPECT_TRUE(navigation_lane_.GeneratePath());
EXPECT_GT(navigation_lane_.Path().path().path_point_size(), 0);
MapMsg map_msg;
EXPECT_TRUE(navigation_lane_.CreateMap(map_param_, &map_msg));
EXPECT_EQ(1, map_msg.hdmap().lane_size());
}
TEST_F(NavigationLaneTest, GenerateTwoLaneMap) {
navigation_line_filenames_.clear();
navigation_info_.Clear();
navigation_line_filenames_.emplace_back(data_file_dir_ + "left.smoothed");
navigation_line_filenames_.emplace_back(data_file_dir_ + "right.smoothed");
EXPECT_TRUE(
GenerateNavigationInfo(navigation_line_filenames_, &navigation_info_));
navigation_lane_.UpdateNavigationInfo(navigation_info_);
EXPECT_TRUE(navigation_lane_.GeneratePath());
EXPECT_GT(navigation_lane_.Path().path().path_point_size(), 0);
MapMsg map_msg;
EXPECT_TRUE(navigation_lane_.CreateMap(map_param_, &map_msg));
EXPECT_EQ(2, map_msg.hdmap().lane_size());
}
TEST_F(NavigationLaneTest, GenerateThreeLaneMap) {
navigation_line_filenames_.clear();
navigation_info_.Clear();
navigation_line_filenames_.emplace_back(data_file_dir_ + "left.smoothed");
navigation_line_filenames_.emplace_back(data_file_dir_ + "middle.smoothed");
navigation_line_filenames_.emplace_back(data_file_dir_ + "right.smoothed");
EXPECT_TRUE(
GenerateNavigationInfo(navigation_line_filenames_, &navigation_info_));
navigation_lane_.UpdateNavigationInfo(navigation_info_);
EXPECT_TRUE(navigation_lane_.GeneratePath());
EXPECT_GT(navigation_lane_.Path().path().path_point_size(), 0);
MapMsg map_msg;
EXPECT_TRUE(navigation_lane_.CreateMap(map_param_, &map_msg));
EXPECT_EQ(3, map_msg.hdmap().lane_size());
}
} // namespace relative_map
} // namespace apollo
最后,在编译文件modules/map/relative_map/BUILD
中添加如下内容:
filegroup(
name = "testdata",
srcs = glob([
"testdata/**",
]),
)
cc_test(
name = "navigation_lane_test",
size = "small",
srcs = [
"navigation_lane_test.cc",
],
data = [
":testdata",
],
deps = [
":relative_map_lib",
"//modules/common",
"//third_party/json",
"@gtest//:main",
],
)
重新编译整个Apollo项目。
二、使用GDB调试测试用例
重新编译Apollo项目后,会在bazel-bin/modules/map/relative_map/
目录中生成可执行程序navigation_lane_test
,直接使用GDB在Docker中调试该程序即可,具体操作步骤如下:
1. 启动并进入Apollo项目的Docker
# 进入Apollo项目根目录(我的路径为:`~/code/apollo`,你需要修改为自己的路径)
cd ~/code/apollo
# 启动Apollo项目的Docker(注意:2.0以上版本在后面加上一个“-C”选项,
# 表示从中国服务器拉取镜像文件,以加快下载速度)
bash docker/scripts/dev_start.sh
# 进入Docker
bash docker/scripts/dev_into.sh
2. 在Docker内部使用GDB调试
gdb -q bazel-bin/modules/map/relative_map/navigation_lane_test
进入GDB调试界面后,使用l
命令查看源代码,使用b 138
在源代码第138行(可根据需要修改为自己所需的代码位置 )设置断点,使用r
命令运行navigation_lane_test
程序,进入断点暂停后,使用p navigation_lane_
查看当前变量值(可根据需要修改为其他变量名),使用n
单步调试一条语句,使用s
单步调试进入函数内部,使用c
继续执行后续程序。如果哪个部分测试通不过,调试信息会立刻告诉你具体原因,可使用bt
查看当前调用堆栈。