天天看點

[開發過程]符号表的測試

    之前在詞法和文法分析部分都有測試,不過那些測試顯得很小打小鬧,而且比較麻煩。好在那都是不複雜的東西,測試負擔較小。而符号表這東西是語義分析需要用到的基礎結構,如果等到最後進行內建測試,出了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;
}
      

繼續閱讀