<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE refentry 
  PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
  "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
<refentry id="tutorial">
<refmeta>
  <refentrytitle role="top_of_page" id="tutorial.top_of_page">チュートリアル</refentrytitle>
  <refmiscinfo>Cutterライブラリ</refmiscinfo>
</refmeta>
<refnamediv>
  <refname>チュートリアル</refname>
  <refpurpose>Cutterの使い方</refpurpose>
</refnamediv>
<refsect1>
  <title>はじめに</title>
  <para>スタックを実現するプログラム（ライブラリ）をC言語で作成する。プログラム作成はテストを作成しながら行う。テストの作成にはC言語用のテスティングフレームワークであるCutterを用いる。</para>
  <para>プログラムのビルドシステムにはGNUビルドシステム（GNU Autoconf/GNU Automake/GNU Libtool）を使用する。GNUビルドシステムはビルド環境の差異を吸収する。これによりプログラム・テストを複数の環境で容易にビルドできるようになる。</para>
  <para>大きなコストをかけずにプログラム本体が複数の環境で動作するのであれば、その方がよい。さらにテストもその環境で動作するのならば、プログラム本体がその環境で正しく動作することを容易に検証できる。プログラム本体だけではなく、テストも複数の環境で容易に動作することは重要である。</para>
  <para>Cutterが依存しているライブラリはGLibのみである。GLibはUNIX系のシステムだけではなく、WindowsやMac OS X上でも動作する移植性の高いライブラリである。CutterはGLibを利用することにより移植性の高い状態を保ちつつ豊富なテスト支援機能を提供するxUnit系のテスティングフレームワークである。</para>
  <para>以下、スタックを作成しながらCutterの使い方について述べる。なお、Cutterはインストールされているものとする。</para>
  <para>このプログラムのソースコード一式はsample/stack/以下にある。</para>
</refsect1>

<refsect1>
  <title>ディレクトリ構成</title>
  <para>まず、プログラムを作成するためのディレクトリを用意する。ディレクトリはstackとする。</para>
  <programlisting>% mkdir -p /tmp/stack
% cd /tmp/stack</programlisting>
  <para>続いて、stack/ディレクトリ以下にビルド補助ファイル用ディレクトリconfig/、プログラム用ディレクトリsrc/、テストプログラム用ディレクトリtest/を作成する。</para>
  <programlisting>[stack]% mkdir config src test</programlisting>
  <para>つまり、ディレクトリ構成は以下のようになる。</para>
  <programlisting>stack/ -+- config/ ビルド補助用ディレクトリ
        |
        +- src/ ソースファイル用ディレクトリ
        |
        +- test/ テストプログラム用ディレクトリ</programlisting>
</refsect1>

<refsect1>
  <title>GNUビルドシステム化</title>
  <para>GNUビルドシステムでは、コマンドを実行し、いくつかのファイルを自動生成する。これらのコマンドは、autogen.shというシェルスクリプトを作成し、そこから呼び出すのが一般的である。ここでも、その慣習に従う。</para>
  <para>autogen.sh:</para>
  <programlisting>#!/bin/sh

run()
{
    $@
    if test $? -ne 0; then
        echo &quot;Failed $@&quot;
        exit 1
    fi
}

run aclocal ${ACLOCAL_ARGS}
run libtoolize --copy --force
run autoheader
run automake --add-missing --foreign --copy
run autoconf</programlisting>
  <para>autogen.shに実行権を付けることを忘れないこと。</para>
  <programlisting>[stack]% chmod +x autogen.sh</programlisting>
  <para>run()はコマンドの実行結果を確認するための便利のための関数である。実行しているコマンドはそれぞれ以下のためである。</para>
  <itemizedlist>
  <listitem>
  <para>aclocal: Automakeが利用するマクロをaclocal.m4に集める</para>
</listitem>
  <listitem>
  <para>libtoolize: libtoolを使用するために必要なファイルを用意</para>
</listitem>
  <listitem>
  <para>autoheader: configureスクリプトが利用するconfig.h.inファイルを作成</para>
</listitem>
  <listitem>
  <para>automake: configureスクリプトが利用するMakefile.inを生成</para>
</listitem>
  <listitem>
  <para>autoconf: configureスクリプトを生成</para>
</listitem>
</itemizedlist>
  <para>もし、Cutterをaclocalと異なるprefixでインストールしている場合はACLOCAL_ARGS環境変数を指定する。この環境変数はautogen.sh内で参照している。ここでは、prefixを$HOME/localとしてCutterをインストールしたものとする。</para>
  <programlisting>[stack]% export ACLOCAL_ARGS=&quot;-I $HOME/local/share/aclocal&quot;</programlisting>
  <para>この時点でautogen.shを実行すると以下のようになる。</para>
  <programlisting>[stack]% ./autogen.sh
aclocal: `configure.ac' or `configure.in' is required
Failed aclocal</programlisting>
  <para>Autoconf用のファイルであるconfigure.acを用意する必要がある。</para>
  <refsect2>
  <title>configure.ac</title>
  <para>autogen.shのための最低限のconfigure.acは以下の通りである。</para>
  <para>configure.ac:</para>
  <programlisting>AC_PREREQ(2.59)

AC_INIT(stack, 0.0.1, you@example.com)
AC_CONFIG_AUX_DIR([config])
AC_CONFIG_HEADER([src/config.h])

AM_INIT_AUTOMAKE($PACKAGE_NAME, $PACKAGE_VERSION)

AC_PROG_LIBTOOL

AC_CONFIG_FILES([Makefile])

AC_OUTPUT</programlisting>
  <para>configure.acを用意してもう一度autogen.shを実行すると以下のようになる。</para>
  <programlisting>[stack]% ./autogen.sh
Putting files in AC_CONFIG_AUX_DIR, `config'.
configure.ac:7: installing `config/install-sh'
configure.ac:7: installing `config/missing'
automake: no `Makefile.am' found for any configure output
Failed automake --add-missing --foreign --copy</programlisting>
  <para>今度はAutomakeのためにMakefile.amを用意する必要がある。</para>
</refsect2>

  <refsect2>
  <title>Makefile.am</title>
  <para>autogen.shのためだけであれば空のMakefile.amで構わない。</para>
  <programlisting>[stack]% touch Makefile.am
