marunomaruno-memo

marunomaruno-memo

CUnit: C 用 単体テスト・フレームワーク

2012年11月30日 | C / C++
                                                                    2012-11-30
CUnit: C 用 単体テスト・フレームワーク
================================================================================

■ Cunit

Cunit は、 C プログラムの単体テストをするためのフレームワークです。
このドキュメントでは、単純な使い方だけを示します。


ダウンロードとインストール

つぎからダウンロードします
CUnit
http://sourceforge.net/projects/cunit/

任意のフォルダーに展開すればインストール完了です。
このドキュメントでは、
    C:¥CUnit-2.1-0
にインストールしたことにします。


■ サンプル

サンプルとして、指定された西暦年に対して、うるう年か平年かを検査する関数 
isLeapYear() のモジュール LeapYear.c の単体テストを考えます。


□ LeapYear.h
---
/*
 * LeapYear.h
 *
 *  Created on: 2012/08/15
 *      Author: maruno
 */

#ifndef LEAPYEAR_H_
#define LEAPYEAR_H_

/**
 * 西暦 year 年がうるう年かどうか判定する。
 * @param year 西暦年
 * @return うるう年のとき1、平年のとき0
 */
int isLeapYear(int year);


#endif /* LEAPYEAR_H_ */
---


□ LeapYear.c
---
#include "LeapYear.h"

int isLeapYear(int year) {
    if ((year % 4) == 0) {
        if ((year % 100) == 0) {
            return 0;
        }
        if ((year % 400) == 0) {
            return 1;
        }
        return 1;
    }
    return 0;
}
---


■ Cunit の使用法

1. テスト・ドライバーの作成

まず、単体テストを行いたいモジュールのテスト・ドライバーを作ります。

□ LeapYearTest.c
---
#include <stdlib.h>
#include <CUnit.h>
#include <Automated.h>
#include <Basic.h>

#include "LeapYear.h"

void testIsLeapYear001(void);
void testIsLeapYear002(void);
void testIsLeapYear003(void);
void testIsLeapYear004(void);

int main(void) {
    CU_pSuite suite;

    CU_initialize_registry();
    suite = CU_add_suite("isLeapYear", NULL, NULL );
    CU_ADD_TEST(suite, testIsLeapYear001);
    CU_ADD_TEST(suite, testIsLeapYear002);
    CU_ADD_TEST(suite, testIsLeapYear003);
    CU_ADD_TEST(suite, testIsLeapYear004);
    CU_automated_run_tests();            // 結果をXML形式で出力
    CU_basic_set_mode(CU_BRM_NORMAL);    // 結果表示を失敗と実行サマリーにする
    CU_basic_run_tests();    // 結果を標準出力に出力
    CU_cleanup_registry();

    return 0;
}

/**
 * 400の倍数でうるう年
 */
void testIsLeapYear001(void) {
    CU_ASSERT_EQUAL(1, isLeapYear(2000));    // 2つの引数が等しければOK
}

/**
 * 4の倍数でうるう年
 */
void testIsLeapYear002(void) {
    CU_ASSERT_TRUE(isLeapYear(2012));    // 結果がtrueであればOK
}

/**
 * 100の倍数で平年
 */
void testIsLeapYear003(void) {
    CU_ASSERT_FALSE(isLeapYear(2100));    // 結果がfalseであればOK
}

/**
 * 4で割り切れず平年
 */
void testIsLeapYear004(void) {
    CU_ASSERT(0 == isLeapYear(2013));    // 条件式の結果がtrueであればOK
}
---

>gcc -IC:¥CUnit-2.1-0¥include¥CUnit -Wall -c LeapYearTest.c
>gcc -c -Wall LeapYear.c
>gcc -LC:¥CUnit-2.1-0¥lib -o test.exe leapYear.o LeapYearTest.o -lcunit


2. ビルドと実行

