历史上的今天
返回首页

历史上的今天

今天是:2024年10月13日(星期日)

正在发生

2020年10月13日 | 适合于嵌入式系统的C语言单元测试框架:SCUNIT

2020-10-13 来源:eefocus

说明

主流语言都有测试框架,在嵌入式领域特别是资源紧缺的单片机工程中没有合适的测试框架。本文发布一种简单的C语言测试框架SCUNIT,可以应用于嵌入式领域。


测试环境

本框架是基于标准C语言编写,对平台无要求,测试环境如下:


单片机:STM32F407

RTOS:RT-THREAD

测试用例测试的是基于链表的一个路由表结构。


API

void scunit_load(PrintFunc func);

void scunit_add_suite(char *name);

void scunit_add_test(char *name, SCUnitFunc func);

void SCUNIT_ASSERT(bool condition, char *tag);

void SCUNIT_ASSERT_MESSAGE(bool condition, char *tag, char *message);

void SCUNIT_PRINT(char *format, ...);


scuit_load函数是scuit模块载入函数,需要提供打印接口。比如在标准c语言环境,可以将printf作为参数传入。在单片机应用里,可以将串口打印函数传入。本文的测试用例,是将SEGGER RTT的打印函数作为参数传入。


/**

* @brief 使用RTT输出

*/


void console_rtt(char *format, ...)

{

    static char log_buf[RT_CONSOLEBUF_SIZE];

    

va_list args;

va_start(args, format);

    

    int length = rt_vsnprintf(log_buf, sizeof(log_buf) - 1, format, args);

    if (length > RT_CONSOLEBUF_SIZE - 1) {

        length = RT_CONSOLEBUF_SIZE - 1;

    }

    log_buf[length] = 0;

    

    SEGGER_RTT_printf(0, log_buf);

    

    va_end(args);

}


scunit_load(console_rtt);


源码

scunit.h

/**

 * Copyright (c), 2019-2019

 * @file cunit.h

 * @brief 单元测试头文件

 * @verbatim 

 * Change Logs:

 * Date           Author       Notes

 * 2019-08-31     jdh          新建

 * @endverbatim 

 */

#ifndef CUNIT_H

#define CUNIT_H


#include "stdio.h"

#include "stdbool.h"

#include "stdint.h"

#include "stdlib.h"

#include "string.h"            

#include "stdarg.h"


typedef void (*PrintFunc)(char *format, ...);

typedef void (*SCUnitFunc)(void);


void scunit_load(PrintFunc func);

void scunit_add_suite(char *name);

void scunit_add_test(char *name, SCUnitFunc func);

void SCUNIT_ASSERT(bool condition, char *tag);


/**

 * @brief 带信息输出的断言

 * @param condition: 条件

 * @param message: 断言失败时输出

 */

 

void SCUNIT_ASSERT_MESSAGE(bool condition, char *tag, char *message);


void SCUNIT_PRINT(char *format, ...);


#endif


scunit.c

/**

 * Copyright (c), 2019-2019

 * @file cunit.c

 * @brief 单元测试主文件

 * @verbatim 

 * Change Logs:

 * Date           Author       Notes

 * 2019-08-31     jdh          新建

 * @endverbatim 

 */


#include "scunit.h"


#define CUNIT_PRINT_SIZE_MAX            128                      


static PrintFunc _print = NULL;

static char *_func_name = NULL;


void scunit_load(PrintFunc func)

{

    _print = func;

}


void scunit_add_suite(char *name)

{

    _print("nSuite:%sn", name);

}


void scunit_add_test(char *name, SCUnitFunc func)

{

    _func_name = name;

    _print("-------------------->case:%s beginn", _func_name);

    func();

    _print("-------------------->case:%s endn", _func_name);

}    


void SCUNIT_ASSERT(bool condition, char *tag)

{

   if (condition) {

       _print("%s tag:%s CUNIT_ASSERT passn", _func_name, tag);

   } else {

       _print("%s tag:%s CUNIT_ASSERT failn", _func_name, tag);

   }

}


/**

 * @brief 带信息输出的断言

 * @param condition: 条件

 * @param message: 断言失败时输出

 */


void SCUNIT_ASSERT_MESSAGE(bool condition, char *tag, char *message)

{

    if (condition) {

       _print("%s tag:%s CUNIT_ASSERT passn", _func_name, tag);

   } else {

       _print("%s tag:%s CUNIT_ASSERT fail:%sn", _func_name, tag, message);

   }

}


void SCUNIT_PRINT(char *format, ...)

{

    static char buf[CUNIT_PRINT_SIZE_MAX];

    

va_list args;

va_start(args, format);

    

    int length = vsnprintf(buf, sizeof(buf) - 1, format, args);

    if (length > CUNIT_PRINT_SIZE_MAX - 1) {

        length = CUNIT_PRINT_SIZE_MAX - 1;

    }

    buf[length] = 0;

    

    _print(buf);

    

va_end(args);

}


测试代码

载入测试模块:


