3_01_GLib库入门与实践_测试框架

简介

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源码并重新编译。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值