19 Jun 2013, 10:09

Rubyで書かれた統合C言語TDD開発環境 「Ceedling」 がけっこう便利そうな件

UnityやCMockについて色々調べて来ましたが、今日はそれらを束ねるツール Ceedlingにつついて調べました。

[toc]

Ceedlingとは

Ceedlingとは、UnityとCMockを使ってテストを実行するためのテスト管理用フレームワーク。テストを助けるための便利なツールがRubyスクリプトで書かれていて、それらを利用してC言語でのTDDをウマクやるためのツール。

Ceedlingのインストール

CeedlingはRuby,Rakeが必要なので、まずはrubyとrakeをインストールしておくこと。また、Rubygemもパッケージ取得のために必要。入れておくこと。

Ceedlingのインストールは以下のコマンドで実施する。

gem install ceedling

Ceedlingプロジェクトの作成する。

ceedling new ceedling_study

Unity・CMockを利用するためのテスト環境が一気に自動生成される。かっこ良い。(・∀・)

デフォルトでいろんなモードが用意されている。これらがテストを実施するためのお助けツール。詳しくは、使い方のドキュメントを参照。

% rake -T
rake clean# Delete all build artifacts and temporary products.
rake clobber  # Delete all generated files (and build artifacts).
rake environment  # List all configured environment variables.
rake files:header # List all collected header files.
rake files:source # List all collected source files.
rake files:test   # List all collected test files.
rake logging  # Enable logging
rake module:create[module_path]   # Generate module (source, header and test files)
rake module:destroy[module_path]  # Destroy module (source, header and test files)
rake paths:source # List all collected source paths.
rake paths:support# List all collected support paths.
rake paths:test   # List all collected test paths.
rake summary  # Execute plugin result summaries (no build triggering).
rake test:*   # Run single test ([*] real test or source file name, no path).
rake test:all # Run all unit tests.
rake test:delta   # Run tests for changed files.
rake test:path[dir]   # Run tests whose test path contains [dir] or [dir] substring.
rake test:pattern[regex]  # Run tests by matching regular expression pattern.
rake verbosity[level] # Set verbose output (silent:[0] - obnoxious:[4]).
rake version  # Display build environment version info.

vendor配下にunityとcmockのディレクトリを持ってくる。

cd vendor
git clone git://github.com/ThrowTheSwitch/Unity.git
git clone git://github.com/ThrowTheSwitch/CMock.git

仕様のドキュメントは vendor/ceedling/docs配下にある。

docs/
├── CExceptionSummary.pdf
├── CMock Summary.pdf
├── CeedlingPacket.pdf
└── Unity Summary.pdf

設定ファイルは project.ymlをいじるけれども、まずはデフォルトでOk.

Unityといっしょに使ってみる

Unityのテストコートを書いてみます。

#include "unity.h"
#include "hogehoge.h"

void setUp(void)
{
}

void tearDown(void)
{
}

void test_hogehogefirst(void)
{
  TEST_IGNORE_MESSAGE("Here");
}

実行結果

[tsu-nera]% rake test:all

Test 'Testhogehoge.c'
---------------------
Generating runner for Testhogehoge.c...
Compiling Testhogehoge_runner.c...
Compiling Testhogehoge.c...
Linking Testhogehoge.out...
Running Testhogehoge.out...

-------------------------
IGNORED UNIT TEST SUMMARY
-------------------------
[Testhogehoge.c]
  Test: test_hogehogefirst
  At line (14): "Here"

-------------------------
OVERALL UNIT TEST SUMMARY
-------------------------
TESTED:  1
PASSED:  0
FAILED:  0
IGNORED: 1

ちなみに、unity fixture形式のテストの書き方だとうまく動作しなかった。ここ3週間、仕事でUnityFixture形式のテストを書きまくったので、どうやって移植しようか。。。ウマクやる方法がわかったら追記する。

CMockといっしょに使ってみる

テストファイルに、モックしたいソースのヘッダファイルを以下の形式で書くと、CeedlingはCMockを利用してモックを自動生成する。

#include "mock_xxxxx.h"

prefixのmock_はproject.ymlの以下のパラメータを変更すれば調整可能。

:cmock:
  :mock_prefix: mock_

CMockサンプル

以下のように、uhauha()の先でhogehogeをコールしている。hogehogeをモックしたい。

uhauha.c

#include "uhauha.h"
#include "hogehoge.h"

void uhauha(void)
{
  hogehoge();
}

テストコードを以下のようにかく。

Testuhauha.c

#include "unity.h"
#include "uhauha.h"
#include "mock_hogehoge.h"

void setUp(void)
{
}

void tearDown(void)
{
}

void test_uhauhafirst(void)
{
  hogehoge_ExpectAndReturn(2);
  uhauha();
}

gcovといっしょに使ってみる

gcovを利用して、gcovデータを生成する。project.ymlにgcovプラグインの記述を追加する。

:tools:のところにgcovを追加。

:plugins:
  :load_paths:
    - vendor/ceedling/plugins
  :enabled:
    - stdout_pretty_tests_report
    - module_generator
    - gcov(ここに追加)

rakeコマンドでgcovを実行。build/gcov/outに gcnoができる。

rake gcov:all

xUnit形式のxmlファイルを生成して、Jenkinsで表示する

もともと、Unityでのテスト結果をxUnit formatのXMLに変換する方法を調べていたらceedlingを見つけたので、これがやりたかったこと。project.ymlに以下を追加する。

:plugins:
  :load_paths:
    - vendor/ceedling/plugins
  :enabled:
    - stdout_pretty_tests_report
    - module_generator
    - xml_tests_report

