为 MariaDB Server 编写好的测试用例

在尝试修复 bug 之前,建议创建一个测试用例来展示错误行为。应该在修复 bug 之前至少运行一次测试,并在修复 bug 之后再次运行,以确保修复是正确的。大多数服务器测试使用 mysql-test 目录中的 mysql-test-run.pl 来运行。

在 MariaDB 源代码目录中,你会找到一个名为 mysql-test 的目录。直到 MariaDB 10.2,该目录中有两个子目录:trt 代表测试目录。在该目录中,你会找到扩展名为 .test 的文件。这些是包含大部分有效 SQL 代码的普通文本文件。测试文件中的每一行都会在运行的服务器上执行,其输出会与 r 目录中相应的 .result 文件中的内容进行比较。

从 MariaDB 10.3 开始,编写测试用例的起点是 mysql-test/main 目录,.test.result 文件应该位于此处。

mysql-test-run.pl 支持特殊的扩展,允许运行其他操作,例如通过 --echo <Message> 将消息输出到测试结果中,或者设置和使用测试本地变量。

创建测试用例最简单的方法是在 mysql-test/t/ 目录中创建一个文件。

 ~/server/$ cd mysql-test && touch t/hello.test

hello.test 文件中写入以下代码

--echo #
--echo # Bug #XXXXXX: INET_ATON() returns signed, not unsigned
--echo #
create table t1 select INET_ATON('255.255.0.1') as `a`;
show create table t1;
drop table t1;

这个测试用例做了多件事情。首先,它使用了 mysql-test-run 特殊的 --echo 语法。echo 后面的文本将在测试运行时打印出来。这对于使结果文件更具可读性且易于跟踪非常有用。之后,我们创建一个表,显示 show create table 的输出,最后 drop 这个表。重要的是不要留下任何测试运行后的残留物。任何剩余的表、函数或数据库都会导致测试失败。恢复任何使用过的会话或全局变量到其初始默认状态也很重要。

如果我们想运行两个或更多测试并确保即使在失败情况下所有测试都能运行,我们需要使用(测试之间的分隔符是空格或逗号):./mtr hello hello2 --mem --force --max-test-fail=0
如果我们想运行特定套件中的所有测试用例:./mtr --suite=funcs_1
如果我们想运行特定套件(套件之间的分隔符是逗号)中的所有测试用例:./mtr --suite=funcs_1,funcs_2
如果我们想运行特定套件中的特定测试用例:./mtr  funcs_1.is_check_constraints

如果我们想使用嵌入式服务器运行特定的测试用例,请确保服务器已编译为嵌入式服务器,然后运行:./mtr is_tables_is_embedded --embedded

创建文件后,我们可以运行测试用例。确保服务器已编译。进入 mysql-test 目录,使用符号链接 mtr 调用 mysql-test-run.pl,后面跟着你刚添加的文件名(不带 .test 扩展名)。 mysql-test-run.pl 选项的完整列表可在此处找到。

~/server/mysql-test/$./mtr hello

MTR 会打印其正在进行的状态信息,并在某个时候开始运行测试。你将直接在控制台中看到测试的输出。它应该看起来像这样

Checking supported features...
MariaDB Version 10.3.4-MariaDB
 - SSL connections supported
Collecting tests...
Installing system database...

==============================================================================

TEST                                      RESULT   TIME (ms) or COMMENT
--------------------------------------------------------------------------

worker[1] Using MTR_BUILD_THREAD 302, with reserved ports 16040..16059
#
# Bug #XXXXXX: INET_ATON() returns signed, not unsigned
#
create table t1 select INET_ATON('255.255.0.1') as `a`;
show create table t1;
Table	Create Table
t1	CREATE TABLE `t1` (
  `a` bigint(21) unsigned DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
drop table t1;
main.hello                               [ pass ]      2
--------------------------------------------------------------------------
The servers were restarted 0 times
Spent 0.002 of 4 seconds executing testcases

Completed: All 1 tests were successful.

如果测试通过并且输出正确,我们需要记录结果。要做到这一点,只需再次运行测试,使用

~/server/mysql-test/$./mtr hello --record

使用 --record 运行测试将在 mysql-test/r/ 目录中创建一个 .result 文件。当测试有结果文件时,运行 mtr 时将不再显示它们的输出。提交测试用例进行评审时,务必也包含结果文件!

测试用例的最佳实践

  • 确保你的测试是确定性的。MTR 会检查结果文件和实际测试运行输出之间的精确匹配。如果 SELECT 的行顺序不正确,测试将被标记为失败。
  • 确保测试用例的结果中不包含特定于环境的内容,例如绝对路径。如果结果必须包含此类信息,请使用 --replace_regex 定义如何将这些信息替换为通用内容,例如 <ABS_PATH>
  • 在为新功能编写测试用例时,也要测试错误情况。要要求一个语句失败,请使用 MTR 指令:--error <error-name>。使用错误名称代替错误编号,这样可以使测试更易于阅读。错误名称应与代码中抛出该错误时遇到的名称相同。
  • 确保测试使用可读的 SQL 语法编码风格。例如,将 SELECT 子句与 FROM 子句放在单独的行中,如果条件太复杂,将 WHERE 子句拆分到不同的行中,并相应地分隔。
  • 恰当地命名事物。不要仅仅使用 ab 作为列名,而应使用 First_nameLast_name 等。
  • 如果合理,请解释 SQL 语句的目的。例如,
    --echo #
    --echo # Create 2 users, one to test full database access and one
    --echo # to test granting rights to.
    --echo #
    
  • 在测试开始前添加注释,简要解释它测试的 bug。
    --echo #
    --echo # MDEV-XXXX Users not created automatically when granting a privilege.
    --echo #
    --echo # Test to see if granting a privilege to a non-existing user actually
    --echo # creates that user.
    --echo #
    
  • 如果测试已经有结果文件,并且你修改了补丁、 编译并构建了,你需要为整个服务器运行 ./mtr。如果存在一些失败的测试,你需要使用 ./mtr failed_test --record 手动运行它们。这将创建一个新的 .result 文件,如果你的补丁有效,该文件应该考虑你的补丁修改并通过。如果你再次遇到一些失败的情况,那么找出是什么导致了失败,如果是由于你的补丁引起的,则尝试解决它。
  • 为了查看用于 mtr 测试的完整标志列表,请转到客户端文件夹并查看 mysqltest.cc 文件。例如,当你运行测试并希望垂直显示结果集(这通常使用 mysql 客户端中的 ego 命令 \G 来完成)时,你需要使用 --vertical_results 标志。要切换回默认标志,请使用 --horizontal_results

另请参阅