zoukankan      html  css  js  c++  java
  • Glib 对 C 函数进行单元测试

    1. Glib 单元测试框架

    Glib 为单元测试提供了一套完整的测试框架,每个测试运行包括以下几个部分

    • 测试数据结构
    • 测试 setup 与 teardown 函数
    • 测试函数

    2. 单元测试数据结构

    在一组测试中使用的元素称为一个 fixture,Glib 要求每个 fixture 都是一个结构,所以我们需要声明一个结构体,包含我们测试数据或是对象。

    /** fixture for Glib test */
    typedef struct Matrix2dTest
    {
      Matrix2d mat; ///< test object
      double TOL;   ///< maximum error
    } Matrix2dTest;
    

    如上代码中定义了我们测试所需结构体 Matrix2dTest,结构体中元素 mat 即为后面进行测试的对象(结构体)。

    3. 测试环境构建

    在每个测试函数运行前,可能都需要进行一系列初始化和后处理操作。Glib 运行时将这两个过程函数作为参数传给测试函数,如此一来,每个测试函数都可以生成一个新的程序来运行,并且互不影响。

    添加测试函数的语句是

    g_test_add("/MatUtils/Test_inverseMatrix2d",  // test label
                Matrix2dTest,                     // test structure
                NULL,                             // input user data
                Setup_Matrix2dTest,               // setup function
                Test_inverseMatrix2d,             // test function
                Teardown_Matrix2dTest);           // teardown function
    

    其中,Matrix2dTestNULL 为测试函数输入参数,Test_inverseMatrix2d 为测试函数,Setup_Matrix2dTestTeardown_Matrix2dTest 为测试初始化和后处理函数。

    在调用测试函数时候,Glib 通过 fork 系统调用生成新的线程来测试失败。运行 g_test_run() 语句即可运行所有测试,具体执行命令可参见第5节。

    4. 单元测试函数

    由于测试框架是确定的,因此单元测试函数参数也是固定的。在上面测试中,我们添加测试函数名为 Test_inverseMatrix2d,函数完整定义为

    static void
    Test_inverseMatrix2d(Matrix2dTest* mattest, const void* test_data)
    {
      Matrix2d mat1;
      mallocMatrix2d(3, 3, &mat1);
      copyMatrix2d(mattest->mat, &mat1);
      inverseMatrix2d(mat1);
      Matrix2d mat2;
      mallocMatrix2d(3, 3, &mat2);
      multiplyMatrix2d(mattest->mat, mat1, 1.0, 0.0, mat2);
      g_assert(fabs(mat2.ets[0][0] - 1.0) < mattest->TOL);
      g_assert(fabs(mat2.ets[1][1] - 1.0) < mattest->TOL);
      g_assert(fabs(mat2.ets[2][2] - 1.0) < mattest->TOL);
      freeMatrix2d(mat1);
      freeMatrix2d(mat2);
    }
    

    可以看到,由于函数只有文件内测试用,因此声明为 static 函数。函数有两个参数,第一个为 mattest 类型指针,第二个为传入用户数据,由于我们不需要其他数据,因此调用时传入 NULL

    这个测试主要用来测试矩阵求逆过程,计算完成后将逆矩阵与矩阵相乘,看结果是否为单元矩阵。最后的判断采用如下语句

      g_assert(fabs(mat2.ets[0][0] - 1.0) < mattest->TOL);
      g_assert(fabs(mat2.ets[1][1] - 1.0) < mattest->TOL);
      g_assert(fabs(mat2.ets[2][2] - 1.0) < mattest->TOL);
    

    5. 完整测试函数

    #include <stdio.h>
    #include <math.h>
    #include "MatUtils.h"
    #include <glib.h>
    
    /** fixture for Glib test */
    typedef struct Matrix2dTest
    {
      Matrix2d mat; ///< test object
      double TOL;   ///< maximum error
    } Matrix2dTest;
    
    static void
    Setup_Matrix2dTest(Matrix2dTest* mattest, const void* test_data)
    {
      mallocMatrix2d(3, 3, &(mattest->mat));
      double vt[] = {
        3.0, -1.0, -1.0, 4.0, -2.0, -1.0, -3.0, 2.0, 1.0,
      };
      mattest->TOL = 1e-6;
      setMatrix2dFromArray(vt, mattest->mat);
    }
    
    static void
    Teardown_Matrix2dTest(Matrix2dTest* mattest, const void* test_data)
    {
      freeMatrix2d(mattest->mat);
    }
    
    static void
    Test_inverseMatrix2d(Matrix2dTest* mattest, const void* test_data)
    {
      Matrix2d mat1;
      mallocMatrix2d(3, 3, &mat1);
      copyMatrix2d(mattest->mat, &mat1);
      inverseMatrix2d(mat1);
      Matrix2d mat2;
      mallocMatrix2d(3, 3, &mat2);
      multiplyMatrix2d(mattest->mat, mat1, 1.0, 0.0, mat2);
      g_assert(fabs(mat2.ets[0][0] - 1.0) < mattest->TOL);
      g_assert(fabs(mat2.ets[1][1] - 1.0) < mattest->TOL);
      g_assert(fabs(mat2.ets[2][2] - 1.0) < mattest->TOL);
      freeMatrix2d(mat1);
      freeMatrix2d(mat2);
    }
    
    int
    main(int argc, char** argv)
    {
      g_test_init(&argc, &argv, NULL);
      g_test_add("/MatUtils/Test_inverseMatrix2d", // test label
                 Matrix2dTest,                     // test structure
                 NULL,                             // input user data
                 Setup_Matrix2dTest,               // setup function
                 Test_inverseMatrix2d,             // test function
                 Teardown_Matrix2dTest);           // teardown function
      return g_test_run();
    }
    

    编译时由于需要链接 Glib 函数库所以需要添加一些附加命令。这里我采用的是 cmake 软件编译,在工程根目录的 CMakeLists.txt 中直接添加如下命令添加 Glib 头文件和库文件目录,

    find_package(PkgConfig REQUIRED)
    pkg_check_modules(GLIB REQUIRED glib-2.0>=2.23)
    include_directories(${GLIB_INCLUDE_DIRS})
    link_directories(${GLIB_LIBRARY_DIRS})
    

    在测试函数所在的文件夹内 CMakeLists.txt 中,则添加如下命令编译测试函数

    target_link_libraries(MatUtilsTest matutils ${GLIB_LIBRARIES})
    

    如此便可对测试函数进行正确的编译和链接了。

    获得可执行的测试函数后,用过命令

    gtester -k -o=test.xml ./MatUtilsTest
    

    生成测试结果文件 test.xml,再利用语句 gtester-report 命令即可将 XML 文件转化为 html 文件,并用留浏览器打开。

    gtester-report test.xml > test.html
    

    但是不知什么原因,在我的 Macbook 笔记本上出现错误

    ➜  build gtester-report test.xml > test.html
    Traceback (most recent call last):
      File "/usr/local/bin/gtester-report", line 490, in <module>
        main()
      File "/usr/local/bin/gtester-report", line 484, in main
        HTMLReportWriter(rr.get_info(), rr.binary_list()).printout()
      File "/usr/local/bin/gtester-report", line 348, in printout
        self.handle_info ()
      File "/usr/local/bin/gtester-report", line 242, in handle_info
        self.oprint ('<h3>Package: %(package)s, version: %(version)s</h3>
    ' % self.info)
    KeyError: 'package'
    

    通过搜索我发现是 xml 文件缺少内容所致 Running gtester-report on gtester output raises KeyError。打开 xml 文件,在 <gtester> 字段内添加

    <info>
    <package>PACKAGENAME</package>
    <version>VERSION</version>
    <revision>REVISION</revision>
    

    语句,声明软件名和软件版本。如此便可得到可视化的测试结果,如图所示:

    参考文献

    理解 GLib 的单元测试框架

    https://segmentfault.com/a/1190000003996312

    Ben Klemens. C程序设计新思维[M]. 人民邮电出版社, 2015.

  • 相关阅读:
    【TCP】TCP三次握手与四次挥手
    【Spring】@PathVariable 获取带点参数,获取不全
    【异常】Caused by: java.sql.SQLTransientConnectionException: HikariPool-1
    【异常】 FlywayException:Validate failed:Migration checksum mismatch
    Eclipse------启动Server时出现弹窗Server at localhost was unable to start within 45 seconds.
    SpringBoot------集成PageHelper分页功能
    SpringBoot------集成MyBatis
    SpringBoot------thymeleaf的使用
    SpringBoot------全局异常捕获
    SpringBoot------JPA连接数据库
  • 原文地址:https://www.cnblogs.com/li12242/p/8419527.html
Copyright © 2011-2022 走看看