rakeコマンドでtestを実行。/build/artifacts/testに report.xmlができる。

<?xml version='1.0' encoding='utf-8' ?>
<TestRun>
        <FailedTests/>
        <SuccessfulTests>
                <Test id="1">
                        <Name>test/Testuhauha.c::test_uhauhafirst</Name>
                </Test>
        </SuccessfulTests>
        <IgnoredTests>
                <Test id="2">
                        <Name>test/Testhogehoge.c::test_hogehogefirst</Name>
                </Test>
        </IgnoredTests>
        <Statistics>
                <Tests>2</Tests>
                <Ignores>1</Ignores>
                <FailuresTotal>0</FailuresTotal>
                <Errors>0</Errors>
                <Failures>0</Failures>
        </Statistics>
</TestRun>

このファイルをJenkinsから表示すればよい。表示するためには、xUnit Pluginを入れる。

xUnit Plugin – Jenkins – Jenkins Wiki

xUnit Pluginには、Unity用の選択肢がない。CppUnitと形式が似ているため、xml生成用のRubyスクリプトに手を入れる。

まず、CppUnitには、Ignoreという概念がないので、L38,L100のIgnore関連の記述をコメントアウト。

L38.

=begin
    write_tests( results[:ignores], stream, 'IgnoredTests' )
=end

L100.

=begin
    stream.puts "\t\t<Ignores>#{counts[:ignored]}</Ignores>"
=end

L68行目にもバグがあるので、修正。

      stream.puts "\t\t</FailedTest>"
      # stream.puts "\t\t</Test>"

Ceedling / Discussion / Ceedling Forum:XML report

これで、Jenkinsでも結果が表示されたた。

09 Jun 2013, 12:48

CMockでモック関数を自動生成する!なんてピーターパン症候群だった。。。手動生成やっつけダミー地獄に落ちたメモ

以前、こんな記事を書きました。

恐るべきレガシーコードの救世主になるか?!ドロドロ依存なモジュールたちを『CMock』ですっ裸にする | Futurismo

 

どんなコードに対しても、テストが簡単にかけるみたいな、素敵なことを夢想したわけだが、それは空虚な夢幻だった。ピーターパンだった。これを、仕事で実践しようとしたときに、現実は冷酷だった。

