組込みC言語の単体テスト自動化を考える

テスト自動化を考える。組込みでテスト自動化したいのだ。

重要な用語とその課題¥

テストとは、バグのある実装を避ける技術ではなく、意図しない破壊的変更を検出するための技術。

テストフレームワーク
複数のテストを実行する。組込み開発環境には組み込み特有の課題がある。
  • 命令がRAMではなく不揮発性のROMに書き込まれ、プログラムの差し替えが難しい
  • RAMが小さく、大規模なことがしにくい
  • ブートローダやリンカを使いこなす必要があり、難しい
  • ファイルシステムやコンソールがないのでprintfによるテスト実行結果が確認しづらい
スタブ
偽の通信相手を作成し、事前に設定した任意の期待値を得る。
  • C言語はコンパイルが必要な言語なので、スクリプト言語のような関数の差し替えが難しい
  • 全ての関数コールを関数ポインタ経由にするのも現実的ではない
  • リターン値を自在にコントロールすることで正常系・異常系のテストができるはず。
  • ハードウェアが揃わないと開発できない、揃っても危険。
  • 割り込み待ち、という処理を抽象化できるか。本物は割り込みも再現してくれる。戦略
  • 関数名は同じだが、実装を変える。コンパイラオプションで
モック
ビヘイビアを確認するために作成するオブジェクト
  • 実は使いどころが難しいと個人的には思っている。
  • 引数が必要な関数に偽オブジェクトを渡す。
  • スタブはステートをいじるもの、モックはステートをいじってもらうもの。
  • オブジェクトが意図した使われ方をするか、をテストできる
  • google test
  • FFF
  • google mock
  • elf spy
  • fakeit
  • unity
  • catch
  • cunit

組込み開発でもPCで実行すればいいじゃないか。

こういったテストフレームワークはPC向けを想定されていて、組み込み環境では使いにくい。それならば最初からPCの上で実行すればいいじゃないか。実際、そういう記事(英語)が多いので、その方法のほうが一般的なんだろう。

そのためには、組込み特有の「ペリフェラルのレジスタ(メモリマップドI/O)」「割り込み」「RTOS」の振る舞いを汎用PCの上で再現する必要がある。

  • レジスタ
    • 特定のアドレスから仮の値をリードする
    • 特定のアドレスに値を書き込んだふりをして、何か機能が実行されたことにする
      • 一定時間後にデータが用意される (HWがなくても必ず成功する)
      • 特定の割り込みハンドラが実行される (HWがなくても必ず成功する)
      • データが読み取れる(HWがなくても必ず成功する)
  • 割り込み
    • _
  • RTOS
    • 待ち状態に入る (waitFlag)
    • 周期的な処理を行う(cyclic handler)
    • 起きてほしいタイミングで起きて、処理を続行する。
    • セマフォ・ミューテックス
    • メッセージバッファ
  • ミドルウェア
    • テスタビリティは考慮されていないので、ミドルウェア全体をエミュレーションする
    • 代表的な構造を想定する
      1. 裏でタスクが動く
      2. タスクがメインタスクに"何らかの手段"で通知/同期通信を行う
      3. メインタスクがデータを受け取り、処理を継続する。

この記事をちゃんと読んだらおもしろそう。http://www.electronvector.com/blog/for-embedded-tdd-dont-worry-so-much-about-testing-on-the-target

RTOS

freeRTOS kernelはWindows上で動くらしいので試してみた。delayTaskしか試してないけど、なんか動いてちょっと感動。求めていたのはこういうことなんだよ、という気分。ただ、どこまでできるのかは不明。

CIは普通Linux環境で実行されるので、Linuxで動作する環境を構築する必要がある。セットアップ面倒だな。

割り込み、レジスタアクセスは抽象化しないといけない。

おまけ