Author: 慎追
每当开发完一个新的功能后,要想让其稳定的上线的前提是必须完成回归测试,回归测试定义为一种软件测试,用于确认最近的程序或代码更改未对现有功能产生负面影响。回归测试只不过是已经执行的测试用例的全部或部分选择,这些测试用例被重新执行以确保现有功能正常工作。对于最先进的开源数据库Postgresql当然也提供了它的回归测试。我们在官方文档中可以找到其具体的使用方法。
make check # parallel running the Tests Against a Temporary Installation
make installcheck # serial running the Tests Against an Existing Installation
make installcheck-paralle # parallel running the Tests Against an Existing Installation
同样的,当我们开发完一个较重要的feature后,也需要添加一些对应新test case,以免以后开发的功能会影响到这个feature。不巧的是官方文档并没有给出添加test case的说明,添加完自己的test case后,我们如何单独的运行这个case,也无法在官网中找到答案,所以我们这次就来对于Postgres regress一探究竟,看一看Postgres的回归测试是如果做的。首先我们进入到Postgres 源码中的src/test/regress中,根据阅读源码的经验,我们首先读一下REDAME是个明智之举。
Documentation concerning how to run these regression tests and interpret the
results can be found in the PostgreSQL manual, in the chapter "Regression Tests".
令人尴尬的是README将我们又推向了官方文档,所以我们不得不自己到代码中去一探究竟。既然官方让我们运行make check,那么我们先去Makefile是一个不错的选择。
在makefile中,我们很容易看到make installcheck 相当于编译源码后运行了下面的命令:
pg_regress --inputdir=. --bindir='tmp_basedir_polardb_pg_1100_bld/bin'
--dlpath=. --max-concurrent-tests=20 --user=regress --schedule=./serial_schedule
我们可以从Makefile看出src/test/regress下面的 pg_regress.c pg_regress_main.c regress.c被编译成了一个二进制为 pg_regress。执行以下命令:
./pg_regress -h
####################################\
PostgreSQL regression test driver
Usage:
pg_regress [OPTION]... [EXTRA-TEST]...
Options:
--bindir=BINPATH use BINPATH for programs that are run;
if empty, use PATH from the environment
--config-auth=DATADIR update authentication settings for DATADIR
--create-role=ROLE create the specified role before testing
--dbname=DB use database DB (default "regression")
--debug turn on debug mode in programs that are run
--dlpath=DIR look for dynamic libraries in DIR
--encoding=ENCODING use ENCODING as the encoding
-h, --help show this help, then exit
--inputdir=DIR take input files from DIR (default ".")
--launcher=CMD use CMD as launcher of psql
--load-extension=EXT load the named extension before running the
tests; can appear multiple times
--load-language=LANG load the named language before running the
tests; can appear multiple times
--max-connections=N maximum number of concurrent connections
(default is 0, meaning unlimited)
--max-concurrent-tests=N maximum number of concurrent tests in schedule
(default is 0, meaning unlimited)
--outputdir=DIR place output files in DIR (default ".")
--schedule=FILE use test ordering schedule from FILE
(can be used multiple times to concatenate)
--temp-instance=DIR create a temporary instance in DIR
--use-existing use an existing installation
--no-restrict-auth disable restricted authentication settings
-V, --version output version information, then exit
Options for "temp-instance" mode:
--no-locale use C locale
--port=PORT start postmaster on PORT
--temp-config=FILE append contents of FILE to temporary config
--temp-initdb-opts=OPTS additional flags passed to initdb
Options for using an existing installation:
--host=HOST use postmaster running on HOST
--port=PORT use postmaster running at PORT
--user=USER connect as USER
The exit status is 0 if all tests passed, 1 if some tests failed, and 2
if the tests could not be run for some reason.
Report bugs to <support@enterprisedb.com>.
pg_regress的入口在pg_regress_main.c中:
int
main(int argc, char *argv[])
{
return regression_main(argc, argv, psql_init, psql_start_test);
}
其中psql_init是初始化regress时连接的数据库,psql_start_test对于每一个test case调用执行。下面我就来简单列出regression_main中的逻辑,如果有兴趣的同学可以自己阅读源码。
以上为pg_regress的源码执行流程,其中读取schedule文件是关键的一个参数,我们知道在回归测试时,有并行和串行之分。本质上就是在–schedule 指定/test/regress下的serial_schedule 和parallel_schedule 文件的区别,我们打开这两个文件对比入下:
test: tablespace
test: boolean
test: char
test: name
test: varchar
test: text
......
# ----------
# The first group of parallel tests
# ----------
test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money rangetypes pg_lsn regproc
......
很明了的发现串行schedule内是一个test对应一个case,而并行schedule中一个test对应这多个case。在源码中, pg_regress会对去每一行test中,每个case 启动一个进程,这就是串行和并行测试的区别了。
经过前面的分析,我们可以发现添加新的test case 可以如下步骤:
尽管我们已经掌握了regress test的基本原理,就是并行或者串行的执行sql和期望的执行结果进行比对。但是我们有时候会需要这种需求,并不完全并行或者执行一些sql去测试,比如我们需要两个以上的session,相互交互的以固定顺序执行sql,一般用来测试事务和lock以及隔离级别等。然而在regress下我们却无法满足这种schedule。所以下面我们就来介绍以下Postgres的另一个重要的测试工具pg_isolation_regress。
同样地,我们去了解一下这个工具如何使用,就需要进入它的源码目录一探究竟,目录位于src/test/isolation。幸运的是它提供了一个看起来相当丰富的README 。首段翻译如下:
该目录包含一组针对并发行为的PostgreSQL的的测试。这些测试需要运行多个交互事务,
这需要管理多个并发连接,因此无法使用正常的pg_regress程序进行测试。
名字“隔离”来自这个事实,原来的动机是测试可序列化隔离级别;
但测试其他类型的并发行为也被添加了。
setup { <SQL> }
The given SQL block is executed once, in one session only, before running
the test. Create any test tables or other required objects here. This
part is optional. Multiple setup blocks are allowed if needed; each is
run separately, in the given order. (The reason for allowing multiple
setup blocks is that each block is run as a single PQexec submission,
and some statements such as VACUUM cannot be combined with others in such
a block.)
teardown { <SQL> }
The teardown SQL block is executed once after the test is finished. Use
this to clean up in preparation for the next permutation, e.g dropping
any test tables created by setup. This part is optional.
session "<name>"
There are normally several "session" parts in a spec file. Each
session is executed in its own connection. A session part consists
of three parts: setup, teardown and one or more "steps". The per-session
setup and teardown parts have the same syntax as the per-test setup and
teardown described above, but they are executed in each session. The
setup part typically contains a "BEGIN" command to begin a transaction.
Each step has the syntax
step "<name>" { <SQL> }
where <name> is a name identifying this step, and SQL is a SQL statement
(or statements, separated by semicolons) that is executed in the step.
Step names must be unique across the whole spec file.
permutation "<step name>" ...
A permutation line specifies a list of steps that are run in that order.
Any number of permutation lines can appear. If no permutation lines are
given, the test program automatically generates all possible orderings
of the steps from each session (running the steps of any one session in
order). Note that the list of steps in a manually specified
"permutation" line doesn't actually have to be a permutation of the
available steps; it could for instance repeat some steps more than once,
or leave others out.
读完了readme,同时又勾起了我们的好奇心,pg_isolation_regress其内部到底是如何实现的呢?
打开pg_isolation_regress源码我们就会看到isolation_main文件,在Makefile中isolation_main.c, isolationtester.c 被编译了成了一个叫做isolationtester的二进制。我们就从isolation_main 入口开看一看其逻辑。其main函数如下:
int
main(int argc, char *argv[])
{
return regression_main(argc, argv, isolation_init, isolation_start_test);
}
咋一看,和pg_regress没什么区别呀? 仔细一看原来是,其传奇的的函数指针不同。前面我们了解了pg_regress的参数是psql_init 和psql_start_test ,pg_regress的原理对于给一个case启动以进程用psql来执行case中sql。 但是在pg_isolation_regress中实现了多个session交互的逻辑, 单单使用psql是无法做到的,所以原来,pg_isolation_regress自己实现了一个isolation_start_test来执行每个case,我们进入isolation_start_test 就会发现,其使用的一个和psql不一样的工具就叫做isolationtester。 其实现在isolationtester.c 中,有兴趣的同学可以自己去品尝。其中的主要逻辑如下:
本文介绍了如何使用Postgres的测试工具进行回归测试。其中主要介绍了两个工具分别为pg_regresss和 pg_isolation_regress。这两个工具都是可以用来回归的,pg_regresss的原理是使用psql来运行每一个case,可以串行的运行,也可以并行的运行。而pg_isolation_regress 是使用libpq来开启多个连接进行交互的运行sql,适用于并发锁的测试。