特性介绍 | MySQL测试框架 MTR 系列教程(四):语法篇

作者:卢文双 资深数据库内核研发

序言

以前对 MySQL 测试框架 MTR 的使用,主要集中于 SQL 正确性验证。近期由于工作需要,深入了解了 MTR 的方方面面,发现 MTR 的能力不仅限于此,还支持单元测试、压力测试、代码覆盖率测试、内存错误检测、线程竞争与死锁等功能,因此,本着分享的精神,将其总结成一个系列。

主要内容如下:

  • 入门篇:工作机制、编译安装、参数、指令示例、推荐用法、添加 case、常见问题、异常调试
  • 进阶篇:高阶用法,包括单元测试、压力测试、代码覆盖率测试、内存错误检测、线程竞争与死锁
  • 源码篇:分析 MTR 的源码
  • 语法篇:单元测试、压力测试、mysqltest 语法、异常调试

由于个人水平有限,所述难免有错误之处,望雅正。

本文是第四篇语法篇。

本文首发于 2023-07-05 21:53:21


MTR 系列基于 MySQL 8.0.29 版本,如有例外,会特别说明。

单元测试

简介

前文「MySQL 测试框架 MTR 系列教程(二):进阶篇 - 内存/线程/代码覆盖率/单元/压力测试」已介绍了单元测试的概念及使用方法,简单回顾一下:

  • MySQL 使用 TAP(Test Anything Protocol) 和 Google Test Framework 来实现单元测试。
    • TAP 是 Perl 与测试模块之间所使用的简单的基于文本的接口。
    • 为了实现 C/C++ 的单元测试,MySQL 开发了一个用于生成 TAP 文本的库libmytap.a,源码路径位于unittest/mytap/
  • 使用方法:在执行 cmake 的目录执行 make testmake test-unit 指令(内容详细,更推荐)。
  • 注意事项:在执行单元测试时,不建议启用 ASAN,否则会因 ASAN 检测到单元测试代码有内存泄漏而导致 case 失败。

unittest/ 目录介绍:

1
2
3
4
CMakeLists.txt
examples # 存放单元测试示例
gunit # 存放所有单元测试用例的代码
mytap # 存放 MyTAP 协议代码

如果新加的测试用例与存储引擎或插件有关,则分别存放在unittest/engine_nameunittest/plugin_name目录或它们的子目录中。

单元测试代码都位于源码目录/unittest/gunit/ 下,其中有文件也有子目录,无论是当前目录还是子目录下的文件,都以xxxxx-t.cc 格式命名,每个xxxxx-t.cc文件都是一个测试 case,编译后都会生成一个二进制文件 bin/xxxxx-t

下面举例说明如何添加单元测试 case 。

示例

比如在 源码目录/unittest/gunit/binlogevents/ 目录下创建一个新的测试用例 myprint

一、创建文件unittest/gunit/binlogevents/myprint-t.cc,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <gtest/gtest.h>


namespace binary_log {
namespace unittests {

class MyPrintTest : public ::testing::Test {
public:
MyPrintTest() {}

void TestBody() { // 必须实现该虚函数
std::cout << "print_test ====>" << std::endl;
ASSERT_TRUE(true);
}
};

// 第二个参数是测试用例名字
TEST_F(MyPrintTest, PrintTest) {
MyPrintTest t;
t.TestBody();
}

} // namespace unittests
} // namespace codecsog

二、修改 unittest/gunit/binlogevents/CMakeLists.txt 文件,添加myprint用例:

1
2
3
4
5
6
7
8
9
10
11
12
......

# Add tests SET(TESTS transaction_payload_codec
transaction_compression
transaction_payload_iterator
gtids
gno_intervals
myprint ####### 新加的行
heartbeat_codec)

......

三、重新执行 cmake(需要设置-DWITH_DEBUG=1 -DWITH_UNIT_TESTS=1)、编译,会生成二进制文件bin/myprint-t

四、运行make testmake test-unit ,或者直接执行bin/myprint-t ,测试用例 passed :