自動生成してもコンパイル通らない(´。・ω・。`)

結局ドロドロな手動ダミー関数地獄に落ちていき、忙殺されたのだった。今日は、そんな苦労のなかで見つけたTipsをメモします。

[toc]

 

64bit対応

コンパイルオプションに以下のデファインを追加すればいいっぽい。

UNITY_INCLUDE_64

//を/* */に置き換え

そもそも、//を利用してコメントするとコンパイルエラー。。手強い。/* */に変換した。

UNITY_OUTPUT_CHARで標準出力関数を置き換え

Unityでは標準出力のために、putcharを利用している。

//-------------------------------------------------------
// Output Method
//-------------------------------------------------------

#ifndef UNITY_OUTPUT_CHAR
//Default to using putchar, which is defined in stdio.h above
#define UNITY_OUTPUT_CHAR(a) putchar(a)
#else
//If defined as something else, make sure we declare it here so it's ready for use
extern int UNITY_OUTPUT_CHAR(int);
#endif

 

自分の環境(vxWorks)では、putcharが使えなかった。printfは利用できるようだった。そこで、UNITY_OUTPUT_CHARを自分で定義しななおして、標準出力関数を置き換えた。

//-------------------------------------------------------
// Output Method
//-------------------------------------------------------

#define UNITY_OUTPUT_CHAR(a) printf("%c",(a))

#if 0
#ifndef UNITY_OUTPUT_CHAR
//Default to using putchar, which is defined in stdio.h above
#define UNITY_OUTPUT_CHAR(a) putchar(a)
#else
//If defined as something else, make sure we declare it here so it's ready for use
extern int UNITY_OUTPUT_CHAR(int);
#endif
#endif

インクルードファイルの挿入

OSのファイルを先頭に取り込まないと行けない場合がある。自分の場合は、仕事でvxWorksを利用しているので、”vxWorks.h”。Windowsだと、”Windows.h”みたいな。生成されたモックファイルたちにも、ソースの頭で読みこむ必要かあった。

cmock/lib/cmock_config.hの以下のパラメータにヘッダファイルを入れることで自動生成するものに挿入してくれる。

    :includes_h_pre_orig_header  => nil,

    :includes_h_post_orig_header => nil,

    :includes_c_pre_header       => nil,

    :includes_c_post_header      => nil

たとえば、Cソースの頭にヘッダファイルを挿入したい場合は、

    :includes_c_pre_header  => [‘hogehog_internal.h’] (ここは配列)

とする。すると、以下のように生成される。

#include

#include

#include

#include “unity.h”

#include “cmock.h”

#include “hogehoge.h”

#include “CMock_hogehoge.h”

string.h、stdlib.h、setjmp.h、unity.h、cmock.hのあとに挿入される。自分の場合は、 の前に挿入したかったので、lib/cmock_generater.rbを直接編集をしてしまったorz。

とりあえず、hogehoge_internal.hを先頭に挿入して、あとはコンパイルエラーが出る度に、hogehoge_internal.hに場当たり的にいろいろ定義していった。

extern宣言している関数をモックする

ヘッダファイルにextern宣言されている関数はモックで自動生成されなかった。調べたら、ヘッダファイルにexternを書くことは、なんの意味もないらしい!(グローバル変数は別)

CMockの場合、デフォルトではextern宣言されている関数のモック作成はしないが、オプションを変更することで可能。

lib/cmock_config.rbの:treat_externs のオプションを:excludeら:includeに変更する。

=begin

    :treat_externs            => :exclude,       
=end

    :treat_externs            => :include,      

困るのが、extern宣言されている関数が複数箇所で宣言されている場合。これは妥協して手動でどちらかの関数を#if 0した。

extern宣言しているグローバル変数の扱い

グローバル変数の場合、extern宣言はヘッダファイルに書いて、実体はCソース書くことがルール。テストをするときに、テストをするときに、テストコードに実体を宣言して利用していたが、このグローバル変数の実体を自動生成してくれないかなとおもった。調べると、こんな記事を発見。

CMock / Discussion / CMock Forum:Mocking extern functions

これによると、自分のテストファイルに変数を宣言しなさいとある。はい、そうします。

自動生成されたモックと手動生成したモックの使い分け

とりあえず、接頭語をMock_とCMock_で分ける。

MakefileでどちらのCMock_(自動生成)かMock_(手動生成)のどちらを利用するかを選ぶ。ある関数は自動生成を利用したいけど、別関数は自前の関数を利用したいなんてこともある。

そのときは、callbackオプションを利用する。

    :plugins                  => [‘callback’],

これで、hogehoge()に対して、hogehoge_StubWithCallbackという関数が生成される。この関数に、自前のモック関数をセットすればよい。

例えば、Mock_hogehoge { return 2;}を使って、こんな風に書く。

#include "Mock_hogehoge.h"

TEST(hogehoge,second)
{
  hogehoge_StubWithCallback(Mock_hogehoge());
  TEST_ASSERT_EQUAL( 2, (hogehoge() + 1) );
}

しかし、この宣言をいちいち記述するのも面倒なので、よく呼ばれる関数は、ダミーかフェイクを自前で作ったほうがよいとおもった。(OSの関数はモックするべきでなかったとおもった)

モック関数としてではなくて、フェイク関数として使う場合

Ignore関数を呼ぶとよい。;pluginにignoreオプションを追加でモック関数が生成される。パラメータチェックや何回呼ばれたかのチェックがされなくなる。

    :plugins                  => [‘ignore’],

モードが2種類あり、呼ばれた回数はチェックする場合とそうでない場合。ignoreオプションで選ぶ。(args_and_call / args_only)

staticなし関数をかってにexternでみてる場合

ヘッダファイルに宣言されていないが、static宣言もない、他のファイルの関数を勝手に使ってる。これは、対処の方法がわからなかった。手動生成。

最後に

CMockは痒いところに手が届く、使い勝手がよいツールなので調べればいい手が見つかるかもしれない。Rakeをもう少し使いこなして、このへんの処理をやりたいことろだ。まあ、地獄に落ちつつも後先考えないやっつけ仕事でテスト環境はとりあえずできたので、よいのだけれども。

09 Jun 2013, 09:30

UnityやCMockをEclipseで使うときに役立つ設定まとめ

最近はようやくUnityやCMockの使い方が分かってきた感じだ。今日は、Eclipseでの便利な設定を試してみた。元ネタ、本家公式サイトからの抜粋。

TEST_IGNOREタグの設定

Eclipseは指定したタグを検索して、タスク・ビューに表示させる機能がある。これに、UnityでのTEST_IGNOREを加えることで、これからするテストに対するリマインダを設定できる。以下を選択。

  • ツールバー
    • ウィンドウ -> 設定
      • C/C++ -> タスク・タグ -> 新規

TEST_IGNOREを追加する。その後、プロジェクトを右クリックして、

  • インデックス -> 全てのファイルを更新

することで、再度タグの検索が走り、TEST_IGNOREがタスクビューに表示される。

SunShade Pluginでテストコードとソースコード間をいったりきたり

SunShade プラグインを入れると、テストコードとソースコード間をいったりきたりできる。通常は、2画面で並べて作業しているので、出番は少なめかもしれない。

The Sunshade suite of eclipse plugins

ツールバー -> ヘルプ -> 新規ソフトウェアのインストール -> 追加 でhttp://sunshade.sourceforge.net/updateを追加してインストールする。

起動するとエラーした(´・ω・`)SWTエラーが発生しましただと。

Cannot initialize Drop

pluginsディレクトリのnet.sourceforge.sunshade.filedrag_3.4.0というディレクトリを削除したらエラーしなくなった。こんな対処で大丈夫か?

