1. 程式人生 > 其它 >自制 Cpp 測試框架(二)

自制 Cpp 測試框架(二)

技術標籤:Cpp

實戰專案中的腦補能力

  • 實現除錯資訊 LOG日誌列印功能
  • 實現 EXPECT系列封裝
  • 實現 COLOR系列封裝
  • 使用 __attribute__ 完成函式註冊
  • 完善功能:測試框架初養成

實現除錯資訊 LOG日誌列印功能

從理解 LOG 日誌功能需求開始

預測處理命令-巨集定義

// 定義符號常量:
#define PI 3.1415926
#define MAX_N 10000

// 定義傻瓜表示式
#define MAX(a, b) (a) > (b) ? (a) : (b)
#define S(a, b) a * b

// 定義程式碼段
#define P(a) {
  printf
("%d\n", a); }

預處理命令-預定義的巨集

巨集說明
__DATE__日期:Mmm dd yyyy
__TIME__時間:hh:mm:ss
__LINE__行號
__FILE__檔名
__func__函式名/非標準
__FUNC__函式名/非標準
__PRETTY_FUNCTION__更詳細的函式資訊/非標準
#include<stdio.h>
int main() {
    printf("__LINE__ = %d\n", __LINE__);
    printf("__FILE__ = %s\n", __FILE__)
; printf("__func__ = %s\n", __func__); printf("__PRETTY_FUNCTION__ = %s\n", __PRETTY_FUNCTION__); return 0; } //__LINE__ = 3 //__FILE__ = /Users/harrytsz/Documents/Workspaces/harrytsz-cpp/ShaoFaBook/main.cpp //__func__ = main //__PRETTY_FUNCTION__ = int main()
#include<stdio.h>
// '\' 表示換行符,巨集定義只有一行 #define LOG(frm, args...) { \ printf("\033[0;33m[%s : %s : %d] \033[0m", __FILE__, __func__, __LINE__);\ printf(frm, ##args);\ printf("\n");\ } void test() { LOG("hello world"); return ; } int main() { LOG("Hello World"); test(); return 0; } // [/Users/harrytsz/Documents/Workspaces/harrytsz-cpp/ShaoFaBook/main.cpp : main : 16] Hello World // [/Users/harrytsz/Documents/Workspaces/harrytsz-cpp/ShaoFaBook/main.cpp : test : 11] hello world
#include<stdio.h>

// ## 表示連線的作用
#define CAT(a, b) a##_##b

int main() {
    int kaikeba_var = 9973;
    CAT(kaikeba, var) = 1000;
    LOG("kaikeba_var = %d", kaikeba_var);
    return 0;
}
// [/Users/harrytsz/Documents/Workspaces/harrytsz-cpp/ShaoFaBook/main.cpp : main : 20] kaikeba_var = 1000

開啟或關閉日誌

  • 第一種方法:設定判斷變數
#include<stdio.h>

int log_flag = 1; // 1 輸出日誌; 0 關閉日誌
// '\' 表示換行符,巨集定義只有一行
#define LOG(frm, args...) do { \
    if (log_flag == 0) break; \
    printf("\033[0;33m[%s : %s : %d] \033[0m", __FILE__, __func__, __LINE__);\
    printf(frm, ##args);\
    printf("\n");\
} while (0);

void test() {
    LOG("hello world");
    int a = 1, b = 2;
    printf("%d %d\n", a, b);
    return ;
}

int main() {
    LOG("Hello World");
    int a = 1, b = 2;
    test();
    return 0;
}
// [/Users/harrytsz/Documents/Workspaces/harrytsz-cpp/ShaoFaBook/main.cpp : main : 20] Hello World
// [/Users/harrytsz/Documents/Workspaces/harrytsz-cpp/ShaoFaBook/main.cpp : test : 13] hello world
// 1 2
  • 第 2 種方法:刪除巨集定義,並修改巨集定義
#include<stdio.h>

// '\' 表示換行符,巨集定義只有一行
#define LOG(frm, args...) do { \
    printf("\033[0;33m[%s : %s : %d] \033[0m", __FILE__, __func__, __LINE__);\
    printf(frm, ##args);\
    printf("\n");\
}

// 刪除巨集定義,並重新定義巨集
#undef LOG
#define LOG(frm, args...) 

void test() {
    LOG("hello world");
    int a = 1, b = 2;
    printf("%d %d\n", a, b);
    return ;
}

int main() {
    LOG("Hello World");
    int a = 1, b = 2;
    test();
    return 0;
}
// 1 2

預處理命令-條件式編譯

函式說明
#ifdef DEBUG是否定義了 DEBUG 巨集
#ifndef DEBUG是否沒定義 DEBUG 巨集
#if MAX_N == 5巨集 MAX_N是否等於 5
#elif MAX_N == 4否則巨集 MAX_N 是否等於 4
#else否則
#endif條件判斷結束
  • 第 3 種方法:條件式編譯
#include<stdio.h>

#define DEBUG

#ifdef DEBUG
// '\' 表示換行符,巨集定義只有一行
#define LOG(frm, args...) {\
    printf("\033[0;33m[%s : %s : %d] \033[0m", __FILE__, __func__, __LINE__);\
    printf(frm, ##args);\
    printf("\n");\
}
#else
#define LOG(frm, args...)
#endif