1
2
3
4
5
6
7
8
9
10
11
12
13
wslu@ubuntu:/data/work/mysql/mysql-server/console-build-debug$ ./bin/myprint-t
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from MyPrintTest
[ RUN ] MyPrintTest.PrintTest
print_test ====>
[ OK ] MyPrintTest.PrintTest (0 ms)
[----------] 1 test from MyPrintTest (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (8 ms total)
[ PASSED ] 1 test.

参考:

代码覆盖率测试

目前涉及 gcov 的程序文件只有 mysys/dbug.cc 文件以及对应的单元测试文件unittest/gunit/dbug-t.cc

gcov 的用法就是在编译时添加选项来实现的:

1
2
3
4
5
wslu@ubuntu:/data/work/mysql/mysql-server/console-build-debug/mysql-test$ grep "coverage" ../../CMakeLists.txt -nr
1128: STRING_APPEND(CMAKE_C_FLAGS " -fprofile-arcs -ftest-coverage -DHAVE_GCOV")
1129: STRING_APPEND(CMAKE_CXX_FLAGS " -fprofile-arcs -ftest-coverage -DHAVE_GCOV")
1130: STRING_APPEND(CMAKE_EXE_LINKER_FLAGS " -fprofile-arcs -ftest-coverage -lgcov")

综上,无需自行编写代码覆盖率测试代码。

压力测试

有两个地方涉及压力测试:

一、压力测试 suite 只有两个:

  • stress
  • innodb_stress

如需要添加新 case,参考对应 suite 已有 case 照猫画虎即可,语法可参考下一章节。

二、mysql-stress-test.pl :被 mysql-test-run.pl 调用,参数是--stress

1
2
stress=ARGS           Run stress test, providing options to
mysql-stress-test.pl. Options are separated by comma.

对于 mysql-stress-test.pl ,更便于自定义测试内容,主要包括:

  • --stress-init-file[=path]

    file_name is the location of the file that contains the list of tests to be run once to initialize the database for the testing. If missing, the default file is stress_init.txt in the test suite directory.

  • --stress-tests-file[=file_name]

    Use this option to run the stress tests. file_name is the location of the file that contains the list of tests. If file_name is omitted, the default file is stress-test.txt in the stress suite directory. (See --stress-suite-basedir).

这部分暂未尝试,不做赘述。

SQL 测试 - mtr/mysqltest 语法

之间文章介绍过 mtr 会将 *.test*.inc等测试 case 的内容传给 mysqltest 来执行。

mysqltest 是 mysql 自带的测试引擎, 它实现了一种小语言,用来描述测试过程,并将测试结果与预期对比。

本节要讲解 mysqltest 语法格式,你可能会好奇学习这个语法有什么用,为了更直观的说明,首先我们看一下如何编写 mtr 的测试用例。

语法格式

mysqltest 解释的是以.test为后缀的文件(包括其引用的.inc文件)。

mysqltest 小语言按照语法大致分为三类:

  • mysql command :用来控制运行时的行为。一般有两种写法:
1
2
command; # 这是后面带;的
--command # 前面带--,不需要;
  • SQL :就是普通的 SQL 语句,测试 case 里大部分是 SQL 语句。
  • comment :注释一般用于描述测试过程,用 # 开头。

示例:借鉴「MySQL 测试框架 MTR 系列教程(一):入门篇」一文中的测试 case(路径是 mysql-test/t/mytest.test ),内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
--echo #
--echo # some comments
--echo #

--disable_warnings
DROP TABLE IF EXISTS t1;
SET @@sql_mode='NO_ENGINE_SUBSTITUTION';
--enable_warnings

SET SQL_WARNINGS=1;

CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1);
INSERT INTO t1 VALUES (2);
SELECT * FROM t1;
DROP TABLE t1;

command 列表

mysqltest 提供了几十个 command,本文只介绍最常用的,更多内容请查阅官方手册:MySQL: mysqltest Language Reference

error 处理预期错误

语法:

1
2
error error_code [, error_code] ...
sql_statements

有些 CASE 就是要验证 sql 失败的情况,在 sql 语句前面加上--error 错误码就可以了。

  • 如果 sql 报错且错误码等于 --error 指定的错误码,mysqltest 不会 abort,而是继续运行。
  • 反之,如果 sql 报错且错误码不等于--error 指定的错误码,mysqltest 会 abort 并报错退出。

--error后面可以跟两种值:一种是error no,另外一种是sqlstate,如果是后者需要加上 S 做前缀。 他们分别是 C API 函数 mysql_errno()mysql_sqlstate() 的返回值。

示例一:使用错误码

1
2
--error 1050
create table t1(pk createtime primary key, a int);

