RTOS任务进行单元测试的4种策略
https://www.beningo.com/4-tactics-to-unit-test-rtos-tasks/
超过50%的嵌入式软件项目使用实时操作系统(RTOS)。不幸的是,使用RTOS会给使用现代开发技术(如测试驱动开发(TDD)、DevOps或自动测试)的开发者带来一些问题。例如,当开发者试图为他们的任务编写测试时,他们遇到的第一个问题是任务函数包含一个无限循环!任何直接调用任务函数的测试都会被认为是一个无限循环!因此,任何直接调用任务函数的测试将永远不会完成。这篇文章将探讨对RTOS任务进行单元测试的几种策略,其中包括:
- 循环的重新定义
- 完成信号
- 任务排除
- 通过OSAL使用主机线程(强烈建议)。
注意:在这篇文章中,我们将把任务和线程作为同义词。我们还将使用ThreadX作为RTOS的例子。
【资料图】
任务的剖析
在RTOS任务中,有几个部分用于管理任务行为。首先,初始化部分声明变量,实例化对象,初始化驱动程序,并负责将传递给任务的任何数据转换成正确的类型。接下来,有一个无限循环,执行所有任务的行为。例如,如果我们写了管理传感器的任务,我们希望任务的无限循环能定期检索和处理传感器的数据。最后,任务完成部分规定了任务完成后的情况。
典型的任务使用ThreadX可能看起来像下面的代码片段:
{ void Task_Sensors(ULONG ThreadInput) { // SECTION 1: Initialization (void) ThreadInput; SensorData_t SensorRawData; SensorData_t SensorData; SensorData_t pSensorDataTx = &SensorData; Sensor_Init(); // SECTION 2: Tasks main function / behavior / purpose while(true) { SensorRawData = Sensor_Sample(); SensorData = SensorProcess(SensorRawData); (void)tx_queue_send(SensorTxQ, (void *)&pSensorDataTx, TX_WAIT_FOREVER); tx_thread_sleep(TASK_SENSORS_PERIOD_MS); } // SECTION 3: TasK Completion Activities }我发现,编写周期性任务的开发人员希望他们的任务能够无限期地运行,而没有考虑如果任务运行到完成会发生什么。
看看这个任务,你可以看到,如果一个开发者想在测试中对Task_Sensors进行调用,他们会遇到几个问题。因此,让我们来看看这些问题,并按照我经常看到的开发人员在达成最直接和最好的解决方案之前所尝试的各种策略。
参考资料
- 软件测试精品书籍文档下载持续更新 https://github.com/china-testing/python-testing-examples 请点赞,谢谢!
- 本文涉及的python测试开发库 谢谢点赞! https://github.com/china-testing/python_cn_resouce
- python精品书籍下载 https://github.com/china-testing/python_cn_resouce/blob/main/python_good_books.md
循环的重新定义
经常看到工程师部署的第一个战术是循环重定义。循环的重新定义是指根据代码是生产代码还是测试代码,对任务中的无限循环进行操作。例如,Task_Sensor的代码将被改写为如下内容:
void Task_Sensors(ULONG ThreadInput) { // SECTION 1: Initialization (void) ThreadInput; SensorData_t SensorRawData; SensorData_t SensorData; SensorData_t pSensorDataTx = &SensorData; Sensor_Init(); // SECTION 2: Tasks main function / behavior / purpose while(LOOP_STATE) { SensorRawData = Sensor_Sample(); SensorData = SensorProcess(SensorRawData); (void)tx_queue_send(SensorTxQ, (void *)&pSensorDataTx, TX_WAIT_FOREVER); tx_thread_sleep(TASK_SENSORS_PERIOD_MS); } // SECTION 3: TasK Completion Activities }这个想法是,开发人员可以创建有条件编译的代码,以控制循环是永远运行还是一次。这段代码通常看起来像下面这样:
#ifdef PRODUCTION #define LOOP_STATE true #else #define LOOP_STATE false #endif除了编译后的代码外,构建时还必须定义或不定义PRODUCTION宏。
一般来说,这不是使RTOS任务可测试的好方法,有几个原因。首先,我们正在测试的代码正在改变。我们的任务在测试过程中的行为会与运行时的行为不同。第二,我们正在添加有条件编译的代码,这通常不是很干净。第三,在定义宏的过程中,有可能出现人为错误。最后,虽然循环的重新定义对于Task_Sensor来说似乎很有吸引力,但测试任务的相互作用会变得过于复杂。事实上,如果我们需要循环运行三到四次会发生什么?我们现在需要为LOOP_STATE定义独特的定义。
完成信号
完成信号是对循环重定义思想的修改,允许任务无限期地运行,直到收到任务应该停止执行的信号。在这种情况下,任务代码被修改为删除宏定义,转而使用循环变量,如下图所示:
void Task_Sensors(ULONG ThreadInput) { // SECTION 1: Initialization (void) ThreadInput; bool isRunning = true; SensorData_t SensorRawData; SensorData_t SensorData; SensorData_t pSensorDataTx = &SensorData; Sensor_Init(); // SECTION 2: Tasks main function / behavior / purpose while(isRunning) { SensorRawData = Sensor_Sample(); SensorData = SensorProcess(SensorRawData); (void)tx_queue_send(SensorTxQ, (void *)&pSensorDataTx, TX_WAIT_FOREVER); tx_thread_sleep(TASK_SENSORS_PERIOD_MS); isRunning = Task_GetDesiredRunState(TASK_SENSOR); } // SECTION 3: TasK Completion Activities }正如你所看到的,在任务结束时,我们检查该任务是否仍在运行。这就解决了运行多个循环的问题,并通过删除宏来清理代码。然而,我们现在已经为任务的运行引入了复杂性,并为内存损坏或单一事件干扰(SEU)打开了机会,以改变isRunning的状态并完成我们的任务。
任务在生产中运行到完成似乎不是什么大问题,但不是所有的实时操作系统都能优雅地处理这个问题。例如,如果你允许FreeRTOS的任务运行到完成,内核就会窒息并停止执行所有的代码!
任务排除法
当我们不测试我们的任务时,任务排除就发生了!我们不需要测试任务!而不是试图从测试线束中调用任务,我们创建测试来运行任务本身的代码。 任务排除要求我们重写我们的函数,使其看起来像下面这样:
void Task_Sensors(ULONG ThreadInput) { // SECTION 1: Initialization (void) ThreadInput; Task_SensorInit(); // SECTION 2: Tasks main function / behavior / purpose while(true) { Task_SensorRun(); tx_thread_sleep(TASK_SENSORS_PERIOD_MS); } // SECTION 3: TasK Completion Activities } /********************************** * Placed in a different module **********************************/ void Task_SensorInit(void) { SensorData_t SensorRawData; SensorData_t SensorData; SensorData_t pSensorDataTx = &SensorData; Sensor_Init(); } void Task_SensorRun(void) { SensorRawData = Sensor_Sample(); SensorData = SensorProcess(SensorRawData); (void)tx_queue_send(SensorTxQ, (void *)&pSensorDataTx, TX_WAIT_FOREVER); }老实说,上面的代码就是任务应该有的样子。这段代码非常干净,也很容易理解。但问题是,我们是通过尝试使用一种不太理想的技术来达到这个目的的。
我们现在在Task_Sensors中看到的任务代码非常简单,任务排除会说我们不需要测试它。因此,相反,我们将在我们的测试线束中测试Task_SensorInit和Task_SensorRun函数。毕竟,这些函数被保存在一个单独的模块中,所以我们可以将任务代码从测试线束中排除,实现我们所期望的100%的代码覆盖,对吗?
任务排除的问题是,在我们在目标上运行应用程序之前,我们永远不会测试任务代码。不幸的是,我们也欺骗自己,认为我们的测试覆盖了所有的代码。
优点是我们可以根据需要从测试中调用任务中的函数。我们已经实现了这一点,并避免了需要处理无限的while循环的问题。代码更加简洁,我们也没有创建一堆条件性编译语句。
虽然这种技术可以使测试任务代码更容易,但从技术上讲,它不是在测试任务代码。相反,它是一种变通方法。为了测试你的任务代码,你应该尝试在你的主机上创建一个线程或任务,并在那里运行这些代码。
通过OSAL使用主机线程(强烈建议)。
测试RTOS任务的真正问题与大多数任务有while语句的事实无关。相反,这个问题来自于开发者对测试的思考和方法。到目前为止,我们所研究的所有策略都假定我们想直接从我们的测试线束中调用Task_Sensors。这就是问题所在。在我们的测试线束中,我们想创建一个运行的Task_Sensors任务或线程,而不是进行函数调用!这就是问题所在!
我们测试困境的根本原因是,开发人员没有利用操作系统抽象层(OSAL)。相反,他们直接进入RTOS的API并使用这些API。虽然调用RTOS APIs很快,而且似乎是一个很好的方法,但它是将RTOS与应用程序代码紧密地耦合在一起。这种耦合使得测试正确使用任务或线程的应用程序代码变得非常困难。
最佳的方法是将RTOS的细节隐藏在操作系统抽象层(OSAL)的后面。例如,如果你检查我们到目前为止所看到的各种版本的Task_Sensors,你会发现我们正在使用ThreadX tx_queue_send API来发送消息到队列。因此,我们应该把这些细节放在OSAL后面,这样我们的任务就会像下面这样:
void Task_Sensors(ULONG ThreadInput) { // SECTION 1: Initialization (void) ThreadInput; bool isRunning = true; SensorData_t SensorRawData; SensorData_t SensorData; SensorData_t pSensorDataTx = &SensorData; Sensor_Init(); // SECTION 2: Tasks main function / behavior / purpose while(true) { SensorRawData = Sensor_Sample(); SensorData = SensorProcess(SensorRawData); (void)OSAL_Q_Send(SensorTxQ, (void *)&pSensorDataTx, OS_WAIT_FOREVER); tx_thread_sleep(TASK_SENSORS_PERIOD_MS); } // SECTION 3: TasK Completion Activities }OSAL_Q_Send是一个抽象,我们的应用程序代码使用它来发送队列中的数据。应用程序不应该关心我们是否在使用ThreadX、FreeRTOS、pthread或任何其他RTOS或任务调度器。根据我们想要编译代码的系统,会提供一个实现文件。这样做有很多好处,比如说:
- 应用程序不与实时操作系统相联系
- 测试可以使用主机的线程框架
- 应用程序是可移植和可重复使用的
- 避免了临时性的和黑客式的测试方法进行测试。
对于许多对使用DevOps和自动测试线束感兴趣的开发者来说,你可能至少有针对你所选择的RTOS和pthread的实现,pthread是Linux的POSIX线程库。不幸的是,我们如何使用pthread以及设计和构建OSAL已经超出了今天的范围,但我们将在未来探讨这些话题。
现在,如果你有兴趣看一些OSAL的例子,我推荐你看一下CMSIS-RTOS-V2和NASA的Aeronautics GSC-18730-1。有可能,对于你的需求来说,这些都是过剩的,但它们是完全实现OSAL的好例子。我建议探索它们,并慢慢设计和建立你的OSAL,你可以在你所有的应用程序中使用。
小结
有几种策略可以让开发者用来对任务或线程进行单元测试。正如我们在今天的文章中所看到的,大多数开发人员可以部署的战术都是针对未能将其任务架构为使用OSAL的变通方法。一旦有了OSAL,任务代码就可以通过提供抽象层背后的必要功能,使用任何RTOS或本地线程库进行测试。OSAL层有助于:
- 简化测试策略
- 保持代码的清洁
- 提高灵活性、可移植性和重用性
如果你想测试你所有的代码,那么通过OSAL来利用pthread是最好的方法。
标签:
RTOS任务进行单元测试的4种策略
2023-06-21
雨一直下张宇mp3下载(趁早张宇mp3下载)-环球观焦点
2023-06-21
6月21日菏泽江源硫酸价格暂稳 开工正常
2023-06-21
蔚来获中东资本79亿元加持,腾讯或将套现25亿 当前热点
2023-06-21
博纳影业:全资子公司浙江博纳近期获得政府补助款1633.92万元|当前快报
2023-06-21
“22金茂MTN002”将于6月30日付息 本计息期债券利率3.4%
2023-06-21
铜陵有色(000630)6月21日主力资金净卖出3117.05万元
2023-06-21
新华人寿保险股份有限公司原党委书记、董事长万峰被开除党籍
2023-06-21
生肖兔运势分析,准的你想哭啊 世界快播报
2023-06-21
微头条丨端午假期将至 这8个提示请收好!
2023-06-21
雨一直下张宇mp3下载(趁早张宇mp3下载)-环球观焦点
6月21日菏泽江源硫酸价格暂稳 开工正常
蔚来获中东资本79亿元加持,腾讯或将套现25亿 当前热点
博纳影业:全资子公司浙江博纳近期获得政府补助款1633.92万元|当前快报
“22金茂MTN002”将于6月30日付息 本计息期债券利率3.4%
铜陵有色(000630)6月21日主力资金净卖出3117.05万元
新华人寿保险股份有限公司原党委书记、董事长万峰被开除党籍
生肖兔运势分析,准的你想哭啊 世界快播报
微头条丨端午假期将至 这8个提示请收好!
学习魏书生教育艺术心得体会
港澳车牌图片_港澳车牌 天天热推荐
石渠县气象台发布雷电黄色预警信号[III级/较重] 【2023-06-20】
【全球速看料】京东企业钱包怎么提现到个人_京东企业钱包登录入口
老板不按合同发工资可以提出解约吗-当前要闻
炎亚纶突袭记者会现场,向耀乐道歉并泪崩否认偷拍,耀乐拒绝接受-环球快看
全球短讯!麦迪森控股(08057.HK)年度来自持续经营业务收益减少约25.5%至约9720万港元
老人骑车路边摔倒 “00后”青年党员组织路人筑“人墙”守护施救 环球快播报
昌乐宝石鉴定机构谈谈潮州人为什么喜欢玉石珠宝
同济科技:二股东拟于7月7日自行召集和主持临时股东大会_每日头条
无画不说|用完就退?“无理由退货”规则不能滥用 环球新消息
世界热头条丨信用卡停息挂账结束后还能用吗?停息挂账还完后征信多久恢复?|全球聚焦
华能水电:6月20日融券卖出8.85万股,融资融券余额21.03亿元
关注:延续和优化新能源汽车车辆购置税减免政策 专家:超预期!
炎亚纶承认与未成年发生关系 称视频不是自己主动外流
好分数网查成绩入口2023_好分数网查成绩入口_全球观热点
容积式制冷剂压缩机容积流量试验方法(关于容积式制冷剂压缩机容积流量试验方法介绍)_信息
新资讯:银川警方打掉宁夏首个利用石油卡为诈骗分子“洗钱”的犯罪团伙
逸富交易软件改名什么软件了(逸富行情交易系统)
蠡湖股份(300694.SZ):与博世新能源有过接触,截至目前未建立正式的合作关系
- 中际旭创再创历史新高,近3个月累涨超400%-新要闻
- 山西春秋墓葬发现楚式青铜器,映射晋楚争霸历史|天天资讯
- 2023云南民族中学中考预估分数线是多少 观焦点
- 今热点:国铁集团:广州白云站全面封顶 建成后成为亚洲最大TOD综合交通枢纽之一
- 最新快讯!以市场需求为导向 重艺新增三大特色专业
- 最新:山财本科招生网官网 山财网官网
- 新朋友上岛!6月22日《奥比岛》×红小豆联动来袭-要闻
- 国元证券:给予西部超导买入评级
- 动态焦点:三亚市首届公益性骨灰海葬活动举办
- 国内有登革热疫苗吗?广东疾控:暂无,主要通过防蚊灭蚊预防|世界速看
- 第243集 | 天津蓟州黄崖关郭二山舍农家院,住宿管三餐,依山而建原生态乡... 今日视点
- 向佐晒视频:郭碧婷素颜出镜气质佳,小奶黄拍手唱歌好可爱! 报资讯
- 六十以后想要长寿,睡前少做3件事,再喜欢也要忍住
- 鱼珠隧道预计2026年12月完工 通车后将连接广州3个重要经济片区
- 天天微头条丨之行无界半导体获前海方舟系基金数千万种子轮投资
- 天天观点:陕西建工(600248):6月19日北向资金减持298.41万股
- 天天即时:福莱特玻璃(06865.HK)遭朱雀基金减持316.8万股
- 微头条丨多部门密集施策 全面激活农村内需潜力
- 审神者好像哪里不对无防盗 小说_审神者好像哪里不对百度云 天天新视野
- 诗蒂兰面膜缺点_诗蒂兰面膜 世界视讯
- 为何番茄没了儿时的味道?-天天精选
- 一封平信不得超过多少克(一封平信不得超过) 焦点短讯
- 中科江南:底部启动形态,机构研究情况精选,数电票改造+区块链应用,ARPU值有望提升
- 上海市十六届人大常委会第三次会议举行,审议养老政策、野保条例、人事任免等 全球聚看点
- 「闭环思维」能解决企业营销中的什么问题?
- 东星医疗: 关于2023年限制性股票激励计划(草案)及相关文件的修订说明公告
- 医思健康(02138.HK)发盈警 预期年度除税后溢利同比减少不多于61%
- 企业为什么要引入“会员制”?会员制的好处与意义 世界热点评
- 今日聚焦!铁矿日报
- 青山26个老旧小区领取“一张施工许可”
- 现在北交所转板能不能转?募集资金可以全部用做研发中心吗?
- 世界信息:老年斑能用激光治疗吗?多久能恢复?
- 2023BRFE连锁加盟展会开启品牌方与创业者线下交流盛会
- 招商轮船(601872.SH):接收1艘1100TEU的集装箱船_天天微资讯
- 海峡影视季项目推介会在厦门举办
- 当前资讯!大行评级|富瑞:体育用品及家电于“618”第二阶段增长急剧放缓
- 5月新能源:比亚迪再超20万辆 埃安欲超特斯拉问鼎亚军?
- 积水过肩、市民游泳前行,地铁喷水池站真的在喷水……特大暴雨夜袭贵阳,有人弃车逃离,还有人被冲走
- 港股恒指震荡下行跌超1% 科技股走低阿里巴巴跌超3%
- 世界快播:滴滴发布橙意保障计划:9成网约车司机月均抽成低于20%
- 确保就业形势总体稳定
- 开抖店和开橱窗哪个好?哪个更适合小白做?
- 全球热文:售价7.99-10.79万元 2023款奇瑞欧萌达上市
- 什么电影网站更新快呀(什么电影网站更新快)
- 王毅会见美国国务卿布林肯_天天热点评
- 大学生智能科技创新创业挑战赛启动-天天资讯
- 明星大侦探第三季百度云资源高清_明星大侦探第三季 百度云|环球热门
- 恢复项目进度。-天天热文
- 有关系才是硬道理_对于有关系才是硬道理简单介绍
- 共商沪陕两地合作大计!陈吉宁龚正与赵一德赵刚所率陕西党政代表团座谈_世界观察
