C言語のライブラリをC++からテストするようにしてみた
C言語で書かれているライブラリがあって、バグがポコポコ出てくるのでなんとかしてテストを仕込みたいと思った。その顛末を書く。
このCのライブラリはそのままC++でも通るコードなのであるが、バージョン管理がVSSであるためファイル名を変えたりすると履歴が吹っ飛ぶ。*1わりと仕方なくCで運用されている。
手動で後片付けとか書きたくないしマクロの嵐とかこわいしgoogletestとか便利なのでC++からこれを呼び出すことは決めていた。
非static関数をテストできるようにした
ライブラリは非static関数とstatic関数からなる。翻訳単位内でしか使わない関数はだいたいstaticにしてある。
外部からアクセスされる非static関数のテストはまあ簡単にできた。C++側ではマングリングを回避するためにextern "C"をつける必要がある。
// hoge.c int fuga( int x ); static int static_hoge( int x ) { return fuga( x ); } int hoge( int x ) { return static_hoge( x ); }
// fuga.c int fuga( int x ) { return x; }
// hoge_test.cpp #include <gtest/gtest.hpp> extern "C" int hoge( int x ); // マングリングを回避するためのextern "C" TEST( hoge, hoge ) { EXPECT_EQ( hoge(1), 1 ); }
libhogeをリンクする必要がある。*2
十分に小さい規模の関数群なら非static関数だけテストしておけば事足りるかもしれない。
しかしあんまりテストされていないstatic関数がたくさんある場合は心許ない。
static関数をテストできるようにした
通常はあんまりやらない方法をテストの時だけ行うということはよくある。
includeは通常はヘッダファイルを貼付けるために使うが、Cファイルを直接貼付けることも普通にできる。
// hoge_test.cpp extern "C" { #include "hoge.c" } #include <gtest/gtest.hpp> TEST( static_hoge, hoge ) { EXPECT_EQ( static_hoge(1), 1 ); }
だいたいコンパイルは通ると思う。必要に応じて調整を行う。*3
さて、コンパイルは通るけれどもリンクが通らない。
libhogeをリンクしてしまうとhogeが多重定義でエラーとなる。一方、リンクしなければfugaが未定義でエラーとなる。
そこでライブラリ側のすべてのCファイルを、対応するC++ファイルに上記の要領でincludeし、libhogeなしでリンクする。
// fuga_test.cpp extern "C" { #include "fuga.c" }
// another_test.cpp extern "C" { #include "another.c" }
...
ファイルが10個を超えると脳みそ腐るのでスクリプトか何かで自動化したほうがよい。*4
そんなこんなでだいぶテストが書きやすくなった気がする。