等价于

1
2
--error ER_TABLE_EXISTS_ERROR
create table t1(pk createtime primary key, a int);

其中数字 1050 对应错误码,ER_TABLE_EXISTS_ERROR 对应错误的逻辑名。

这样在 mysqltest 运行后,会将返回的错误信息一起写入结果文件,这些错误信息就作为期望结果的一部分了。

示例二:使用 SQLSTATE

也可以使用 SQLSTATE 来指示期望有错误返回,例如与 MySQL 错误码 1050 对应的 SQLSTATE 值是 42S01,使用下面的方式,注意编码增加了 S 前缀:

1
2
--error S42S01
create table t1(pk createtime primary key, a int);

示例三:指定多个错误码,满足其一则通过

在指令 error 后面是可以加入多个错误码作为参数的,使用逗号分隔即可:

1
2
3
--error 1050,1052
create table t1(pk createtime primary key, a int);

如果该 SQL 报错,若错误码是 1050 或 1051,则符合预期,测试继续。

错误码参考 MySQL 安装包 include 子目录下的 mysqld_error.h

disable_abort_on_error / enable_abort_on_error

默认情况下(enable_abort_on_error),sql 执行失败后 mysqltest 就退出了,后面的内容就不会执行,也不会生成 .reject文件。

显示执行disable_abort_on_error命令可以在 sql 失败后继续执行后面的内容,并生成 .reject文件。

1
2
3
--disable_abort_on_error
sql_statements
--enable_abort_on_error

disable_query_log / enable_query_log

默认情况下(enable_query_log),所有的 sql 语句都会记录输出结果。

在一些情况下(比如,使用了循环,query 特别多)不想记录某些 sql 语句及结果,显示调用 disable_query_log 既可。

1
2
--disable_query_log
--enable_query_log

其他形如enable_xx/disable_xx的命令还有很多,用法都类似

connect

创建一个到 mysql server 的新连接并作为当前连接。

语法格式:

1
connect (name, host_name, user_name, password, db_name [,port_num [,socket [,options [,default_auth [,compression algorithm, [,compression level]]]]]])
  • name is the name for the connection (for use with the connection, disconnect, and dirty_close commands). This name must not already be in use by an open connection.
  • host_name indicates the host where the server is running.
  • user_name and password are the user name and password of the MySQL account to use.
  • db_name is the default database to use. As a special case, NO-ONE means that no default database should be selected. You can also leave db_name blank to select no database.
  • port_num, if given, is the TCP/IP port number to use for the connection. This parameter can be given by using a variable.
  • socket, if given, is the socket file to use for connections to localhost. This parameter can be given by using a variable.
  • options can be one or more of the following words, separated by spaces:
    • CLEARTEXT: Enable use of the cleartext authentication plugin.
    • COMPRESS: Use the compressed client/server protocol, if available.
    • PIPE: Use the named-pipe connection protocol, if available.
    • SHM: Use the shared-memory connection protocol, if available.
    • SOCKET: Use the socket-file connection protocol.
    • SSL:Use SSL network protocol to have encrypted connection.
    • TCP: Use the TCP/IP connection protocol.
      Passing PIPE or SHM on non-Windows systems causes an error, and, similarly, passing SOCKET on Windows systems causes an error.
  • default_auth is the name of an authentication plugin. It is passed to the mysql_options() C API function using the MYSQL_DEFAULT_AUTH option. If mysqltest does not find the plugin, use the –plugin-dir option to specify the directory where the plugin is located.
  • compression algorithm is the name of compression algorithm to be used to compress data transferred between client server. It is passed to the mysql_options() C API function using the MYSQL_OPT_COMPRESSION_ALGORITHMS option.
  • zstd compression level is the extent of compression to be applied when zstd compression algorithm is used. It is passed to the mysql_options() C API function using the MYSQL_OPT_COMPRESSION_ALGORITHMS option.
  • compression level is the extent of compression to be applied based on the compression algorithm used. It is passed to the mysql_options() C API function using the MYSQL_OPT_COMPRESSION_ALGORITHMS option.

示例:

1
2
3
connect (conn1,localhost,root,,);
connect (conn2,localhost,root,mypass,test);
connect (conn1,127.0.0.1,root,,test,$MASTER_MYPORT);

connection

语法:

1
connection connection_name

选择 connection_name作为当前连接。

示例:

1
2
3
connection master;
connection conn2;
connection default;

disconnect

语法:

1
disconnect connection_name

关闭连接connection_name

示例:

1
2
disconnect conn2;
disconnect slave;

测试 session 的时候会用到上述三个命令,以在多个 connection 之间切换。比如:

1
2
3
4
5
connect (conn3,127.0.0.1,root,,test,25042);
connection conn3;
create table t1(a int primary key);
drop table t1;
disconnect conn3;

exec

执行 shell 命令。语法:

1
2
exec command [arg] ...

示例:

1
2
3
--exec $MYSQL_DUMP --xml --skip-create test
--exec rm $MYSQLTEST_VARDIR/tmp/t1
exec $MYSQL_SHOW test -v -v;

On Cygwin, the command is executed from cmd.exe, so commands such as rm cannot be executed with exec. Use system instead.

perl [terminator]

嵌入 perl 代码,以 EOF 为结束符,也可以自定义结束符。

受限于 mtr 语法,很多操作无法完成,嵌入 perl 脚本可以简化问题。

1
2
3
perl;
// your perl script
EOF

示例:

1
2
3
perl;
print "This is a test\n";
EOF
1
2
3
perl END_OF_FILE;
print "This is another test\n";
END_OF_FILE

perl 内外的变量交互:

1、可以使用 let 设置环境变量

如果使用 let 时变量名不加 $ 即为设置为环境变量,在 perl 中可以通过 $ENV{'name'} 获取和设置

1
2
3
4
5
6
7
8
--let name = "env value"

--perl
print "name: $ENV{'name'}";
$ENV{'name'} = 'new env value';
EOF

--echo name: $name

2、在 perl 中拼接 mtr 脚本,然后在 mtr 脚本中执行

1
2
3
4
5
6
7
8
9
perl;
my $dir = $ENV{'MYSQLTEST_VARDIR'};
open ( OUTPUT, ">$dir/tmp/name.inc") ;
print OUTPUT "let \$name = abc;\n";
close (OUTPUT)
EOF

--source $MYSQLTEST_VARDIR/tmp/name.inc
--echo $name

vertical_results/horizontal_results

设置 SQL 语句结果的默认显示方式(vertical_results 表示纵向,horizontal_results 表示横向,默认是横向),功能跟 sql 语句的'\G'类似。

示例:

1
--vertical_results

exit

退出当前测试 case,后续指令不再执行。

let

变量赋值,可支持整数、字符串。

语法:

1
2
3
let $var_name = value
let $var_name = query_get_value(query, col_name, row_num)

示例:

1
2
3
4
5
6
--let $1= 0 # 加 -- 前缀,就不用以分号结尾
let $count= 10;

# 将查询结果赋给变量 q
let $q= `SELECT VERSION()`;

inc/dec

为整数加 1/减 1 。

语法:

1
inc $var_name/dec $var_name

示例:

1
2
--inc $i;
inc $3;

eval

语法:

1
eval statement

执行sql 语句,支持变量的传递。示例:

1
2
3
eval USE $DB;
eval CHANGE MASTER TO MASTER_PORT=$SLAVE_MYPORT;
eval PREPARE STMT1 FROM "$my_stmt";

query

语法:

1
query [statement]

显示指定当前语句是 SQL 语句,而不是 command。即使 query 之后是 command(比如sleep),也会当成 statement 来解析。

send

语法:

1
send [statement]

向 server 发送一条 query,但并不等待结果,而是立即返回,该 query 的结果必须由 reap 指令来接收。

在上一条 query 结果被 reap 指令接收之前,不能向当前 session 发送新的 SQL 语句。

如果 statement 省略了,则执行下一行的 SQL 语句。

示例:

1
2
3
4
5
6
7
send SELECT 1;

等效于

send;
SELECT 1;

send_eval

语法:

1
send_eval [statement]

等效于 send + evalsend不同在于支持变量传递

如果 statement 省略了,则执行下一行的 SQL 语句。

示例:

1
2
3
4
5
6
7
--send_eval $my_stmt

等效于

--send_eval
$my_stmt;

reap

如果当前 session 之前有通过 send 指令向 server 发送 SQL 语句,reap 指令用来接收该 SQL 语句的执行结果。

如果之前没有通过 send 向 server 发送 SQL,则不要执行 reap 指令。

示例:在同一个 session 中用 send 后台执行,用 reap 恢复等待。

1
2
3
4
5
6
7
8
--connection 1
--send select sleep(20)

--connection 2
--send select 1

--connection 1
--reap

echo

语法:

1
echo text

text 文本输出到测试 result 中。

示例:

1
2
--echo Another sql_mode test
echo should return only 1 row;

query_get_value

语法:

1
query_get_value(query, col_name, row_num)

获得 query 返回的结果中某行某列的值

示例:

假如 .test 文件内容如下:

1
2
3
4
CREATE TABLE t1(a INT, b VARCHAR(255), c DATETIME);
SHOW COLUMNS FROM t1;
let $value= query_get_value(SHOW COLUMNS FROM t1, Type, 1);
echo $value;

输出结果为:

1
2
3
4
5
6
7
CREATE TABLE t1(a INT, b VARCHAR(255), c DATETIME);
SHOW COLUMNS FROM t1;
Field Type Null Key Default Extra
a int(11) YES NULL
b varchar(255) YES NULL
c datetime YES NULL
int(11)

int(11) 就是 Type 列第一行的值。

source

语法:

1
source file_name

多个 case 可能共用一块代码,这块代码可以单独放到一个.inc文件,再通过 source 导入。

示例:

1
--source path/to/script.inc

sleep

语法:

1
sleep num

Sleep num seconds.

示例:

1
2
--sleep 10
sleep 0.5;

replace_column

语法:

1
replace_column col_num value [col_num value] ...

将下一条语句的结果中的某些列的值进行替换,可以指定多组替换规则,列序号从 1 开始。

示例:

1
2
--replace_column 9 #
replace_column 1 b 2 d;

expr 命令 (MySQL 8 之后可用)

语法:

1
expr $var_name= operand1 operator operand2

对数值变量进行运算,支持 +, -, *, /, %, &&, ||, &, |, ^, <<, >>

1
2
3
4
--let $val1= 10
--let $var2= 20
--expr $res= $var1 + $var2
--echo $res

在 5.7 版本中用 SQL 语句替代

1
2
3
--let $val1= 10
--let $var2= 20
--expr $res= `select $var1 + $var2`

if

语法:

1
if (expr)

与其他语言的 if 语句含义相同。

示例:

1
2
3
4
5
6
7
8
9
10
let $counter= 0;
if ($counter)
{
echo Counter is not 0;
}

if (!$counter)
{
echo Counter is 0;
}

while

语法:

1
while (expr)

与其他语言的 while 语句含义相同。

示例:

1
2
3
4
5
6
let $i=5;
while ($i)
{
echo $i;
dec $i;
}

其他命令

其他的命令还有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
assert (expr)

change_user [user_name], [password], [db_name]
character_set charset_name

delimiter str

die [message]
skip [message]

disable_async_client, enable_async_client
disable_connect_log, enable_connect_log
disable_info, enable_info
disable_metadata, enable_metadata
disable_ps_protocol, enable_ps_protocol
disable_query_log, enable_query_log
disable_reconnect, enable_reconnect
disable_result_log, enable_result_log
disable_rpl_parse, enable_rpl_parse
disable_session_track_info, enable_session_track_info
disable_testcase bug_number, enable_testcase
disable_warnings, enable_warnings

end
start_timer
end_timer

# exec command
exec_in_background command [arg] ...
execw command [arg] ...


# 目录相关
force-cpdir src_dir_name dst_dir_name
force-rmdir dir_name
mkdir dir_name
rmdir dir_name
list_files dir_name [pattern]
list_files_append_file file_name dir_name [pattern]
list_files_write_file file_name dir_name [pattern]
# 文件相关
diff_files file_name1 file_name2
remove_files_wildcard dir_name pattern [retry]
write_file file_name [terminator]
append_file file_name [terminator]
cat_file file_name
chmod octal_mode file_name
copy_file from_file to_file [retry]
copy_files_wildcard src_dir_name dst_dir_name pattern [retry]
file_exists file_name [retry]
move_file from_name to_name [retry]
output file_name
remove_file file_name [retry]

# 结果集
lowercase_result
result_format version
sorted_result
partially_sorted_result start_column
query_horizontal statement
query_vertical statement
replace_numeric_round precision
replace_regex /pattern/replacement/[i] ...
replace_result from_val to_val [from_val to_val] ...

