首页 > 代码库 > 让hg版本库自动化测试

让hg版本库自动化测试

前言

在嵌入式开发中,测试是很重要的一个环节,但是开发人员往往会忽视它。所以把自动化测试与代码紧密结合在一起是这篇文章的主题。
我们开发人员平时维护代码的时候使用最多的是版本库工具,很多时候代码修改完了,本地一编译,通过就提交了。但是却忽视了一个问题:程序编译通过就一定能正常运行了?显然是不对的。

前期准备

这里需要准备以下工具:
1,linux  -- 这里我使用了ubuntu
2,gcc    -- ubuntu是默认自带的
3,hg      -- 版本库管理工具
4,unity  -- 测试夹具
5,scons -- 一个自动化编译工具(你也可以使用makefile)

创建测试工程

首先我们需要在服务器端搭建一个版本库,
>>mkdir hg_hook
>>cd hg_hook
>>hg init
>>touch main.c
>>touch Sconstruct

然后我们修改main.c,代码如下:

#include <stdio.h>

int main(void)
{
    printf("hello world!\r\n");
    return 0;
}

修改Sconstruct文件,代码如下:

import os
import sys

BUILD = 'debug'
# toolchains
CC = 'gcc'

if BUILD == 'debug':
    CFLAGS = ' -Wall -g -O0'
else:
    CFLAGS = ' -O2'

CFLAGS += ' -I'+os.getcwd() + '/'


TARGET = 'main.out'

env = Environment(CC=CC,
                  CCFLAGS=CFLAGS)

env.Program(TARGET, 'main.c')

env.Command('-r', TARGET, './'+TARGET)
Scons是一个类似于makefile的自动编译工具,大家如果有什么以后可以网上google一下,很多Scons的使用介绍。

这里我主要介绍最后一行:

env.Command('-r', TARGET, './'+TARGET)
这是自定义一个命令,在我们编译好以后自动执行我们生成的可执行文件。便于我们测试程序运行结果。

我们在终端输入:scons -r,可以看到输出结果。


提交版本库

上面的工作完成以后,我们可以把源文件进行一次提交。
>>hg add .
>>hg commit -m "packed init"
这样我们就有了一个版本库。

修改版本库配置

因为我们需要用到的是hg的hook功能,所以我们需要修改hg的配置文件。
>>thg
是hg的自带图形界面,进入File->Setting;


点击编辑文件,输入:
[hooks]
changegroup = update
update = scons -r
第一行表示当有用户提交代码的时候,自动调用update。
第二行表示执行hg update的时候执行scons -r。
点击ok。然后输入:
>>hg serve
启动版本库服务器。

用户提交代码

首先我们从服务器端clone代码,然后本地修改main.c代码:
#include <stdio.h>

int main(void)
{
    printf("hello world!\r\n");
    x=6;  // 添加的错误代码
    return 0;
}
本地commit一下,然后pull到服务器,在log窗口可以看到如下信息:


我们可以看到remote返回了很多信息,其中把编译结果也返回了回来。就可以看出我们提交的代码首先是编译不正确的。
到了这里,我们基本上可以通过版本库的hook自动编译代码,来检测程序的编译阶段的错误。

添加unity测试夹具

添加unity源代码到hg-hook目录下,并在unity目录下添加Sconscript文件,内容如下:
objs = Object(Glob('*.c'))
Return('objs')
修改hg-hook目录下的Sconstruct文件,添加几行代码:
import os
import sys

BUILD = 'debug'
# toolchains
CC = 'gcc'

if BUILD == 'debug':
    CFLAGS = ' -Wall -g -O0'
else:
    CFLAGS = ' -O2'

CFLAGS += ' -I'+os.getcwd() + <span style="font-family: Arial, Helvetica, sans-serif;">' -I'+os.getcwd() + ‘/unity’</span>

MY_ROOT = os.path.normpath(os.getcwd())  # 添加的代码:获取当前根目录

TARGET = 'main.out'

env = Environment(CC=CC,
                  CCFLAGS=CFLAGS)

objs = env.Object(Glob("*.c"))            # 获取当前的源文件对象
objs += SConscript(['unity/Sconscript'])  # 获取unity文件夹内的所有源文件对象

env.Program(TARGET, objs)

env.Command('-r', TARGET, './'+TARGET)

移动源程序

我们把hello world代码从main里面移除出去,单独创建hello-world.c,hello-world.h2个源文件;也就是我们需要测试的程序
hello-world.c
#include <hello-world.h>
#include <stdio.h>

void hello_world(void)
{
    printf("hello world\r\n");
}
hello-world.h
#ifndef __HELLO_WORLD_H__
#define __HELLO_WORLD_H__

void hello_world(void);

#endif
然后我们修改main.c为:
#include <unity_fixture.h>

static void run_all_test(void)
{
    RUN_TEST_GROUP(hello_world);
}

static char *arg_string[] =
{
    "main",
    "-v",
};

int main(int argc, char *argv[])
{
    argc = sizeof(arg_string)/sizeof(char *);
    argv = arg_string;

    return UnityMain(argc, argv, run_all_test);
}
这个是我们测试夹具的框架。下面添加测试用例。

添加测试用例

我们添加hello-world-test.c文件,用于我们测试hello-world的函数。
#include "unity_fixture.h"
#include "hello-world.h"

TEST_GROUP(hello_world);

TEST_SETUP(hello_world)
{
    printf("\r\nstart\r\n");
}

TEST_TEAR_DOWN(hello_world)
{
    printf("end\r\n");
}

TEST(hello_world, test1)
{
    hello_world();
}
添加hello-world-runner.c文件,用于测试夹具的启动:
#include "unity_fixture.h"

TEST_GROUP_RUNNER(hello_world)
{
    RUN_TEST_CASE(hello_world, test1);
}

接着我们运行hg commit,hg push,我们可以看到:

unity测试夹具对hello-world的测试通过了。


让hg版本库自动化测试