之前在词法和语法分析部分都有测试,不过那些测试显得很小打小闹,而且比较麻烦。好在那都是不复杂的东西,测试负担较小。而符号表这东西是语义分析需要用到的基础结构,如果等到最后进行集成测试,出了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; }