简介
GLib提供了一套测试框架,可以帮助我们实现自动测试,而且还能生成报告,下面简单介绍一下这个框架的基本使用方法。
数据结构
enum GTestFileType
enum GTestTrapFlags
enum GTestSubprocessFlags
typedef GTestCase // 测试例
typedef GTestSuite // 测试套件,一个TestSuite可包含多个TestCase
函数列表
void g_test_minimized_result ()
void g_test_maximized_result ()
void g_test_init ()
#define g_test_initialized
#define g_test_quick
#define g_test_slow
#define g_test_thorough
#define g_test_perf
#define g_test_verbose
#define g_test_undefined
#define g_test_quiet
gboolean g_test_subprocess ()
int g_test_run ()
void (*GTestFunc) ()
void g_test_add_func ()
void (*GTestDataFunc) ()
void g_test_add_data_func ()
void g_test_add_data_func_full ()
#define g_test_add()
gchar * g_test_build_filename ()
const gchar * g_test_get_filename ()
const gchar * g_test_get_dir ()
void g_test_fail ()
void g_test_skip ()
void g_test_incomplete ()
gboolean g_test_failed ()
void g_test_message ()
void g_test_bug_base ()
void g_test_bug ()
gboolean (*GTestLogFatalFunc) ()
void g_test_log_set_fatal_handler ()
void g_test_timer_start ()
double g_test_timer_elapsed ()
double g_test_timer_last ()
void g_test_queue_free ()
void g_test_queue_destroy ()
#define g_test_queue_unref()
void g_test_expect_message ()
#define g_test_assert_expected_messages
void g_test_trap_subprocess ()
gboolean g_test_trap_has_passed ()
gboolean g_test_trap_reached_timeout ()
#define g_test_trap_assert_passed
#define g_test_trap_assert_failed
#define g_test_trap_assert_stdout()
#define g_test_trap_assert_stdout_unmatched()
#define g_test_trap_assert_stderr()
#define g_test_trap_assert_stderr_unmatched()
gboolean g_test_trap_fork ()
#define g_test_rand_bit
gint32 g_test_rand_int ()
gint32 g_test_rand_int_range ()
double g_test_rand_double ()
double g_test_rand_double_range ()
#define g_assert()
#define g_assert_not_reached
#define g_assert_cmpstr()
#define g_assert_cmpint()
#define g_assert_cmpuint()
#define g_assert_cmphex()
#define g_assert_cmpfloat()
#define g_assert_cmpmem()
#define g_assert_no_error()
#define g_assert_error()
#define g_assert_true()
#define g_assert_false()
#define g_assert_null()
#define g_assert_nonnull()
void g_test_set_nonfatal_assertions ()
void (*GTestFixtureFunc) ()
GTestCase * g_test_create_case ()
GTestSuite * g_test_create_suite ()
GTestSuite * g_test_get_root ()
void g_test_suite_add ()
void g_test_suite_add_suite ()
int g_test_run_suite ()
函数功能分类
初始化
void g_test_init ()
#define g_test_initialized
断言函数族
#define g_assert()
#define g_assert_not_reached
#define g_assert_cmpstr()
#define g_assert_cmpint()
#define g_assert_cmpuint()
#define g_assert_cmphex()
#define g_assert_cmpfloat()
#define g_assert_cmpmem()
#define g_assert_no_error()
#define g_assert_error()
#define g_assert_true()
#define g_assert_false()
#define g_assert_null()
#define g_assert_nonnull()
void g_test_set_nonfatal_assertions ()
带参数的测试例
void (*GTestFunc) ()
void g_test_add_func ()
void (*GTestDataFunc) ()
void g_test_add_data_func ()
void g_test_add_data_func_full ()
#define g_test_add()
运行测试例
int g_test_run ()
夹具
void (*GTestFixtureFunc) ()
工作模式函数族
#define g_test_quick
#define g_test_slow
#define g_test_thorough
#define g_test_perf
#define g_test_verbose
#define g_test_undefined
#define g_test_quiet
子进程运行测试例及子进程断言函数
void g_test_trap_subprocess ()
gboolean g_test_trap_has_passed ()
gboolean g_test_trap_reached_timeout ()
#define g_test_trap_assert_passed
#define g_test_trap_assert_failed
#define g_test_trap_assert_stdout()
#define g_test_trap_assert_stdout_unmatched()
#define g_test_trap_assert_stderr()
#define g_test_trap_assert_stderr_unmatched()
gboolean g_test_trap_fork ()
随机数生成函数
#define g_test_rand_bit
gint32 g_test_rand_int ()
gint32 g_test_rand_int_range ()
double g_test_rand_double ()
double g_test_rand_double_range ()
计时器
void g_test_timer_start ()
double g_test_timer_elapsed ()
double g_test_timer_last ()
其他
void g_test_minimized_result ()
void g_test_maximized_result ()
gboolean g_test_subprocess ()
gchar * g_test_build_filename ()
const gchar * g_test_get_filename ()
const gchar * g_test_get_dir ()
void g_test_fail ()
void g_test_skip ()
void g_test_incomplete ()
gboolean g_test_failed ()
void g_test_message ()
void g_test_bug_base ()
void g_test_bug ()
gboolean (*GTestLogFatalFunc) ()
void g_test_log_set_fatal_handler ()
void g_test_queue_free ()
void g_test_queue_destroy ()
#define g_test_queue_unref()
void g_test_expect_message ()
#define g_test_assert_expected_messages
GTestCase * g_test_create_case ()
GTestSuite * g_test_create_suite ()
GTestSuite * g_test_get_root ()
void g_test_suite_add ()
void g_test_suite_add_suite ()
int g_test_run_suite ()
函数功能说明及综合演示
基本演示
首先举个例子演示如何使用GLib测试框架。
源码见glib_examples\glib_testframe\glib_testframe_basic
#include <glib.h>
static void test_case (void)
{
gint x = 2, y = 3, z = 5;
g_print("%s \n", x+y==z ? "Equal" : "Not equal");
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/test/aaa", test_case);
return g_test_run();
}
运行结果:
/test/testcase: Equal!
OK
上述程序使用GLib测试框架的三个函数实现了一个测试用例,g_test_init、g_test_add_func和g_test_run。test_case是测试用例,该测试用例的功能为,如果x+y的值和z的值相等,则输入Equal,否则输出Not equal。程序运行结果是Equal,这个是测试用例的输出。在测试用例输出之外,还有/test/aaa:OK这样的输出,这就是测试框架本身的输出。
可以看出,测试框架知道测试运行结果是否正确。那测试例是怎样得知运行结果是正确的呢,毕竟test_case是一个无返回值的函数,难道x+y==z这个表达式为真测试例就正确?将x的初始值改为3,此时输出Not Equal,但测试例仍是OK,没有达到想要的效果。
要想让测试例返回失败,就需要用到接下来要介绍的测试框架的断言函数:g_assert_xxx()系列函数。
断言函数族
断言函数族是一组以g_assert_开头的函数。为了实现自动化测试,g_print在测试用例中很少出现,因为测试框架是无法识别用户输出的,将所需要的预期值和实际返回值都传给g_assert_xxx()函数族就可以了。
断言函数族示例程序:
源码见glib_examples\glib_testframe\glib_testframe_assert
#include <glib.h>
static void test_case (void)
{
gint x = 2, y = 3, z = 5;
g_assert( x + y == z);
}
static void test_assert(void)
{
g_assert( FALSE );
}
static void test_assert_not_reached(void)
{
g_assert_not_reached();
}
static void test_assert_cmpstr(void)
{
gchar *s1 = "abcd";
g_assert_cmpstr(s1, ==, "abcd");
}
static void test_assert_cmpint(void)
{
gint n1 = 100;
g_assert_cmpint(n1, == , 100);
}
static void test_assert_cmpuint(void)
{
guint n1 = 200;
g_assert_cmpuint(n1, ==, 200);
}
static void test_assert_cmphex(void)
{
guint n1 = 0xdeadbeef;
g_assert_cmphex(n1, ==, 0xdeedbeef);
}
static void test_assert_cmpfloat(void)
{
gfloat n1 = 0.00001;
g_assert_cmpfloat(n1, ==, 0.00001);
}
static void test_assert_no_error(void)
{
GDir *dir = NULL;
GError *err = NULL;
dir = g_dir_open("/tmp", 0, &err);
g_assert (dir != NULL);
g_assert_no_error(err);
if(NULL != err) {
g_error_free(err);
}
}
static void test_assert_error(void)
{
GDir *dir;
GError *error;
error = NULL;
dir = g_dir_open ("/pfrkstrf", 0, &error);
g_assert (dir == NULL);
g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT);
if(NULL != error) {
g_error_free (error);
}
}
static void test_assert_true(void)
{
g_assert_true(TRUE);
}
static void test_assert_false(void)
{
g_assert_false(FALSE);
}
static void test_assert_null(void)
{
gpointer *p = NULL;
g_assert_null(p);
}
static void test_assert_nonnull(void)
{
gpointer *p = NULL;
g_assert_nonnull(p);
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/test/testcase", test_case);
g_test_add_func("/test/test_assert", test_assert);
g_test_add_func("/test/test_assert_not_reached", test_assert_not_reached);
g_test_add_func("/test/test_assert_cmpstr", test_assert_cmpstr);
g_test_add_func("/test/test_assert_cmpint", test_assert_cmpint);
g_test_add_func("/test/test_assert_cmpuint", test_assert_cmpuint);
g_test_add_func("/test/test_assert_cmphex", test_assert_cmphex);
g_test_add_func("/test/test_assert_cmpfloat", test_assert_cmpfloat);
g_test_add_func("/test/test_assert_no_error", test_assert_no_error);
g_test_add_func("/test/test_assert_error", test_assert_error);
g_test_add_func("/test/test_assert_true", test_assert_true);
g_test_add_func("/test/test_assert_false", test_assert_false);
g_test_add_func("/test/test_assert_null", test_assert_null);
g_test_add_func("/test/test_assert_nonnull", test_assert_nonnull);
return g_test_run();
}
运行结果:
[root@centos7 build]# ./glib_testframe_assert
/test/testcase: OK
/test/test_assert: **
ERROR:/root/glib_examples/glib_testframe/glib_testframe_assert/glib_testframe_assert.c:13:test_assert: assertion failed: (FALSE)
已放弃(吐核)
运行到第二个测试用例的时候,程序崩溃了,后面的测试例也无法执行。这时候,可以使用gtester测试工具。用法如下:
#gtester -k --verbose -o test.log glib_testframe_assert
参数含义为:
- -k 当测试例失败程序崩溃后仍继续运行(具体如何实现的后面会讲)
- –verbose 正确的测试用例也显示出来
- -o 运行结果保存到test.log
重新执行,运行结果如下:
[root@centos7 build]# gtester -k --verbose -o test.log glib_testframe_assert
TEST: glib_testframe_assert... (pid=65247)
/test/testcase: **
ERROR:/root/glib_examples/glib_testframe/glib_testframe_assert/glib_testframe_assert.c:13:test_assert: assertion failed: (FALSE)
OK
/test/test_assert: FAIL
GTester: last random seed: R02Sf19cedfc4cdc419cea8bc3f82bc52e81
(pid=65250)
/test/test_assert_not_reached: **
ERROR:/root/glib_examples/glib_testframe/glib_testframe_assert/glib_testframe_assert.c:18:test_assert_not_reached: code should not be reached
FAIL
GTester: last random seed: R02Sf5f5a78c06b92bbaa0b26fa9fc2087f9
(pid=65252)
**
ERROR:/root/glib_examples/glib_testframe/glib_testframe_assert/glib_testframe_assert.c:42:test_assert_cmphex: assertion failed (n1 == 0xdeedbeef): (0xdeadbeef == 0xdeedbeef)
/test/test_assert_cmpstr: OK
/test/test_assert_cmpint: OK
/test/test_assert_cmpuint: OK
/test/test_assert_cmphex: FAIL
GTester: last random seed: R02Sc6bdaabfa84bdfd15e2d0f862a4bd9a5
(pid=65254)
/test/test_assert_cmpfloat: **
ERROR:/root/glib_examples/glib_testframe/glib_testframe_assert/glib_testframe_assert.c:48:test_assert_cmpfloat: assertion failed (n1 == 0.00001): (9.99999975e-06 == 1e-05)
FAIL
GTester: last random seed: R02S43f7bfae67e271545d650bce782c357d
(pid=65256)
**
ERROR:/root/glib_examples/glib_testframe/glib_testframe_assert/glib_testframe_assert.c:100:test_assert_nonnull: 'p' should not be NULL
/test/test_assert_no_error: OK
/test/test_assert_error: OK
/test/test_assert_true: OK
/test/test_assert_false: OK
/test/test_assert_null: OK
/test/test_assert_nonnull: FAIL
GTester: last random seed: R02S3aa2624f45d86e36bd20a0c13411c2c2
(pid=65258)
FAIL: glib_testframe_assert
[root@centos7 build]#
可以看到所有的测试例都运行到了,有些运行成功,有些运行失败。
断言函数族各函数具体含义如下:
- #define g_assert():期望表达式为真,如果表达式为假则产生断言
- #define g_assert_not_reached:期望不运行到此处,如果运行到此函数则产生断言
- #define g_assert_cmpstr():期望两个字符串比较结果为真,如果字符串比较结果为假则产生断言
- #define g_assert_cmpint():期望两个int值比较(大于小于等于)结果为真,如果结果为假则产生断言
- #define g_assert_cmpuint():期望两个uint值比较结果为真,如果结果为假则产生断言
- #define g_assert_cmphex():期望两个十六进制值比较结果为真,如果结果为假则产生断言
- #define g_assert_cmpfloat():期望两个float值比较结果为真,如果结果为假则产生断言
- #define g_assert_cmpmem():期望两段内存比较结果为真,如果结果为假则产生断言
- #define g_assert_no_error():期望GError没有被赋值,如果GError被赋值了则产生断言
- #define g_assert_error():判断GError的值是否为期望值,如果和期望值不同则产生断言
- #define g_assert_true():期望表达式为真,如果表达式为假则产生断言
- #define g_assert_false():期望表达式为假,如果表达式为真则产生断言
- #define g_assert_null():期望表达值为空,如果表达式非空则产生断言
- #define g_assert_nonnull():期望表达式非空,如果表达式为空则产生断言
g_assert_xxx类函数特点非常鲜明,基本上都是期望表达式为真,如果表达式为假则产生断言退出程序,这和C语言的assert()效果相同。
有些函数可以被其他函数所替代,所以掌握其中的三四个基本上就够用了。
- g_assert()
- g_assert_cmp_str()
- g_assert_cmp_int()
- g_assert_ture()
- g_assert_null()
带参数的测试例
测试例可以带参数运行,示例如下。
源码见glib_examples\glib_testframe\glib_testframe_param
#include <glib.h>
#define TEST_CASE_USER_DATA "my test data"
void test_case_func(void)
{
return;
}
void test_case_func_with_data(gconstpointer user_data)
{
g_assert_cmpstr((gchar *)user_data, ==, TEST_CASE_USER_DATA);
}
void _func_with_data_full_free_func(gpointer data)
{
GList *l = (GList *)data;
if(NULL != l) {
if(NULL != l->data) {
g_free(l->data);
}
g_free(l);
}
}
void test_case_func_with_data_full(gconstpointer user_data)
{
GList *l = (GList *)user_data;
g_assert(l);
g_assert_cmpstr((gchar *)l->data, ==, TEST_CASE_USER_DATA);
}
int main(int argc, char **argv)
{
GList *l = NULL;
l = g_new0(GList, 1);
l->data = (gpointer)g_strdup(TEST_CASE_USER_DATA);
g_test_init(&argc, &argv, NULL);
g_test_add_func("/test/testcase", test_case_func);
g_test_add_data_func("/test/func_with_data", (gconstpointer)TEST_CASE_USER_DATA, test_case_func_with_data);
g_test_add_data_func_full("/test/func_with_data_full", (gpointer)l, test_case_func_with_data_full, _func_with_data_full_free_func);
return g_test_run();
}
运行结果:
[root@centos7 build]# ./glib_testframe_param
/test/testcase: OK
/test/func_with_data: OK
/test/func_with_data_full: OK
[root@centos7 build]#
夹具
GLib测试框架提供了夹具的功能。下述夹具示例演示的是将一个素数转换成无符号长整形。从例子中我们可以看出,夹具由左夹具和右夹具两个函数组成,左边的我们一般叫做fixture_setup函数,右边的叫做fixture_teardown函数。左夹具负责对测试例的初始化准备工作,右夹具负责测试例运行后的清理工作。
源码见glib_examples\glib_testframe\glib_testframe_fixture
#include <glib.h>
typedef struct {
guint seed;
guint prime;
gchar *msg;
} Fixturetest;
static void
fixturetest_setup (Fixturetest *fix,
gconstpointer test_data)
{
g_assert (test_data == (void*) 0xc0cac01a);
fix->seed = 18;
fix->prime = 19;
fix->msg = g_strdup_printf ("%d", fix->prime);
}
static void
fixturetest_test (Fixturetest *fix,
gconstpointer test_data)
{
guint prime = g_spaced_primes_closest (fix->seed);
g_assert_cmpint (prime, ==, fix->prime);
prime = g_ascii_strtoull (fix->msg, NULL, 0);
g_assert_cmpint (prime, ==, fix->prime);
g_assert (test_data == (void*) 0xc0cac01a);
}
static void
fixturetest_teardown (Fixturetest *fix,
gconstpointer test_data)
{
g_assert (test_data == (void*) 0xc0cac01a);
g_free (fix->msg);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add ("/misc/primetoul", Fixturetest, (void*) 0xc0cac01a, fixturetest_setup, fixturetest_test, fixturetest_teardown);
return g_test_run();
}
运行结果:
[root@centos7 build]# ./glib_testframe_fixture
/misc/primetoul: OK
测试模式
以下几个函数用来控制测试模式
#define g_test_quick
#define g_test_slow
#define g_test_thorough
#define g_test_perf
#define g_test_undefined
#define g_test_verbose
#define g_test_quiet
这几种模式体现在gtester工具的选项中
[root@centos7 build]# gtester
Usage:
gtester [OPTIONS] testprogram...
Help Options:
-h, --help Show this help message
Utility Options:
-v, --version Print version informations
--g-fatal-warnings Make warnings fatal (abort)
-k, --keep-going Continue running after tests failed
-l List paths of available test cases
-m {perf|slow|thorough|quick} Run test cases according to mode
-m {undefined|no-undefined} Run test cases according to mode
-p=TESTPATH Only start test cases matching TESTPATH
-s=TESTPATH Skip test cases matching TESTPATH
--seed=SEEDSTRING Start tests with random seed SEEDSTRING
-o=LOGFILE Write the test log to LOGFILE
-q, --quiet Suppress per test binary output
--verbose Report success per testcase
以上各参数含义如下:
- -m quick :默认运行模式
- -m slow: 与quick模式互斥
- -m thorough: 与slow相同,与quick互斥
- -m perf: 运行在perf模式,当设置成quick模式时,perf模式不生效
- -m undefined: 运行在undefined模式下
- –verbose: 冗余模式,与安静模式互斥,尽可能打印多的信息
- -q: 安静模式,与verbose模式互斥
在编写测试代码时,可以使用这几种模式判断宏判断当前是哪种模式,再决定执行哪些代码。
下面是对上述模式的示例程序。
源码见glib_examples\glib_testframe\glib_testframe_testmode
#include <glib.h>
void test_func_a(void)
{
if(g_test_verbose()) {
g_print("in verbose mode \n");
}
if(g_test_quiet()) {
g_print("in quiet mode \n");
}
}
void test_func_b(void)
{
if(g_test_perf()) {
g_print("\n in perf mode \n");
}
if(g_test_thorough()) {
g_print("\n in thorough mode \n");
}
if(g_test_slow()) {
g_print("\n in slow mode \n");
}
if(g_test_quick()) {
g_print("\n in quick mode \n");
}
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/test1/a", test_func_a);
g_test_add_func("/test2/b", test_func_b);
return g_test_run();
}
编译完后得到glib_testframe_testmode程序,使用gtester工具带参数运行,结果如下。
[root@centos7 build]# gtester -k -m quick --verbose glib_testframe_testmode
TEST: glib_testframe_testmode... (pid=18427)
in quiet mode
in quick mode
/test1/a: OK
/test2/b: OK
PASS: glib_test_frame
[root@centos7 build]# gtester -k -m slow --verbose glib_testframe_testmode
TEST: glib_testframe_testmode... (pid=18431)
in quiet mode
in thorough mode
/test1/a:
in slow mode
OK
/test2/b: OK
PASS: glib_test_frame
[root@centos7 build]# gtester -k -m thorough --verbose glib_testframe_testmode
TEST: glib_testframe_testmode... (pid=18435)
in quiet mode
/test1/a:
in thorough mode
in slow mode
OK
/test2/b: OK
PASS: glib_test_frame
[root@centos7 build]# gtester -k -m perf --verbose glib_testframe_testmode
TEST: glib_testframe_testmode... (pid=18438)
/test1/a: in quiet mode
OK
/test2/b:
in perf mode
in quick mode
OK
PASS: glib_test_frame
[root@centos7 build]# gtester -k --verbose glib_testframe_testmode
TEST: glib_testframe_testmode... (pid=18442)
/test1/a: in quiet mode
OK
in quick mode
/test2/b: OK
PASS: glib_test_frame
[root@centos7 build]# gtester -k -q glib_testframe_testmode
in quiet mode
in quick mode
[root@centos7 build]#
子进程运行测试例及子进程断言函数
子进程相关的测试函数含义如下:
// 创建子进程并运行,第一个参数是要运行的测试例,如果为空则运行自身,第二个参数是子进程超时时间,第三个参数是子进程是否继承父进程输入输出错误文件描述符
void g_test_trap_subprocess ()
// 判断子进程是否运行成功,注:如果运行的子进程如果有断言之类的错误,则表示子进程运行失败
gboolean g_test_trap_has_passed ()
// 判断子进程是否为超时退出,如果子进程是正常退出,则返回的值是FALSE
gboolean g_test_trap_reached_timeout ()
// 期待子进程运行成功,如果运行失败则会抛出断言
#define g_test_trap_assert_passed
// 期待子进程运行失败,如果子进程运行成功,则会抛出断言
#define g_test_trap_assert_failed
// 捕获标准输出,如果未捕获到则抛出断言
#define g_test_trap_assert_stdout()
// 类似g_test_trap_assert_stdout,但期待未捕获到,如果捕获到参数中的内容则抛出断言
#define g_test_trap_assert_stdout_unmatched()
// 捕获错误输出,如果未捕获到则抛出断言
#define g_test_trap_assert_stderr()
// 类似g_test_trap_assert_stderr,但期待未捕获到,如果捕获到参数中的内容则抛出断言
#define g_test_trap_assert_stderr_unmatched()
// 过时函数,同g_test_trap_subprocess
gboolean g_test_trap_fork ()
举例如下:
源码见glib_examples\glib_testframe\glib_testframe_subprocess
#include <glib.h>
static void test_open_close_dir(const gchar *path)
{
GDir *dir = NULL;
GError *error = NULL;
dir = g_dir_open(path, 0, &error);
if(NULL != dir) {
g_dir_close(dir);
} else {
g_print("stdout: cannot open dir \n");
g_printerr("stderr: cannot open dir: %s \n", error->message);
}
}
void test_func_open_close_dir(void)
{
test_open_close_dir("/aabbcc");
}
void test_func_trap_assert_passed(gconstpointer user_data)
{
if(g_test_subprocess()) {
g_assert(NULL != user_data);
return;
}
g_test_trap_subprocess(NULL, 0, 0);
g_test_trap_assert_passed();
}
void test_func_trap_assert_failed(gconstpointer user_data)
{
if(g_test_subprocess()) {
g_assert(NULL != user_data);
return;
}
g_test_trap_subprocess(NULL, 0, 0);
g_test_trap_assert_failed();
}
void test_func_trap_assert_stdout(gconstpointer user_data)
{
if(g_test_subprocess()) {
test_open_close_dir((gchar *)user_data);
return;
}
g_test_trap_subprocess(NULL, 0, 0);
g_test_trap_assert_stdout("*stdout: cannot open*");
}
void test_func_trap_assert_stderr(gconstpointer user_data)
{
if(g_test_subprocess()) {
test_open_close_dir((gchar *)user_data);
return;
}
g_test_trap_subprocess(NULL, 0, 0);
g_test_trap_assert_stderr("*stderr: cannot open*");
}
void test_func_trap_reached_timeout(gconstpointer user_data)
{
if(g_test_subprocess()) {
g_usleep(2000000);
return;
}
g_test_trap_subprocess(NULL, GPOINTER_TO_INT(user_data), 0);
if(GPOINTER_TO_INT(user_data) > 2000000) {
// not timeout, subprocess return success
g_assert_false(g_test_trap_reached_timeout());
} else {
// subprocess timeout
g_assert_true(g_test_trap_reached_timeout());
}
}
void test_func_trap_has_passed(void)
{
g_test_trap_subprocess("/test/base/openclosedir", 0, 0);
g_assert(g_test_trap_has_passed());
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/test/base/openclosedir", test_func_open_close_dir);
g_test_add_data_func("/test/trap/assert_passed", (gconstpointer)"/home", test_func_trap_assert_passed);
g_test_add_data_func("/test/trap/assert_failed", NULL, test_func_trap_assert_failed);
g_test_add_data_func("/test/trap/assert_stdout", (gconstpointer)"/aabbcc", test_func_trap_assert_stdout);
g_test_add_data_func("/test/trap/assert_stderr", (gconstpointer)"/aabbcc", test_func_trap_assert_stderr);
g_test_add_data_func("/test/trap/timeout1", GINT_TO_POINTER(1000000), test_func_trap_reached_timeout);
g_test_add_data_func("/test/trap/timeout2", GINT_TO_POINTER(3000000), test_func_trap_reached_timeout);
g_test_add_func("/test/trap/haspassed", test_func_trap_has_passed);
return g_test_run();
}
运行结果:
[root@centos7 build]# ./glib_testframe_subprocess
/test/base/openclosedir: stdout: cannot open dir
stderr: cannot open dir: Error opening directory '/aabbcc': No such file or directory
OK
/test/trap/assert_passed: OK
/test/trap/assert_failed: OK
/test/trap/assert_stdout: OK
/test/trap/assert_stderr: OK
/test/trap/timeout1: OK
/test/trap/timeout2: OK
/test/trap/haspassed: OK
随机数生成函数
生成随机数供测试使用。更详细的随机数请参考Random Numbers,GRand提供了更详细的使用说明。
#define g_test_rand_bit // 生成位随机数
gint32 g_test_rand_int () // 生成32位int型随机数
gint32 g_test_rand_int_range () // 在区间内生成随机数
double g_test_rand_double () // 生成浮点型随机数
double g_test_rand_double_range () // 在区间内生成浮点型随机数
计时器测试函数
注意这个是计时器,不是定时器。计时器用来计算代码运行所消耗的时间。
更详细的计时器请参考Timers,GTimer提供了计时器更详细的使用说明。
void g_test_timer_start () //计时器开始
double g_test_timer_elapsed () //计时器从start开始到现在所经过的时间
double g_test_timer_last () //上次调用g_test_timer_elapsed的结果
专题
gtester测试例的部分执行
先看一下gtester的-l -p -s选项含义
- -l 列举当前可用测试例的路径
- -p 只执行和-p选项相同的测试例
- -s 不执行和-s选项相同的测试例
接下来以断言函数族为例,讲解一下如何部分执行测试例。先把断言族函数程序中测试例路径修改一下:
源码见glib_examples\glib_testframe\glib_testframe_gtester
#include <glib.h>
static void test_case (void)
{
gint x = 2, y = 3, z = 5;
g_assert( x + y == z);
}
static void test_assert(void)
{
g_assert( FALSE );
}
static void test_assert_not_reached(void)
{
g_assert_not_reached();
}
static void test_assert_cmpstr(void)
{
gchar *s1 = "abcd";
g_assert_cmpstr(s1, ==, "abcd");
}
static void test_assert_cmpint(void)
{
gint n1 = 100;
g_assert_cmpint(n1, == , 100);
}
static void test_assert_cmpuint(void)
{
guint n1 = 200;
g_assert_cmpuint(n1, ==, 200);
}
static void test_assert_cmphex(void)
{
guint n1 = 0xdeadbeef;
g_assert_cmphex(n1, ==, 0xdeedbeef);
}
static void test_assert_cmpfloat(void)
{
gfloat n1 = 0.00001;
g_assert_cmpfloat(n1, ==, 0.00001);
}
static void test_assert_no_error(void)
{
GDir *dir = NULL;
GError *err = NULL;
dir = g_dir_open("/tmp", 0, &err);
g_assert (dir != NULL);
g_assert_no_error(err);
if(NULL != err) {
g_error_free(err);
}
}
static void test_assert_error(void)
{
GDir *dir;
GError *error;
error = NULL;
dir = g_dir_open ("/pfrkstrf", 0, &error);
g_assert (dir == NULL);
g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT);
if(NULL != error) {
g_error_free (error);
}
}
static void test_assert_true(void)
{
g_assert_true(TRUE);
}
static void test_assert_false(void)
{
g_assert_false(FALSE);
}
static void test_assert_null(void)
{
gpointer *p = NULL;
g_assert_null(p);
}
static void test_assert_nonnull(void)
{
gpointer *p = NULL;
g_assert_nonnull(p);
}
gint main(gint argc, gchar **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/test/testcase", test_case);
g_test_add_func("/test/test_assert", test_assert);
g_test_add_func("/test/test_assert_not_reached", test_assert_not_reached);
g_test_add_func("/test/test_assert/cmp/cmpstr", test_assert_cmpstr);
g_test_add_func("/test/test_assert/cmp/cmpint", test_assert_cmpint);
g_test_add_func("/test/test_assert/cmp/cmpuint", test_assert_cmpuint);
g_test_add_func("/test/test_assert/cmp/cmphex", test_assert_cmphex);
g_test_add_func("/test/test_assert/cmp/cmpfloat", test_assert_cmpfloat);
g_test_add_func("/test/test_assert/error/no_error", test_assert_no_error);
g_test_add_func("/test/test_assert/error/error", test_assert_error);
g_test_add_func("/test/test_assert/boolean/true", test_assert_true);
g_test_add_func("/test/test_assert/boolean/false", test_assert_false);
g_test_add_func("/test/test_assert/null/null", test_assert_null);
g_test_add_func("/test/test_assert/null/nonnull", test_assert_nonnull);
return g_test_run();
}
列举当前所有测试例
[root@centos7 build]# gtester -l glib_testframe_gtester
TEST: glib_testframe_gtester... (pid=387)
/test/testcase
/test/test_assert
/test/test_assert_not_reached
/test/test_assert/cmp/cmpstr
/test/test_assert/cmp/cmpint
/test/test_assert/cmp/cmpuint
/test/test_assert/cmp/cmphex
/test/test_assert/cmp/cmpfloat
/test/test_assert/error/no_error
/test/test_assert/error/error
/test/test_assert/boolean/true
/test/test_assert/boolean/false
/test/test_assert/null/null
/test/test_assert/null/nonnull
PASS: glib_testframe_gtester
如果只运行/test/test_assert/cmp/路径下的测试例,则可以加-p参数。
#gtester -k -p=/test/test_assert/cmp/ --verbose -o test.log glib_testframe_gtester
如果想跳过/test/test_assert/boolean/路径下的测试例,则可以加-s参数。
#gtester -k -s=/test/test_assert/boolean/ --verbose -o test.log glib_testframe_gtester
使用gtester-report生成测试报告
仍以上一专题的程序为例,使用gtester-report生成测试报告。
分两个步骤,先生成log文件,再生成html文件。
1 生成log文件,
#gtester -k --verbose -o test.log glib_testframe_gtester
2 生成html文件
运行gtester-report命令
#gtester-report test.log > test.html
可以看到,在当前目录已生成html文件。
test.html文件已生成,使用浏览器(firefox可能会显示不出内容,建议使用chrome浏览器)打开,即可看到可视化的结果。
注:使用gtester-report生成测试报告可能出现的错误及解决方法。
在生成html一步,低版本可能会出现以下报错:Traceback (most recent call last): File "/usr/bin/gtester-report", line 490, in <module> main() File "/usr/bin/gtester-report", line 484, in main HTMLReportWriter(rr.get_info(), rr.binary_list()).printout() File "/usr/bin/gtester-report", line 348, in printout self.handle_info () File "/usr/bin/gtester-report", line 242, in handle_info self.oprint ('<h3>Package: %(package)s, version: %(version)s</h3>\n' % self.info) KeyError: 'package'
这是旧版本GLib的BUG,原因是生成的log文件没有指定包名、版本号、修订号。
可以在生成的test.log文件标签内添加如下五行:<info> <package>PACKAGENAME</package> <version>VERSION</version> <revision>REVISION</revision> </info>
再次执行
gtester-report test.log > test.html
命令即可成功。
可视化结果如下图所示:
通过上述可视化报告,这里发现一个问题,测试用例test_assert预期执行结果是failed,但实际报告是success,这是什么原因呢?
这是glib在迭代过程中引入的一个bug,具体原因可参考下面三个链接:
缺陷描述:https://bugzilla.gnome.org/show_bug.cgi?id=790934
引入版本:https://gitlab.gnome.org/GNOME/glib/-/commit/ed620183cbb762eec8a0d0ff1575e5dc3acc329a
解决版本:https://gitlab.gnome.org/GNOME/glib/-/commit/ac0999a1cff568aba8dcafc562c381d1e56816e9
从gitlab的Graph视图可以看到,该问题的影响glib版本为:2.55.1-2.57.2
,如果要在这几个版本上使用gtester,需要修改gtester.c源码并重新编译。