[stack]% ./autogen.sh
Putting files in AC_CONFIG_AUX_DIR, `config'.</programlisting>
  <para>これでconfigureスクリプトが生成される。この時点で一般的なソフトウェアのようにconfigure; make; make installができるようになる。</para>
  <programlisting>[stack]% ./configure
...
[stack]% make
[stack]% make install</programlisting>
  <para>ただし、ビルドするものもインストールするものも何もないため、今は何も起きない。</para>
</refsect2>

</refsect1>

<refsect1>
  <title>はじめてのテスト作成</title>
  <para>最低限のビルド環境が整ったので、テストの作成にはいる。まずは、新しく作ったばかりのスタックは空であることをテストする。コードにすると以下の通りである。</para>
  <programlisting>void
test_new_stack (void)
{
    Stack *stack;
    stack = stack_new();
    if (stack_is_empty(stack))
        PASS;
    else
        FAIL;
}</programlisting>
  <para>ここでは、上記のテストをCutterのテストとして動作させる。</para>
  <refsect2>
  <title>テストプログラムの作成</title>
  <para>テストプログラムはtest/以下に作成する。ここではtest/test-stack.cとして作成するものとする。</para>
  <para>まず、Cutterを使うためにcutter.hをincludeする。</para>
  <para>test/test-stack.c:</para>
  <programlisting>#include &lt;cutter.h&gt;</programlisting>
  <para>また、テスト対象のスタックの実装のAPIが書かれているstack.hもincludeする。（stack.hは後で作成する。）</para>
  <para>test/test-stack.c:</para>
  <programlisting>#include &lt;stack.h&gt;</programlisting>
  <para>続いて、このスタックのAPIを用いてテストを作成する。</para>
  <para>test/test-stack.c:</para>
  <programlisting>void
test_new_stack (void)
{
    Stack *stack;
    stack = stack_new();
    cut_assert(stack_is_empty(stack));
}</programlisting>
  <para>cut_assert()は引数が0ならテストが失敗、0以外ならテストが成功と判断するマクロである。Cutterのテストとはcut_XXX()マクロを使用して、特定の状況で望んだ動作をしているかを検証するプログラムを作成するということである。</para>
  <para>以下に、「作成したばかりのスタックは空である」ということを検証するテストのソースコード全体を示す。</para>
  <para>test/test-stack.c:</para>
  <programlisting>#include &lt;cutter.h&gt;
#include &lt;stack.h&gt;

void test_new_stack (void);

void
test_new_stack (void)
{
    Stack *stack;
    stack = stack_new();
    cut_assert(stack_is_empty(stack));
}</programlisting>
</refsect2>

  <refsect2>
  <title>テストのビルド</title>
  <para>Cutterの各テストは共有ライブラリになる。上記で作成したテストを共有ライブラリとしてビルドするために、Makefile.amを変更する。</para>
  <refsect3>
  <title>test/以下でのビルド設定</title>
  <para>現在のMakefile.amは空である。</para>
  <para>まず、make経由でaclocalが実行された場合にもautogen.sh用に設定したACLOCAL_ARGS環境変数が使われるように以下を追記する。</para>
  <para>Makefile.am:</para>
  <programlisting>ACLOCAL_AMFLAGS = $$ACLOCAL_ARGS</programlisting>
  <para>次に、サブディレクトリであるtest/以下のtest/test-stack.cをビルドするためにはMakefile.amにtest/以下がサブディレクトリとして存在することを指定する。</para>
  <para>Makefile.am:</para>
  <programlisting>...
SUBDIRS = test</programlisting>
  <para>Makefile.amを変更した後にmakeを実行すると、makeがMakefile.amの変更を検出し、Makefileなどを自動的に更新する。</para>
  <programlisting>[stack]% make
 cd . &amp;&amp; /bin/sh /tmp/stack/config/missing --run automake-1.10 --foreign  Makefile
 cd . &amp;&amp; /bin/sh ./config.status Makefile 
config.status: creating Makefile
Making all in test
make[1]: ディレクトリ `/tmp/stack/test' に入ります
make[1]: *** ターゲット `all' を make するルールがありません.  中止.
make[1]: ディレクトリ `/tmp/stack/test' から出ます
make: *** [all-recursive] エラー 1</programlisting>
  <para>test/以下もビルドしにいこうとしているのがわかる。ただし、test/Makefileがないためtest/以下でのビルドは失敗している。</para>
  <para>test/以下でビルドを行うようにするため、test/Makefile.amを作成する。また、configureがtest/Makefileを生成するようにconfigure.acに指定する。</para>
  <para>test/以下でのmakeが失敗しないようにするには、空のtest/Makefile.amでもよい。</para>
  <programlisting>[stack]% touch test/Makefile.am</programlisting>
  <para>あとはconfigure.acにtest/Makefileを生成するように指定すればmakeは通るようになる。</para>
  <para>configure.ac:</para>
  <programlisting>...
AC_CONFIG_FILES([Makefile
                 test/Makefile])
...</programlisting>
  <para>実際にmakeを実行すると自動で再びconfigureが走り、test/Makefileが生成され、test/以下でのmakeが失敗しなくなる。</para>
  <programlisting>[stack]% make
...
config.status: creating test/Makefile
config.status: creating src/config.h
config.status: src/config.h is unchanged
config.status: executing depfiles commands
Making all in test
make[1]: ディレクトリ `/tmp/stack/test' に入ります
make[1]: `all' に対して行うべき事はありません.
make[1]: ディレクトリ `/tmp/stack/test' から出ます
make[1]: ディレクトリ `/tmp/stack' に入ります
make[1]: `all-am' に対して行うべき事はありません.
make[1]: ディレクトリ `/tmp/stack' から出ます</programlisting>
</refsect3>

  <refsect3>
  <title>test/test_stack.soのビルド</title>
  <para>それではtest/test-stack.cを共有ライブラリとしてビルドできるようにtest/Makefile.amを編集する。テスト用の共有ライブラリは「test_」から始まる名前にする（「test_」の前に「lib」が付いても良い）。また、テストプログラムはインストールする必要がないため「noinst_」を使う。</para>
  <para>test/Makefile.am:</para>
  <programlisting>noinst_LTLIBRARIES = test_stack.la</programlisting>
  <para>テストの共有ライブラリはCutterが提供するテスト実行コマンドcutterから動的に読み込まれる。動的に読み込まれる共有ライブラリは、libtoolに-moduleオプションを渡す必要がある。また、-moduleオプションを指定する場合-rpathも指定する必要がある。そこで、LDFLAGSを以下のように指定する。-avoid-versionはテストの共有ライブラリにはバージョン番号を付ける必要がないため指定している。-no-undefinedは未定義のシンボルがある場合にエラーを報告するオプションである。環境によっては-no-undefinedを指定しないと共有ライブラリが作成されないため指定している。（例えば、Windows上でDLLを作成する場合）</para>
  <para>test/Makefile.am:</para>
  <programlisting>...
LDFLAGS = -module -rpath $(libdir) -avoid-version -no-undefined</programlisting>
  <para>test/test_stack.laのビルド（test_stack.soはtest/.libs/以下に作成される）にはtest/test-stack.cを使用するので、それを指定する。</para>
  <para>test/Makefile.am:</para>
  <programlisting>...
test_stack_la_SOURCES = test-stack.c</programlisting>
  <para>これでtest/test_stack.laがビルドできる。</para>
  <programlisting>[stack]% make
...
 cd .. &amp;&amp; /bin/sh /tmp/stack/config/missing --run automake-1.10 --foreign  test/Makefile
test/Makefile.am: required file `config/depcomp' not found
test/Makefile.am:   `automake --add-missing' can install `depcomp'
make[1]: *** [Makefile.in] エラー 1
...</programlisting>
  <para>config/depcompを生成するには--add-missingオプション付きでautomakeを実行する必要がある。これにはautogen.shを使用できる。また、configureを再実行する必要もある。</para>
  <programlisting>[stack]% ./autogen.sh
[stack]% ./configure</programlisting>
  <para>これでmakeを実行することによりtest/test_stack.laができるようになる。</para>
  <programlisting>[stack]% make
...
test-stack.c:1:20: error: cutter.h: そのようなファイルやディレクトリはありません
test-stack.c:2:19: error: stack.h: そのようなファイルやディレクトリはありません
test-stack.c: In function ‘test_new_stack’:
test-stack.c:9: error: ‘Stack’ undeclared (first use in this function)
test-stack.c:9: error: (Each undeclared identifier is reported only once
test-stack.c:9: error: for each function it appears in.)
test-stack.c:9: error: ‘stack’ undeclared (first use in this function)
make[1]: *** [test-stack.lo] エラー 1
make[1]: ディレクトリ `/tmp/stack/test' から出ます
make: *** [all-recursive] エラー 1</programlisting>
  <para>ただし、上記のようにCutterを使用する設定を行っていないためcutter.hが読み込めない。また、スタックの実装もないためstack.hの読み込みにも失敗する。</para>
</refsect3>

  <refsect3>
  <title>Cutterの使用</title>
  <para>まずは、cutter.hを読み込めるようにする。Cutterはaclocal用のマクロファイルを提供している。そのため、容易にGNUビルドシステムから利用することができる。</para>
  <para>まず、configure.acにCutterを検出するコードを追加する。</para>
  <para>configure.ac:</para>
  <programlisting>...
AC_CHECK_CUTTER

AC_CONFIG_FILES([Makefile
                 test/Makefile])
...</programlisting>
  <para>また、test/Makefile.amでは検出したCutter用の設定を利用する。</para>
  <para>test/Makefile.am:</para>
  <programlisting>...
INCLUDES = $(CUTTER_CFLAGS)
LIBS = $(CUTTER_LIBS)
...</programlisting>
  <para>現時点での完全なconfigure.ac、Makefile.am、test/Makefile.amは以下のようになる。</para>
  <para>configure.ac:</para>
  <programlisting>AC_PREREQ(2.59)

AC_INIT(stack, 0.0.1, you@example.com)
AC_CONFIG_AUX_DIR([config])
AC_CONFIG_HEADER([src/config.h])

AM_INIT_AUTOMAKE($PACKAGE_NAME, $PACKAGE_VERSION)

AC_PROG_LIBTOOL

AC_CHECK_CUTTER

AC_CONFIG_FILES([Makefile
                 test/Makefile])

AC_OUTPUT</programlisting>
  <para>Makefile.am:</para>
  <programlisting>ACLOCAL_AMFLAGS = $$ACLOCAL_ARGS

SUBDIRS = test</programlisting>
  <para>test/Makefile.am:</para>
  <programlisting>noinst_LTLIBRARIES = test_stack.la

INCLUDES = $(CUTTER_CFLAGS)
LIBS = $(CUTTER_LIBS)

LDFLAGS = -module -rpath $(libdir) -avoid-version -no-undefined

test_stack_la_SOURCES = test-stack.c</programlisting>
  <para>AC_CHECK_CUTTERマクロ内では一般的なパッケージ情報管理ツールであるpkg-configを使用している。もし、Cutterをpkg-configと異なるprefixでインストールしている場合はPKG_CONFIG_PATH環境変数を指定する。この環境変数はpkg-configが.pcファイルを検索するために利用する。ここではprefixを$HOME/localとしてCutterをインストールしたものとする。</para>
  <programlisting>[stack]% export PKG_CONFIG_PATH=$HOME/local/lib/pkgconfig</programlisting>
  <para>変更後、makeを実行すると自動的にconfigureが実行され、Cutterを利用したビルドが行われる。</para>
  <programlisting>[stack]% make
...
test-stack.c:2:19: error: stack.h: そのようなファイルやディレクトリはありません
test-stack.c: In function ‘test_new_stack’:
test-stack.c:9: error: ‘Stack’ undeclared (first use in this function)
test-stack.c:9: error: (Each undeclared identifier is reported only once
test-stack.c:9: error: for each function it appears in.)
test-stack.c:9: error: ‘stack’ undeclared (first use in this function)
make[1]: *** [test-stack.lo] エラー 1
make[1]: ディレクトリ `/tmp/stack/test' から出ます
make: *** [all-recursive] エラー 1</programlisting>
  <para>cutter.hが読み込めないというエラーがなくなった。</para>
