2011年8月13日 星期六

使用Python 直接載入 C 函式庫進行多平台測試

(圖示為Python.org 版權所有)

最近有個朋友聊起TDD (Test Driven Programming) 測試導向程式設計,測試導向的優點:
便是能減少重覆測試所需要人工測試時間,當網友若修改程式庫時,
敢對進行程式優化,也較對修改有把握,因為可利用自動測試的程式來立刻驗證。
他最常使用C來寫程式,但是使用C來寫測試函式,就算附加CPPUnit 之類,還是需要比較多時間實作。
所以筆者寫了本篇心得,使用Python 來直接對C函式庫進行測試。

本次範例包括下列:

檔案名稱
說明
test.c 編繹為函式庫libtest.so或libtest.dll
main.c 編譯連結測試main()
main.py 用來載入測試函式庫的python 腳本檔
Makefile 執行編譯及測試

test.c
#include <stdio.h>
#define UBOUND 10  // 上限
#define LBOUND 1 // 下限
int foo(int r)
{
        if (r > UBOUND)
                return UBOUND;
        if (r < LBOUND)
                return LBOUND;
        printf("foo(%d) executed.", r);
        return r;
}

main.py部分程式範例:

此腳本主要使用cdll.LoadLibrary來載入所要進行測試函式庫

from ctypes import *
foolib = cdll.LoadLibrary(libname)

接著使用下列函式來驗查超出上限、下限及正常值。
(註:為求易懂並無使用其他測試函式庫。網友們可在網上找尋到許多python 測試函式庫,來縮短開發時程。)
def verify_lbound():
    p = 0
    r = foolib.foo(p)
    return verify_ifeq(1, r, p,"case lbound")
def verify_ubound():
    p = 11
    r = foolib.foo(p)
    return verify_ifeq(10, r, p, "case ubound")
def verify_general():
    p = 5
    r = foolib.foo(p)
    return verify_ifeq(p, r, p, "case general")


多平台編繹測試

在Makefile中,我們可使用uname –s 來取得使用平台的資訊,
再利用 ifeq 來實作平台不同的部分,以本次的範例而言,
平台編繹的主要在windows 使用函式庫名為.dll及執行程式副檔名為.exe,
而在Linux或Mac中,函式庫名要前綴lib,後置.so,可參考下方框紅色部分
KNAMEFULL  = $(shell uname -s | sed 's/_.*//g')
# cygwin on windows xp
# CYGWIN_NT-5.1
KNAME = $(KNAMEFULL)
ifeq ($(KNAME),CYGWIN)
EXEEXT = .exe
DLLEXT = .dll
else
EXEEXT =
DLLEXT = .so
endif


編繹並執行測試:

Makefile檔案在 all: 加上 run,以用表示編繹 foo$(EXEEXT),
然後,從run程式段來看,在Linux或Mac中, export LD_LIBRARY_PATH=.
來通知載入程式,函式庫在目前目錄中。
緊接著,執行  foo$(EXEEXT) ,驗證二進位連結無誤,
再執行python 腳本測試,如有任何錯誤產生,顯示make失敗錯誤。
all: run

run: foo$(EXEEXT)
        @ export LD_LIBRARY_PATH=.; ./$^ ; \
          if [ $$? -eq 3 ]; then \
          echo "Binary linking passed" ; \
          python main.py $(LIBNAME)$(DLLEXT) ; \
          if [ $$? -ne 0 ] ; then \
                exit 1; \
          fi \
        else \
          echo "Binary link failed" ; \
        fi


測試結果:
$ make
cc -c -o main.o main.c  -Wall
cc -c -o test.o test.c  -Wall
cc -o libtest.so -shared test.o
cc -o foo main.o -ltest -L.
foo(3) executed.Binary linking passed
case ubound:verified return value(10) from test.foo(11) [Passed]
case lbound:verified return value(1) from test.foo(0) [Passed]
foo(5) executed.case general:verified return value(5) from test.foo(5) [Passed]
total/passed cases: 3/3
Python load libtest.so [Passed]

下列平台經過測試無誤:
- Ubuntu 10.10: Python 2.6.6
- Win XP SP3: CYGWIN_NT-5.1 + Python 2.6.5
- Snowleopard 10.6.8: Xcode4.0.2+Python 2.6.1
本次所有範例可從源碼GitHub使用git下載,歡迎交流經驗或提供建議。

沒有留言: