Swoole 扩展 PHP 单元测试指南:tests 目录的运行机制、PHPT 用例编写与工程实践
2026/9/20 19:23:55 网站建设 项目流程

Swoole 扩展 PHP 单元测试指南:tests 目录的运行机制、PHPT 用例编写与工程实践

【免费下载链接】swoole-src🚀 Coroutine-based concurrency library for PHP项目地址: https://gitcode.com/gh_mirrors/sw/swoole-src

本指南基于 Swoole 仓库 tests/README.md 编写,系统讲解 Swoole 扩展(ext-src/)自带的 PHP 单元测试体系:如何初始化测试依赖的数据库、如何通过start.sh全量/部分运行用例、如何理解日志文件与默认配置,以及如何用./new快速生成符合 PHPT 规范的测试脚本。读完本文,你将能够在本地或 CI 环境中完整跑通 Swoole 扩展测试,并独立编写高质量的回归用例。

测试体系概览:为什么需要一套独立于 phpt 官方的测试框架

Swoole 是运行在 PHP 底层的 C/C++ 扩展,涉及协程、异步网络、进程管理、内存表等大量与运行环境强相关的特性。仓库将测试划分为两大部分:

  • 核心测试(core-tests):针对src/目录下纯 C++ 核心库,基于 googletest,与 PHP 无关,详见 docs/TESTS.md;
  • PHP 测试(PHP Unit-test):针对ext-src/目录下 PHP 扩展代码,使用 PHPT(.phpt)格式,运行在真实 PHP 环境中,这正是 tests/README.md 描述的主体。

整个tests/目录不仅存放测试用例,还包含一套完整的测试运行工具链:启动脚本 tests/start.sh、数据库初始化脚本 tests/init、日志清理脚本 tests/clean、用例生成器 tests/new 以及测试执行器 tests/run-tests。这些脚本共同保证"你安装的 Swoole 扩展确实能正常工作"(原文:Run these tests to make certain that the swoole extension you installed can work well)。

准备阶段:初始化测试依赖的数据库

执行 ./init 初始化数据库

在运行测试之前,需要先执行初始化脚本:

cd tests ./init

该脚本会完成三件工作(可通过./init --help查看全部参数):

参数作用
--mysql仅初始化 MySQL 数据库
--firebird仅初始化 Firebird 数据库
--odbc-mysql仅初始化 ODBC MySQL 配置
--help显示帮助信息

不传任何参数时,默认初始化全部数据库。从 tests/init 源码看,MySQL 初始化会读取 tests/test.sql(一个基于 Navicat 导出的test库结构脚本),逐条执行其中的 SQL;Firebird 初始化则创建swoole_test测试表;ODBC 初始化会向/etc/odbcinst.ini/etc/odbc.ini写入名为mysql-test的 DSN。此外脚本还会通过swoole_library_set_option启动默认的 remote object server,并初始化 SSH2 测试用户与密钥对(详见 tests/swoole_ssh2/ssh2_test.inc)。

MySQL / Redis 测试服务连接约定

README 给出了测试框架对 MySQL 与 Redis 服务的连接参数约定,这是编写与运行数据库相关用例的前提:

配置项mysqlredis
path (env)$MYSQL_SERVER_PATH$REDIS_SERVER_PATH
path (actions)${actions}/data/run/mysqld/mysqld.sock${actions}/data/run/redis/redis.sock
host (raw)127.0.0.1127.0.0.1
host (docker)mysqlredis
port33066379
userroot-
passwordrootroot(可选)
databasetest0

上述约定的落地实现在 tests/include/config.php 中,所有连接参数都优先读取环境变量、其次按 CI / macOS / Linux 给出合理默认值:

  • MySQL:MYSQL_SERVER_HOST(默认127.0.0.1,CI 下为mysql)、MYSQL_SERVER_PORT(默认3306)、MYSQL_SERVER_USER(默认root)、MYSQL_SERVER_PWD(默认root)、MYSQL_SERVER_DB(默认test);
  • Redis:REDIS_SERVER_HOSTREDIS_SERVER_PORT(默认6379)、REDIS_SERVER_PWDREDIS_SERVER_DB(默认0);
  • 其余如 PostgreSQL、Oracle、Firebird、Sqlite、FTP、HTTP 代理等服务的连接信息也一并集中在此文件中,测试用例通过require __DIR__ . '/../include/config.php'引用。

值得一提的细节:在 tests/run-tests 的 swoole patch 中,测试器会自动调用netstat -ln探测本机正在监听的mysqld.sockredis.sock路径,作为MYSQL_SERVER_PATH/REDIS_SERVER_PATH的兜底发现机制。

运行测试:start.sh 的三种用法

README 给出了三种运行方式,对应 tests/start.sh 的实现逻辑:

1. 全量运行:./start.sh

cd tests ./start.sh

不带参数时,start.sh使用默认 globswoole_*,即遍历tests/下所有以swoole_开头的目录(swoole_atomicswoole_coroutineswoole_serverswoole_runtimeswoole_http_server等),覆盖约两千个.phpt用例。