</refsect3>

  <refsect3>
  <title>スタックAPIの作成</title>
  <para>次はstack.hが読み込めないエラーを解消する。</para>
  <para>スタックの実装はsrc/以下に作成するので、スタックのAPIであるstack.hはsrc/stack.hに置く。</para>
  <programlisting>[stack]% touch src/stack.h</programlisting>
  <para>インクルードパスを設定し、テストプログラムからstack.hを読み込めるようにする。</para>
  <para>test/Makefile.am:</para>
  <programlisting>...
INCLUDES = $(CUTTER_CFLAGS) -I$(top_srcdir)/src
...</programlisting>
  <para>makeを実行するとstack.hが読み込めないエラーが解消されているのが分かる。</para>
  <programlisting>[stack]% make
...
test-stack.c: In function ‘test_new_stack’:
test-stack.c:9: error: ‘Stack’ undeclared (first use in this function)
test-stack.c:9: error: (Each undeclared identifier is reported only once
test-stack.c:9: error: for each function it appears in.)
test-stack.c:9: error: ‘stack’ undeclared (first use in this function)
make[1]: *** [test-stack.lo] エラー 1
make[1]: ディレクトリ `/tmp/stack/test' から出ます
make: *** [all-recursive] エラー 1</programlisting>
  <para>残りのエラーがStack型が宣言されていないことだけになった。</para>
</refsect3>

  <refsect3>
  <title>Stack型の宣言</title>
  <para>src/stack.hにStack型を宣言し、テストプログラムをビルドできるようにする。</para>
  <para>src/stack.h:</para>
  <programlisting>#ifndef __STACK_H__
#define __STACK_H__

typedef struct _Stack Stack;

#endif</programlisting>
  <para>stack_new()が宣言されていないため警告がでるが共有ライブラリを作成することはできる。</para>
  <programlisting>[stack]% make
...
test-stack.c: In function ‘test_new_stack’:
test-stack.c:10: warning: assignment makes pointer from integer without a cast
...
[stack]% file test/.libs/test_stack.so
test/.libs/test_stack.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, not stripped</programlisting>
  <para>注: Cygwin上では未解決のシンボルがあるときは作成できない。Cygwin環境の場合は気にせずに次に進むこと。</para>
</refsect3>

  <refsect3>
  <title>stack_new()/stack_is_empty()の宣言</title>
  <para>stack_new()、stack_is_empty()を宣言し、警告を解消する。</para>
  <para>src/stack.h:</para>
  <programlisting>...
Stack *stack_new      (void);
int    stack_is_empty (Stack *stack);
...</programlisting>
  <para>makeをして警告がでないことを確認する。</para>
  <programlisting>[stack]% make</programlisting>
</refsect3>

</refsect2>

  <refsect2>
  <title>テスト起動</title>
  <para>共有ライブラリが作成できたので、cutterコマンドでこのテストを起動できる。</para>
  <programlisting>[stack]% cutter test/
cutter: symbol lookup error: test/.libs/test_stack.so: undefined symbol: stack_new</programlisting>
  <para>stack_new()が定義されていないため読み込みに失敗するが、テストプログラムが読み込まれることは確認できる。</para>
  <para>注: Cygwin上では未解決のシンボルがあるときはDLL作成できないので、エラーにならず、「0個のテストを実行して失敗しなかった」結果が報告される。以降の作業の中でスタックを実装し、未解決のシンボルが解決されればDLLが作成され、テストが実行できるようになる。それまではテストの実行結果が異なる。Cygwin環境の場合は気にせずに次に進むこと。</para>
  <refsect3>
  <title>テスト起動の自動化</title>
  <para>GNUビルドシステムでは一般的にmake checkでテストが起動する。スタックの実装でも同様にテストが起動するようにする。</para>
  <para>まず、テストを起動するスクリプトtest/run-test.shを作成する。cutterコマンドのパスは環境変数CUTTERで受け取ることにする。</para>
  <para>test/run-test.sh:</para>
  <programlisting>#!/bin/sh

export BASE_DIR=&quot;`dirname $0`&quot;
$CUTTER -s $BASE_DIR &quot;$@&quot; $BASE_DIR</programlisting>
  <para>実行権を付けることを忘れないこと。</para>
  <programlisting>[stack]% chmod +x test/run-test.sh</programlisting>
  <para>test/Makefile.amにテスト起動スクリプトとしてtest/run-test.shを使うことを指定する。</para>
  <para>test/Makefile.am:</para>
  <programlisting>TESTS = run-test.sh
TESTS_ENVIRONMENT = CUTTER=&quot;$(CUTTER)&quot;
...</programlisting>
  <para>TESTS_ENVIRONMENTではcutterコマンドのパスを環境変数CUTTERで渡している。cutterコマンドのパスはconfigure.ac内に追加したAC_CHECK_CUTTERが検出している。</para>
  <para>make -s checkでテストが走ることを確認する。-sオプションはmakeの出力を抑えるオプション（silent）であり、これを指定することによりテスト結果が見やすくなる。</para>
  <programlisting>[stack]% make -s check
Making check in test
cutter: symbol lookup error: ./.libs/test_stack.so: undefined symbol: stack_new
FAIL: run-test.sh
================================
1 of 1 tests failed
Please report to you@example.com
================================
...</programlisting>
  <para>注: 前述のとおり、Cygwin上ではDLLが作成できないため、エラーは発生しない。Cygwin環境の場合は実行結果が異なっても気にせずに次に進むこと。</para>
</refsect3>

  <refsect3>
  <title>test/run-test.shの単独実行のサポート</title>
  <para>make -s checkではビルドログなどテスト結果以外の出力がでて、テスト結果が埋もれてしまう。そこで、make -s check経由ではなくtest/run-test.shを実行できるようにする。</para>
  <para>まず、test/run-test.shを環境変数CUTTERが指定されていない場合は、cutterコマンドのパスを自動的に検出する。さらにmake check経由でtest/run-test.shが起動された場合はmakeを実行し、必要なファイルをビルドする。</para>
  <para>test/run-test.sh:</para>
  <programlisting>#!/bin/sh

export BASE_DIR=&quot;`dirname $0`&quot;
top_dir=&quot;$BASE_DIR/..&quot;

if test -z &quot;$NO_MAKE&quot;; then
    make -C $top_dir &gt; /dev/null || exit 1
fi

if test -z &quot;$CUTTER&quot;; then
    CUTTER=&quot;`make -s -C $BASE_DIR echo-cutter`&quot;
fi

$CUTTER -s $BASE_DIR &quot;$@&quot; $BASE_DIR</programlisting>
  <para>このtest/run-test.shに対応するためにtest/Makefile.amを以下のように変更する。</para>
  <para>test/Makefile.am:</para>
  <programlisting>...
TESTS_ENVIRONMENT = NO_MAKE=yes CUTTER=&quot;$(CUTTER)&quot;
...
echo-cutter:
	@echo $(CUTTER)</programlisting>
  <para>test/Makefile.am全体は以下のようになる。</para>
  <para>test/Makefile.am:</para>
  <programlisting>TESTS = run-test.sh
TESTS_ENVIRONMENT = NO_MAKE=yes CUTTER=&quot;$(CUTTER)&quot;

noinst_LTLIBRARIES = test_stack.la

INCLUDES = $(CUTTER_CFLAGS) -I$(top_srcdir)/src
LIBS = $(CUTTER_LIBS)

LDFLAGS = -module -rpath $(libdir) -avoid-version -no-undefined

test_stack_la_SOURCES = test-stack.c

echo-cutter:
	@echo $(CUTTER)</programlisting>
  <para>test/run-test.shを直接実行してテストが起動することを確認する。</para>
  <programlisting>[stack]% test/run-test.sh
cutter: symbol lookup error: test/.libs/test_stack.so: undefined symbol: stack_new</programlisting>
  <para>注: Cygwin環境ではエラーは発生しない。</para>
  <para>ここからはmake -s checkではなくtest/run-test.shを使用する。これは必要な情報のみが出力され、本当に興味のあるテストの結果が埋もれてしまうのを防ぐためである。</para>
  <para>また、スタックの実装を行う前にテストの実行環境を整備しているのは、テストを実行するコストを下げるためである。これは、テストを実行することが面倒になるとテストを実行しなくなり、その結果、プログラムの品質低下につながるためである。</para>
  <para>最初にテスト環境の整備を行うと、その分、プログラム本体の開発着手が遅れてしまう。しかし、プログラム本体が開発・保守され続ける間は常にテストを実行し、品質を保持する必要があるため、最初にテスト環境整備に当てたコストは回収可能である。今後、快適に品質の高いプログラムの開発を行うために、最初にテスト環境の整備を行うことは重要である。</para>
</refsect3>

</refsect2>

  <refsect2>
  <title>スタックの実装</title>
  <para>テスト環境の整備ができたため、スタックの実装に入る。</para>
</refsect2>

  <refsect3>
  <title>簡単なstack_new()の実装</title>
  <para>まず、stack_new()を定義し、実行時エラーの原因を解決する。</para>
  <para>スタックの実装はsrc/stack.cで行う。簡単なstack_new()の実装は以下の通りである。</para>
  <para>src/stack.c:</para>
  <programlisting>#include &lt;stdlib.h&gt;
#include &quot;stack.h&quot;

Stack *
stack_new (void)
{
    return NULL;
}</programlisting>
</refsect3>

  <refsect3>
  <title>src/libstack.laのビルド</title>
  <para>それでは、makeでsrc/stack.cをビルドできるようにする。</para>
  <para>まず、test/以下をビルド対象に加えたように、src/以下もビルド対象とする。</para>
  <para>Makefile.am:</para>
  <programlisting>ACLOCAL_AMFLAGS = $$ACLOCAL_ARGS

SUBDIRS = src test</programlisting>
  <para>configure.ac:</para>
  <programlisting>...
AC_CONFIG_FILES([Makefile
                 src/Makefile
                 test/Makefile])
...</programlisting>
  <para>これでsrc/以下もビルド対象となる。</para>
  <programlisting>[stack]% test/run-test.sh
configure.ac:13: required file `src/Makefile.in' not found
make: *** [Makefile.in] エラー 1</programlisting>
  <para>src/Makefile.amを作成するとこのエラーはなくなる。</para>
  <programlisting>[stack]% touch src/Makefile.am
