之前在詞法和文法分析部分都有測試,不過那些測試顯得很小打小鬧,而且比較麻煩。好在那都是不複雜的東西,測試負擔較小。而符号表這東西是語義分析需要用到的基礎結構,如果等到最後進行內建測試,出了bug還真不好找。是以這東西有必要單獨拿來測試一下。不過C語言的測試驅動開發工具還真是難找,這裡就山寨一個先。
測試資料這裡簡單給出4組
// 測試資料數目
#define NR_TEST_CASE (4)
// 測試資料的基位址
#define BASE_ADDR (1 << 3)
// 待測試的符号表指針
struct SymbolTable* table;
// 次元資訊,等于0時結束
// 比如第一組資料,表示 i [2][3][4][5]
int DIM[NR_TEST_CASE][5] = {
{ 2, 3, 4, 5, 0 },
{ 2, 4, 0 },
{ 0 },
{ 2, 3, 4, 0 }
};
// 其它資料
struct {
char* ident; // 辨別符
AcceptType type; // 類型
int nrDim; // 次元數
int* dims; // 每一維大小
int size; // 總大小
} data[NR_TEST_CASE] = {
{ "i", INTEGER },
{ "ij", INTEGER },
{ "j", REAL },
{ "k", REAL }
};
按照單元測試的規範,對于每個函數的測試不應該依賴于其它函數。是以每個測試應該獨立地進行資料準備工作。這兩個函數進行資料準備和測試結束後的清理
void prepare(void)
{
int i, j, size;
table = newSymbolTable(BASE_ADDR);
for (i = 0; i < NR_TEST_CASE; ++i ) {
// 填充資料中其它部分:總大小及每一維的大小
size = INTEGER == data[i].type ? INT_SIZE : REAL_SIZE;
data[i].dims = DIM[i];
for (j = 0; 0 != DIM[i][j]; ++j) {
size *= DIM[i][j];
}
data[i].nrDim = j;
data[i].size = size;
}
for (i = 0; i < NR_TEST_CASE; ++i) {
// 将符号注冊進符号表,并順帶判斷是否每次都成功
assert (SymTab_OK ==
regVar(table, data[i].ident, data[i].type, data[i].nrDim,
data[i].dims));
}
}
void clear(void)
{
finalizeSymbolTable(table);
showAlloc;
}
再就是為每個函數準備的測試,依次是
/*
* 測試 regVar
* 測試涵蓋可能出現的錯誤,包括 SymTab_MultiDef 和 SymTab_SizeExceeded
* 因為正确的變量注冊在 prepare 中完成了,是以這裡不測試
*/
void testRegister(void);
/*
* 測試 getVarType
* 測試涵蓋正确的傳回及 SymTab_NotDef 錯誤
*/
void testVarType(void);
/*
* 測試 getVarAddr
* 測試涵蓋正确的傳回及 SymTab_NotDef 錯誤
*/
void testVarAddr(void);
/*
* 測試 getVarNrDim
* 測試涵蓋正确的傳回及 SymTab_NotDef 錯誤
*/
void testNrDim(void);
/*
* 測試 getVarDimSize
* 測試過程中會根據數組大小計算出其單元偏移
* 測試涵蓋正确的傳回及 SymTab_NotDef 和 SymTab_ArrDimOutOfRange 錯誤
*/
void testDimSize(void);
它們的具體實作如下
void testRegister(void)
{
prepare();
// totalSize: 總的記憶體使用量,判别在試圖重複注冊同名變量失敗後,符号表内使用記憶體數是否不變
int i, totalSize = table->used;
for (i = 0; i < NR_TEST_CASE; ++i) {
// 重複注冊在 prepare 中已經注冊的資料
assert (SymTab_MultiDef ==
regVar(table, data[i].ident, data[i].type, data[i].nrDim,
data[i].dims));
}
assert (totalSize == table->used);
// 記憶體超出最大限度,試圖注冊該變量
int exceed = MAX_VAR_IN_STACK - BASE_ADDR;
int exceedDim[1] = { exceed };
assert (SymTab_SizeExceeded
== regVar(table, "non-exist", INTEGER, 1, exceedDim));
clear();
puts(" -> Register test done.\n");
}
void testVarType(void)
{
prepare();
int i;
AcceptType type; // 變量類型,其位址傳入函數以存放傳回值
for (i = 0; i < NR_TEST_CASE; ++i) {
assert (SymTab_OK == getVarType(table, data[i].ident, &type));
assert (data[i].type == type);
}
assert (SymTab_NotDef == getVarType(table, "non-exist", &type));
clear();
puts(" -> Type getter test done.\n");
}
void testVarAddr(void)
{
prepare();
// addr: 變量位址,其位址傳入函數以存放傳回值
// accumulator: 位址積累量,也就是期望的變量位址
int i, addr, accumulator = BASE_ADDR;
for (i = 0; i < NR_TEST_CASE; ++i) {
assert (SymTab_OK == getVarAddr(table, data[i].ident, &addr));
assert (addr == accumulator);
accumulator += data[i].size; // 調整到下一變量的期望位址
}
assert (accumulator - BASE_ADDR == table->used);
assert (SymTab_NotDef == getVarAddr(table, "non-exist", &addr));
clear();
puts(" -> Address getter test done.\n");
}
void testNrDim(void)
{
prepare();
// nrDim: 次元數目,其位址傳入函數以存放傳回值
int i, nrDim;
for (i = 0; i < NR_TEST_CASE; ++i) {
assert (SymTab_OK == getVarNrDim(table, data[i].ident, &nrDim));
assert (data[i].nrDim == nrDim);
}
assert (SymTab_NotDef == getVarNrDim(table, "non-exist", &nrDim));
clear();
puts(" -> Dimension number getter test done.\n");
}
void testDimSize(void)
{
prepare();
// dimSize: 次元大小,其位址傳入函數以存放傳回值
// expected: 期望次元大小,計算得出
int i, j, dimSize, expected;
for (i = 0; i < NR_TEST_CASE; ++i) {
// 越界錯誤
assert (SymTab_ArrDimOutOfRange
== getVarDimSize(table, data[i].ident, &dimSize, -1));
expected = INTEGER == data[i].type ? INT_SIZE : REAL_SIZE;
for (j = 0; j < data[i].nrDim; ++j) {
assert(SymTab_OK
== getVarDimSize(table, data[i].ident, &dimSize, j));
assert(expected == dimSize);
expected *= DIM[i][j];
}
assert (expected == data[i].size);
// 越界錯誤
assert (SymTab_ArrDimOutOfRange
== getVarDimSize(table, data[i].ident, &dimSize, j));
}
assert (SymTab_NotDef == getVarDimSize(table, "non-exist", &dimSize, 0));
clear();
puts(" -> Dimension size getter test done.\n");
}
最後是檔案中其它的散件及main函數
#include<stdio.h>
#include<assert.h>
#define _DEBUG_MODE
#include"symbol-table.h"
int main(void)
{
testRegister();
testVarType();
testVarAddr();
testNrDim();
testDimSize();
puts(":-) All test done.");
return 0;
}