测试类型
- 功能测试 - 一组查询和脚本,包含以下几个相互重叠的子集
- 集成测试,由
pytest在集群中运行 - 单元测试
- 性能测试
- 构建测试
- Sanitizers
- Fuzzers 以及其他一些测试,详见下文各节。
功能测试
./tests/queries 目录中。
每个测试都属于以下两种类型之一:.sql 和 .sh。
.sql测试是通过管道传给clickhouse-client的简单 SQL 脚本。.sh测试是自行执行的脚本。
.sh 测试。
只有在必须测试某些无法仅通过纯 SQL 覆盖的功能时,才应使用 .sh 测试,例如将某些输入数据通过管道传给 clickhouse-client,或测试 clickhouse-local。
测试 data types
DateTime 和 DateTime64 时,一个常见错误是以为服务器会使用某个特定时区 (例如 “UTC”) 。实际并非如此:CI 测试运行中的时区
会被刻意随机化。最简单的解决办法是为测试值显式指定时区,例如 toDateTime64(val, 3, 'Europe/Amsterdam')。在本地运行测试
01428_hash_set_nan_key,请切换到仓库目录并运行以下命令:
stderr 和 stdout) 会写入文件 01428_hash_set_nan_key.[stderr|stdout],这些文件位于对应测试文件旁边 (对于 queries/0_stateless/foo.sql,输出将位于 queries/0_stateless/foo.stdout) 。
有关 clickhouse-test 的所有选项,请参见 tests/clickhouse-test --help。
你可以运行所有测试,也可以通过为测试名称提供过滤器来运行部分测试:./clickhouse-test substring。
此外,还有一些选项可用于并行运行测试或按随机顺序运行测试。
运行快速测试
t3.2xlarge AWS amd64 Ubuntu 实例上验证可用。
- 安装前置条件,然后重新登录。
- 获取源代码。
- 构建代码并运行”快速测试”。
nohup 或 disown,这样即使 ssh 连接断开后也会继续运行。
运行无状态测试
m7i.8xlarge 实例上验证可用。
- 安装前置条件并重新登录。
- 获取源代码。
- 编译代码。
- 运行可并行执行的无状态测试。
python -m ci.praktika run 命令会运行一个特定的持续集成 job。有关 ClickHouse CI 的更多信息,请参阅这里。
添加新测试
queries/0_stateless 目录中创建一个 .sql 或 .sh 文件。
然后使用 clickhouse-client < 12345_test.sql > 12345_test.reference 或 ./12345_test.sh > ./12345_test.reference 生成对应的 .reference 文件。
测试应仅在预先自动创建的 test database 中对表执行 create、drop、select 等操作。
也可以使用临时表。
要在本地搭建与 CI 相同的环境,请安装测试配置 (它们会使用 Zookeeper 的模拟实现,并调整一些设置)
测试应当
- 尽量精简:只创建最低限度所需的表、列和复杂度,
- 尽量快速:耗时不要超过几秒钟 (最好在 1 秒以内) ,
- 保证正确且具备确定性:当且仅当被测功能未正常工作时才失败,
- 保持隔离/无状态:不要依赖环境和时序,
- 尽量覆盖全面:涵盖零值、NULL、空集、异常等边界情况 (负向测试请使用语法
-- { serverError xyz }和-- { clientError xyz }) , - 在测试结束时清理表 (以防有残留) ,
- 确保其他测试没有覆盖相同内容 (即先
grep) 。
限制测试运行
.sql 测试,标签放在第一行,并以 SQL 注释的形式写出:
.sh 测试,标签写在第二行的注释中:
| Tag name | 作用 | 使用示例 |
|---|---|---|
disabled | 不运行该测试 | |
long | 测试执行时间从 1 分钟延长到 10 分钟 | |
deadlock | 测试会在循环中长时间运行 | |
race | 与 deadlock 相同。优先使用 deadlock | |
shard | 要求 server 监听 127.0.0.* | |
distributed | 与 shard 相同。优先使用 shard | |
global | 与 shard 相同。优先使用 shard | |
zookeeper | 运行该测试需要 Zookeeper 或 ClickHouse Keeper | 测试使用 ReplicatedMergeTree |
replica | 与 zookeeper 相同。优先使用 zookeeper | |
no-fasttest | 该测试不会在 快速测试 下运行 | 测试使用 MySQL 表 engine,而该 engine 在快速测试中被禁用 |
fasttest-only | 该测试仅在 快速测试 下运行 | |
no-[asan, tsan, msan, ubsan] | 在启用 sanitizers 的构建中禁用该测试 | 测试在 QEMU 下运行,而 QEMU 无法与 sanitizers 配合使用 |
no-replicated-database | 当默认 database 使用 ReplicatedDatabaseEngine 时禁用该测试 | |
no-ordinary-database | 当默认 database engine 为 Ordinary 时禁用该测试 | |
no-parallel | 禁止其他测试与该测试并行运行 | 测试会读取 system 表,不变量可能会被破坏 |
no-parallel-replicas | 在启用并行副本时禁用该测试 | |
no-debug | 在 Debug 构建中禁用该测试 | |
no-release | 在 Release 构建中禁用该测试 | |
no-darwin | 在 macOS (Darwin) 上禁用该测试 | 测试依赖 Linux 特有功能,例如 distributed queries、procfs 或 HTTP server |
no-stress、no-polymorphic-parts、no-random-settings、no-random-merge-tree-settings、no-backward-compatibility-check、no-cpu-x86_64、no-cpu-aarch64、no-cpu-ppc64le、no-s3-storage。
除上述设置外,你还可以使用 system.build_options 中的 USE_* 标志来标识是否使用特定的 ClickHouse feature。
例如,如果测试使用了 MySQL 表,则应添加 use-mysql 标签。
为随机设置指定限制
.sh 测试,如果指定了标签,则将限制写在标签所在行旁边的注释中;如果未指定标签,则写在第二行:
.sql 测试,标签以 SQL 注释的形式写在 tags 所在行的下一行,或写在第一行:
None。
选择测试名称
00422_hash_function_constexpr.sql。
要确定这个前缀,请先找出该目录中现有的最大前缀,再在其基础上加一。
检查必须发生的错误
x。
如果没有报错,或报错内容不符,测试就会失败。
如果你想确保错误发生在客户端,请改用 clientError 注解。
不要检查错误消息的具体措辞,因为它将来可能会变化,从而导致测试无谓地失败。
只检查错误代码。
如果现有错误代码还不足以满足你的需求,可以考虑新增一个。
测试分布式查询
remote 表函数,并通过 127.0.0.{1..2} 地址让服务器查询自身;或者使用服务器配置文件中预定义的测试集群,例如 test_shard_localhost。
请记得在测试名称中加入 shard 或 distributed 字样,这样它才能在相应配置的 CI 环境中运行,即在服务器已配置为支持分布式查询的环境中运行。
使用临时 File
$CLICKHOUSE_TEST_UNIQUE_NAME 为临时 File 指定一个当前测试专用的唯一名称。
这样你就可以确保,无论是在设置阶段创建文件,还是在清理阶段删除文件,操作的都只是当前测试正在使用的文件,而不是其他并行运行测试所使用的文件。
已知问题
tests/queries/bugs 目录中。
这些问题修复后,相应的测试会移到 tests/queries/0_stateless。
集成测试
tests/integration/README.md。
请注意,ClickHouse 与第三方驱动程序的集成情况不在测试范围内。
此外,我们目前也没有针对自有 JDBC 和 ODBC 驱动程序的集成测试。
单元测试
ENABLE_TESTS CMake 选项启用或禁用测试的构建。
单元测试 (以及其他测试程序) 位于代码各处的 tests 子目录中。
要运行单元测试,请输入 ninja test。
有些测试使用 gtest,但也有一些只是普通程序,在测试失败时会返回非零退出码。
如果代码已经由功能测试覆盖,就不一定还需要单元测试 (而且功能测试通常更简单,也更容易使用) 。
你可以通过直接调用可执行文件来运行单个 gtest 检查,例如:
性能测试
tests/performance/。
每个测试都由一个 .xml 文件表示,其中包含测试用例的描述。
测试通过 docker/test/performance-comparison 工具运行。调用方法请参见 readme 文件。
每个测试会在循环中运行一个或多个查询 (可能包含多种参数组合) 。
如果你想提升 ClickHouse 在某种场景下的性能,并且这些改进可以通过简单查询观察到,强烈建议编写性能测试。
此外,在添加或修改相对独立且不太偏门的 SQL 函数时,也建议编写性能测试。
在测试过程中使用 perf top 或其他 perf 工具始终是有意义的。
测试工具和脚本
tests 目录中的一些程序并不是现成的测试用例,而是测试工具。
例如,对于 Lexer,有一个工具 src/Parsers/tests/lexer,它只是对 stdin 进行标记化,并将带颜色的结果输出到 stdout。
你可以将这类工具用作代码示例,也可用于探索和手动测试。
杂项测试
tests/external_models 中有一些针对机器学习模型的测试。
这些测试没有持续更新,必须迁移到集成测试中。
另外还有一个单独用于测试 quorum insert 的测试。
该测试会在不同服务器上运行一个 ClickHouse 集群,并模拟各种故障场景:网络分区、丢包 (发生在 ClickHouse 节点之间、ClickHouse 与 ZooKeeper 之间、ClickHouse server 与客户端之间等) 、kill -9、kill -STOP 和 kill -CONT,类似于 Jepsen。随后,测试会检查所有已确认的 insert 都已写入,而所有被拒绝的 insert 都没有写入。
手动测试
programs/clickhouse-server 目录,然后执行 ./clickhouse-server。默认情况下,它会使用当前目录中的配置 (config.xml、users.xml,以及 config.d 和 users.d 目录中的文件) 。要连接到 ClickHouse 服务器,请运行 programs/clickhouse-client/clickhouse-client。
请注意,所有 clickhouse 工具 (server、client 等) 都只是指向同一个名为 clickhouse 的 binary 的符号链接。
你可以在 programs/clickhouse 找到这个 binary。
此外,所有工具也都可以用 clickhouse tool 的形式调用,而不是 clickhouse-tool。
或者,你也可以安装 ClickHouse 软件包:可以使用 ClickHouse repository 中的稳定版本发布包,也可以在 ClickHouse 源码根目录中通过 ./release 自行构建软件包。
然后使用 sudo clickhouse start 启动 server (或使用 stop 停止 server) 。
日志可在 /etc/clickhouse-server/clickhouse-server.log 中查看。
如果系统中已经安装了 ClickHouse,你可以构建一个新的 clickhouse binary,并替换现有的 binary:
config.xml 中修改端口号 (或在 config.d 目录中的某个文件里覆盖这些设置) ,指定合适的数据路径,然后运行它。
clickhouse 可执行文件几乎没有依赖,可在多种 Linux 发行版上运行。
如果你想在服务器上快速粗略地测试自己的改动,只需用 scp 将刚构建好的 clickhouse 可执行文件传到服务器上,然后按照上面的示例运行即可。
构建测试
- 为 Darwin x86_64 (macOS) 交叉编译
- 为 FreeBSD x86_64 交叉编译
- 为 Linux AArch64 交叉编译
- 在 Ubuntu 上使用系统软件包中的库进行构建 (不推荐)
- 以库的共享链接方式进行构建 (不推荐)
测试协议兼容性
clickhouse-client 是否能与新版 clickhouse-server 兼容,以及新版 clickhouse-client 是否能与旧版 clickhouse-server 兼容 (只需运行相应软件包中的二进制文件即可) 。
我们还会通过集成测试自动验证一些场景:
- 旧版本 ClickHouse 写入的数据能否被新版本成功读取;
- 在由不同 ClickHouse 版本组成的集群中,分布式查询能否正常工作。
来自编译器的帮助
src 目录中) 在构建时会启用 -Wall -Wextra -Werror,以及一些额外的警告选项。
不过,第三方库不会启用这些选项。
Clang 还提供了更多有用的警告;你可以通过 -Weverything 查看它们,并从中挑选一些加入默认构建。
无论是在开发环境还是生产环境中,我们始终使用 clang 来构建 ClickHouse。
你可以在自己的机器上以调试模式构建 (这样能省一点笔记本电量) ,但请注意,由于控制流和过程间分析更完善,编译器在 -O3 下能够生成更多警告。
使用 clang 以调试模式构建时,会使用 libc++ 的调试版本,从而在运行时捕获更多错误。
Sanitizers
如果在本地运行时,进程 (ClickHouse server 或客户端) 在启动时崩溃,可能需要禁用地址空间布局随机化:
sudo sysctl kernel.randomize_va_space=0AddressSanitizer
线程 Sanitizer
Memory sanitizer
未定义行为检测器
Valgrind (memcheck)
re2 库中有一个误报,参见这篇文章。
模糊测试
src/Parsers/fuzzers/lexer_fuzzer.cpp。
libFuzzer 专用的配置、字典和语料库存放在 tests/fuzz。
我们建议你为每个处理用户输入的功能编写模糊测试。
默认不会构建 Fuzzer。
要构建 Fuzzer,必须同时设置 -DENABLE_FUZZING=1 和 -DENABLE_TESTS=1 选项。
我们建议在构建 Fuzzer 时禁用 Jemalloc。
用于将 ClickHouse 模糊测试集成到
Google OSS-Fuzz 的配置可在 docker/fuzz 中找到。
我们还使用简单的模糊测试来生成随机 SQL 查询,并检查 server 在执行这些查询时不会崩溃。
你可以在 00746_sql_fuzzy.pl 中找到它。
此测试应持续运行 (隔夜及更长时间) 。
我们还使用了基于 AST 的高级查询 Fuzzer,能够发现大量边界情况。
它会对查询 AST 进行随机置换和替换。
它会记住之前测试中的 AST 节点,并在后续测试中按随机顺序处理时将其用于模糊测试。
你可以在这篇博客文章中进一步了解这个 Fuzzer。
压力测试
- 服务器不会崩溃,也不会触发调试器或 sanitizer 陷阱;
- 不会发生死锁;
- 数据库结构保持一致;
- 服务器在测试结束后能够成功停止,并且再次启动时不会出现异常。
Thread Fuzzer
安全审计
静态分析器
clang-tidy。
同时也启用了 clang-static-analyzer 检查。
clang-tidy 还用于执行一些风格检查。
我们评估过 clang-tidy、Coverity、cppcheck、PVS-Studio、tscancode 和 CodeQL。
你可以在 tests/instructions/ 目录中找到使用说明。
如果你使用 CLion 作为 IDE,可以直接利用其中的一些 clang-tidy 检查。
我们还使用 shellcheck 对 shell 脚本进行静态分析。
加固
运行时完整性检查
- 而且这并不会拖慢速度。
代码风格
utils/check-style 脚本。
如果要强制代码符合规范,可以使用 clang-format。
.clang-format 文件位于源码根目录。
它基本符合我们当前的代码风格。
但不建议对现有文件直接运行 clang-format,因为这可能会让格式变得更糟。
你也可以使用 clang-format-diff 工具,可在 clang 的源码仓库中找到。
或者,也可以尝试使用 uncrustify 工具重新格式化代码。
配置文件 uncrustify.cfg 位于源码根目录。
相比 clang-format,它的测试还不够充分。
CLion 自带代码格式化工具,但需要调整为符合我们的代码风格。
我们还使用 codespell 来查找代码中的拼写错误。
这同样已实现自动化。