[stack]% test/run-test.sh
cutter: symbol lookup error: test/.libs/test_stack.so: undefined symbol: stack_new</programlisting>
  <para>注: Cygwin環境ではエラーは発生しない。</para>
  <para>makeは通るようになるが、この時点ではsrc/stack.cはビルドされないし、テストプログラムもlibstack.soをリンクしていないのでstack_new()が定義されていないエラーは変わらない。</para>
  <para>src/Makefile.amに以下を追加し、src/stack.cからlibstack.soを作成する。</para>
  <para>src/Makefile.am:</para>
  <programlisting>lib_LTLIBRARIES = libstack.la

LDFLAGS = -no-undefined

libstack_la_SOURCES = stack.c</programlisting>
  <para>makeでlibstack.soが生成できるようになるはずである。</para>
  <programlisting>[stack]% make
...
make[1]: ディレクトリ `/tmp/stack/src' に入ります
Makefile:275: .deps/stack.Plo: そのようなファイルやディレクトリはありません
make[1]: *** ターゲット `.deps/stack.Plo' を make するルールがありません.  中止.
...</programlisting>
  <para>上記のエラーを修正するためにconfigureをもう一度実行する必要がある。</para>
  <programlisting>[stack]% ./configure</programlisting>
  <para>makeでsrc/.libs/libstack.so.0.0.0を生成することができる。</para>
  <programlisting>[stack]% make
[stack]% file src/.libs/libstack.so.0.0.0
src/.libs/libstack.so.0.0.0: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, not stripped</programlisting>
  <para>注: Cygwin上ではsrc/.libs/cyglibstack.dllが作成される。</para>
</refsect3>

  <refsect3>
  <title>src/libstack.laのリンク</title>
  <para>libstack.soはできたがテストプログラムにはリンクしていないので、まだ実行時エラーは発生する。</para>
  <programlisting>[stack]% test/run-test.sh
cutter: symbol lookup error: test/.libs/test_stack.so: undefined symbol: stack_new</programlisting>
  <para>注: Cygwin環境ではエラーは発生しない。</para>
  <para>libstack.soをリンクするためにtest/Makefile.amを以下のように変更する。</para>
  <para>test/Makefile.am:</para>
  <programlisting>...
LIBS = $(CUTTER_LIBS) $(top_builddir)/src/libstack.la
...</programlisting>
  <para>Cygwin環境の場合はsrc/.libs/以下に生成されるスタックを実装したDLLを利用するために、src/.libs/にパスを通す必要がある。そのため、以下のようにtest/run-test.shでcutterを実行する前にPATHを設定する。</para>
  <para>test/run-test.sh:</para>
  <programlisting>...
case `uname` in
    CYGWIN*)
        PATH=&quot;$top_dir/src/.libs:$PATH&quot;
        ;;
    Darwin)
        DYLD_LIBRARY_PATH=&quot;$top_dir/src/.libs:$DYLD_LIBRARY_PATH&quot;
        export DYLD_LIBRARY_PATH
        ;;
    *BSD)
        LD_LIBRARY_PATH=&quot;$top_dir/src.libs:$LD_LIBRARY_PATH&quot;
        export LD_LIBRARY_PATH
        ;;
    *)
        :
        ;;
esac

$CUTTER -s $BASE_DIR &quot;$@&quot; $BASE_DIR</programlisting>
  <para>テストプログラムを再リンクする必要があるため、一度make cleanしてからビルドしなおす。</para>
  <programlisting>[stack]% make clean
[stack]% make
[stack]% test/run-test.sh
cutter: symbol lookup error: test/.libs/test_stack.so: undefined symbol: stack_is_empty</programlisting>
  <para>今まではstack_new()が見つからずにエラーが発生していたが、stack_is_empty()が見つからないというエラーに変わった。これにより、libstack.soがリンクされていることが確認できた。</para>
  <para>注: Cygwin環境ではエラーは発生しない。</para>
</refsect3>

  <refsect3>
  <title>stack_is_empty()の実装</title>
  <para>テストプログラム中ではstack_is_empty()の結果を以下のようにテストしている。</para>
  <para>test/test-stack.c:</para>
  <programlisting>...
