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是最好的方法。
關鍵詞:
您可能也感興趣:
今日熱點
為您推薦
高途 9 周年,陳向東:投入重資、多團隊探索 GPT 相關方向 世界最資訊
世界播報:惠州一百萬人,月繳1000元,都不知道公積金有這個BUG!
天天消息!50角鋼每米重量是多少_50角鋼每米重量
排行
最近更新
- RTOS任務進行單元測試的4種策略
- 郵儲銀行株洲市分行開展《反電信網絡詐騙法》宣傳活動
- 全球今熱點:實名曝光!大連14人終生禁駕
- 一線采風|粽香迎端午 巾幗暖人心
- 兒童入托入學需查驗疫苗接種證明,梧州市民可提前在網上自助...
- 實時:賞非遺賽龍舟品美食 廣西玉林端午文化盛宴引客來
- 世界最新:走出世界!掘金拉美2023拉美跨境電商產業帶高峰論...
- 出口商品竟是“冒牌貨” 廣州港航公安迅速破案 世界最新
- 全球熱訊:寧德時代電池有重大缺陷 已被特斯拉暫停供貨?寧德...
- 8363人!三亞萬科公園里安居房項目申報審核結果出爐
- 今日觀點!60余位詩人、作家端午節前走進彭州,以文學之力助力...
- 列車上乘客暈倒 甘肅臨澤護士呂婷婷緊急救助
- 高質量發展調研行|福州:“數治”帶來高品質生活
- 句容稅務:送稅法進高校 “稅”助力向未來 全球微速訊
- 北海市財政局:“四個一” 創新建立政府采購信用體系
- 今頭條!泉峰汽車最新公告:終止實施2022年限制性股票激勵計...
- 中航證券:給予東方電氣買入評級_全球視點
- 天天快播:“你是不是開通了會員,每個月要扣500元......”小...
- 當前報道:vivo V29或在未來幾周亮相 搭載驍龍778G+移動平臺
- 減脂時,是不是不能吃豬肉?
- 3歲孩子吃牛肉和什么菜配好。? 看點
- 阿米番茄牛腩的做法正宗?
- 當前熱門:巨灣技研發布“鳳凰電池”,中低壓平臺也能實現超快充
- “千名大學生看奉賢”暨“發現奉賢”短視頻征集大賽啟動
- 全球消息!鄰里贈祝福 端午粽飄香
- 內地端午假期預計旅游人次達1億 旅游消費達370億 每日視訊
- 【全球新視野】內蒙古自治區達拉特旗發布高溫黃色預警
- 小米巨省電新一級空調上架:1.5匹、直降550元 世界通訊
- 每日關注!不朽者傳奇跳票到8月22
- 自動下載!《最終幻想16》已開啟預載-環球觀焦點
今日要聞
- 列車上乘客暈倒 甘肅臨澤護士呂婷婷緊急救助
- 天天熱訊:【程序源代碼】人工智能ChatGPT實現的微信小程序
- 招商局港口(00144)擬透過以股代息方式派付末期股息
- 許錦波當選國際計算生物學學會Fellow|焦點熱訊
- 天天快播:“你是不是開通了會員,每個月要扣500元......”小心,這是詐騙!
- 電動車戴頭盔規定2020 電動車戴頭盔規定2020最新-天天速訊
- 科沃斯618全周期成績釋出,穩居行業第一,發揮龍頭領跑優勢
- 多家豬企探索全產業鏈布局 能否對沖豬價走低風險?
- AI生成的音樂能獲獎嗎?格萊美獎制定新規則_世界資訊
- 臨港集團董事長袁國華:“常熟綠洲芯城”是臨港集團繼大豐園區之后又一大戰略落子