wrdoct

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

项目场景:

学习github上的googletest,参考其测试代码,实现自己的轻量级自动测试框架。
使用宏进行功能拓展,实现日志打印功能,通过不同颜色的提示来告知用户功能测试所用时长以及相关错误所在位置(所在文件、行数等)。

谷歌测试框架

首先在github上搜索googletest进行下载并解压。
在这里插入图片描述
新建一个文件夹并进入,编译生成所需要的库文件,在终端输入:

mkdir build
cd build
cmake ../
make

在这里插入图片描述
编译结束后进入lib可看到四个库

cd lib

在这里插入图片描述
将该libgoogletest文件夹中的include复制出来(这里统一放在start文件夹里)

cd ..
cp lib -R ../../
cd googletest
cp include -R ../../

回到start文件夹下,建立google_test.cpp

vim google_test.cpp

输入以下代码:

/*************谷歌测试框架************/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <gtest/gtest.h>

using namespace std;

int add(int a, int b){
    return a + b;
}

TEST(test, add1){
    EXPECT_EQ(add(3, 4), 7); // ==
    EXPECT_NE(add(3, 4), 6); // !=
    EXPECT_LT(add(3, 4), 8); // <
    EXPECT_LE(add(3, 4), 7); // <=
    EXPECT_GT(add(3, 4), 6); //>
    EXPECT_GE(add(3, 4), 7); //>=
}

TEST(test, add2){
    EXPECT_EQ(add(3, 4), 7); // ==
    EXPECT_NE(add(3, 4), 6); // !=
    EXPECT_LT(add(3, 4), 6); // < error
    EXPECT_LE(add(3, 4), 7); // <=
    EXPECT_GT(add(3, 4), 6); //>
    EXPECT_GE(add(3, 4), 7); //>=
}

int main(){
    return RUN_ALL_TESTS();

    //return 0;
}

保存并退出。
编写makefile文件:

vim makefile

输入

all:
g++ -std=c++11 -I./include -L./lib google_test.cpp -lgtest -lpthread

保存并退出。

编译并运行

make
./a.out

效果图如下图。
在这里插入图片描述
接下来便是对这个测试框架进行分析以实现自己的轻量级自动测试框架,并且加上日志打印功能。

轻量级自动测试框架功能实现及扩展

本次实现主要采用来进行功能实现及扩展。宏的作用就是基础替换

1.带颜色的文字

在C++里要实现文字的带颜色输出,则应在要输出的字符串前面加上以下输出:

"\033[0;31m 输出的字符串\n\033[0m"   //其中0的位置代表底色, 31的位置是代表字的颜色 
字背景颜色范围:40----49 
40:41:深红 
42:绿 
43:黄色 
44:蓝色 
45:紫色 
46:深绿 
47:白色 

字颜色:30-----------39 
30:31:32:绿 
33:34:蓝色 
35:紫色 
36:深绿 
37:白色 

使用宏(替换)对颜色打印功能进行COLOR的封装。

#define COLOR(msg, code) "\033[0;" #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)

2.EXPECT_XX

使用宏实现6个EXPECT_XX比较测试的功能,进行EXPECT的封装。

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

#define EXPECT_EQ(a, b) EXPECT(a, ==, b)
#define EXPECT_NE(a, b) EXPECT(a, !=, b)
#define EXPECT_LT(a, b) EXPECT(a, <, b)
#define EXPECT_LE(a, b) EXPECT(a, <=, b)
#define EXPECT_GT(a, b) EXPECT(a, >, b)
#define EXPECT_GE(a, b) EXPECT(a, >=, b)

3.TEST和RUN_ALL_TESTS

实现TEST的功能要使用__attribute__ 机制。attribute 可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。
要实现TEST功能则要将函数属性设置为注册函数(保存函数信息的函数,随机调用执行),即:

__attribute__((constructor))

使用宏封装TEST

#define FUNC_NAME(a, b) test_##a##_##b

#define TEST(a, b) \
void FUNC_NAME(a, b)(); \
__attribute__((constructor)) \
void reg_##test_##a##_##b(){ \
   add_test_function(FUNC_NAME(a, b), #a "." #b); \
} \
void FUNC_NAME(a, b)()

struct FuncData{
    void (*func)();
    const char *func_name;
}func_arr[100];
int func_arr_cnt = 0;

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

其中#表示将其后面的宏参数进行字符串化操作。
其中##表示连接运算符==>允许参数为空。

使用宏封装RUN_ALL_TESTS
首先定义func_flag代表测试的用例是否正确(在EXPECT_XX中也使用)。

int func_flag;

const char *RUN    = GREEN("[  RUN   ]");
const char *OK     = GREEN("[    OK  ]");
const char *FAILED =   RED("[ FAILED ]");

int RUN_ALL_TESTS(){
    for(int i = 0; i < func_arr_cnt; i++){
        printf("%s  %s\n", RUN, func_arr[i].func_name);
        func_flag = 1;
        long long b = clock();
        func_arr[i].func();
        long long e = clock();
        if(func_flag){
            printf("%s  ", OK);
        }else{
            printf("%s  ", FAILED);
        }

        printf("%s " YELLOW("(%lld ms)") "\n",
                func_arr[i].func_name,
                (e - b) * 1000 / CLOCKS_PER_SEC
              );
    }
    return 0;
}

4.测试所用时间

使用clock()计时函数,再使用“带颜色的打印”功能即可实现:

long long b = clock();
func_arr[i].func();
long long e = clock();