cut_assert(stack_is_empty(stack));
...</programlisting>
  <para>つまり、stack_is_empty()が真を返すことをテストしている。よって、src/stack.cでのstack_is_empty()は真を返す必要がある。</para>
  <para>src/stack.c:</para>
  <programlisting>...
#define TRUE 1
#define FALSE 0
...
int
stack_is_empty (Stack *stack)
{
    return TRUE;
}</programlisting>
  <para>src/stack.c全体は以下のようになる。</para>
  <para>src/stack.c:</para>
  <programlisting>#include &lt;stdlib.h&gt;
#include &quot;stack.h&quot;

#define TRUE 1
#define FALSE 0

Stack *
stack_new (void)
{
    return NULL;
}

int
stack_is_empty (Stack *stack)
{
    return TRUE;
}</programlisting>
  <para>このstack_is_empty()の実装は常に真を返すため、テストは成功するはずである。</para>
  <programlisting>[stack]% test/run-test.sh
.

Finished in 0.000028 seconds

1 test(s), 1 assertion(s), 0 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
100% passed</programlisting>
  <para>「.」がひとつ表示されているのは1つのテストがパスしたことを表している。現在は1つしかテストがないので1つのテストにパスしたということはすべてのテストにパスしたということである。</para>
  <para>環境によっては表示が緑になっているはずである。これはテストがパスしているので次に進んでもよいという意味である。</para>
  <para>テストが動作することが確認できたので、以降ではテストを作成しながらスタックの実装を完成させる。</para>
</refsect3>

</refsect1>

<refsect1>
  <title>pushの実装</title>
  <para>まずはpushを実装する。今回の実装では、スタックにはintのみを格納できることする。</para>
  <refsect2>
  <title>pushのテスト</title>
  <para>pushをした後はスタックのサイズが1になり、スタックは空ではなくなるはずである。これをテストにすると以下のようになる。</para>
  <para>test/test-stack.c:</para>
  <programlisting>...
void test_push (void);
...
void
test_push (void)
{
    Stack *stack;

    stack = stack_new();
    cut_assert_equal_int(0, stack_get_size(stack));
    stack_push(stack, 100);
    cut_assert_equal_int(1, stack_get_size(stack));
    cut_assert(!stack_is_empty(stack));
}</programlisting>
  <para>テストを実行すると、stack_get_size()が定義されていないため実行時エラーになる。</para>
  <programlisting>[stack]% test/run-test.sh
cutter: symbol lookup error: ./test/.libs/test_stack.so: undefined symbol: stack_get_size</programlisting>
  <para>注: Cygwin環境ではエラーは発生しない？</para>
  <para>このテストをパスするようにpushを実装する。</para>
</refsect2>

  <refsect2>
  <title>cut_stack_push()の実装</title>
  <para>まずは、パスしなくても良いのでテストが動くようにstack_get_size()とstack_push()を実装する。</para>
  <para>まず、src/stack.hに宣言を追加する。</para>
  <para>src/stack.h:</para>
  <programlisting>...
int    stack_get_size (Stack *stack);
void   stack_push     (Stack *stack, int value);
...</programlisting>
  <para>続いてsrc/stack.cに定義を追加する。</para>
  <para>src/stack.c:</para>
  <programlisting>...
int
stack_get_size (Stack *stack)
{
    return 0;
}

void
stack_push (Stack *stack, int value)
{
}</programlisting>
  <para>stack_get_size()が0を返しているのは、最初のstack_get_size()は以下のように0を期待されているからである。</para>
  <para>test/test-stack.c:</para>
  <programlisting>...
stack = stack_new();
cut_assert_equal_int(0, stack_get_size(stack));
...</programlisting>
  <para>pushの実装ができたのでテストを実行する。</para>
  <programlisting>[stack]% test/run-test.sh
.F

1) Failure: test_push
&lt;1 == stack_get_size(stack)&gt;
expected: &lt;1&gt;
 but was: &lt;0&gt;
test/test-stack.c:23: test_push()

Finished in 0.000113 seconds

2 test(s), 2 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
50% passed</programlisting>
  <para>「F」はテストが失敗（Failure）したことを表している。環境によっては表示が赤くなっているはずである。これは、テストがパスしていないので先に進むことは危険であることを示している。popの実装に移る前にテストをパスさせるようにpushを実装を改良するべきだということである。</para>
  <para>cutterからのメッセージでは、test/test-stack.cの23行目、test_push()関数の中でstack_get_size(stack)の値が1ではなく0のために失敗したということを表している。該当する行は以下の通りである。</para>
  <para>test/test-stack.c:23:</para>
  <programlisting>cut_assert_equal_int(1, stack_get_size(stack));</programlisting>
  <para>これはstack_get_size()が常に0を返しているためである。stack_push()された後には内部のカウンタを1つ進める必要がある。</para>
</refsect2>

  <refsect2>
  <title>メモリ開放</title>
  <para>今まではstack_new()でNULLを返していたが、test_pushテストをパスするためには内部にカウンタを持つ必要があるため、メモリを割り当てる必要がある。メモリを割り当てた場合、使用済のメモリを開放する必要がある。</para>
  <para>例えば、test_new_stack()では以下のようにする必要がある。</para>
  <programlisting>void
test_new_stack (void)
{
    Stack *stack;
    stack = stack_new();
    cut_assert(stack_is_empty(stack));
    stack_free(stack);
}</programlisting>
  <para>しかし、stack_free()の前のcut_assert()が失敗した場合はその時点で処理が終了してしまうため、stack_free()が呼ばれずメモリリークが発生する。（ただし、テストプログラムはすぐに終了する短命なプログラムため、害が大きくなることは少ない。）</para>
  <para>Cutterではテストの前後に必ず実行される関数を設定することができる。それがcut_setup()/cut_teardown()である。これらはテストが成功した場合も失敗した場合も呼ばれるためテスト中で割り当てたメモリを確実に開放する処理などに使うことができる。</para>
  <para>test_new_stack()をcut_setup()/cut_teardown()を使って確実にメモリを開放するようにすると以下のようになる。</para>
  <para>test/test-stack.c:</para>
  <programlisting>...
static Stack *stack;

void
cut_setup (void)
{
    stack = NULL;
}

void
cut_teardown (void)
{
    if (stack)
        stack_free(stack);
}

void
test_new_stack (void)
{
    stack = stack_new();
    cut_assert(stack_is_empty(stack));
}
...</programlisting>
  <para>同様に、test_push()でも、関数内のローカル変数stackを使わずにファイル中でstaticなstackを使用すれば確実にメモリを開放できる。</para>
  <para>test/test-stack.c:</para>
  <programlisting>...
void
test_push (void)
{
    stack = stack_new();
    cut_assert_equal_int(0, stack_get_size(stack));
    stack_push(stack, 100);
    cut_assert_equal_int(1, stack_get_size(stack));
    cut_assert(!stack_is_empty(stack));
}
...</programlisting>
  <para>cut_setup()/cut_teardown()を使用したtest/test-stack.c全体は以下のようになる。</para>
  <para>test/test-stack.c:</para>
  <programlisting>#include &lt;cutter.h&gt;
#include &lt;stack.h&gt;

void test_new_stack (void);
void test_push (void);

static Stack *stack;

void
cut_setup (void)
{
    stack = NULL;
}

void
cut_teardown (void)
{
    if (stack)
        stack_free(stack);
}

void
test_new_stack (void)
{
    stack = stack_new();
    cut_assert(stack_is_empty(stack));
}

void
test_push (void)
{
    stack = stack_new();
    cut_assert_equal_int(0, stack_get_size(stack));
    stack_push(stack, 100);
    cut_assert_equal_int(1, stack_get_size(stack));
    cut_assert(!stack_is_empty(stack));
}</programlisting>
  <para>この変更の後でもテストが変更前と同じ結果を返すことを確認する。</para>
  <programlisting>[stack]% test/run-test.sh
.F

1) Failure: test_push
&lt;1 == stack_get_size(stack)&gt;
expected: &lt;1&gt;
 but was: &lt;0&gt;
test/test-stack.c:35: test_push()

Finished in 0.000084 seconds

2 test(s), 2 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
50% passed</programlisting>
</refsect2>

  <refsect2>
  <title>stack_new()/stack_free()の実装</title>
  <para>それでは、stack_new()でメモリを割り当て、stack_free()で開放する処理を実装する。</para>
  <para>まず、src/stack.hにstack_free()を宣言する。</para>
  <para>src/stack.h:</para>
  <programlisting>...