void test() {
    LOG("hello world");
    int a = 1, b = 2;
    printf("%d %d\n", a, b);
    return ;
}

int main() {
    LOG("Hello World");
    int a = 1, b = 2;
    test();
    return 0;
}
// [/Users/harrytsz/Documents/Workspaces/harrytsz-cpp/ShaoFaBook/main.cpp : main : 24] Hello World
// [/Users/harrytsz/Documents/Workspaces/harrytsz-cpp/ShaoFaBook/main.cpp : test : 17] hello world
// 1 2
//
// Created by harrytsz on 2021/1/4.
//

#ifndef SHAOFABOOK_KTEST_H
#define SHAOFABOOK_KTEST_H

#define EXPECT(a, cmp, b) { \
    if(!((a) cmp (b))) { \
        printf("error\n"); \
    }\
}

#define EXPECT_EQ(a, b) EXPECT(a, ==, b);
#define EXPECT_EQ(a, b) EXPECT(a, ==, b);
#define EXPECT_EQ(a, b) EXPECT(a, ==, b);
#define EXPECT_EQ(a, b) EXPECT(a, ==, b);
#define EXPECT_EQ(a, b) EXPECT(a, ==, b);
#define EXPECT_EQ(a, b) EXPECT(a, ==, b);

#endif //SHAOFABOOK_KTEST_H

實現 COLOR系列封裝

ktest.h

//
// Created by harrytsz on 2021/1/4.
//

#ifndef SHAOFABOOK_KTEST_H
#define SHAOFABOOK_KTEST_H

#define EXPECT(a, cmp, b) { \
    if(!((a) cmp (b))) { \
        printf("error\n"); \
    }\
}

#define EXPECT_EQ(a, b) EXPECT(a, ==, b);
#define EXPECT_EQ(a, b) EXPECT(a, ==, b);
#define EXPECT_EQ(a, b) EXPECT(a, ==, b);
#define EXPECT_EQ(a, b) EXPECT(a, ==, b);
#define EXPECT_EQ(a, b) EXPECT(a, ==, b);
#define EXPECT_EQ(a, b) EXPECT(a, ==, b);


#define COLOR(msg, code) "\033[0;1;" #code "m" msg "\033[0m"
#define RED(msg) COLOR(msg, 31)
#define GREEN(msg) COLOR(msg, 32)
#define YELLOW(msg) COLOR(msg, 33)
#define BLUE(msg) COLOR(msg, 34)


#endif //SHAOFABOOK_KTEST_H

#include<stdio.h>
#include "ktest.h"

int main() {
    printf("hello world\n");
    printf(RED("hello world\n"));
    printf(GREEN("hello world\n"));
    printf(YELLOW("hello world\n"));
    printf(BLUE("hello world\n"));
    return 0;
}

使用 __attribute__ 完成函式註冊功能

#include<stdio.h>

__attribute__((constructor)) void test() {
    printf("test : hello world\n");
    return ;
}

int main() {
    printf("main : hello world\n");
    return 0;
}

// test : hello world
// main : hello world

完善功能:測試框架初養成

//
// Created by harrytsz on 2021/1/4.
//

#ifndef SHAOFABOOK_KTEST_H
#define SHAOFABOOK_KTEST_H

#define EXPECT(a, cmp, b) { \
    __typeof(a) __a = (a), __b = (b); \
    if(!((__a) cmp (__b))) { \
        printf(YELLOW("%s:%d: Failure\n", __FILE__, __LINE__)); \
        printf(YELLOW("Expected: (%s) %s (%s), actual : %d vs %d\n", \
        #a, #cmp, #b, __a, __b)); \
    }\
}

#define EXPECT_EQ(a, b) EXPECT(a, ==, b);
#define EXPECT_EQ(a, b) EXPECT(a, ==, b);
#define EXPECT_EQ(a, b) EXPECT(a, ==, b);
#define EXPECT_EQ(a, b) EXPECT(a, ==, b);
#define EXPECT_EQ(a, b) EXPECT(a, ==, b);
#define EXPECT_EQ(a, b) EXPECT(a, ==, b);


#define COLOR(msg, code) "\033[0;1;" #code "m" msg "\033[0m"
#define RED(msg) COLOR(msg, 31)
#define GREEN(msg) COLOR(msg, 32)
#define YELLOW(msg) COLOR(msg, 33)
#define BLUE(msg) COLOR(msg, 34)

#define TEST(a, b) \
void kaikeba_##a##_##b(); \
__attribute__((constructor)) \
void reg_##a##_##b() { \
    add_test_func(kaikeba_##a##_##b, #a "." #b); \
} \
void kaikeba_##a##_##b()

struct {
    void (*func)();
    const char *func_name;
} func_arr[100];

int func_cnt = 0;

void add_test_func(void (*func)(), const char *str) {
    func_arr[func_cnt].func = func;
    func_arr[func_cnt].func_name = str;
    func_cnt++;
    return ;
}

int RUN_ALL_TESTS() {
    // 遍歷所有的測試用例
    // run each test case
    for(int i = 0; i < func_cnt; i++) {
        printf(GREEN("[     RUN    ]") " %s\n", func_arr[i].func_name);
        func_arr[i].func();
    }
    return 0;
}

#endif //SHAOFABOOK_KTEST_H