欢迎光临
我们一直在努力

为 libevent 贡献代码:checkpatch 代码风格检查与 tinytest 回归测试实践指南

为 libevent 贡献代码:checkpatch 代码风格检查与 tinytest 回归测试实践指南

【免费下载链接】libevent Event notification library 【免费下载链接】libevent 项目地址: https://gitcode.com/gh_mirrors/li/libevent

本篇指南以 CONTRIBUTING.md 为骨架,系统讲解向 libevent(事件通知库)提交代码的完整协作流程:如何遵循项目代码风格、如何用仓库自带的 checkpatch.sh 脚本对 git 提交、补丁和单个文件做自动化风格检查,以及如何在 test/regress_*.c 中编写单元测试并通过 make verify 跑通全量回归。读完本文,你将掌握向 libevent 贡献高质量补丁的"提交前自检清单",并能独立定位测试用例的挂载位置与运行方式。

一、贡献流程总览

向 libevent 提交代码通常遵循以下路径:

  • 遵循代码风格:先"环顾四周"(just look around)观察既有代码的写法,再借助 ./checkpatch.sh 自动化校验。
  • 为改动编写单元测试:将新测试写入 test/regress_{MORE_SUITABLE_FOR_YOU}.c(如 bufferevent 相关测试放 test/regress_bufferevent.c,DNS 相关放 test/regress_dns.c)。
  • 运行回归测试:执行 make verify 确保没有破坏既有功能。
  • 提交并提问:通过拉取请求提交补丁;遇到问题可在项目维护的讨论渠道提问。
  • 该流程在仓库中有完整的工具链支撑:风格检查依赖 checkpatch.sh 及其配置文件 .clang-format、.uncrustify;回归测试依赖 tinytest 框架(test/tinytest.c、test/tinytest.h)与测试驱动器 test/test.sh。

    二、代码风格:第一原则是"just look around"

    CONTRIBUTING.md 给出的首要且最通用的规则是:just look around(环顾四周)。即新代码在风格上应与周边既有代码保持一致,这是比任何静态规则都更优先的约定。

    为了让风格约束可自动化执行,libevent 仓库固化了两套格式化配置:

    • .clang-format:默认使用的 clang-format 配置。其关键约定包括:基于 LLVM 风格、ColumnLimit: 80(单行不超过 80 列)、DerivePointerAlignment: false(指针星号对齐不自动推导)、函数体大括号另起一行(BraceWrapping: { AfterFunction: true })等,并针对 libevent 内部大量使用的队列/哈希宏(如 LIST_FOREACH、TAILQ_FOREACH、HT_FOREACH)注册了 ForEachMacros,避免格式化器误改这些宏调用。
    • .uncrustify:可选的 uncrustify 风格配置,供偏好 uncrustify 的开发者使用。

    从 checkpatch.sh 的脚本实现看,它默认以 .clang-format 作为检查基准(cfg() 函数中 options[clang] 默认置 1),并在仓库根目录校验该配置文件存在(parse_options 中 abort "Config '%s' does not exist")。

    三、checkpatch.sh:补丁与文件风格检查的完整用法

    checkpatch.sh 是 libevent 提供的风格检查脚本,可以检查 git 提交(git ref)、补丁(patch) 和 普通文件 三类对象。原文档给出的全部用法如下,均可直接复制运行:

    3.1 检查 git 提交

    # 检查 HEAD 提交
    ./checkpatch.sh -r
    ./checkpatch.sh -r HEAD

    -r 是默认模式(parse_options 中 options[ref]=1),不传参数时脚本默认检查 HEAD。-r 模式下脚本通过 git format-patch –stdout "$@" -1 取出对应提交并交给 clang-format-diff 比较(对应脚本中的 clang_format_git 函数)。

    3.2 检查补丁(patch)

    # 将最近 1 个提交导出为补丁并从 stdin 检查
    git format-patch –stdout -1 | ./checkpatch.sh -p

    # 将当前提交内容直接从 stdin 检查
    git show -1 | ./checkpatch.sh -p

    # 导出最近 2 个提交的补丁序列(配合 -p 检查,或结合下面的 *.patch 方式)
    git format-patch –stdout -2

    -p 模式会把输入当作统一 diff 格式的补丁处理,逐 hunk 检查格式是否符合项目风格(对应脚本中的 clang_format_diff,即 clang-format-diff -p1 -style=…)。若 ./checkpatch.sh -p 后未跟文件名,脚本会读取标准输入(checkpatch.sh 中会把 stdin 内容暂存到临时文件再检查)。

    3.3 检查补丁文件

    # 对当前目录下的所有 .patch 文件逐一检查
    ./checkpatch.sh *.patch

    3.4 检查单个源文件

    # 以普通文件方式检查,只输出格式差异摘要
    ./checkpatch.sh -d event.c
    ./checkpatch.sh -d < event.c

    # 打印整个文件的格式化结果(而非仅摘要)
    ./checkpatch.sh -f event.c
    ./checkpatch.sh -f < event.c

    -d(file-diff)模式会把原文件与格式化结果做 diff 输出(diff -u "$@" <(check_file "$@")),便于快速定位差异行;-f(file)模式则直接输出经 clang-format 处理后的完整文件内容。两者都支持从 stdin 读取(./checkpatch.sh -d < event.c),方便在管道中组合使用。

    3.5 查看帮助与其它选项

    ./checkpatch.sh -h

    -h 会打印完整的选项说明。脚本支持的选项汇总如下(依据 checkpatch.sh 中的 usage() 输出):

    选项含义说明
    -p patch 模式 将输入视为补丁(stdin 或文件)
    -f file 模式 将输入视为普通文件,打印完整格式化结果
    -d file-diff 模式 将输入视为普通文件,打印与格式化结果的 diff
    -r git ref 模式 检查 git 提交,默认值,默认对象为 HEAD
    -C clang-format 使用 clang-format 检查(默认开启)
    -U uncrustify 改用 uncrustify 检查
    -c 指定配置文件 为 clang-format/uncrustify 指定自定义配置路径
    -h 帮助 打印用法说明

    实践提示:提交前最简自检组合是 ./checkpatch.sh -r HEAD(检查最新提交)与 ./checkpatch.sh -d < 你改过的文件(检查具体文件)。脚本最终输出经由 less -FRSX 分页展示,方便交互式查看。

    四、编写单元测试:test/regress_*.c 与 tinytest 框架

    CONTRIBUTING.md 对测试的要求只有两条,但背后是 libevent 一套完整的测试基础设施:

    • Write new unit test in test/regress_{MORE_SUITABLE_FOR_YOU}.c
    • make verify

    4.1 测试文件如何组织

    test/regress_*.c 是按功能模块划分的回归测试源码,构建定义位于 test/include.am(test_regress_SOURCES 列出了全部源文件)。常见模块包括:

    • test/regress.c:事件核心(event_base、事件优先级、信号、fd 读写等)
    • test/regress_buffer.c:evbuffer 缓冲
    • test/regress_bufferevent.c:bufferevent
    • test/regress_dns.c:异步 DNS
    • test/regress_http.c:HTTP
    • test/regress_util.c:util 工具函数(字符串、时间、地址解析等)
    • test/regress_rpc.c、test/regress_listener.c、test/regress_watch.c、test/regress_ws.c 等

    写新测试时,先判断改动属于哪个模块,把测试用例放进对应的 regress_*.c;如果都不合适,也可以新建文件并在 test/include.am 的 test_regress_SOURCES 中登记。

    4.2 测试用例的数据结构

    测试运行器是 tinytest(test/tinytest.h、test/tinytest.c)。一个测试用例由 struct testcase_t 描述(test/tinytest.h):

    struct testcase_t {
    const char *name; /**< 用例标识符 */
    testcase_fn fn; /**< 执行该用例的函数 */
    unsigned long flags; /**< TT_* 标志位集合 */
    const struct testcase_setup_t *setup; /**< 可选的 setup/cleanup 函数 */
    void *setup_data; /**< 供 setup 使用的附加数据 */
    };
    #define END_OF_TESTCASES { NULL, NULL, 0, NULL, NULL }

    用例数组必须以 END_OF_TESTCASES 结尾。多个用例可用 struct testgroup_t 分组(如 "util/"、"http/"、"dns/",见 test/regress_main.c),分组前缀会在运行时拼接到用例名前。

    4.3 真实用例示范:以 util 模块为例

    以 test/regress_util.c 中的 util_testcases[] 为例,一个典型用例形如:

    { "date_rfc1123", test_evutil_date_rfc1123, 0, NULL, NULL },

    其含义是:用例名 date_rfc1123,测试函数 test_evutil_date_rfc1123(定义在同文件 test/regress_util.c,内部使用 tt_str_op(result, ==, c.date) 逐项断言 RFC 1123 日期格式),无特殊标志,无 setup。另一些用例带有标志与 setup,例如:

    { "monotonic_res", test_evutil_monotonic_res, TT_RETRIABLE, &basic_setup, (void*)"" },
    { "getaddrinfo_live", test_evutil_getaddrinfo_live, TT_FORK|TT_OFF_BY_DEFAULT, NULL, NULL },

    其中 &basic_setup 会自动创建 event_base 与 socketpair(对应 test/regress.h 的 struct basic_test_data),TT_FORK 表示在子进程中运行以隔离崩溃,TT_RETRIABLE 表示失败可重试,TT_OFF_BY_DEFAULT 表示默认关闭(需显式指定才运行,多用于依赖真实网络的用例)。

    4.4 便捷宏:BASIC 与 LEGACY

    test/regress.h 提供两个声明宏,绝大多数用例都用它们注册:

    #define BASIC(name,flags) { #name, test_##name, flags, &basic_setup, NULL }
    #define LEGACY(name,flags) { #name, run_legacy_test_fn, flags|TT_LEGACY, &legacy_setup, test_##name }

    • BASIC:面向新式测试,自动获得 basic_setup 提供的 event_base 与 socketpair,标志位在 flags 参数传入。
    • LEGACY:包装旧式测试函数(通过全局 test_ok/pair[]/global_base 通信,见 test/regress_main.c 的 legacy_test_setup 与 run_legacy_test_fn),便于渐进迁移老用例。

    新代码建议优先使用 BASIC 写法。常用标志位(定义于 test/regress.h)包括 TT_NEED_SOCKETPAIR、TT_NEED_BASE、TT_NEED_THREADS、TT_ISOLATED(等价于 fork+socketpair+base)等。

    五、运行测试:make verify 的全貌

    5.1 verify 目标是什么

    在 Makefile.am 中可以看到:

    verify: check

    即 make verify 等价于运行 automake 的 check 目标,它按 test/include.am 中的 TESTS 列表依次执行 test_runner_epoll、test_runner_select、test_runner_kqueue、test_runner_evport、test_runner_devpoll、test_runner_poll、test_runner_win32、test_runner_wepoll、test_runner_timerfd、test_runner_changelist、test_runner_timerfd_changelist、test_runner_signalfd 等驱动脚本。

    每个 test_runner_* 都会调用 test/test.sh(如 test/test.sh -b EPOLL),test.sh 会依次运行 test-eof test-closed test-weof test-time test-changelist test-fdleak 等专用程序,以及聚合回归程序 test/regress(并额外以 EVENT_DEBUG_MODE=1 跑一遍 debug 模式,见 test/test.sh)。测试输出默认写到 /dev/null,可通过环境变量 TEST_OUTPUT_FILE 重定向;REGRESS_ARGS 可向 regress 传递额外参数。

    5.2 构建与验证的完整命令

    基于 README.md 与 Documentation/Building.md,两种构建方式都支持验证:

    # CMake(推荐,Unix)
    mkdir build && cd build
    cmake ..
    make
    make verify # 可选但推荐

    # Autoconf(自 2.2 起已弃用)
    ./configure
    make
    make verify # 可选但推荐
    sudo make install

    5.3 单独运行某个测试

    调试时不必每次都跑全量矩阵,可以直接运行聚合测试程序并按名字过滤:

    # 编译后在 test/ 目录下执行(CMake 构建产物位于 build/bin/)
    ./test/regress –list-tests # 列出所有用例
    ./test/regress util/date_rfc1123 # 只跑单个用例
    ./test/regress util/.. # 跑 util 组全部用例
    ./test/regress –quiet # 静默模式跑全部

    tinytest 支持 glob 风格选择器(+ 前缀启用、- 禁用、.. 通配组),例如 ./test/regress evbuffer/.. 只运行 evbuffer 组。test/regress_main.c 还预置了三个别名:all(全部用例)、live_net(依赖真实网络的用例)、fine_timing(时间敏感的精确计时用例),可写作 ./test/regress all、./test/regress live_net 等。这些别名通过 testaliases[] 注册。

    另外,test/test.sh 本身支持 -b(指定后端,如 -b KQUEUE)、-t(timerfd 模式,设置 EVENT_PRECISE_TIMER=1)、-c(changelist 模式,设置 EVENT_EPOLL_USE_CHANGELIST=yes)、-S(signalfd 模式,设置 EVENT_USE_SIGNALFD=1),在调试特定后端时非常有用。

    5.4 后端与跨平台覆盖

    libevent 支持多事件后端,test/test.sh 中的默认后端矩阵为 EVPORT KQUEUE EPOLL DEVPOLL POLL SELECT WIN32 WEPOLL。test.sh 通过 EVENT_NO<BACKEND>=yes 环境变量逐一屏蔽其它后端、只保留被测后端(setup() 函数),从而验证每个后端各自的正确性。因此 make verify 跑完意味着改动在可用的所有后端上都通过了回归——这是 libevent 对"提交前必须验证"的硬性要求。

    六、提问与交流

    CONTRIBUTING.md 明确鼓励提问:与其在本仓库提 issue,不如直接在项目官方维护的即时聊天频道(Gitter Room)留言讨论,适合快速答疑与方案探讨。正式的问题报告、功能请求(RFC)与补丁提交则通过拉取请求与 issue 渠道进行。

    七、进一步阅读

    • CONTRIBUTORS.md:项目致谢名单,可了解过往贡献者
    • README.md:构建安装总览
    • Documentation/Building.md:CMake / Autoconf 构建细节
    • Documentation/Developers.md:开发者附加资料(如发布包签名校验)
    • test/include.am:测试程序与测试目标的构建定义
    • test/tinytest.h:tinytest 框架 API 与标志位说明
    • test/regress_main.c:测试分组注册、别名与测试入口
    • test/regress.h:BASIC / LEGACY 宏与测试辅助设施

    掌握以上流程后,一次合格的 libevent 贡献应当是:改动文件通过 ./checkpatch.sh -d <file> 风格检查 → 在对应 test/regress_*.c 中补充新测试 → make verify 全绿 → 提交补丁。

    【免费下载链接】libevent Event notification library 【免费下载链接】libevent 项目地址: https://gitcode.com/gh_mirrors/li/libevent

    创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

    赞(0)
    未经允许不得转载:171主机测评 » 为 libevent 贡献代码:checkpatch 代码风格检查与 tinytest 回归测试实践指南
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址