void   stack_free     (Stack *stack);
...</programlisting>
  <para>続いて、src/stack.cにStackを定義する。Stackはスタックのサイズを保存するフィールドを含む。</para>
  <para>src/stack.c:</para>
  <programlisting>...
struct _Stack {
    int size;
};
...</programlisting>
  <para>stack_new()でStackのためのメモリを割り当て、stack_free()で開放する。</para>
  <para>src/stack.c:</para>
  <programlisting>...
Stack *
stack_new (void)
{
    Stack *stack;

    stack = malloc(sizeof(Stack));
    if (!stack)
        return NULL;

    stack-&gt;size = 0;
    return stack;
}

void
stack_free (Stack *stack)
{
    free(stack);
}
...</programlisting>
  <para>この変更の後でもテストが変更前と同じ結果を返すことを確認する。</para>
  <programlisting>[stack]% test/run-test.sh
.F

1) Failure: test_push
&lt;1 == stack_get_size(stack)&gt;
expected: &lt;1&gt;
 but was: &lt;0&gt;
test/test-stack.c:35: test_push()

Finished in 0.000113 seconds

2 test(s), 2 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
50% passed</programlisting>
</refsect2>

  <refsect2>
  <title>stack_push()の本実装</title>
  <para>Stackにスタックサイズを持つことができるようになったので、これを使用してテストをパスするようにstack_push()/stack_get_size()を実装する。</para>
  <para>src/stack.c:</para>
  <programlisting>...
int
stack_get_size (Stack *stack)
{
    return stack-&gt;size;
}

void
stack_push (Stack *stack, int value)
{
    stack-&gt;size++;
}</programlisting>
  <para>pushする毎にスタックサイズを増やし、そのサイズを返すようにした。これで今まで失敗していたstack_get_size()のテストがパスするはずである。</para>
  <programlisting>[stack]% test/run-test.sh
.F

1) Failure: test_push
expected: &lt;!stack_is_empty(stack)&gt; is not FALSE/NULL
test/test-stack.c:36: test_push()

Finished in 0.000113 seconds

2 test(s), 3 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
50% passed</programlisting>
  <para>期待通りstack_get_size()のテストはパスしたがその後のtest/test-stack.cの36行目、stack_is_empty()で失敗している。</para>
  <para>test/test-stack.c:36:</para>
  <programlisting>cut_assert(!stack_is_empty(stack));</programlisting>
  <para>スタックにpushしたら空ではなくなるはずである。</para>
</refsect2>

  <refsect2>
  <title>stack_is_empty()の本実装</title>
  <para>スタックが空なのはスタックサイズが0のときである。よって、stack_is_empty()の実装は以下のようになる。</para>
  <para>src/stack.c:</para>
  <programlisting>...
int
stack_is_empty (Stack *stack)
{
    return stack-&gt;size == 0;
}
...</programlisting>
  <para>テストを実行し、すべてのテストにパスすることを確認する。</para>
  <programlisting>% test/run-test.sh
..

Finished in 0.000036 seconds

2 test(s), 4 assertion(s), 0 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
100% passed</programlisting>
  <para>pushのテストもパスし、既存のスタックを作った直後の場合のテストもパスしたままである。すべてのテストがパスしたため、結果表示も緑に戻った。これで安心してpopの実装に進むことができる。</para>
</refsect2>

</refsect1>

<refsect1>
  <title>popの実装</title>
  <para>pushが実装できたので、次はpushで入れたデータを取り出すpopを実装する。</para>
  <refsect2>
  <title>popのテスト</title>
  <para>popをすると最後にpushした値が順番に返ってくる。また、popする毎にスタックサイズが減り、最後は空になる。これをテストにすると以下のようになる。</para>
  <para>test/test-stack.c:</para>
  <programlisting>...
void test_pop (void);
...
void
test_pop (void)
{
    stack = stack_new();

    stack_push(stack, 10);
    stack_push(stack, 20);
    stack_push(stack, 30);

    cut_assert_equal_int(3, stack_get_size(stack));
    cut_assert_equal_int(30, stack_pop(stack));
    cut_assert_equal_int(2, stack_get_size(stack));
    cut_assert_equal_int(20, stack_pop(stack));
    cut_assert_equal_int(1, stack_get_size(stack));

    stack_push(stack, 40);
    cut_assert_equal_int(2, stack_get_size(stack));
    cut_assert_equal_int(40, stack_pop(stack));
    cut_assert_equal_int(1, stack_get_size(stack));
    cut_assert_equal_int(10, stack_pop(stack));
    cut_assert_equal_int(0, stack_get_size(stack));
    cut_assert(stack_is_empty(stack));
}</programlisting>
  <para>テストを走らせる。</para>
  <programlisting>[stack]% test/run-test.sh
..cutter: symbol lookup error: test/.libs/test_stack.so: undefined symbol: stack_pop</programlisting>
  <para>stack_pop()を定義していないためエラーが発生している。エラーメッセージの前に「.」が二つ出ているので既存のテストはパスしていることが確認できる。</para>
  <para>注: Cygwin環境ではエラーは発生しない？</para>
</refsect2>

  <refsect2>
  <title>stack_pop()の実装</title>
  <para>まず、src/stack.hにstack_pop()の宣言を追加する。</para>
  <para>src/stack.h:</para>
  <programlisting>...
int    stack_pop      (Stack *stack);
...</programlisting>
  <para>つづいて、src/stack.cにstack_pop()の実装を追加する。</para>
  <para>src/stack.c:</para>
  <programlisting>...
int
stack_pop (Stack *stack)
{
    return 30;
}</programlisting>
  <para>ここで30を返すようにしているのは最初のstack_pop()では30を返すことが期待されているからである。</para>
  <para>test/test-stack.c:50:</para>
  <programlisting>cut_assert_equal_int(30, stack_pop(stack));</programlisting>
  <para>テストを実行し、popのテストもエラーが発生せずに実行されることを確認する。</para>
  <programlisting>[stack]% test/run-test.sh
..F

1) Failure: test_pop
&lt;2 == stack_get_size(stack)&gt;
expected: &lt;2&gt;
 but was: &lt;3&gt;
test/test-stack.c:51: test_pop()

Finished in 0.000307 seconds

3 test(s), 6 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
66.6667% passed</programlisting>
  <para>popのテストが実行された。しかし、現在のstack_pop()ではスタックサイズを変更していないため、popした後のスタックサイズを確認しているtest/test-stack.cの50行目のstack_get_size()で失敗している。</para>
  <para>test/test-stack.c:51:</para>
  <programlisting>cut_assert_equal_int(2, stack_get_size(stack));</programlisting>
</refsect2>

  <refsect2>
  <title>データ領域の確保</title>
  <para>テストが実行されることが確認できたのでテストにパスするようにstack_pop()を実装する。</para>
  <para>後でpopで取り出すために、pushされたデータを保存しておく必要がある。Stackに保存されたデータを示す場所を用意し、stack_push()/stack_pop()で動的に必要な領域を割り当てる。</para>
  <para>まず、Stackに保存されたデータを示す場所を用意し、stack_new()で初期化、stack_free()で開放する。</para>
  <para>src/stack.c:</para>
  <programlisting>...
struct _Stack {
    int size;
    int *data;
};

Stack *
stack_new (void)
{
    ...
    stack-&gt;data = NULL;
    ...
}

void
stack_free (Stack *stack)
{
    free(stack-&gt;data);
    free(stack);
}
...</programlisting>
  <para>この時点では外部向けの処理の内容は変わっていないはずなので、テストを実行して変更前と同じように失敗することを確認する。</para>
  <programlisting>[stack]% test/run-test.sh
..F

1) Failure: test_pop
&lt;2 == stack_get_size(stack)&gt;
expected: &lt;2&gt;
 but was: &lt;3&gt;
test/test-stack.c:51: test_pop()

Finished in 0.000097 seconds

3 test(s), 6 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
66.6667% passed</programlisting>
</refsect2>

  <refsect2>
  <title>stack_pop()の本実装</title>
  <para>保存したデータを示す場所が用意できたのでstack_push()/stack_pop()でそこに必要な分だけ領域を割り当て、データを保存する。</para>
  <para>src/stack.c:</para>
  <programlisting>...