ビルド
---
>gcc -IC:¥CUnit-2.1-0¥include¥CUnit -Wall -c LeapYearTest.c
>gcc -c -Wall LeapYear.c
>gcc -LC:¥CUnit-2.1-0¥lib -o test.exe leapYear.o LeapYearTest.o -lcunit
---

※ CUnit を C:¥CUnit-2.1-0 にインストールしています。


実行

実行すると、標準出力に実行結果が出ます。
これとあわせて、つぎの結果ファイルができます。
    CUnitAutomated-Results.xml 
同じフォルダに、C:¥CUnit-2.1-0¥share¥CUnit にある
    CUnit-Run.dtd
    CUnit-Run.xsl
をコピーして、CUnitAutomated-Results.xml をダブルクリックすると、IE が立ち上がり、
グラフィカルに単体テストの結果を見ることができます。


□ 実行結果
---
>test

     CUnit - A Unit testing framework for C - Version 2.1-0
     http://cunit.sourceforge.net/


Suite isLeapYear, Test testIsLeapYear001 had failures:
    1. LeapYearTest.c:35  - CU_ASSERT_EQUAL(1,isLeapYear(2000))

--Run Summary: Type      Total     Ran  Passed  Failed
               suites        1       1     n/a       0
               tests         4       4       3       1
               asserts       4       4       3       1
---



なお、すべてパスするように修正した後の実効結果は以下のようになります。
---
     CUnit - A Unit testing framework for C - Version 2.1-0
     http://cunit.sourceforge.net/

--Run Summary: Type      Total     Ran  Passed  Failed
               suites        1       1     n/a       0
               tests         4       4       4       0
               asserts       4       4       4       0
---


■ ASSERT マクロ

代表的なマクロとして、以下のようなマクロが用意されています。

    CU_ASSERT(式)
    CU_ASSERT_TRUE(式)
    CU_ASSERT_FALSE(式)
    CU_ASSERT_EQUAL(式1, 式2)
    CU_ASSERT_NOT_EQUAL(式1, 式2)
    CU_ASSERT_PTR_EQUAL(ポインタ1, ポインタ2)
    CU_ASSERT_PTR_NOT_EQUAL(ポインタ1, ポインタ2)
    CU_ASSERT_PTR_NULL(ポインタ)
    CU_ASSERT_PTR_NOT_NULL(ポインタ)
    CU_ASSERT_STRING_EQUAL(文字列1, 文字列2)
    CU_ASSERT_STRING_NOT_EQUAL(文字列1, 文字列2)
    CU_ASSERT_NSTRING_EQUAL(文字列1, 文字列2, 文字数)
    CU_ASSERT_NSTRING_NOT_EQUAL(文字列1, 文字列2, 文字数)
    CU_ASSERT_DOUBLE_EQUAL(実数1, 実数2, 精度)
    CU_ASSERT_DOUBLE_NOT_EQUAL(実数1, 実数2, 精度)
    CU_PASS(メッセージ文字列)
    CU_FAIL(メッセージ文字列)


■ 参考

CUnit
http://sourceforge.net/projects/cunit/

CUnit チュートリアル
http://homepage3.nifty.com/kaku-chan/cunit/index.html

                                                                            以上



[C] gprof: プロファイラ・プログラム

2012年11月29日 | Android
                                                                    2012-11-29
gprof: プロファイラ・プログラム
================================================================================

■gprof

gprof は、プロファイラとして、つぎのことを測定します。

・コードの各セクションが消費する計算時間 


コンパイル時に -pg オプションをつけることで使えるようになります。


■gprof の使用法

1. コンパイル

コンパイル時に -pg オプションをつけることで使えるようになります。

>gcc -Wall -pg prof_sample.c

コンパイルすると、つぎのファイルができます。
    prof_sample.gcno 


□ prof_sample.c
---
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>

#define MAX (10 * 1000)

void sort(int a[], int size);
void swapIfDesc(int a[], int i, int j);
void swap(int a[], int i, int j);