2. 部分运行:./start.sh ./swoole_*

./start.sh ./swoole_coroutine ./start.sh ./swoole_timer

脚本会把传入的参数(如tests/swoole_server)中的tests/前缀剥掉后作为 glob 传给测试器,因此也可以写成./start.sh tests/swoole_server

3. 基础测试:./start.sh base

./start.sh base

base是脚本内置的"基础测试集合",只运行最核心、依赖最少的用例组,适合快速回归。从 tests/start.sh 源码看,该集合包含:

swoole_atomic swoole_event swoole_function swoole_global swoole_process swoole_process_pool swoole_table swoole_coroutine* swoole_channel_coro swoole_client_coro swoole_http_client_coro swoole_http2_client_coro swoole_server swoole_http_server swoole_websocket_server swoole_redis_server swoole_socket_coro swoole_runtime

base还支持追加参数(./start.sh base extra_arg),追加的内容会拼在 glob 最前面一并运行。

start.sh 的底层执行细节

除了 glob 选择,tests/start.sh 在调用测试器前还会做几件事,理解它们有助于排查环境问题:

  • 清理残留进程:先kill掉所有*.php残留进程(排除 phpstorm / php-fpm),避免上次测试残留的服务占用端口;
  • 调整文件描述符:若ulimit -n小于 16384,则尝试提升到 16384,满足高并发用例(如 c10k 类压力测试)的 socket 需求;
  • 指定 PHP 可执行文件:若未设置TEST_PHP_EXECUTABLE,默认使用which php;运行结束后再次清理进程并删除/tmp/swoole.log

最终执行的是:

PHPT=1 php -d "memory_limit=1024m" ./run-tests {glob}

PHPT=1环境变量告诉测试基础设施"当前处于 PHPT 运行模式"(tests/include/config.php 中通过IS_PHPTESTSING常量读取)。

默认配置与结果日志

默认开启的展示项

README 明确了 tests/run-tests 默认启用的输出项,这一行为在脚本的 swoole patch 中有对应实现($cfg['show']['diff'] = true; $cfg['show']['mem'] = true; $cfg['show']['slow'] = true; $slow_min_ms = 1000;):

配置默认
show-diffyes
show-memyes
show-slow1000(ms)

含义分别是:失败时展示实际输出与期望输出的差异、展示内存占用信息、展示耗时超过 1000ms 的慢用例。

测试运行器支持的关键参数

tests/run-tests 源自 PHP 官方的run-tests.php(文件头保留了 PHP Group 版权声明),因此继承了完整的参数体系,常用的包括:

参数作用
-j<workers>并行执行,如-j16使用 16 个 worker 加快测试
-p <php>/-P指定 PHP 可执行文件
-q安静模式,无需交互(等价于环境变量NO_INTERACTION=1
-x设置SKIP_SLOW_TESTS,跳过慢用例
--offline设置SKIP_ONLINE_TESTS,跳过依赖外网(如 httpbin.org、www.baidu.com)的用例
--show-all/--show-php/--show-diff/--show-slow展示各类文件内容
--keep-all保留测试产生的全部中间文件
--set-timeout [n]设置单个用例超时(默认 60 秒,内存检测模式下 300 秒)
-m/-M <tool>用 Valgrind 检测内存泄漏
-g PASS,FAIL,...只展示指定状态的用例(PASS/FAIL/XFAIL/SKIP/BORK/WARN/LEAK 等)
-l <file>/-w <file>从文件读取用例列表 / 把失败用例写入文件

日志文件后缀说明

每次运行测试都会在用例目录生成带后缀的中间文件,README 用一张表说明了它们的用途:

后缀说明
diff实际输出与期望输出的差异
out脚本实际输出
exp期望输出
log以上全部内容的汇总
phpPHP 临时脚本文件

例如tests/swoole_coroutine/all_asleep.phpt运行后会生成同名的all_asleep.diffall_asleep.outall_asleep.expall_asleep.log等文件,便于逐字节核对失败原因。

清理:./clean 一键删除日志文件

测试会产生大量中间文件,README 建议运行./clean清理:

./clean

从 tests/clean 源码看,该脚本递归扫描tests/下所有swoole_*目录,删除扩展名为diffexplogoutphpsh的文件,并在删除时打印DELETE: {path}便于确认。

编写测试用例:./new 与 PHPT 规范

用 ./new 生成用例骨架

README 提供了标准的用例创建方式:

./new [test-script-filename] # 例如: ./new ./swoole_coroutine/co_sleep.phpt

从 tests/new 源码看,该脚本是一个交互式生成器,它会依次向你询问:

  • [Test name]:用例名(将作为文件名);
  • [Test intro]:用例说明(写入--TEST--段);

随后根据 tests/template 模板生成文件,自动执行git add将其纳入暂存区;在 macOS 上还会尝试调用 PhpStorm 打开新文件(README 原文注明auto open on your ide (MacOS only))。如果目标目录不存在会询问是否创建,文件已存在会询问是否覆盖。

PHPT 用例的标准结构

生成的文件遵循 PHPT 四段式结构,下面以模板 tests/template 和真实用例 tests/swoole_coroutine/all_asleep.phpt 说明:

--TEST-- swoole_coroutine: all asleep --SKIPIF-- <?php require __DIR__ . '/../include/skipif.inc'; ?> --FILE-- <?php require __DIR__ . '/../include/bootstrap.php'; // 用例主体逻辑…… ?> --EXPECT--

各段职责如下:

  • --TEST--:用例名称与简介,例如swoole_coroutine: all asleep
  • --SKIPIF--:环境预检。当运行环境不满足条件时输出skip让用例跳过(不计入失败)。仓库提供了非常丰富的跳过辅助函数,见 tests/include/skipif.inc,例如skip_if_no_ssl()skip_if_no_http2()skip_if_offline()skip_if_in_valgrind()skip_if_php_version_lower_than('7.0')等,几乎覆盖了 Swoole 编译选项与平台差异的所有分支;
  • --FILE--:用例主体。通常先require bootstrap.php,再编写真实测试逻辑;
  • --EXPECT--:期望输出,测试器将其与真实输出做逐字符比对,产生diff

测试脚手架:bootstrap 与 ProcessManager

[--FILE--] 段引用的 tests/include/bootstrap.php 会统一完成环境装配:加载 tests/include/config.php、设置memory_limit=1024M、开启断言、关闭 DNS 缓存、设置协程socket_timeout=5、初始化默认 remote object server 等,保证每个用例拥有确定性的运行环境。

模板中还使用了SwooleTest\ProcessManager这一核心测试脚手架。它通过parentFunc(父进程)/childFunc(子进程)双回调配合childFirst()run(),在单文件内模拟"客户端 + 服务端"的多进程场景,这是大量swoole_server/swoole_http_server/swoole_websocket_server用例的标准写法:

$pm = new SwooleTest\ProcessManager; $pm->parentFunc = function () use ($pm) { // 客户端逻辑:发起连接、发送请求、断言结果 }; $pm->childFunc = function () use ($pm) { // 服务端逻辑:创建 Swoole\Server 并启动 }; $pm->childFirst(); $pm->run();

代码风格

tests/README.md 明确要求测试代码遵循PSR1/PSR2编码规范。建议在提交前通读 docs/CODE-STYLE.md 并利用 scripts/code-format.sh 对改动做统一格式化,保证新用例与既有代码风格一致。

在 CI / Docker 环境中运行(进阶)

针对持续集成场景,仓库在 docs/TESTS.md 中补充了更完整的运行方案:

  • 一键多环境测试:运行 scripts/route.sh 即可自动创建多个 PHP 版本的 Docker 容器,并在其中依次执行 Swoole 的编译与单元测试,无需手动搭建环境;指定分支时使用SWOOLE_BRANCH=alpine ./scripts/route.sh
  • 进入容器调试docker exec -it -e LINES=$(tput lines) -e COLUMNS=$(tput cols) swoole /bin/bash,可在容器内复跑用例(可用CTRL+C取消);
  • 核心 C++ 测试:如需测试src/下的 C++ 核心库,则需安装 googletest(要求 GCC/G++ 8.0+,完整支持 C++17),依次执行cmake . && make -j$(nproc) lib-swoolemake -j$(nproc) core-tests,再通过./bin/core-tests --gtest_filter=server.*--gtest_list_tests精确筛选用例。

常见问题排查路径

  1. 用例被跳过而非失败:查看是否命中--SKIPIF--段的某个skip分支(如缺少 SSL/HTTP2 编译选项、处于离线环境、PHP 版本不符),可用-g SKIP单独查看跳过原因;
  2. 数据库用例失败:确认 MySQL / Redis 已按上文连接约定启动,必要时显式导出MYSQL_SERVER_HOSTREDIS_SERVER_PORT等环境变量覆盖默认值,并重新运行./init
  3. 日志文件干扰:调试完毕后运行./clean清理,避免残留的diff/out/exp文件混淆判断;
  4. 慢用例或高并发用例失败:检查是否因ulimit -n过小导致 socket 创建失败,start.sh会自动尝试提升到 16384。

小结

Swoole 的tests/目录并非简单的用例集合,而是一套自包含的测试工程:./init负责数据库与服务装配,./start.sh负责按 glob 灵活选择用例集合并以正确环境参数启动 tests/run-tests,./new+ tests/template 提供规范的 PHPT 用例生成流水线,./clean负责运行产物回收。对开发者而言,掌握这套流程既能快速验证自己编译安装的扩展是否可用,也能用统一的规范为 Swoole 贡献高质量的回归用例——这正是 tests/README.md 的初衷所在。

【免费下载链接】swoole-src🚀 Coroutine-based concurrency library for PHP项目地址: https://gitcode.com/gh_mirrors/sw/swoole-src

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询