这段代码定义了一组用于单元测试的宏和辅助函数,主要目的是方便地进行各种类型的断言,并提供清晰的错误信息输出。
1. details::pretty(T)
函数:
这是一个模板函数,用于将各种类型的值转换为易于阅读的字符串表示形式。它使用模板特化来处理不同类型:
-
通用类型:
使用
std::stringstream
将值转换为字符串。 -
指针类型:
将指针转换为其整数地址的字符串表示。
-
Eigen 矩阵类型:
以格式化的方式输出矩阵,包括换行符,使其更易于阅读。
2. processTestResult(bool passed)
函数:
此函数根据测试结果输出信息。如果测试失败(passed
为 false
),则在 std::cerr
上输出 "failed!" 并调用 exit(-1)
终止程序。如果测试通过,则输出 "passed."。LCOV_EXCL_START
和 LCOV_EXCL_STOP
注释用于代码覆盖率工具,表示这部分代码(错误处理)可以从覆盖率分析中排除。
3. 测试宏:
这些宏定义了各种类型的断言,用于在测试中检查条件是否满足。如果断言失败,则会输出详细的错误信息,包括失败的条件、左右两边的值(如果适用)以及可选的描述信息。所有宏都使用 do { ... } while (false)
结构,以确保在任何上下文中都能正确使用,即使在单行语句中也是如此。
-
SOPHUS_TEST(passed, condition, descr, ...)
:测试一个条件是否为真。如果条件为假,则将
passed
设置为false
,并输出错误信息,包含条件表达式的字符串表示。 -
SOPHUS_TEST_EQUAL(passed, left, right, descr, ...)
:测试
left
是否等于right
。如果两者不相等,则将passed
设置为false
,并输出详细的错误信息,包括left
和right
的值及其字符串表示。 -
SOPHUS_TEST_NEQ(passed, left, right, descr, ...)
:测试
left
是否不等于right
。如果两者相等,则将passed
设置为false
,并输出错误信息。 -
SOPHUS_TEST_APPROX(passed, left, right, thr, descr, ...)
:测试
left
是否近似等于right
,使用阈值thr
进行比较。它使用Sophus::maxMetric
计算left
和right
之间的最大差值,如果差值大于等于thr
,则断言失败。错误信息包含left
、right
、thr
和计算出的差值。 -
SOPHUS_TEST_NOT_APPROX(passed, left, right, thr, descr, ...)
:测试
left
是否不近似等于right
,使用阈值thr
进行比较。如果差值小于thr
,则断言失败。错误信息类似SOPHUS_TEST_APPROX
。
4. 其他宏:
-
SOPHUS_STRINGIFY(x)
:将宏参数
x
转换为字符串字面量。
总结:
这段代码提供了一个简单的单元测试框架,通过宏定义简化了各种断言的编写,并利用 details::pretty
函数提供了清晰的错误信息输出,方便开发者快速定位测试失败的原因。其中,SOPHUS_TEST_APPROX
和 SOPHUS_TEST_NOT_APPROX
宏特别针对浮点数的比较,避免了直接使用 ==
运算符可能带来的问题。整个设计简洁有效,易于使用和扩展。
xml
#pragma once // 防止头文件重复包含
#include <iostream> // 引入输入输出流库#include <sstream> // 引入字符串流库
#include <sophus/types.hpp> // 引入Sophus库中的类型定义
namespace Sophus {namespace details { // 定义details命名空间
template <class Scalar, class Enable = void> // 定义模板类Pretty,模板参数为Scalar和Enableclass Pretty { public: // 公有成员 static std::string impl(Scalar s) { // 定义静态成员函数impl,用于将标量转换为字符串 std::stringstream sstr; // 定义字符串流 sstr << s; // 将标量写入字符串流 return sstr.str(); // 返回字符串 }};
template <class Ptr> // 定义模板类Pretty的特化版本,模板参数为Ptrclass Pretty<Ptr, std::enable_if_t<std::is_pointer<Ptr>::value>> { // 如果Ptr是指针类型,则启用该模板 public: // 公有成员 static std::string impl(Ptr ptr) { // 定义静态成员函数impl,用于将指针转换为字符串 std::stringstream sstr; // 定义字符串流 sstr << std::intptr_t(ptr); // 将指针的整数值写入字符串流 return sstr.str(); // 返回字符串 }};
template <class Scalar, int M, int N> // 定义模板类Pretty的特化版本,模板参数为Scalar, M和Nclass Pretty<Eigen::Matrix<Scalar, M, N>, void> { // 如果类型是Eigen矩阵,则启用该模板 public: // 公有成员 static std::string impl(Matrix<Scalar, M, N> const& v) { // 定义静态成员函数impl,用于将矩阵转换为字符串 std::stringstream sstr; // 定义字符串流 sstr << "\n" << v << "\n"; // 将矩阵写入字符串流 return sstr.str(); // 返回字符串 }};
template <class T> // 定义模板函数pretty,模板参数为Tstd::string pretty(T const& v) { // 将任意类型转换为字符串 return Pretty<T>::impl(v); // 调用Pretty类的impl函数}
} // namespace details
void processTestResult(bool passed) { // 定义函数processTestResult,用于处理测试结果 if (!passed) { // 如果测试未通过 // LCOV_EXCL_START std::cerr << "failed!" << std::endl << std::endl; // 输出失败信息 exit(-1); // 退出程序 // LCOV_EXCL_STOP } std::cerr << "passed." << std::endl << std::endl; // 输出通过信息}} // namespace Sophus
#define SOPHUS_STRINGIFY(x) #x // 定义宏SOPHUS_STRINGIFY,将宏参数转换为字符串
/// 测试条件是否为真。/// 如果测试失败,in-out参数passed将被设置为false。#define SOPHUS_TEST(passed, condition, descr, ...) \ do { \ if (!(condition)) { \ SOPHUS_DETAILS_FMT_LOG("Test failed: condition ``{}`` is false\n", \ SOPHUS_STRINGIFY(condition)); \ passed = false; \ } \ } while (false)
/// 测试left是否等于right。/// 如果测试失败,in-out参数passed将被设置为false。#define SOPHUS_TEST_EQUAL(passed, left, right, descr, ...) \ do { \ if (left != right) { \ std::string msg = SOPHUS_DETAILS_FMT_STR( \ "Test failed: {} (={}) is not equal to {} (={})\n", \ SOPHUS_STRINGIFY(left), Sophus::details::pretty(left), \ SOPHUS_STRINGIFY(right), Sophus::details::pretty(right)); \ msg += SOPHUS_DETAILS_FMT_STR(descr, ##__VA_ARGS__); \ SOPHUS_DETAILS_FMT_LOG( \ "{} (={}) is not equal to {} (={})\n", SOPHUS_STRINGIFY(left), \ Sophus::details::pretty(left), SOPHUS_STRINGIFY(right), \ Sophus::details::pretty(right)); \ passed = false; \ } \ } while (false)
/// 测试left是否不等于right。/// 如果测试失败,in-out参数passed将被设置为false。#define SOPHUS_TEST_NEQ(passed, left, right, descr, ...) \ do { \ if (!(left != right)) { \ SOPHUS_DETAILS_FMT_LOG( \ "Test failed: {} (={}) should not be equal to {} (={})\n", \ SOPHUS_STRINGIFY(left), Sophus::details::pretty(left), \ SOPHUS_STRINGIFY(right), Sophus::details::pretty(right)); \ passed = false; \ } \ } while (false)
/// 测试left是否近似等于right,误差在阈值范围内。/// 如果测试失败,in-out参数passed将被设置为false。#define SOPHUS_TEST_APPROX(passed, left, right, thr, descr, ...) \ do { \ auto nrm = Sophus::maxMetric((left), (right)); \ if (!(nrm < (thr))) { \ SOPHUS_DETAILS_FMT_LOG( \ "Test failed: {} (={}) is not approx {} (={}); {} is {}; nrm is " \ "{}\n", \ SOPHUS_STRINGIFY(left), Sophus::details::pretty(left), \ SOPHUS_STRINGIFY(right), Sophus::details::pretty(right), \ SOPHUS_STRINGIFY(thr), Sophus::details::pretty(thr), \ Sophus::details::pretty(nrm)); \ passed = false; \ } \ } while (false)
/// 测试left是否不近似等于right。/// 如果测试失败,in-out参数passed将被设置为false。#define SOPHUS_TEST_NOT_APPROX(passed, left, right, thr, descr, ...) \ do { \ auto nrm = Sophus::maxMetric((left), (right)); \ if (nrm < (thr)) { \ SOPHUS_DETAILS_FMT_LOG( \ "Test failed: {} (={}) is approx {} (={}), but it should not!\n\ nrm is {}\n", \ SOPHUS_STRINGIFY(left), Sophus::details::pretty(left), \ SOPHUS_STRINGIFY(right), Sophus::details::pretty(right), \ Sophus::details::pretty(nrm)); \ passed = false; \ } \ } while (false)