scunit_load(console_rtt);

1

测试用例:


/**

* Copyright (c), 2019-2019

* @file test_routing_table.c

* @brief 路由表测试主文件

* @verbatim 

* Change Logs:

* Date           Author       Notes

* 2019-08-30     jdh          新建

* @endverbatim 

*/


#include "test_routing_table.h"

#include "routing_table.h"

#include "test.h"


#define MAX_TEST_NUM                100


static void test_case0(void);

static void test_case1(void);


void test_routing_table_run(void) 

{   

    scunit_add_suite("test_routing_table");

    scunit_add_test("case0", test_case0);

    scunit_add_test("case1", test_case1);

}


static void test_case0(void)

{

    bool result = false;

    uint8_t port = 0;

    

    routing_table_add(0x1234567812345678, 1);

    result = routing_table_query(0x1234567812345678, &port);

    SCUNIT_ASSERT(result && port == 1, "1");

    

    routing_table_delete(0x1234567812345678);

    result = routing_table_query(0x1234567812345678, &port);

    SCUNIT_ASSERT(result == false, "2");

    

    routing_table_add(0x1234567812345679, 12);

    result = routing_table_query(0x1234567812345679, &port);

    SCUNIT_ASSERT(result && port == 12, "3");

}


static void test_case1(void)

{   

    uint64_t t1 = get_local_time_us();

for (uint32_t i = 0; i < MAX_TEST_NUM; i++) {

        routing_table_add(i, i);

    }

    uint64_t t2 = get_local_time_us();

    int delta = t2 - t1;

    SCUNIT_PRINT("add %d consume time:%ldus single:%ldusn", MAX_TEST_NUM, delta, delta / MAX_TEST_NUM);

    

    bool result = false;

    uint8_t port = 0;

    

    routing_table_add(1, 5);

    for (uint32_t i = 0; i < 5; i++) {

        t1 = get_local_time_us();

        routing_table_query(i, &port);

        t2 = get_local_time_us();

        delta = t2 - t1;

        

        SCUNIT_PRINT("query ia:%x result:%d port:%d consume time:%ldusn", i, result, port, delta);

    }

    

    t1 = get_local_time_us();

    for (uint32_t i = 0; i < MAX_TEST_NUM; i++) {

        routing_table_delete(i);

    }

    t2 = get_local_time_us();

    delta = t2 - t1;

    SCUNIT_PRINT("delete %d consume time:%ldus single:%ldusn", MAX_TEST_NUM, delta, delta / MAX_TEST_NUM);

    

    test_case0();

}


测试输出

 0> Suite:test_routing_table

 0> -------------------->case:case0 begin

 0> case0 tag:1 SCUNIT_ASSERT pass

 0> case0 tag:2 SCUNIT_ASSERT pass

 0> case0 tag:3 SCUNIT_ASSERT pass

 0> -------------------->case:case0 end

 0> -------------------->case:case1 begin

 0> add 100 consume time:2087us single:20us

 0> query ia:0 result:0 port:0 consume time:34us

 0> query ia:1 result:0 port:5 consume time:2us

 0> query ia:2 result:0 port:2 consume time:33us

 0> query ia:3 result:0 port:3 consume time:33us

 0> query ia:4 result:0 port:4 consume time:33us

 0> delete 100 consume time:1825us single:18us

 0> case1 tag:1 SCUNIT_ASSERT pass

 0> case1 tag:2 SCUNIT_ASSERT pass

 0> case1 tag:3 SCUNIT_ASSERT pass

 0> -------------------->case:case1 end


推荐阅读

史海拾趣

Crameda Intersys公司的发展小趣事

作为一家有社会责任感的企业,Crameda Intersys公司不仅关注自身的经济效益,还积极履行社会责任。公司积极参与公益事业,支持教育、环保等领域的发展。同时,公司还注重环保和可持续发展,通过采用环保材料和节能技术,降低生产过程中的能耗和排放。这些举措不仅提升了公司的社会形象,也为社会的可持续发展做出了积极贡献。

这五个故事只是Crameda Intersys公司发展历程中的一部分,但它们足以展现出公司在电子行业中的成长轨迹和不懈追求。在未来的发展中,Crameda Intersys公司将继续以技术创新为引领,积极拓展市场渠道,培养更多优秀人才,加强质量管理和社会责任履行,为电子行业的发展贡献更多的力量。

Antex公司的发展小趣事

到了1965年,Antex公司迎来了一次重要的技术突破。公司成功研发出“环形件轧机”,这一创新技术不仅提高了生产效率,还大幅提升了产品的精度和稳定性。这一技术的成功应用,使得Antex的业务范围得到了进一步扩大,公司在电子行业中的地位也愈发稳固。

A1 PROS公司的发展小趣事

在竞争激烈的电子行业中,A1 PROS始终保持对技术创新的追求。公司不断投入研发资金,引进先进的生产设备和技术人才,致力于开发更高性能、更环保、更节能的产品。同时,A1 PROS还积极关注行业动态和市场需求变化,不断调整和优化产品结构和市场策略。正是凭借这种持续创新的精神和对市场变化的敏锐洞察,A1 PROS得以在电子行业中保持领先地位,并引领着行业的发展方向。

