変更履歴
- 2023/7/7 追記
こちらに関連記事を書きました。よければ参照ください。
シリアルコンソールから任意のタイミングで、任意のCppUTestのテストを実施できる環境をつくってみました。
この記事はなに?
STM32マイコンの統合開発環境(以降STM32CubeIDE)にC/C++のテストフレームワーク(CppUTest)を環境構築しました。
ソースコードはつぎになります。
https://github.com/grace2riku/hello_stm32_cpputest
結果、STM32マイコンでもCppUTestでテストを実行できましたので環境構築の手順を書きます。
とはいえ既にWebに書いてある情報で何番煎じだ、という感じですがなにか参考になれば嬉しいです。
環境
ホストPC環境
ホストPC環境はつぎのとおりです。
- macOS Big Sur バージョン 11.6.4
STM32CubeIDE
STM32CubeIDEのバージョンはつぎのとおりです。
- Version: 1.9.0 Build: 12015_20220302_0855 (UTC)
※2022/6/2現在の最新バージョンでした。
CppUTest
C/C++の組込みソフトウェア向けテストフレームワークの選択肢はつぎがあるかと思います。
- CppUTest
- Unity
- Google Test
メモリ制約の厳しい小規模なマイコン上でTDDすることになると
- CppUnit
- Unity
が妥当かな・・・と思いました。
私が【テスト駆動開発による組み込みプログラミング】の本を写経していることもあり、本で多く使われているCppUTestで環境構築したかった、というのが大きな理由です。
ハードウェア
使用したマイコンボードはつぎです。このマイコンボードを使った理由ですが特に深い理由はありません。
過去、セミナー参加時に使ったことがあり他のSTM32マイコンボードより少し多く使っていて慣れている、くらいの理由です。
- NUCLEO-F446RE
- STM32F446RET6 64ピン
- Arm Cortex-M4コア 180MHz
- フラッシュ: 512Kbyte
- SRAM: 128Kbyte
- Arm Mbed対応
環境構築目標
環境構築するにあたり次を達成したいと思いました。
- RAMでテストを実行したい
- STM32CubeIDE単体でテスト結果がわかる
1の理由ですがTDDは
- テストを書く
- テスト失敗確認
- 成功するテストを書く
- テスト成功確認
- リファクタリング
のサイクルを素早く回すリズムが重要と思います。テスト実行に時間がかかるのは嫌だったので少しでも早く動くようRAMでテストを実行したいと思いました。また確認時間・書換え回数も気になるのでフラッシュを何度も書換えしたくない、というのも理由です。
これはSTM32CubeIDEでプロジェクト作成するとフラッシュ用・RAM用で2つのリンカスクリプトが用意されるのでRAM用のリンカスクリプトを選択することで実現できました。
2の理由はITMというものを使えばSTM32CubeIDEだけでテスト結果を確認できそう・・・と調査してわかりました。デバッグ情報はUART出力しテラタームなどのシリアルコンソールに表示する、というのがデバッグのセオリーだと思いますが、今回はSTM32CubeIDE単体でテスト結果がわかるように試してみました。
環境構築手順
STM32CubeIDEにCppUTestを構築する手順は既にいくつもWebに情報があります。
大きく分類すると3つの手順があります。
CppUTestをStatic Libraryでビルドする
CppUTestをStatic Libraryでビルドし、STM32プロジェクトでリンクします。
参考1のリンクの手順とおりに作業していきました。
個人的につぎの点にはまりました。
- Static Libraryのプロジェクトを新規でつくる場合はC/C++ Projectではなく、STM32 Projectにする。
最初C/C++ ProjectでCppUTestのStatic Libraryをつくろうとしましたがつくれませんでした。
STM32マイコンのプロジェクトを作成する
STM32マイコンのプロジェクトを作成します。さきほどつくったCppUTestのStatic Libraryをリンクします。
参考1のリンクの手順とおりに作業していきました。
ITMを設定する
ITMを設定し、STM32CubeIDEのSWV ITM Data Consoleにテスト結果が表示されるようにします。
参考3のリンクの手順とおりに作業していきました。
今回のマイコンボードNUCLEO-F446REの場合はシリアルワイヤビューワ(SWW)のCore Clockはデフォルトの16MHzにしました。
ソースコードの説明
テストコード
お試しでつぎのテストコードを書きました。
#include "CppUTest/TestHarness.h"
TEST_GROUP(FirstTestGroup)
{
};
TEST(FirstTestGroup, FirstTest)
{
FAIL("FAIL: FirstTestGroup, FirstTest\n");
}
TEST(FirstTestGroup, SecondTest)
{
CHECK_EQUAL_ZERO(0);
}
TEST(FirstTestGroup, IntSize)
{
LONGS_EQUAL(4, sizeof(int));
}
TEST(FirstTestGroup, FirstTest)のFirstTestGroupはテストグループの名称です。
2つ目の引数がテストの名称になります。
- FirstTestは失敗するテストで、引数の文字列を表示します。
- SecondTestは引数が0か否かを確認するテストで成功する見込みです。
- IntSizeはint型のバイト数を確認するテストで成功する見込みです(対象マイコンは32bitマイコン)。
テストコードの実行
テストを実行しているのは次のコードです。
/* USER CODE BEGIN 2 */
const char* empty[] = {};
const char* command_v[] = {"cpputest", "-v"};
printf("\n----- RunAllTests(0, empty) -----\n");
CommandLineTestRunner::RunAllTests(0, empty);
printf("\n----- RunAllTests(2, command_v) -----\n");
CommandLineTestRunner::RunAllTests(2, command_v);
/* USER CODE END 2 */
【CommandLineTestRunner::RunAllTests(0, empty)】はコマンドラインオプションなしでテストを実行しています。
【CommandLineTestRunner::RunAllTests(2, command_v)】はvオプションでテストを実行しています。テストの結果が詳しく表示されるオプションです。
テスト動作確認
テストコード実行
マイコンボードNUCLEO-F446REとPCをUSBケーブルで接続します。
STM32CubeIDEのデバッグモードに移動し、プログラムを実行します。
つぎのようにSWV ITM Data Consoleにテスト結果が表示されます。
予想とおりの結果になりました。
- FirstTestはテスト失敗
- SecondTestはテスト成功
- IntSizeはテスト成功
vオプション付きでテストを実行すると少し詳しくテスト実行の結果が表示されています。
----- RunAllTests(0, empty) -----
..
../Core/Src/test_src.cpp:10: error: Failure in TEST(FirstTestGroup, FirstTest)
FAIL: FirstTestGroup, FirstTest
.
Errors (1 failures, 3 tests, 3 ran, 3 checks, 0 ignored, 0 filtered out, 0 ms)
----- RunAllTests(2, command_v) -----
TEST(FirstTestGroup, IntSize) - 0 ms
TEST(FirstTestGroup, SecondTest) - 0 ms
TEST(FirstTestGroup, FirstTest)
../Core/Src/test_src.cpp:10: error: Failure in TEST(FirstTestGroup, FirstTest)
FAIL: FirstTestGroup, FirstTest
- 0 ms
Errors (1 failures, 3 tests, 3 ran, 3 checks, 0 ignored, 0 filtered out, 0 ms)
FirstTestが失敗し、SecondTest・IntSizeのテストは成功しています(テストは失敗したらメッセージ表示されます)。
次の動画はSTM32CubeIDEでデバッグモードに移動しテストを実行・結果を表示する動画です。
まとめ
環境構築目標に書いた目標を達成することができました。
気になった点をつぎに書きます。
メモリ使用量
CppUTestのStatic Libraryをリンクし、テストを書くするとどのくらいのメモリを使用するのか気になりました。
資源が少ない組み込み向けマイコンは重要なポイントですよね。
RAMの使用量は 79.10% になっていました。
このままだとこのマイコンボード(SRAM:128K)でTDDを続けるのは厳しそうですね・・・。
参考2に書いてあるように次の対応をするのが良さそうです。
- Memory Leak Detection 機能をリンクせずにメモリ容量を削減する
- サイズ優先の最適化をおこなう
最後に
最後まで読んでいただきありがとうございました。
なにか参考になれば嬉しいです。
参考
つぎのページを参考にさせていただきました。ありがとうございます。
1 STM32CubeIDEでCppUTest, STM32プロジェクトの作成する手順
https://embeddedarea.com/unit-testing-in-stm32cubeide/
2 Atollic TrueSTUDIOでCppUTest, STM32プロジェクトの作成する手順
https://qiita.com/tk23ohtani/items/1f1cc4b9fa58a04f520c
3 ITMの設定手順
https://moons.link/post-544/