# 连接
ping
reset_connection
dirty_close connection_name
send_quit connection
send_shutdown
shutdown_server [timeout]
sync_slave_with_master [connection_name]

# 复制
save_master_pos
sync_with_master offset
wait_for_slave_to_stop

详见官方手册:MySQL: mysqltest Commands

编写规范

  1. 尽可能避免每行超过 80 个字符;
  2. #开头,作为注释;
  3. 缩进使用空格,避免使用 tab;
  4. SQL 语句使用相同的风格,包括关键字大写,其它变量、表名、列名等小写;
  5. 增加合适的注释。特别是文件的开头,注释出测试的目的、可能的引用或者修复的 bug 编号;
  6. 为了避免可能的冲突,习惯上表命名使用 t1、t2…,视图命名使用 v1、v2…;

异常调试

本小节已添加到《MySQL 测试框架 MTR 系列教程(一):入门篇》,看过前文的可跳过本节。

分析日志

默认情况下,在目录 mysql-test/var/log/中有日志生成(若指定 --vardir 参数,则以该参数路径为准),分析该日志也能得到一些有用信息。

比如 启动失败,则可以查看 bootstrap.log 文件,去掉命令中的 --bootstrap 并运行即可启动对应的 MySQL 服务来验证、调试。

verbose 参数

启动 mtr 时加 --verbose 参数,定位到引用的脚本位置后可以配置 --echo 命令修改调试。

如果加上 --verbose 打印的内容还不够详细,可以再加一个,即 --verbose --verbose,能打印出 mtr perl 脚本中的日志信息。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
wslu@ubuntu:/data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test$ perl mysql-test-run.pl --timer  --force --parallel=1 --vardir=var-rpl --suite=rpl --verbose
Logging: mysql-test-run.pl --timer --force --parallel=1 --vardir=var-rpl --suite=rpl --verbose
> exe_name: mysqld
MySQL Version 8.0.29
Checking supported features
- Binaries are debug compiled
> Testing FIPS: --test-ssl-fips-mode 0 error:0F06D065:common libcrypto routines:FIPS_mode_set:fips mode not supported

Using suite(s): rpl
Collecting tests
> Collecting: rpl
> suitedir: /data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/suite/rpl
> testdir: /data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/suite/rpl/t
> resdir: /data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/suite/rpl/r
> Read combinations file /data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/suite/rpl/combinations.
- Adding combinations for rpl
> Collecting: i_rpl
Removing old var directory
> opt_vardir: /data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var-rpl
> Removing /data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var
> Removing /data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var-rpl/
> Removing /data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var-rpl/tmp/
Creating var directory '/data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var-rpl'
> Creating /data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var-rpl
Installing system database
### safe_path: /data/work/mysql/mysql80-install.bak_asan_ubsan/bin//mysqltest_safe_process --verbose -- /data/work/mysql/mysql80-install.bak_asan_ubsan/bin/mysqld --no-defaults --initialize-insecure --loose-skip-ndbcluster --tmpdir=/data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var-rpl/tmp/ --core-file --datadir=/data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var-rpl/data/ --secure-file-priv=/data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var-rpl --innodb_buffer_pool_size=24M --innodb-log-file-size=5M --innodb_autoextend_increment=8 --character-sets-dir=/data/work/mysql/mysql80-install.bak_asan_ubsan/share/charsets --loose-auto_generate_certs=OFF --loose-sha256_password_auto_generate_rsa_keys=OFF --loose-caching_sha2_password_auto_generate_rsa_keys=OFF --init-file=/data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var-rpl/tmp/bootstrap.sql
Using parallel: 1

