Cunit在嵌入式平台上的移植(STM32)

前言

最近在公司为新开发的bluetooth chip写driver,根据流程在做integrate test之前要先为自己的模块做unit test。之前也从没写过unit test,查了很多资料,发现现有比较通用的unit test 框架都是为C++,java,python等写的,基本没看到有用于C语言的。最后找到一个比较合适的cunit 测试框架,但它是用于windows平台的C测试框架,所以就产生了将它移植到MCU上去的想法。

Cunit 简介

cunit源代码可以在这里获取到cunit的源代码。
cunit是一种简洁的,专门用于C语言的测试框架。它以链接库(动态或者静态)的方式给出,供用户使用(如果拿到源代码,以何种形式就由你自己决定了)。它提供了丰富的测试断言,且包含多种模式和交互接口,以供测试和输出测试报告。

cunit所使用的数据结构和方法在下列头文件中说明:
在这里插入图片描述
cunit包含4中测试模式:
(1)automate:自动测试模式,以xml格式输出测试报告,无人机交互接口;
(2)basic:基本测试模式,以windows终端输出测试报告,无人机交互接口;
(3)console:控制台测试模式,使用windows控制台作为交互以及报考输出接口;
(4)curses:xx测试模式(有道翻译为“畜生”_),使用unix平台控制台为交互和报告输出接口。

cunit的结构组织如下:
在这里插入图片描述
最上层包含一个测试注册点,它由很多的测试程序集组成,每个测试程序集又由很多的test case组成,每一个test case就是unit test 的最小单元(函数),这样可以将不同的待模块分类,组织成以上的结构。

cunit的一种典型使用方法和步骤:
Write functions for tests (and suite init/cleanup if necessary).
Initialize the test registry - CU_initialize_registry()
Add suites to the test registry - CU_add_suite()
Add tests to the suites - CU_add_test()
Run tests using an appropriate interface, e.g. CU_console_run_tests
Cleanup the test registry - CU_cleanup_registry

经过以上的介绍和铺垫,可以看出,不同测试模式使用不同的接口交互和输出报告,但是没有一种是适用于嵌入式软件的,所以要移植到嵌入式平台中去就需要修改测试接口。

开始移植

这里以stm32为移植平台,因为除basic mode以外的其他3种mode输出报告形式不太适合嵌入式的移植,这里只是选择basic test mode,将cunit源代码和头文件添加到工程中,如图:
在这里插入图片描述
开始编译,提示错误如下,一条条解build error。
(1)…\Output\STM32-DEMO.axf: Error: L6218E: Undefined symbol __aeabi_assert (referred from automated.o).
根据提示,在链接时提示找不到__aeabi_assert (它是assert() 的定义)这个symbol,显然是没有定义,从assert.h中可以看到如下声明:
在这里插入图片描述
哇靠,这么复杂。分析分析。。。。。假如没有定义了 NDEBUG 那就用到__aeabi_assert ,假如定义了,assert()就会用 #define assert(ignore) ((void)0) 这个宏定义,这里报错显然是没有定义NDEBUG。假如你想在检测到错误参数时assert掉,那你就需要去实现这个函数。这里为了省得麻烦,我们就不去实现它,所以在编译选项里面加上NDEBUG
在这里插入图片描述
(2)…\Output\STM32-DEMO.axf: Error: L6218E: Undefined symbol time (referred from automated.o).
和(1)中相似,也是没有定义time() 这个函数,它的申明在“time.h”中,但是在报告中打印出相关的时间信息对我们没有什么参考意义,所以我们将这个函数定义为空。
time_t time(time_t * timer)
{
return 0;
}
…error has been double kill.

(3)…\Output\STM32-DEMO.axf: Error: L6218E: Undefined symbol exit (referred from cuerror.o).
定义 void exit() 函数
void exit(int status)
{

}
…error has been triple kill.

接下来开始修改打印输出报告的接口,在源文件中所有的打印输出都是使用的fprintf(),这是往文件中输出流的方式,假如你的工程中包含文件系统,则可以使用这个方式,没有的话就需要将它替换成最常用的printf()。
接下来开始写test case来测试移植结果:
static void test_case1(void)
{
CU_ASSERT(1);
}
static void test_case2(void)
{
CU_ASSERT_EQUAL(1, 2);
}
static void test_case3(void)
{
CU_ASSERT_STRING_EQUAL(“abc”, “edf”);
}
然后根据使用步骤:
int main(void)
{
CU_ErrorAction error_action = CUEA_IGNORE;
CU_pSuite pSuite;
int test_return = 0;
USART1_Config();
/1.初始化注册点/
if (CU_initialize_registry()) {
printf("\nInitialization of Test Registry failed.");
return -1;
}
/2.添加测试集/
pSuite = CU_add_suite(“suite_for_test”, NULL, NULL);
/3.添加测试用例/
CU_add_test(pSuite, “test_case1”, test_case1);
CU_add_test(pSuite, “test_case2”, test_case2);
CU_add_test(pSuite, “test_case3”, test_case3);
/4.设置测试模式,这里选择冗余模式,即最大程度的输出测试细节/
CU_basic_set_mode(CU_BRM_VERBOSE);
/5.设置发生错误后的action,这里选择忽略,继续跑测试*/
CU_set_error_action(error_action);
/6.开始测试/
test_return = CU_basic_run_tests()
printf("\nTests completed with return value %d.\n", test_return);
/7.清理注册点,释放内存/
CU_cleanup_registry();

return 0;

}

测试结果为:
在这里插入图片描述
可以看到测试结果符合预期,测试失败的项目也有打印出出错的文件名,行号以及错误原因。这里因为没有实现time相关函数,所以测试花费时间为0,如果你需要测试代码的性能和效率,可以自己去实现time相关的函数。最后需要注意的是,cunit的源码实现中,有很多地方都用到了malloc()去申请内存,且测试用例越多,消耗的内存越大,当你发现调用CU_add_suite添加测试集老是失败时,应该就是heap耗尽,需要加大heap size。
在这里插入图片描述
移植后的STM32 demo工程链接如下:
https://github.com/xihua13104/CUnit/tree/master/Examples/CUnit_STM32_Demo

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值