2011年05月22日

型の擬似的な上書き ――BOOST_AUTO_TEST_CASE_FIXTUREの仕組みより――

個人的に目から鱗だったのでメモ。

Boost.Testsetup/teardown的な物を書くにはどうすれば良いのかなーとググって、IT戦記のコメント欄経由でBOOST_FIXTURE_TEST_SUITE()の存在を知る。
で、ふと該当部分のコードがどうなってるのか気になったので覗いてみた。

以下引用コードは全てBoost 1.45.0のunit_test_suite.hppから。

#define BOOST_FIXTURE_TEST_SUITE( suite_name, F )					   \
BOOST_AUTO_TEST_SUITE( suite_name )									 \
typedef F BOOST_AUTO_TEST_CASE_FIXTURE;								 \
/**/

53~56行、BOOST_FIXTURE_TEST_SUITE()マクロの定義部分。このマクロの第2引数に指定したクラスがfixture実装クラスとなり、コンストラクタがsetup処理、デストラクタがteardown処理になる。利用法は上に挙げたメモの日々(2009-05-26)を参照。

さて、このコードを見ると、fixtureを使わない場合に使用するBOOST_AUTO_TEST_SUITE()を内部で利用している。BOOST_AUTO_TEST_SUITE()は44~47行にて定義されており、次のようになっている。

#define BOOST_AUTO_TEST_SUITE( suite_name )							 \
namespace suite_name {												  \
BOOST_AUTO_TU_REGISTRAR( suite_name )( BOOST_STRINGIZE( suite_name ) ); \
/**/

で、実際のテストケースを記述するBOOST_AUTO_TEST_CASE()マクロは次のように定義されている。114~116行。

#define BOOST_AUTO_TEST_CASE( test_name )							   \
BOOST_FIXTURE_TEST_CASE( test_name, BOOST_AUTO_TEST_CASE_FIXTURE )
/**/

BOOST_FIXTURE_TEST_SUITE()の定義内にてtypedefされているBOOST_AUTO_TEST_CASE_FIXTUREが此処で利用されている。
……あれ?でもこれってBOOST_AUTO_TEST_SUITE()利用時には定義されていない筈だよな?一体どうなってるんだろう?

"BOOST_AUTO_TEST_CASE_FIXTURE"でコード内を検索すると、以下の記述が見つかる。193~202行。

namespace boost { namespace unit_test { namespace ut_detail {

struct nil_t {};

} // namespace ut_detail
} // unit_test
} // namespace boost

// Intentionally is in global namespace, so that FIXURE_TEST_SUITE can reset it in user code.
typedef ::boost::unit_test::ut_detail::nil_t BOOST_AUTO_TEST_CASE_FIXTURE;

グローバルスコープにて全く同じ名前の型がtypedefされている。

つまり、BOOST_AUTO_TEST_SUITE()使用時は、

typedef ::boost::unit_test::ut_detail::nil_t BOOST_AUTO_TEST_CASE_FIXTURE;

namespace test_suite_name {
// 略
BOOST_FIXTURE_TEST_CASE(test_case_name, BOOST_AUTO_TEST_CASE_FIXTURE); // グローバルの物が使われる
// 略

……のように展開され、グローバルスコープに定義されたBOOST_AUTO_TEST_CASE_FIXTUREが利用される。

が、対してBOOST_FIXTURE_TEST_SUITE()使用時は、

typedef ::boost::unit_test::ut_detail::nil_t BOOST_AUTO_TEST_CASE_FIXTURE;

namespace test_suite_name {
typedef fixture_class_name BOOST_AUTO_TEST_CASE_FIXTURE;
// 略
BOOST_FIXTURE_TEST_CASE(test_case_name, BOOST_AUTO_TEST_CASE_FIXTURE); // 同じ名前空間内の物が使われる
// 略

……のように展開され、名前空間内のtypedefに因ってグローバルスコープのBOOST_AUTO_TEST_CASE_FIXTUREが隠蔽されるようになっているのである。

確かに、このような場合には、より内側の物で外側が隠蔽されると入門書で読んだ記憶があるが、実際に使われているコードを見るのは初めてかも。

目から鱗というか、盲点だったというか。

setup/teardown
それぞれ、テスト開始時に必ず行う処理、テスト終了時に必ず行う処理
Boost 1.45.0
2011年5月22日現在、最新のバージョンは1.46.1だが、ものぐさこいてアップデートしてなかった……
ラベル:C++ boost Boost.Test
posted by 天井冴太 at 04:05| Comment(0) | TrackBack(0) | Hack | 更新情報をチェックする
この記事へのコメント
コメントを書く
コチラをクリックしてください

この記事へのトラックバック