int main(void) {
    int a[MAX];
    int i;
    puts("program start");
    fflush(stdout);
    for (i = 0; i < MAX; ++i) {
        a[i] = rand();
    }

    long start = clock();

    sort(a, MAX);

    printf("%6ld ms¥n", (clock() - start));
    fflush(stdout);

    for (i = 1; i < MAX; ++i) {
        assert(a[i-1] <= a[i]);
    }

    puts("program end");
    fflush(stdout);
    return EXIT_SUCCESS;
}

void sort(int a[], int size) {
    int i, j;

    for (i = 0; i < size; ++i) {
        for (j = i; j < size; ++j) {
            swapIfDesc(a, i, j);
        }
    };
}

void swapIfDesc(int a[], int i, int j) {
    if (a[i] >= a[j]) {
        swap(a, i, j);
    }
}

void swap(int a[], int i, int j) {
    int w = a[i];
    a[i] = a[j];
    a[j] = w;
}
-----------------------------------------------------------


2. 実行

>a.exe
---
program start
  3062 ms
program end
---


3. プロファイルを見る

つぎのように、gprof コマンドを使って、測定した結果を見ます。

> gprof a.exe


□ 実行結果
---
Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total
 time   seconds   seconds    calls   s/call   s/call  name
 53.21      0.83     0.83 50005000     0.00     0.00  swapIfDesc
 33.33      1.35     0.52        1     0.52     1.56  sort
 13.46      1.56     0.21 24964410     0.00     0.00  swap

 %         the percentage of the total running time of the
time       program used by this function.
            (全体の総実行時間に対するその関数の総実行時間の占める割合)

cumulative a running sum of the number of seconds accounted
 seconds   for by this function and those listed above it.
            (その関数より上位に示されているすべての関数の総実行時間と,
            その関数の総実行時間の累積時間(秒))

 self      the number of seconds accounted for by this
seconds    function alone.  This is the major sort for this
           listing.
            (その関数の総実行時間(秒))

calls      the number of times this function was invoked, if
           this function is profiled, else blank.
            (その関数が呼び出された総回数)

 self      the average number of milliseconds spent in this
ms/call    function per call, if this function is profiled,
           else blank.
            (その関数の一回の呼び出しに対する平均実行時間(ミリ秒))

 total     the average number of milliseconds spent in this
ms/call    function and its descendents per call, if this
           function is profiled, else blank.
            (その関数の一回の呼び出しに対する,その関数とその関数に呼び出された
            サブルーチンの平均実行時間(ミリ秒))

name       the name of the function.  This is the minor sort
           for this listing. The index shows the location of
           the function in the gprof listing. If the index is
           in parenthesis it shows where it would appear in
           the gprof listing if it were to be printed.

                     Call graph (explanation follows)


granularity: each sample hit covers 4 byte(s) for 0.64% of 1.56 seconds

index % time    self  children    called     name
                0.52    1.04       1/1           main [2]
[1]    100.0    0.52    1.04       1         sort [1]
                0.83    0.21 50005000/50005000     swapIfDesc [3]
-----------------------------------------------
                                                 <spontaneous>
[2]    100.0    0.00    1.56                 main [2]
                0.52    1.04       1/1           sort [1]
-----------------------------------------------
                0.83    0.21 50005000/50005000     sort [1]
[3]     66.7    0.83    0.21 50005000         swapIfDesc [3]
                0.21    0.00 24964410/24964410     swap [4]
-----------------------------------------------
                0.21    0.00 24964410/24964410     swapIfDesc [3]
