1. 程式人生 > >Glib 對 C 函數進行單元測試

Glib 對 C 函數進行單元測試

error ati 完成 structure 是否 pac str txt b-

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>\n‘ % 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.

Glib 對 C 函數進行單元測試