can't go to sleep

組み込み開発の多くをRubyで自動化することに興味があります

組み込みC言語用の単体テストのテクニックについて

ざっくり箇条書きしておきます。

  • レジスタアクセス関連は全てpragma inlineでビルド時に自動展開される関数化して、ヘッダファイル内に定義する。こうすることで、製品コードでのビルド時には直接マクロを呼ぶ場合と同じアセンブラに展開される。更に、レジスタアクセス関連のヘッダファイルのincludeを、ifdefで切って単体テスト時にはfff.hで作成したモック関数を呼ぶようにする。ざっくり、以下のような感じ。
// source.c
#ifdef _UNITY_UNIT_TEST_
#include "testMacro.h"
#else
#include "Macro.h"
#endif

void func1(void)
{
  func_macro();
}

// testMacro.h
// プロトタイプ宣言のみ行う。実体はfff.hにてテストケース部に記載する。
void func_macro(void);

// Macro.h
#pragma inline(func_macro)
static void func_macro(void)
{
  REGISTOR.REG1.BIT = 1;
}
  • スタティック関数は、単体テスト時にはグローバル関数になるようにする。具体的には、STATICという型をtypedefして、製品コード時にはstaticに設定して、単体テスト時には何もしないように設定しておく。こうすることで、すべての関数を単体テストできる。
  • C言語では、どういつファイル内の関数をモック関数に置き換えることはできない。そのため、どうしても原理主義的に全ての関数をモックにしてテストをしたい場合は、すべての関数にifdefをつけて、単体テスト時にifdefを切りながらビルドをする必要がある。ただし、現行のUnityにはその機能は無いため、拡張しないといけない。個人的には、そこまでやらないとテストケースが爆発するような関数(ファイル)は、そもそもファイルわけがうまくできていないということなので、ファイルわけを行うことをおススメしたいわけだが・・・。やはり、ファイルにインタフェース関数は2つ(1つは実行関数で1つは初期化関数)にするのが関数(ファイル)設計としては好ましいかと思う。
#ifndef _UT_VOID_FUNC1_VOID_
void func1(void)
{
}
#endif

#ifdef _UT_VOID_FUNC2_INT_
void func2(int a)
{
}
#endif
  • Unityで単体テストをする場合は、基本的には1ファイルごとに分断してテストすることが好ましい。そのため、includeで他ファイルを呼ぶ場合はその必要な関数と変数と定数をテストケース内にて定義する。関数の場合はfffを使って定義して、変数の場合はテストケース内部で実体化して、テスト対象のコードはexternで呼び出す。(定数はどうするか不明。これもテストケース内で記述する?)そのため、テストケースでincludeして良いヘッダファイルは、テスト対象のヘッダファイルとfffとunityのヘッダファイルだけ。ただし、定数だけ定義しているようなヘッダファイルはincludeしてOK。こうなると実は、定数の扱いがなかなか厄介だったりする。この辺りは、ルールを作って、テストケースを自動で作るスクリプトを作りたいところ。というか、それが無いとUnityの導入障壁が高すぎる。

Excelファイルをjsonファイルに変換するツールを作成しました

imanityに新ツールを追加しました。
https://github.com/tatssuki/imanity

imanityはExcelスプレッドシートを使用してテストケースを管理するツールです。
しかしExcelスプレッドシートはgitなどのバージョン管理ツールでの使用は非常に面倒でした。

そこで、Excelで作成したファイルをjsonファイルに変換するツールを作成しました。
もちろん、jsonファイルからExcelファイルを復元することもできます。

使い方は簡単で、
imanity_xls2json.rbに引数としてExcelスプレッドシートファイルを与えるだけです。
jsonファイルから戻すときは、imanity_json2xls.rbに引数としてjsonファイルを与えます。
例)imanity_xls2json.rb test.xls

WIN32OLEを使用しているため、windows上でしか動作しません。


また、これに対応して、上記ツールで作成したjson形式のテストケースをimanityで解釈できるようにしました。
これで、Excelの入っていないCIサーバ上でimanity形式の単体テストを自動実行することが可能になります。


多分、このツールはimanityとか関係なく、Excelファイルをgitやsubversionなどのバージョン管理ツールで使いたい人には便利な気がします。まあ、抽出するデータの種類が少ないので、ちょっといまいちかもしれませんが(データサイズを小さくしたかったため、必要最低限のデータのみ集めるように設計しました)。

ツールの簡単な説明。imanityに使用するであろう、数式と値と文字色、セル色のみを抽出して、jsonに吐き出す、それだけ。
WIN32OLE以外のツールではなかなかうまくできませんでした。imanityも全部WIN32OLEでやった方が良いような…。でもWIN32OLEは見えないところでExcelが動くのがちょっと気持ち悪い…。

unityのテストケース管理ツールimanity

テストケース管理ツールimanityを作成し、githubで公開を始めました。
tatssuki/imanity · GitHub
使い方の説明などは、日本語readmeを参照してください。
https://github.com/tatssuki/imanity/blob/master/README_ja.md


何はともあれ、exampleを実行して、感覚をつかんでもらえると良いかと思います。
いちいちimanityを実行するのは面倒だと思うので、rake実行時に同時にimanityを自動実行する形式は後で作ってex2として公開します。

unity支援ツール