==============================================================================
TEST NAME RESULT TIME (ms) COMMENT
------------------------------------------------------------------------------
> Client connected
worker[1] > mtr_ping_port: 13000
worker[1] > FREE
worker[1] > mtr_ping_port: 13001
worker[1] > FREE
worker[1] > mtr_ping_port: 13002
worker[1] > FREE
worker[1] > mtr_ping_port: 13003
worker[1] > FREE
......
worker[1] > mtr_ping_port: 13029
worker[1] > FREE
worker[1] > Using MTR_BUILD_THREAD 300, with reserved ports 13000..13029
worker[1] Creating var directory '/data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var-rpl'
worker[1] > result: , file_mode: 0
[ 0%] rpl.rpl_atomic_ddl [ skipped ] Test needs 'big-test' or 'only-big-test' option.
[ 0%] rpl.rpl_atomic_ddl_no_binlog [ skipped ] Test needs 'big-test' or 'only-big-test' option.
[ 0%] rpl.rpl_binlog_cache_encryption [ skipped ] Test needs 'big-test' or 'only-big-test' option.
[ 0%] rpl.rpl_filters_error_cases_on_startup [ skipped ] Test needs 'big-test' or 'only-big-test' option.
[ 0%] rpl.rpl_group_commit_deadlock [ skipped ] Test needs 'big-test' or 'only-big-test' option.
[ 0%] rpl.rpl_group_commit_deadlock_myisam [ skipped ] Test needs 'big-test' or 'only-big-test' option.
[ 0%] rpl.rpl_innodb_auto_increment [ skipped ] Test needs 'big-test' or 'only-big-test' option.
[ 0%] rpl.rpl_killed_ddl [ skipped ] Test needs 'big-test' or 'only-big-test' option.
[ 0%] rpl.rpl_log_info_repository_persistence_assign_gtids_to_anonymous_transactions [ skipped ] Test needs 'big-test' or 'only-big-test' option.
[ 0%] rpl.rpl_log_info_repository_persistence_require_row [ skipped ] Test needs 'big-test' or 'only-big-test' option.
[ 0%] rpl.rpl_log_info_repository_persistence_require_table_primary_key_check [ skipped ] Test needs 'big-test' or 'only-big-test' option.
[ 0%] rpl.rpl_row_crash_safe [ skipped ] Test needs 'big-test' or 'only-big-test' option.
[ 0%] rpl.rpl_row_mts_rec_crash_safe [ skipped ] Test needs 'big-test' or 'only-big-test' option.
[ 0%] rpl.rpl_stm_mixed_crash_safe [ skipped ] Test needs 'big-test' or 'only-big-test' option.
[ 0%] rpl.rpl_stm_mixed_mts_rec_crash_safe [ skipped ] Test needs 'big-test' or 'only-big-test' option.
[ 0%] rpl.rpl_stm_mixed_mts_rec_crash_safe_checksum [ skipped ] Test needs 'big-test' or 'only-big-test' option.
[ 0%] rpl.rpl_io_thd_wait_for_disk_space_stress [ disabled ] BUG#23581287 Disabled until bug is fixed.
[ 0%] rpl.rpl_writeset_add_unique_key [ disabled ] Bug#33134835 RPL_WRITESET_ADD_UNIQUE_KEY FAILS SPORADICALLY
worker[1] > Running test: rpl.rpl_plugin_load
worker[1] > Setting timezone: GMT-3
worker[1] > Cleaning datadirs...
worker[1] > clean_dir: /data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var-rpl/tmp
worker[1] > unlink: '/data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var-rpl/tmp/bootstrap.sql'
worker[1] > Generating my.cnf from '/data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/suite/rpl/my.cnf'
worker[1] > MASTER_MYPORT = 13000
worker[1] > MASTER_MYSOCK = /data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var-rpl/tmp/mysqld.1.sock
worker[1] > MASTER_X_MYSOCK = /data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var-rpl/tmp/mysqlx.1.sock
worker[1] > SLAVE_MYPORT = 13002
worker[1] > SLAVE_MYSOCK = /data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var-rpl/tmp/mysqld.2.sock
worker[1] > SLAVE_X_MYSOCK = /data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var-rpl/tmp/mysqlx.2.sock
worker[1] > mysqld_start: [' --plugin-dir=/data/work/mysql/mysql80-install.bak_asan_ubsan/lib/plugin', '--binlog-format=mixed ']

### safe_path: /data/work/mysql/mysql80-install.bak_asan_ubsan/bin//mysqltest_safe_process --verbose -- /data/work/mysql/mysql80-install.bak_asan_ubsan/bin/mysqld --defaults-group-suffix=.1 --defaults-file=/data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test/var-rpl/my.cnf --log-output=file --loose-debug-sync-timeout=600 --plugin-dir=/data/work/mysql/mysql80-install.bak_asan_ubsan/lib/plugin --binlog-format=mixed --core-file
worker[1] > Started [mysqld.1 - pid: 61921, winpid: 61921]
worker[1] > mysqld_start: [' --plugin-dir=/data/work/mysql/mysql80-install.bak_asan_ubsan/lib/plugin', '--binlog-format=mixed ']