void
stack_push (Stack *stack, int value)
{
    int *new_data;

    stack-&gt;size++;
    new_data = realloc(stack-&gt;data, sizeof(*stack-&gt;data) * stack-&gt;size);
    if (!new_data) {
        free(stack-&gt;data);
        stack-&gt;data = NULL;
        stack-&gt;size = 0;
        return;
    }
    stack-&gt;data = new_data;

    stack-&gt;data[stack-&gt;size - 1] = value;
}

int
stack_pop (Stack *stack)
{
    int value;
    int *new_data;

    stack-&gt;size--;
    value = stack-&gt;data[stack-&gt;size];

    new_data = realloc(stack-&gt;data, sizeof(*stack-&gt;data) * stack-&gt;size);
    if (stack-&gt;size &gt; 0 &amp;&amp; !new_data) {
        free(stack-&gt;data);
        stack-&gt;data = NULL;
        stack-&gt;size = 0;
        return value;
    }
    stack-&gt;data = new_data;

    return value;
}</programlisting>
  <para>テストを実行し、popのテストがパスすることを確認する。</para>
  <programlisting>[stack]% test/run-test.sh
...

Finished in 0.000076 seconds

3 test(s), 15 assertion(s), 0 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
100% passed</programlisting>
</refsect2>

</refsect1>

<refsect1>
  <title>重複の排除</title>
  <para>stack_push()/stack_pop()の実装では動的なメモリ割り当て部分、メモリ割り当て失敗時のエラー処理部分に重複があった。一般的にコード中に重複があることは、メンテナンス性の面などの理由で悪いこととされている。</para>
  <para>ここでは、既存の動作を変更せずに重複している悪い部分を修正する。既存の動作が変わっていないことはテストを実行することで確認することができる。</para>
  <refsect2>
  <title>メモリ割り当て部分の重複の除去</title>
  <para>まず、以下のメモリ割り当て部分の重複を除去する。</para>
  <para>src/stack.c:</para>
  <programlisting>new_data = realloc(stack-&gt;data, sizeof(*stack-&gt;data) * stack-&gt;size);</programlisting>
  <para>この部分はstack_realloc()として切り出す。</para>
  <para>src/stack.c:</para>
  <programlisting>...
static int *
stack_realloc (Stack *stack)
{
    return realloc(stack-&gt;data, sizeof(*stack-&gt;data) * stack-&gt;size);
}

void
stack_push (Stack *stack, int value)
{
    ...
    new_data = stack_realloc(stack);
    ...
}

int
stack_pop (Stack *stack)
{
    ...
    new_data = stack_realloc(stack);
    ...
}</programlisting>
  <para>この変更の後でも以前と同じ挙動をしているかを確かめる。</para>
  <programlisting>[stack]% test/run-test.sh
...

Finished in 0.000078 seconds

3 test(s), 15 assertion(s), 0 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
100% passed</programlisting>
  <para>結果が緑なので次へ進める。</para>
</refsect2>

  <refsect2>
  <title>エラー処理部分の重複の除去</title>
  <para>次に、以下のメモリ割り当て失敗時のエラー処理部分の重複を除去する。</para>
  <para>src/stack.c:</para>
  <programlisting>...
void
stack_push (Stack *stack, int value)
{
    ...
    new_data = stack_realloc(stack);
    if (!new_data) {
        free(stack-&gt;data);
        stack-&gt;data = NULL;
        stack-&gt;size = 0;
        return;
    }
    ...
}

int
stack_pop (Stack *stack)
{
    ...
    new_data = stack_realloc(stack);
    if (stack-&gt;size &gt; 0 &amp;&amp; !new_data) {
        free(stack-&gt;data);
        stack-&gt;data = NULL;
        stack-&gt;size = 0;
        return value;
    }
    ...
}</programlisting>
  <para>これらのエラー処理をstack_realloc()の中に移動し、stack_realloc()は割り当てたメモリを返すのではなく、新しくメモリを割り当てることに成功したかどうかを返すことにする。</para>
  <para>src/stack.c:</para>
  <programlisting>...
static int
stack_realloc (Stack *stack)
{
    int *new_data;

    new_data = realloc(stack-&gt;data, sizeof(*stack-&gt;data) * stack-&gt;size);
    if (stack-&gt;size &gt; 0 &amp;&amp; !new_data) {
        free(stack-&gt;data);
        stack-&gt;data = NULL;
        stack-&gt;size = 0;
        return FALSE;
    }
    stack-&gt;data = new_data;

    return TRUE;
}

void
stack_push (Stack *stack, int value)
{
    stack-&gt;size++;
    if (!stack_realloc(stack))
        return;
    stack-&gt;data[stack-&gt;size - 1] = value;
}

int
stack_pop (Stack *stack)
{
    int value;

    stack-&gt;size--;
    value = stack-&gt;data[stack-&gt;size];
    stack_realloc(stack);
    return value;
}</programlisting>
  <para>この変更の後でも以前と同じ挙動をしているかを確かめる。</para>
  <programlisting>[stack]% test/run-test.sh
...

Finished in 0.000076 seconds

3 test(s), 15 assertion(s), 0 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s)
100% passed</programlisting>
  <para>既存の動作を変更することなく、プログラム中の重複部分を取り除き、プログラムを改良したことを確認できた。</para>
</refsect2>

</refsect1>


<refsect1>
  <title>まとめ</title>
  <para>本稿では小さなスタックの実装を例にしてGNUビルドシステムを用いたビルド環境の構築方法・Cutterを用いたテストの作成方法・テストのあるプログラムでのプログラムの改良方法を示した。</para>
  <refsect2>
  <title>メリット</title>
  <para>GNUビルドシステムを用いることにより、ビルド環境の差異を吸収することが比較的容易になる。これはプログラムの移植性を向上させることにつながる。</para>
  <para>Cutterを用いることにより、簡単にテストが書ける。既存のC言語用テスティングフレームワークではテストを定義するために独自のマクロを用いたり、テストの定義とテストの登録を別々に行う必要があるなどテスト以外にも書かなければいけないことが多い。Cutterはこの点を改善し、テスト定義のための独自のマクロを提供せず、通常どおりに関数を定義するだけでテストを定義できるようにした。明示的にテストを定義する必要もない。</para>
  <para>本稿ではcut_assert()とcut_assert_equal_int()しか使用しなかったが、cut_assert_equal_string()など期待値と実際の値を比較するための方法を多数用意している。これにより、テストプログラムのための比較方法を定義しなければいけない機会が減り、より簡潔にテストプログラムを書けるようになる。</para>
  <para>また、Cutterのテスト結果出力は必要のない情報はなるべく表示せず、必要な情報はできるだけ多く提供する。これは必要な情報が埋もれてしまうのを防ぎ、プログラムの修正を支援する。また、C言語ではよくある異常終了時には、バックトレースの出力を試み、プログラム修正のためのより多くの情報を提供する。</para>
  <para>既存の機能を変更せずにプログラムの内部構造を改良することはメンテナンス性を向上させるのに非常に役立つ。自動化されたテストを作成することにより、既存の機能が変更されていないことを容易に確認できる。</para>
  <para>また、新規に機能を追加する場合でも、自動化されたテストがあれば、既存の機能を壊すことなく機能を追加していることを確認できる。自動化テストを用意することはメンテナンス面でも、新機能開発面でも品質の高いプログラムを作成する上で有用である。</para>
</refsect2>
  <refsect2>
  <title>スタックのテスト</title>
  <para>最終的なテストは以下の通りである。</para>
  <para>test/test-stack.c</para>
  <programlisting>#include &lt;cutter.h&gt;
#include &lt;stack.h&gt;

void test_new_stack (void);
void test_push (void);
void test_pop (void);

static Stack *stack;

void
cut_setup (void)
{
    stack = NULL;
}

void
cut_teardown (void)
{
    if (stack)
        stack_free(stack);
}

void
test_new_stack (void)
{
    stack = stack_new();
    cut_assert(stack_is_empty(stack));
}

void
test_push (void)
{
    stack = stack_new();
    cut_assert_equal_int(0, stack_get_size(stack));
    stack_push(stack, 100);
    cut_assert_equal_int(1, stack_get_size(stack));
    cut_assert(!stack_is_empty(stack));
}