機能を利用するためには、ツールバー -> ウィンドウ -> 設定 -> Sunshade -> Scripted Actionsを選択。たとえば、src/*\**.cとtest/Test**.cの間を行き来するためには、以下のように書く。

importPackage(java.lang, Packages.org.eclipse.ui);
importPackage(Packages.net.sourceforge.sunshade.util);
var wb = PlatformUI.getWorkbench(); // IWorkbench API
var editor = // IEditorPart API
  wb.getActiveWorkbenchWindow().getActivePage().getActiveEditor(); 
if (editor)
{
  var input = editor.getEditorInput(); // IEditorInput API
  var path = input.getPath(); // IPath API
  var filename = path.toOSString();
  if (filename.indexOf("Test") == -1) // ここを書き換え
    filename = filename.replace("src\\","test\\Test"); // ここを書き換え
  else
    filename = filename.replace("test\\Test","src\\"); // ここを書き換え
  //alert("Filename: " + filename);
  FileUtil.openEclipseEditorForFile(filename);
}

Test拡張子以外に対応する場合でも、例えばMock拡張子でも、すこしコードを書き換えれば利用できる。

このSunshadeにはErrorlinkというツールがあるけれども、こっちはうまく動かない。

コードテンプレートの設定

Eclipseのコードテンプレート機能を利用すれば、Ctrl + Spaceでテストのテンプレートを呼び出すことができる。

レッツ メタプログラミング(^O^)/

ツールバー -> ウィンドウ -> 設定 -> C/C++ -> エディタ- -> テンプレートを選択。

新規を選択して、テスト用のテンプレートを記述する。たとえば、Unityのフィクスチャ機能のテンプレートは以下。

TEST(${test_group_name},${test_case_name})
{
  ${line_selection}${cursor}

  TEST_IGNORE_MESSAGE("${test_case_name} Needs Definition.");
}

RUN_TEST_CASE(${test_group_name}, ${test_case_name});

別にテンプレートを利用しなくても、testまで書いて Ctrl + Spaceである程度の自動補完はできる。

最近はようやくUnityやCMockの使い方が分かってきた感じだ。今日は、Eclipseでの便利な設定を試してみた。元ネタ、本家公式サイトからの抜粋。

Throw The Switch! – White Papers – Using Eclipse IDE

TEST_IGNOREタグの設定

Eclipseは指定したタグを検索して、タスク・ビューに表示させる機能がある。これに、UnityでのTEST_IGNOREを加えることで、これからするテストに対するリマインダを設定できる。以下を選択。

  • ツールバー
    • ウィンドウ -> 設定
      • C/C++ -> タスク・タグ -> 新規

TEST_IGNOREを追加する。その後、プロジェクトを右クリックして、

  • インデックス -> 全てのファイルを更新

することで、再度タグの検索が走り、TEST_IGNOREがタスクビューに表示される。

SunShade Pluginでテストコードとソースコード間をいったりきたり

SunShade プラグインを入れると、テストコードとソースコード間をいったりきたりできる。通常は、2画面で並べて作業しているので、出番は少なめかもしれない。

The Sunshade suite of eclipse plugins

ツールバー -> ヘルプ -> 新規ソフトウェアのインストール -> 追加 でhttp://sunshade.sourceforge.net/updateを追加してインストールする。

起動するとエラーした(´・ω・`)SWTエラーが発生しましただと。

Cannot initialize Drop

pluginsディレクトリのnet.sourceforge.sunshade.filedrag_3.4.0というディレクトリを削除したらエラーしなくなった。こんな対処で大丈夫か?

機能を利用するためには、ツールバー -> ウィンドウ -> 設定 -> Sunshade -> Scripted Actionsを選択。たとえば、src/*\**.cとtest/Test**.cの間を行き来するためには、以下のように書く。

importPackage(java.lang, Packages.org.eclipse.ui);
importPackage(Packages.net.sourceforge.sunshade.util);
var wb = PlatformUI.getWorkbench(); // IWorkbench API
var editor = // IEditorPart API
  wb.getActiveWorkbenchWindow().getActivePage().getActiveEditor(); 
if (editor)
{
  var input = editor.getEditorInput(); // IEditorInput API
  var path = input.getPath(); // IPath API
  var filename = path.toOSString();
  if (filename.indexOf("Test") == -1) // ここを書き換え
    filename = filename.replace("src\\","test\\Test"); // ここを書き換え
  else
    filename = filename.replace("test\\Test","src\\"); // ここを書き換え
  //alert("Filename: " + filename);
  FileUtil.openEclipseEditorForFile(filename);
}

Test拡張子以外に対応する場合でも、例えばMock拡張子でも、すこしコードを書き換えれば利用できる。

このSunshadeにはErrorlinkというツールがあるけれども、こっちはうまく動かない。

コードテンプレートの設定

Eclipseのコードテンプレート機能を利用すれば、Ctrl + Spaceでテストのテンプレートを呼び出すことができる。

レッツ メタプログラミング(^O^)/

ツールバー -> ウィンドウ -> 設定 -> C/C++ -> エディタ- -> テンプレートを選択。

新規を選択して、テスト用のテンプレートを記述する。たとえば、Unityのフィクスチャ機能のテンプレートは以下。

TEST(${test_group_name},${test_case_name})
{
  ${line_selection}${cursor}
  
  TEST_IGNORE_MESSAGE("${test_case_name} Needs Definition.");
}

RUN_TEST_CASE(${test_group_name}, ${test_case_name});

別にテンプレートを利用しなくても、testまで書いて Ctrl + Spaceである程度の自動補完はできる。

12 May 2013, 13:11

cmockで指定したディレクトリ配下のインクルードファイルからモックファイルを自動生成する

はじめに

cmockネタの続きです。指定されたディレクトリに含まれる全てのヘッダファイルに対するモックファイルを自動作成してみようと思います。

恐るべきレガシーコードの救世主になるか?!ドロドロ依存なモジュールたちを『CMock』ですっ裸にする | Futurismo

事前準備

サンプルは前回と同じ。

git clone git://github.com/ThrowTheSwitch/CMockExample.git

cmockからlib/ config/をプロジェクトにコピーしてきます。

今回のシェルスクリプト

やりたいこと

  1. mocksディレクトリがなければ作成
  2. 指定されたディレクトリ配下のhogehoge.hからMockhogehog.cを生成。

結果

いい感じに、mocks配下にMockhogehogeが生成されました。

tsu-nera@TSUNEMICHI-VAIO ~/temp/CMockExample
$ ./generate_mocks.sh src
mocks directory is not exist, make directory.
Creating mock for Gpio…
Creating mock for LedControl…
Creating mock for main…
Creating mock for System…

tsu-nera@TSUNEMICHI-VAIO ~/temp/CMockExample
$ ls mocks/
MockGpio.c  MockGpio.h  MockLedControl.c  MockLedControl.h  Mockmain.c  Mockmain.h  MockSystem.c  MockSystem.h

今後の展望

気づいた。モックファイルを生成しても、これをコンパイルするためには、Makefileをかかなければいけない。(´・ω・`)

というわけで、今度はMakefile自動生成をするための方法を調査してみる。うーん、圧倒的なスキル不足だ。スクリプト言語を身につけたい。

21 Apr 2013, 12:32

恐るべきレガシーコードの救世主になるか?!ドロドロ依存なモジュールたちを『CMock』ですっ裸にする

はじめに

仕事では、今までは新規開発だった。なので、真っ白なテキストからテストを書くことができた。しかし、これからは、流用開発。既存コードに機能追加しなければならない。そしてその既存コードはうんざりするほどの量があり、かつテストは1つもない。。。

1つのファイルは5000行くらいあって、インクルードはし放題。インターフェイスもごちゃまぜな感じ。そもそも、ヘッダファイルがソースコードごとにない!テストを通そうにも、うんざりするほどのUndef関数を作成しなければならず、実行ファイルも自力では作れなさそうだ。。。

そんなゲームオーバーで泣きたくなるような状況でも、なんとかテスト環境を構築する方法を模索していた。Undef関数をヘッダファイルから自動生成するツールがどうしても欲しかった。そこで、いろいろと探した所、”CMock”がよさそうだったので、今日は遊んでみた。

CMockとは

CMockとは、C言語用のモッキングフレームワーク。

Throw The Switch! – White Papers – CMock Intro

 

 

以下、概要を日本語訳。

CMock はヘッダファイルからモックインターフェイスを生成する小粋なツールです。CMockによって依存関係のあるモジュールをより簡単にUnitTestすることができます。

int DoesSomething(int a, int b);

…自動的にDoesSomething関数が生成され、実際のDoesSomething関数の代わりにリンクすることができます。このモック化されたものをつかうことによって、期待したデータをモジュールが受け取ったかを検証することができ、モジュールからどんなデータでも望むように返すことができます。あなたが欲しいエラーを返すことだって、もっともっと・・・隣接する実際の最新モジュールの振る舞いはなんだって作り出し、瞬時にしてあなたは以下のような力を得るのです。

“オレは作った出来立てのモジュールのあらゆる細部をコントロールし、検証できるのだ!”

このことをより簡単にするために、CMockでは以下のような関数郡も用意しています。なのでテストごとに、その生成されたDoesSomething関数に、どのように振る舞うのかをきけばよいのです。

void DoesSomething_ExpectAndReturn(int a, int b, int toReturn);
void DoesSomething_ExpectAndThrow(int a, int b, iEXCEPTION_T error);
void DoesSomething_StubWithCallback(CMOCK_DoesSomething_CALLBACK YourCallback);
void DoesSomething_IgnoreAndReturn(int toReturn);

これらの関数を次々に重ねあわせていくことで、あなたがなにを検証したいのかを意識させます。こんなふうに、

test_CallsDoesSomething_ShouldDoJustThat(void)
{
    DoesSomething_ExpectAndReturn(1,2,3);
   
DoesSomething_ExpectAndReturn(4,5,6);
   
DoesSomething_ExpectAndThrow(7,8, STATUS_ERROR_OOPS);

    CallsDoesSomething( );
}

このテストはCallsDoesSomethingを呼びます。これが、われわれがテストしたい関数です。この関数がDoesSomethngを3回呼ぶことを期待しています。初めは、DoesSomething(1,2)で呼ばれることを確かに確認し、魔法のように3を返します。二回目は、DoesSomething(4,5)で呼ばれることを確認し、6を返します。3回めはDoesSomething(7,8)で呼ばれて値の代わりにエラーを投げます。もしCallsDoesSomethingがこの通りでないならば、テストは失敗します。DoesSomethingを呼ばなくても、呼びすぎても、また間違った引数でよんでも、間違った順番で読んでも、テストは失敗します。

CMockはUnityをベースにしています。そしてそれは、全ての内部でのテストに使われています。CMockは全ての作業でRubyを使います(version 1.8.6 – 1.9.2)

CMockのダウンロード

CMockのダウンロードは以下のサイトから。

CMock プロジェクト日本語トップページ – SourceForge.JP

githubでも公開されている。

$ git clone git://github.com/ThrowTheSwitch/CMock.git .

(補足)git cloneだと、vendor配下にあるunityとc_expectionがダウンロードできなかった。それぞれを追加で持ってくる。

$ git clone git://github.com/ThrowTheSwitch/Unity.git vendor/unity
$ git clone git://github.com/ThrowTheSwitch/CException.gitvendor/c_exception/ vendor/c_expeption

CMockをとりあえず動かしてみる

事前にRuby と rakeをインストールしておくことが必要。

examples配下で実行

とりあえず、ちゃんとダウンロードできたかどうかを確認するために、examples配下に移動して以下を実行。(mocksとbuildディレクトリをつくっておかないとエラーした(´・_・`))

$ cd examples/
$ mkdir mocks
$ mkdir build
$ rake

それっぽいモック生成メッセージとテストが走ってやや感動するはず。最後までテストが通らなかったけれども、とりあえずよしとしよう。

cmock配下で実行

つづいて、cmockインストールディレクトリでrakeを実行。

$ rake
rake aborted!
cannot load such file — rspec/core/rake_task

rspecがないよといわれるのでrspecをインストールする。

$ gem install rspec

再度rakeを実行すると以下のエラー。

/usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require’: cannot load such file — hardmock (LoadError)

hardmockをインストールする。

$ gem install hardmock

再度rakeを実行て、それっぽいテストが動作した。ヤタ───v(-∀-)v───♪

CMockの使い方

これも、原文をそのまま日本語訳してしまう。

CMockはRubyスクリプトとクラスです。なので、コマンドラインから直接リヨ出来ます。また、自分のスクリプトやrakeファイルにインクルードできます。

コマンドラインからモックする

CMockを解凍したら、’lib’ディレクトリ内にCMock.rbがある。これが動作させるものです。モックするためには、ヘッダファイルのリストが必要です。それとともに、より詳細な構成をしていするためには、オブションとしてyamlファイルが必要です。(configration optionの章を参照)

たとえば、これは構成をMyConfig.ymlで指定して、3つのモックを生成する。

ruby cmock.rb -oMyConfig.yml super.h duper.h awesome.h

これは、デフォルト構成で2つのモックを生成する。

ruby cmock.rb ../mocking/stuff/is/fun.h ../try/it/yourself.h

スクリプトやRakeからモックする

CMockはスクリプトやRakeファイルから直接利用できる。cmock.rbをインクルードして、CMockの実装を生成する。実装を生成したら、3つのうちのいずれかの方法で初期化する。

なにも指定しなければ、デフォルトの設定で動作させることになる。
cmock = CMock.new

YAMLファイルを指定することで、好きな構成オプションを指定できる。
cmock = CMock.new(‘../MyConfig.yml’)

例外として、オプション指定可能。
cmock = Cmock.new(:plugins => [:cexception, :ignore], :mock_path => ‘my/mocks/’)

既存コードにcmockを組み込んで実行する

ここからが本題。一つのファイル(モジュール)をテストしたい時、他のファイルが存在しなくてもテストを実行するために、Undef関数をcmockで自動生成したい。

cmockと同じgithub上にあった、以下のサンプルを利用する。

git clone git://github.com/ThrowTheSwitch/CMockExample.git .

src/LedControl.cをテストしたいとする。普通にコンパイルしようとすると、当然怒られる。

$ gcc src/LedControl.c
/tmp/ccsR1nMW.o:LedControl.c:(.text+0x14): undefined reference to `_GPIO_SetPin’
/tmp/ccsR1nMW.o:LedControl.c:(.text+0x28): undefined reference to `_GPIO_SetPin’
/tmp/ccsR1nMW.o:LedControl.c:(.text+0x68): undefined reference to `_GPIO_ClearPin’
/tmp/ccsR1nMW.o:LedControl.c:(.text+0x7c): undefined reference to `_GPIO_ClearPin’
/usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../libcygwin.a(libcmain.o): In function `main’:
/usr/src/debug/cygwin-1.7.17-1/winsup/cygwin/lib/libcmain.c:39: undefined reference to
`_WinMain@16′
collect2: ld はステータス 1 で終了しました

モックを生成する。モックを生成するためのディレクトリを作成して、コマンド実行。

$ mkdir mocks
$ ruby ../lib/cmock.rb src/Gpio.h src/main.h src/System.h
Creating mock for Gpio…
Creating mock for main…
Creating mock for System…
$ ls mocks/
MockGpio.c  MockGpio.h  Mockmain.c  Mockmain.h  MockSystem.c  MockSystem.h

おお、感動的だ。゚(●’ω’o)゚。

ココからが独自カスタマイズ。必要な部品をunityプロジェクト、cmockプロジェクトからカレントディレクトリにかき集めてくる。

  • unity/src/unity.c  unity.h unity_internals.h
  • unity/auto/*
  • cmock/lib/*
  • cmock/config/*
  • cmock/src/cmock.c cmock.h

rakeをよく知らんので、Makefileもちょろっと書いてmakeしてみる。Makefileって自動生成できないのかな。。

TARGET_BASE = LedControl
TARGET_EXTENSION=.exe
TARGET = $(TARGET_BASE)$(TARGET_EXTENSION)

Unity Files

SRC_FILES = src/unity.c

CMock Files

SRC_FILES += src/cmock.c

Source Files

SRC_FILES += src/LedControl.c SRC_FILES += test/test_LedControl.c SRC_FILES += build/test_LedControl_Runner.c

SRC_FILES += mocks/mock_Gpio.c SRC_FILES += mocks/mock_main.c SRC_FILES += mocks/mock_System.c

INC_DIRS =-Isrc INC_DIRS +=-Imocks

SYMBOLS=-DTEST -DUNITY_SUPPORT_64

CLEANUP = rm -f build/*.o ; rm -f $(TARGET); mkdir -p build

all: clean default

default: ruby auto/generate_test_runner.rb test/test_LedControl.c build/test_LedControl_Runner.c ruby lib/cmock.rb src/Gpio.h ruby lib/cmock.rb src/main.h ruby lib/cmock.rb src/System.h $(C_COMPILER) $(INC_DIRS) $(SYMBOLS) $(SRC_FILES) -o $(TARGET) ./$(TARGET)

clean: $(CLEANUP)

>
> >

> このままだと、mocks/Mock*というモックオブジェクトが生成される。test_と結合しようとするとエラーしたので、lib/CMockConfigでmock_prefixを変更する。 >

> >
>

> :mockprefix => ‘mock’, >

>
> >

> 再度実行してみる。 >

> >
>

> $ make

rm -f build/*.o ; rm -f LedControl.exe  ; mkdir -p build

>
>

> ruby auto/generate_test_runner.rb test/test_LedControl.c build/test_LedControl_Runner.c >

>
>

> ruby lib/cmock.rb src/Gpio.h >

>
>

> Creating mock for Gpio… >

>
>

> ruby lib/cmock.rb src/main.h >

>
>

> Creating mock for main… >

>
>

> ruby lib/cmock.rb src/System.h >

>
>

> Creating mock for System… >

>
>

> gcc  -Isrc  -Imocks  -DTEST -DUNITY_SUPPORT_64 src/unity.c  src/cmock.c  src/LedControl.c  test/test_LedControl.c  build/test_LedControl_Runner.c mocks/mock_Gpio.c mocks/mock_main.c mocks/mock_System.c -o LedControl.exe >

>
>

> ./LedControl.exe >

>
>

> test/test_LedControl.c:13:test_LedControl_TurnLedOn_should_turn_on_GPIO_pin_1_when_turning_on_the_red_LED:PASS >

>
>

> test/test_LedControl.c:22:test_LedControl_TurnLedOn_should_turn_on_GPIO_pin_2_when_turning_on_the_blue_LED:PASS >

>
>

> test/test_LedControl.c:31:test_LedControl_TurnLedOff_should_turn_off_GPIO_pin_1_when_turning_off_the_red_LED:PASS >

>
>

> test/test_LedControl.c:40:test_LedControl_TurnLedOff_should_turn_off_GPIO_pin_2_when_turning_off_the_blue_LED:PASS >

>
>

> ———————– >

>
>

> 4 Tests 0 Failures 0 Ignored >

>
>

> OK >

>
>

> 。゚(●’ω’o)゚。おお、感動的だ。゚(●’ω’o)゚。 >

>
>

> 果たして、このツールは泥沼スパゲッティコードを断ち切る勇者となるか、使えない愚者になるか。まだまだ評価が必要そうだ。 >

>
>

> これさえあれば、どんなに依存関係があって、Undef関数だらけなコンパイルエラーを攻略できそうだ。たとえそれが、10000行くらいのソースコードであっても・・・orz?? >

>
>

> ああ、無情。でもガンバルじゃん。 >

>
>

> おまけ  書籍の紹介 >

>
>

> Embedded Testing with Unity and CMock >

>
>

> UnityとCMockの使い方について、電子書籍とペーパーブックが出ている。6ドルくらい。 >

>
>

> Embedded Testing with Unity and CMock by Mark VanderVoord (eBook) – Lulu >

>
>

>
>

>
>

> 内容はとても異色だ。まず、ページが正方形なのが、なんかおかしい。物語形式で話が進んでいく。途中に可愛い?!マンガチックなイラストがたくさんでてくる。文学表現が初めの方はおおくて、知らない単語が多かったりした。前半がUnityの話、後半がCMockの話。 >

>
>

> UnityとCMockの使い方が分かる本『Embedded Testing with Unity and CMock』を読んだ | Futurismo >

>
>

> test driven development for embedded c >

>
>

> UnityおよびCMockについては、以下の書籍でも話題にでている。 >

>
> >
>

> (電子書籍はここから)

http://pragprog.com/book/jgade/test-driven-development-for-embedded-c >

>
>

> Unityはけっこうベージが割かれているけれども、CMockについては2ページくらい。どちらかというと、Mockを自力で作成するためのアイデアに役に立つ。 >

>
>

> そろそろ『test driven development for embedded c』について書いてみる | Futurismo >

21 Apr 2013, 11:36

UnityとCMockの使い方が分かる本『Embedded Testing with Unity and CMock』を読んだ

UnityとCMockはC言語のためのxUnitとMockフレームワーク。UnityとCMockの使い方について、電子書籍とペーパーブックが出ている。6ドルくらいなので、購入して読んでみた。


Embedded Testing with Unity and CMock by Mark VanderVoord (eBook) – Lulu


内容はとても異色だ。まず、ページが正方形なのが、なんかおかしい。物語形式で話が進んでいく。途中に可愛い?!マンガチックなイラストがたくさんでてくる。文学表現が初めの方はおおくて、知らない単語が多かったりした。

前半がUnityの話、後半がCMockの話。全部で40ページくらいなので、あまり内容は深くない。使い方が説明されている。ツールについてくるドキュメントよりは詳しくツールが解説されている。特に、オプション周り。

この本、手順はしょりすぎ。。。けっこう苦労したので、補う意味で手順をメモしておく。

なお、UnityとCMock利用するためには、Rubyが必須ツール。Rubyスクリプトをガンガン使うため。事前にインストールすること。

CMockのダウンロード

githubからダウンロードします。unityやceelingというプロジェクトも必要になります。サブモジュール扱いされているので、それらも取得します。

#CMockをcloneする
$ git clone git://github.com/ThrowTheSwitch/CMock.git
# Unityとceelingをcloneする
$ git submodule init
$ git submodule update

CMockをcloneする

$ git clone git://github.com/ThrowTheSwitch/CMock.git

Unityとceelingをcloneする

$ git submodule init
$ git submodule update

Unityはどんなコンパイラだって、コンパイルできるらしい(と書いてあった)。まずは、自分が使う予定のコンパイラでUnityが動作するかチェックする。

Cygwinだと、どうもWindowsに解釈されるので、makefileをチョット修正。

$ cd CMock/vendor/unity
$ emacs makefile
$ make

こんな感じに修正。

#ifeq ($(OS),Windows_NT)    
# TARGET_EXTENSION=.exe
#else
TARGET_EXTENSION=.out
#endif

—————–

#ifeq ($(OS),Windows_NT)
# CLEANUP = del /F /Q build\* && del /F /Q $(TARGET1) && del /F /Q $(TARGET2)
#else
CLEANUP = rm -f build/*.o ; rm -f $(TARGET1) ; rm -f $(TARGET2)
#endif

—————-

./$(TARGET1)

これで174のテストが走る。Great! v(o´∀`o)v

別のコンパイラを試すには、makefileの先頭にC_COMPILER=gccという部分を修正する。

C_COMPILER=g++
C_COMPILER=gcc-3

CMockはデフォルトコンパイラがgccのようだが、他のコンパイラでのテスト方法がかワカラなかった。gccでのテスト方法は、以下を実行する。

$ cd CMock
$ rake

Unity編(p3~)

以下のようにコマンドを実行することで、MenacingLED.c/MenacingLED.h/TestMenacingLED.cができる。

$ ruby auto/generate_module.rb MenacingLED
File /home/TSUNEMICHI/src/unity/book/auto/../src//MenacingLED.c created    
File /home/TSUNEMICHI/src/unity/book/auto/../src//MenacingLED.h created
File /home/TSUNEMICHI/src/unity/book/auto/../test//TestMenacingLED.c created
Generate Complete

次に以下のコマンドでTestMenacingLED_Runner.cができる。

$ ruby auto/generate_test_runner.rb test/TestMenacingLED.c

makefileを編集する。以下の4つを追加する。作成された4つのファイルをコンパイルしてリンクし、実行ファイルを生成。実行する。

# Unity File
SRC_FILES = src/unity.c

# Source Files
SRC_FILES += src/MenacingLED.c
SRC_FILES += test/TestMenacingLED.c
SRC_FILES += test/TestMenacingLED_Runner.c

$ make
rm -f build/*.o ; rm -f book.exe ; mkdir -p build
gcc -Isrc -DTEST -DUNITY_SUPPORT_64 src/unity.c src/MenacingLED.c test/TestMenacingLED.c test/TestMenacingLED_Runner.c -o book.exe

./book.exe
test/TestMenacingLED.c:14:test_MenacingLED_NeedToImplement:IGNORE
-----------------------
1 Tests 0 Failures 1 Ignored
OK

CMock編(p14~)

S5モジュールを生成。

$ ruby auto/generate_module.rb S5    
File /home/TSUNEMICHI/src/unity/book/auto/../src//S5.c created
File /home/TSUNEMICHI/src/unity/book/auto/../src//S5.h created
File /home/TSUNEMICHI/src/unity/book/auto/../test//TestS5.c created
Generate Complete

S5_Exec()をS5.hとS5.cに空実装する。

同様にして、ButtonsとS5モジュールを生成する。

$ ruby auto/generate_module.rb Buttons    
$ ruby auto/generate_module.rb S5Ctrl

Buttons_CheckS5Switch と S5Ctrl_Silenceをヘッダに宣言する。

ここからが書いていないのだが、モックオブジェクトを生成する。以下をカレントディレクトリにコピーしてくる。

  • cmock/lib
  • cmock/config/
  • cmock/src(cmock.cとcmock.hが欲しい)

mocksディレクトリを作成して、cmock生成用のスクリプトを実行する。

$mkdir mocks
$ ruby lib/cmock.rb src/Buttons.h src/S5Ctrl.h
Creating mock for Buttons...
Creating mock for S5Ctrl...

Makefileを編集する。インクルードファイルにもmocksディレクトリを追加。

cmockを利用するためには、unity.cとcmock.cも同時にコンパイルしてやる。最終的なMakefileはこんな感じ

C_COMPILER=gcc 
TARGET_BASE = MenacingLED 
TARGET_BASE2 = S5 
TARGET_EXTENSION=.exe 
TARGET = $(TARGET_BASE)$(TARGET_EXTENSION) 
TARGET2 = $(TARGET_BASE2)$(TARGET_EXTENSION)

# Unity Files 
SRC_FILES   = src/unity.c 
SRC_FILES2  = src/unity.c

# CMock Files 
SRC_FILES  += src/cmock.c 
SRC_FILES2 += src/cmock.c

# Source Files 
SRC_FILES += src/MenacingLED.c 
SRC_FILES += test/TestMenacingLED.c 
SRC_FILES += build/TestMenacingLED_Runner.c

SRC_FILES2 += src/S5.c 
SRC_FILES2 += test/TestS5.c 
SRC_FILES2 += build/TestS5_Runner.c

SRC_FILES2 += mocks/MockButtons.c 
SRC_FILES2 += mocks/MockS5Ctrl.c

INC_DIRS  =-Isrc 
INC_DIRS +=-Imocks 
SYMBOLS=-DTEST -DUNITY_SUPPORT_64

CLEANUP = rm -f build/*.o ; rm -f $(TARGET); rm -f $(TARGET2) ; mkdir -p build

all: clean default

default: 
        ruby auto/generate_test_runner.rb test/TestMenacingLED.c build/TestMenacingLED_Runner.c 
        ruby auto/generate_test_runner.rb test/TestS5.c build/TestS5_Runner.c 
        ruby lib/cmock.rb src/S5Ctrl.h 
        ruby lib/cmock.rb src/Buttons.h 
        $(C_COMPILER) $(INC_DIRS) $(SYMBOLS) $(SRC_FILES) -o $(TARGET) 
        $(C_COMPILER) $(INC_DIRS) $(SYMBOLS) $(SRC_FILES2) -o $(TARGET2) 
        ./$(TARGET) 
        ./$(TARGET2)

clean: 
        $(CLEANUP)