......

脚本自身支持 debug 参数

如果引用(source)的脚本支持 debug 参数,比如常用的 $rpl_debug,则可以修改相应的 .inc 文件以获得更多的 debug 信息。

perl 的调试模式

添加-d 参数可进入 perl 语言的 debug 模式。示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
wslu@ubuntu:/data/work/mysql/mysql80-install.bak_asan_ubsan/mysql-test$ perl -d mysql-test-run.pl --timer  --force --parallel=1 --vardir=var-rpl --suite=rpl

Loading DB routines from perl5db.pl version 1.60
Editor support available.

Enter h or 'h h' for help, or 'man perldebug' for more help.

main::(mysql-test-run.pl:54): push @INC, ".";
DB<1> l
54==> push @INC, ".";
55
56: use My::ConfigFactory;
57: use My::CoreDump;
58: use My::File::Path; # Patched version of File::Path
59: use My::Find;
60: use My::Options;
61: use My::Platform;
62: use My::SafeProcess;
63: use My::SysInfo;
DB<1> n
main::(mysql-test-run.pl:72): require "lib/mtr_gcov.pl";
DB<1> l
72==> require "lib/mtr_gcov.pl";
73: require "lib/mtr_gprof.pl";
74: require "lib/mtr_io.pl";
75: require "lib/mtr_lock_order.pl";
76: require "lib/mtr_misc.pl";
77: require "lib/mtr_process.pl";
78
79: our $secondary_engine_support = eval 'use mtr_secondary_engine; 1';
80
81 # Global variable to keep track of completed test cases
DB<1>

调试模式常用命令:

1
2
3
4
5
6
h       查看帮助文档
c line 运行到指定行
n 运行到下一行
s 跳到函数内部运行
l 查看代码
q 退出

参考

MySQL: The MySQL Test Framework

MySQL: Writing Test Cases

MySQL: mysqltest Language Reference

MySQL: Creating and Executing Unit Tests

mysqltest 语法整理 - 叶落 kiss - 博客园 (cnblogs.com)


欢迎关注我的微信公众号【数据库内核】:分享主流开源数据库和存储引擎相关技术。

欢迎关注公众号数据库内核
标题 网址
GitHub https://dbkernel.github.io
知乎 https://www.zhihu.com/people/dbkernel/posts
思否(SegmentFault) https://segmentfault.com/u/dbkernel
掘金 https://juejin.im/user/5e9d3ed251882538083fed1f/posts
CSDN https://blog.csdn.net/dbkernel
博客园(cnblogs) https://www.cnblogs.com/dbkernel
文章目录
  1. 1. 单元测试
    1. 1.1. 简介
    2. 1.2. 示例
  2. 2. 代码覆盖率测试
  3. 3. 压力测试
  4. 4. SQL 测试 - mtr/mysqltest 语法
    1. 4.1. 语法格式
    2. 4.2. command 列表
      1. 4.2.1. error 处理预期错误
      2. 4.2.2. disable_abort_on_error / enable_abort_on_error
      3. 4.2.3. disable_query_log / enable_query_log
      4. 4.2.4. connect
      5. 4.2.5. connection
      6. 4.2.6. disconnect
      7. 4.2.7. exec
      8. 4.2.8. perl [terminator]
      9. 4.2.9. vertical_results/horizontal_results
      10. 4.2.10. exit
      11. 4.2.11. let
      12. 4.2.12. inc/dec
      13. 4.2.13. eval
      14. 4.2.14. query
      15. 4.2.15. send
      16. 4.2.16. send_eval
      17. 4.2.17. reap
      18. 4.2.18. echo
      19. 4.2.19. query_get_value
      20. 4.2.20. source
      21. 4.2.21. sleep
      22. 4.2.22. replace_column
      23. 4.2.23. expr 命令 (MySQL 8 之后可用)
      24. 4.2.24. if
      25. 4.2.25. while
      26. 4.2.26. 其他命令
    3. 4.3. 编写规范
  5. 5. 异常调试
    1. 5.1. 分析日志
    2. 5.2. verbose 参数
    3. 5.3. 脚本自身支持 debug 参数
    4. 5.4. perl 的调试模式
  6. 6. 参考
|