unity支援ツールを2つ作成しました。
1つ目は、既存のソースコードを、unity実施可能なレベルにinclude関係などを自動で整形して解決するツールです。
既存のソースコードにunityを実施するためには、依存関係を断ち切るような工夫が必要となり、unityの導入障壁になっていますが、そのコストをゼロにするのが目的です。ただし、このツールではソースコードを自動整形するため、unity実施可能とするためのコピーコードを作成します。したがって、実コードを使用して単体テストを実施する、という狙いからは離れてしまうという欠点があります。

2つ目は、unityのテストケースをExcelなどのスプレッドシートで管理するツールです。有料のc++testに近い動きになるかと思いますが、fffと組み合わせることでモック関数の動作も容易に設定でき、無料であるという点で言えば、結構便利かもしれません。また、変数の組み合わせを定義すればテストケースを自動生成できますし、この変数組み合わせは1つ目のツールを応用して生成できると考えています。また、Excelがない環境でも使用できるように、csv形式にも対応しています。

個人的には2つ目のツールGitHubで公開して、無料で組み込み用C言語単体テスト自動化がここまでできるんだ、というのを一般的にしたいと考えています。一方で、そもそもunityでテストを実施するのが難しい、それ以前の問題だ、という人が多い場合には、せっかく単体テストを有効にできるツールを作っても意味がないのではないのか、と思い、1つ目のツールを作った次第です。
今後、タイミングを見て2つのツールGitHubにて公開したいと考えています。2つ目のツールは、割と早いうちに公開できるかと思っています。

e2studioとUnity2

前回の記事から結構な日数が経ちましたが、e2studioとUnityの連携を確認してみました(とは言っても、実はe2studioではコンソール出力が良くわからなかったので、CS+にて実施しました。方法は、ルネサスのマニュアル通りで簡単にできました)。
以下、感想です。

  • ルネサス提供のマニュアルにはテスト自動化という観点は無い
  • そのため、定期的なテストとしては使えない
  • このマニュアル通りに実施するメリットは、実コンパイラ単体テストが実施出来る点のみ

以上のため、基本的にはEclipseやSublimeTextなどでgccでテストをして、最終的に、e2studioで実コンパイラでテストする、という流れが良いかと思います。

一方で、マニュアル記載の方式では、テストファイルは1つしか使用できません。
そのため、Unityのように、ソースファイルごとにテストファイルを準備する方法では、テストファイルを都度入れ替えながら評価する必要があり、それが非常に手間になります。

この問題を解決するためには、AutoItなどを使用して、e2studioのGUI操作を自動化して自動実行させる必要があるかと思います。もしかしたら、CS+だと、IronPythonが使用できるため、うまくやれば自動化できるかもしれません。

e2studioとUnity

ルネサスエレクトロニクス統合開発環境といえばHEWが有名だと思っていたのですが、いつの間にはHEWは終了していました。
これからは、CubeSuite+改めCS+と、e2studioの2つでまとめていくようです。
e2studioはEclipseベースとのことなので、ARM系をEclipseで開発したことがある人などをターゲットにしているのでしょうか。

e2studioですが、Eclipseをベースにしているということで、Unityとの連携が可能です。
これは、ルネサスで公式ドキュメントが公開されています。

http://documentation.renesas.com/doc/products/tool/apn/r20an0313jj0100_e2_unity.pdf

しかし、残念ながら非常にわかりづらい資料です。
Unityは単体テストフレームワークとして非常に有用で、敷居も低いはずなのに、どうしてもわかりやすい日本語ドキュメントが無くて、いまいち流行っていないという印象です(e2studioと連携してルネサスが公式で牽引すると変わってくると思いますが)。

できれば、e2studioでUnityを連携した場合を調査して、ブログに書いて、少しでもUnityが流行るように力添えしたいと考えています。

ちなみに、EclipseとUnityの連携に関しては、公式のThrow The Switchにも記述しています。


Throw The Switch! - White Papers - Using Eclipse IDE

同一フォルダ以下にあるcsvファイルを抽出するスクリプト

最近Pythonを勉強し始めています。
とりあえず、同一フォルダ以下にあるcsvファイルを抽出するスクリプトを作成してみました。

# csv_file_search.py
import os
import re

def recursive(path, file_list):
  dir_list = os.listdir(path)
  for dirfile in dir_list:
    abs_dirfile = path + '\\' + dirfile # 絶対パスにする
    if os.path.isdir(abs_dirfile):
      recursive(abs_dirfile, file_list)
    else:
      file_list.append(abs_dirfile)

path = os.path.dirname(__file__)

file_list = []
recursive(path, file_list)

pattern = re.compile(r'\.csv$')
for file in file_list:
  if pattern.search(file):
    print file

ちなみに、rubyだとこんな感じ。

# csv_file_search.rb
def recursive(path, file_list)
  Dir::foreach(path) do |dirfile|
    next if dirfile == "." or dirfile == ".."
    abs_dirfile = path + "/" + dirfile

    if FileTest.directory?(abs_dirfile)
      recursive(abs_dirfile, file_list)
    else
      file_list << abs_dirfile
    end
  end
end

file_list = Array.new
path = File.dirname(File.expand_path(__FILE__))
recursive(path, file_list)

file_list.each{|file|
  if file =~ /\.csv$/
    print file
    print "\n"
  end
}

また大体同じですね。
unityをいじる場合はrubyが必須なのですが、組み込み系エンジニアは、Pythonの方が向いていると思います。scipyなどで複雑な演算が得意だったり、そもそも処理速度が速かったり、英語圏ではよく使用されているようです。
scipyは毎年シンポジウムもあるようです。
ということで、これからpythonを覚えて、mathcadの代わりにpython+sphinxで出来れば良いなあと考えています。どれだけかかるのやら・・・。