printf("%s " YELLOW("(%lld ms)") "\n",
                func_arr[i].func_name,
                (e - b) * 1000 / CLOCKS_PER_SEC  //计算一个进程运行时间 //CLOCKS_PER_SEC表示一秒钟有多少个时钟计时单元
              );

5.LOG日志打印

使用宏进行打印日志的功能扩展,首先对常用的预定义符号作一介绍:

__DATE__  日期
__TIME__  时间
__LINE__  行号
__FILE__  文件名
//以下三种(非标准)不建议使用
__func__  函数名
__FUNC__  函数名
__PRETTY_FUNCTION__  函数信息

LOG宏的封装。

#define LOG(frm, args...) { \
    printf("[%s : %d]", __FILE__, __LINE__); \
    printf(frm, ##args); \
    printf("\n"); \
}

args...表示参数可以为多个。
其中##表示连接运算符==>允许参数为空。

轻量级自动测试框架功能测试

1.使用该轻量级自动测试框架测试

测试代码见文首,把谷歌测试框架的头文件换为自己的头文件即可,并在主函数里加入

int a = 0, b = 1;
LOG("a = %d, b = %d", a, b);//日志打印(文件名和所在行数)

测试效果如下图。
在这里插入图片描述

2.使用该轻量级自动测试框架测试快速排序

代码如下:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
#include <map>
#include <set>
//#include <gtest/gtest.h>

#include <test/gtest.h>
#include <cstring>

using namespace std;

//快速排序
void QuickSort_v1(int *arr, int l, int r){
    if(l >= r) return;

    int x = l, y = r, z = arr[l];
    while(x < y){
        while(x < y && arr[y] >= z) --y;
        if(x <y) arr[x++] = arr[y];
        while(x < y && arr[x] <= z) ++x;
        if(x < y) arr[y--] = arr[x];
    }

    arr[x] = z;

    QuickSort_v1(arr, l, x - 1);
    QuickSort_v1(arr, x + 1, r);

    return;
}

#define swap(a, b){ \
   __typeof(a) __a = a; \
  a = b, b = __a; \
}

//三点取中法
inline int median(int a, int b, int c){
    if(a > b) swap(a, b);
    if(a > c) swap(a, c);
    if(b > c) swap(b, c);
    return b;
}

//快速排序优化
void QuickSort_v2(int *arr, int l, int r){
    while(l < r) {
        int x = l, y = r, z = median(arr[l], arr[(l + r) >> 1], arr[r]);

        do{
            while(arr[x] < z) ++x;
            while(arr[y] > z) --y;
            if(x <= y){
                swap(arr[x], arr[y]);
                ++x, --y;
            }
        }while(x <= y);

        QuickSort_v2(arr, x, r);
        r = y;
    }

    return;
}

const int threshold = 16; 

void _QuickSort_v3(int *arr, int l, int r){
    while(r - l > threshold) {
        int x = l, y = r, z = median(arr[l], arr[(l + r) >> 1], arr[r]);
        do{
            while(arr[x] < z) ++x;
            while(arr[y] > z) --y;
            if(x <= y){
                swap(arr[x], arr[y]);
                ++x, --y;
            }
        }while(x <= y);

        _QuickSort_v3(arr, x, r);
        r = y;
    }

    return;
}

void InsertSort(int *arr, int l, int r){
    int ind = l;
    for (int i = l + 1; i <= r; i++) {
        if(arr[ind] > arr[i]) ind = i;
    }
    swap(arr[ind], arr[l]);

    for (int i = l + 2; i <= r; i++) {
        int j = i;
        while (arr[j] < arr[j - 1]) {
            swap(arr[j], arr[j - 1]);
            --j;
        }
    }

    return;
}

//快速排序+插入排序
void QuickSort_v3(int *arr, int l, int r){
    _QuickSort_v3(arr, l, r);
    InsertSort(arr, l, r);
    return;
}

#define MAX_N 10000000
int *arr;

int *GetRandData(int n){
    int *arr = (int *)malloc(sizeof(int) *n);
    for(int i = 0; i < n; i++){
        arr[i] = rand() % n;
    }
    return arr;
}

int *CopyArray(int *arr, int n){
    int *tmp = (int *)malloc(sizeof(int) *n);
    memcpy(tmp, arr, sizeof(int) *n);
    return tmp;
}

int Check(int *arr, int n){
    for(int i = 1; i < n; i++){
        if(arr[i] < arr[i - 1]) return 0;
    }
    return 1;
}

TEST(test, QuickSort_v1){
    int *tmp = CopyArray(arr, MAX_N);
    QuickSort_v1(tmp, 0, MAX_N - 1);
    EXPECT_EQ(Check(tmp, MAX_N), 1);
}

TEST(test, QuickSort_v2){
    int *tmp = CopyArray(arr, MAX_N);
    QuickSort_v2(tmp, 0, MAX_N - 1);
    EXPECT_EQ(Check(tmp, MAX_N), 1);
}

TEST(test, QuickSort_v3){
    int *tmp = CopyArray(arr, MAX_N);
    QuickSort_v3(tmp, 0, MAX_N - 1);
    EXPECT_EQ(Check(tmp, MAX_N), 1);
}

int main(){
    srand(time(0));
    arr = GetRandData(MAX_N);
    return RUN_ALL_TESTS();
}

测试结果:
在这里插入图片描述

posted on 2022-07-03 20:28  wrdoct  阅读(72)  评论(0)    收藏  举报