以上便是关于A1 PROS公司在电子行业里发展起来的五个故事。这些故事展示了A1 PROS从初创期的黑白CCD业务起步,到成功研发彩色CCD传感器、获得技术认可、拓展国际市场以及持续创新引领行业发展的全过程。虽然这些故事是基于虚构的,但它们反映了电子行业发展的一般规律和趋势,也展现了A1 PROS作为一家优秀电子企业在行业中的成长轨迹和贡献。

CML Microcircuits公司的发展小趣事

随着技术的不断进步,CML Microcircuits公司始终保持对创新的追求。在模拟、数字和混合信号集成电路领域,CML不断推出具有创新性的产品,满足市场的多样化需求。通过与全球顶级客户的紧密合作,CML深入了解市场趋势,并将其转化为具有竞争力的产品。这些努力使CML在行业中逐渐建立了技术创新的引领者地位。

Analytic Instruments Corp公司的发展小趣事

随着技术的不断成熟和产品线的不断丰富,Analytic Instruments Corp开始积极拓展市场。公司通过与行业内的知名企业和研究机构建立合作关系,成功地将产品打入多个重要的应用领域。同时,公司还加大了品牌宣传力度,通过参加行业展会、举办技术研讨会等方式,提升了品牌知名度和影响力。

Elite公司的发展小趣事

进入21世纪后,电子行业迎来了新的发展机遇和挑战。面对行业内的激烈竞争和技术的快速迭代,Elite意识到必须加快转型升级步伐。公司开始涉足智能家居、物联网等新兴市场领域,并成功推出了一系列具有竞争力的产品。这些新产品不仅为公司带来了新的增长点,也进一步巩固了Elite在电子行业的领先地位。

问答坊 | AI 解惑

Tieto招聘:嵌入式软件工程师

叠拓(原迪易通)信息技术有限公司 (Tieto),成立于1968年,是一家北欧的从事软件解决方案的全外资集团公司,分别在赫尔辛基和斯德哥尔摩证券交易所挂牌。是综合实力北欧地区第一,欧洲前三的IT业务供应商。 请将简历发送到 xiaoli.yang@tieto.co ...…

查看全部问答>

TTL与非门的问题

下图TTL与非门中,D1和D2起什么作用? 请指教,谢谢! …

查看全部问答>

初学者有用不???

初学者有用不???——LED驱动电路概述 [ 本帖最后由 雪山飞狐 于 2010-5-20 18:03 编辑 ]…

查看全部问答>

WINCE下TOUCH消息有时会丢失?

通过触摸屏连续点击按钮控件,有时后点击没有反应;而用USB鼠标连续点击却一切正常。是什么原因导致触摸屏 消息有时候会丢失呢?…

查看全部问答>

电脑显示器半边黑屏,求助于各位达人

各位达人,我的台式显示器开机是正常的,但过一会儿就会上半边黑屏,下半边是正常的。这不知道是什么问题。请各位给我一些帮助。我很感激你们。…

查看全部问答>

c#开发智能设备应用使用了WebBrowser控件的问题

vs2005 c#开发智能设备应用,在使用了WebBrowser控件后,就接收不到ppc2003的仿真器上的按键响应了,但如果把WebBrowser控件删掉后,就能正正常的接收到按键响应了,请问这是什么原因啊?怎么解决?…

查看全部问答>

wince的啟動問題

請問,我觀看e-boot的source code,發現 // Function prototypes extern void Launch(unsigned int uAddr); extern void Launch2(unsigned int uAddr); 但卻沒有該function的實作,是否要自己去實作這兩個function 才能啟動wince …

查看全部问答>

建议:关于“团购板子中不带触摸板”的解决办法

首先这次团购活动确实是EEWORLD为大家争取到的福利,这块板子上的仿真器部分的芯片就不止25元,网上一个这样的仿真器价格在50元左右,加上板子其实实际价值差不多100元,25元出去15元运费,相当于大家以10元来买了价值100元的板子,并且后面坛子里 ...…

查看全部问答>

纯数字电路设计的一个8路抢答器(带报警,倒计时,led指示等)

纯数字电路设计的一个8路抢答器(带报警,倒计时,led指示等) 小弟第一次来此下帖,希望对有需要的有所帮助! 课设的报告还没写完,暂时没贴上,有待更新!…

查看全部问答>

【视频】TI制胜解决方案系列:4-20mA输出的现场变送器和智能传感器方案

各位工程师,好久没和大家聊聊啦。 今天将有好东西重磅推出,TI 从2013年开始面向中国市场客户推崇制胜解决方案,涉及工业、医疗、汽车等各应用领域,是由TI 精选产品组成的产品解决方案。 今天,和大家分享的是4-20mA输出的现场变送器和智能传感 ...…

查看全部问答>