void
test_pop (void)
{
    stack = stack_new();

    stack_push(stack, 10);
    stack_push(stack, 20);
    stack_push(stack, 30);

    cut_assert_equal_int(3, stack_get_size(stack));
    cut_assert_equal_int(30, stack_pop(stack));
    cut_assert_equal_int(2, stack_get_size(stack));
    cut_assert_equal_int(20, stack_pop(stack));
    cut_assert_equal_int(1, stack_get_size(stack));

    stack_push(stack, 40);
    cut_assert_equal_int(2, stack_get_size(stack));
    cut_assert_equal_int(40, stack_pop(stack));
    cut_assert_equal_int(1, stack_get_size(stack));
    cut_assert_equal_int(10, stack_pop(stack));
    cut_assert_equal_int(0, stack_get_size(stack));
    cut_assert(stack_is_empty(stack));
}</programlisting>
</refsect2>
  <refsect2>
  <title>スタックの実装</title>
  <para>最終的なプログラムは以下の通りである。このスタックは素朴な実装であるため、エラーの通知方法やパフォーマンスのチューニングなどの課題が残っているが、テストが示している通りの基本的な機能は実装されている。</para>
  <para>src/stack.c:</para>
  <programlisting>#include &lt;stdlib.h&gt;
#include &quot;stack.h&quot;

#define TRUE 1
#define FALSE 0

struct _Stack {
    int size;
    int *data;
};

Stack *
stack_new (void)
{
    Stack *stack;

    stack = malloc(sizeof(Stack));
    if (!stack)
        return NULL;

    stack-&gt;size = 0;
    stack-&gt;data = NULL;
    return stack;
}

void
stack_free (Stack *stack)
{
    free(stack-&gt;data);
    free(stack);
}

int
stack_is_empty (Stack *stack)
{
    return stack-&gt;size == 0;
}

int
stack_get_size (Stack *stack)
{
    return stack-&gt;size;
}

static int
stack_realloc (Stack *stack)
{
    int *new_data;

    new_data = realloc(stack-&gt;data, sizeof(*stack-&gt;data) * stack-&gt;size);
    if (stack-&gt;size &gt; 0 &amp;&amp; !new_data) {
        free(stack-&gt;data);
        stack-&gt;data = NULL;
        stack-&gt;size = 0;
        return FALSE;
    }
    stack-&gt;data = new_data;

    return TRUE;
}

void
stack_push (Stack *stack, int value)
{
    stack-&gt;size++;
    if (!stack_realloc(stack))
        return;
    stack-&gt;data[stack-&gt;size - 1] = value;
}

int
stack_pop (Stack *stack)
{
    int value;

    stack-&gt;size--;
    value = stack-&gt;data[stack-&gt;size];
    stack_realloc(stack);
    return value;
}</programlisting>
</refsect2>
  <refsect2>
  <title>Cutterがある場合だけテストをサポート</title>
  <para>ここで作成したtest/test-stack.cはCutterがない場合はビルドに失敗する。つまり、makeが失敗する。開発者であればテストを実行するのが当然なので、Cutterがない場合は失敗しても問題はない。むしろ、問題に気づきやすいのでそうである方がよいと言える。</para>
  <para>しかし、ライブラリとしてスタックを使いたいユーザにはCutterがない場合でもビルドが正常に終了できた方がよい。そのようなユーザは開発者がテストしたリリース版のライブラリを使用していると考えられるからである。</para>
  <para>以下はCutterがない場合でもビルドできるようにする方法である。</para>
  <para>まず、configure.acのAC_CHECK_CUTERの部分を以下のように変更し、Cutterが提供するm4（cutter.m4）がない場合でもautogen.sh（より詳しくいうとaclocal）が動くようにする。（autogen.shを実行するのが開発者のみであれば、この設定は必要ない。その場合はAC_CHECK_CUTTERの定義がないためにaclocalが失敗する。）</para>
  <para>configure.ac:</para>
  <programlisting>...
m4_ifdef([AC_CHECK_CUTTER], [AC_CHECK_CUTTER], [ac_cv_use_cutter=&quot;no&quot;])
...</programlisting>
  <para>ここでac_cv_use_cutterという変数名を使っているのは、AC_CHECK_CUTTERが同じ名前の変数を使っているからである。この変数はCutterの検出が失敗した場合にも&quot;no&quot;になるので、cutter.m4がない場合（autogen.shを実行した環境にCutterがない場合）は常にCutterを検出できなかった状態となる。</para>
  <para>次に、Cutterの検出に失敗したという情報をMakefile.am中で利用するために、AC_CHECK_CUTTERの後ろでMakefile.am中で使える条件を設定する。</para>
  <para>configure.ac:</para>
  <programlisting>...
m4_ifdef([AC_CHECK_CUTTER], [AC_CHECK_CUTTER], [ac_cv_use_cutter=&quot;no&quot;])
AM_CONDITIONAL([WITH_CUTTER], [test &quot;$ac_cv_use_cutter&quot; != &quot;no&quot;])
...</programlisting>
  <para>後は、WITH_CUTTERが真の場合だけtest/test-stack.cをビルドし、test/run-test.shを実行すればよい。</para>
  <para>test/Makefile.am:</para>
  <programlisting>if WITH_CUTTER
TESTS = run-test.sh
TESTS_ENVIRONMENT = NO_MAKE=yes CUTTER=&quot;$(CUTTER)&quot;

noinst_LTLIBRARIES = test_stack.la
endif
...</programlisting>
  <para>以上の変更を加えたconfigure.acとtest/Makefile.amの全体は以下のとおりである。</para>
  <para>configure.ac:</para>
  <programlisting>AC_PREREQ(2.59)

AC_INIT(stack, 0.0.1, you@example.com)
AC_CONFIG_AUX_DIR([config])
AC_CONFIG_HEADER([src/config.h])

AM_INIT_AUTOMAKE($PACKAGE_NAME, $PACKAGE_VERSION)

AC_PROG_LIBTOOL

m4_ifdef([AC_CHECK_CUTTER], [AC_CHECK_CUTTER], [ac_cv_use_cutter=&quot;no&quot;])
AM_CONDITIONAL([WITH_CUTTER], [test &quot;$ac_cv_use_cutter&quot; != &quot;no&quot;])

m4_ifdef([AC_CHECK_COVERAGE], [AC_CHECK_COVERAGE])

AC_CONFIG_FILES([Makefile
                 src/Makefile
                 test/Makefile])

AC_OUTPUT</programlisting>
  <para>test/Makefile.am:</para>
  <programlisting>if WITH_CUTTER
TESTS = run-test.sh
TESTS_ENVIRONMENT = NO_MAKE=yes CUTTER=&quot;$(CUTTER)&quot;

noinst_LTLIBRARIES = test_stack.la
endif

INCLUDES = -I$(top_srcdir)/src
LIBS = $(CUTTER_LIBS) $(top_builddir)/src/libstack.la

AM_CFLAGS = $(CUTTER_CFLAGS)

LDFLAGS = -module -rpath $(libdir) -avoid-version -no-undefined

test_stack_la_SOURCES = test-stack.c

echo-cutter:
	@echo $(CUTTER)</programlisting>
</refsect2>
  <refsect2>
  <title>関連項目</title>
  <itemizedlist>
  <listitem>
  <para>xUnit: Cutterも属するassertXXXといった方法で結果を確認しながらテストを書いていくテストの書き方をサポートするライブラリのこと。テスティングフレームワークとも呼ぶ。様々な言語で実装されている。</para>
  <itemizedlist>
  <listitem>
  <para>SUnit (Smalltalk)</para>
</listitem>
  <listitem>
  <para>JUnit (Java)</para>
</listitem>
  <listitem>
  <para>Test::Unit (Ruby)</para>
</listitem>
  <listitem>
  <para>PyUnit (Pytnon)</para>
</listitem>
  <listitem>
  <para>...</para>
</listitem>
</itemizedlist>
</listitem>
  <listitem>
  <para>エクストリーム・プログラミング（Extreme Programming, XP）:品質の高いプログラムを開発するための方法を集めたプログラミング方法。テストの作成も重要視している。</para>
</listitem>
</itemizedlist>
</refsect2>
</refsect1>
</refentry>