[4]     13.5    0.21    0.00 24964410         swap [4]
-----------------------------------------------

 This table describes the call tree of the program, and was sorted by
 the total amount of time spent in each function and its children.

 Each entry in this table consists of several lines.  The line with the
 index number at the left hand margin lists the current function.
 The lines above it list the functions that called this function,
 and the lines below it list the functions this one called.
 This line lists:
     index      A unique number given to each element of the table.
                Index numbers are sorted numerically.
                The index number is printed next to every function name so
                it is easier to look up where the function in the table.
                (関数につけられる連続した番号)

     % time     This is the percentage of the `total' time that was spent
                in this function and its children.  Note that due to
                different viewpoints, functions excluded by options, etc,
                these numbers will NOT add up to 100%.
                (全実行時間に対するその関数の総実行時間(その関数から呼び出された
                サブルーチンの実行時間も含む) の割合)

     self       This is the total amount of time spent in this function.
                (その関数の総実行時間)

     children   This is the total amount of time propagated into this
                function by its children.
                (その関数に呼び出されたサブルーチンの総実行時間)

     called     This is the number of times the function was called.
                If the function called itself recursively, the number
                only includes non-recursive calls, and is followed by
                a `+' and the number of recursive calls.
                (その関数が呼び出された総回数)

     name       The name of the current function.  The index number is
                printed after it.  If the function is a member of a
                cycle, the cycle number is printed between the
                function's name and the index number.


 For the function's parents, the fields have the following meanings:

     self       This is the amount of time that was propagated directly
                from the function into this parent.

     children   This is the amount of time that was propagated from
                the function's children into this parent.

     called     This is the number of times this parent called the
                function `/' the total number of times the function
                was called.  Recursive calls to the function are not
                included in the number after the `/'.

     name       This is the name of the parent.  The parent's index
                number is printed after it.  If the parent is a
                member of a cycle, the cycle number is printed between
                the name and the index number.

 If the parents of the function cannot be determined, the word
 `<spontaneous>' is printed in the `name' field, and all the other
 fields are blank.

 For the function's children, the fields have the following meanings:

     self       This is the amount of time that was propagated directly
                from the child into the function.

     children   This is the amount of time that was propagated from the
                child's children to the function.

     called     This is the number of times the function called
                this child `/' the total number of times the child
                was called.  Recursive calls by the child are not
                listed in the number after the `/'.

     name       This is the name of the child.  The child's index
                number is printed after it.  If the child is a
                member of a cycle, the cycle number is printed
                between the name and the index number.

 If there are any cycles (circles) in the call graph, there is an
 entry for the cycle-as-a-whole.  This entry shows who called the
 cycle (as parents) and the members of the cycle (as children.)
 The `+' recursive calls entry shows the number of function calls that
 were internal to the cycle, and the calls entry for each member shows,
 for that member, how many times it was called from other members of
 the cycle.


Index by function name

   [1] sort                    [4] swap                    [3] swapIfDesc
---


□ オプション

-b         説明部分を省略
-p         Flat profile のみを出力
-P         Call graph のみを出力
-c         関数内部で使われる関数についての使用状況も出力
-z         実行時に使っていない関数や時間を計測できない程度の関数も、使用状況を出力
-e 関数名  指定した関数の出力を結果から除く

                                                                            以上


ロボロボロボコン2012

2012年11月01日 | LEGO
ロボロボロボコン2012
http://www.enjoy-robo.com/images/pdf/roboroborobocon2012_book.pdf

ロボロボロボコンとは
レゴNXTマインドストームを使用したロボット大会です。小学2年生から大人まで、誰でも参加できます。レースは、年齢に関係なく同じレースで競い合います。

ロボットレースを通じて、”考える楽しさ”を共有する場所です。(前身の「クリスマスロボコン」が2010年から始まり、今年で3回目を迎えます。)

日時:12月16日(日) 11時~16時30分
場所:かながわ労働プラザホールB
http://www.zai-roudoufukushi-kanagawa.or.jp/?l-plaza/
レース:鬼ごっこレース!
(A)自走型部門
(B)ラジコン部門

お申込み http://www.enjoy-robo.com/form_roborobo/
お問い合わせ info@enjoy-robo.com

※まだ、募集しておりますので、興味のある方はお問い合わせください。