C言語での単体テスト環境になかなかいいのがないので自作してみる。
目的
- 通常のビルド環境とは別に単体テストを簡単に実行できる環境を作る。("make test"でテスト実行)
- テストコードは関数を追加するだけにしたい。
実装結果
テスト対象
main.c
#include <stdio.h>
static int func1(int parm)
{
if (parm > 0)
return 1;
return 0;
}
int main(int argc, char *argv[])
{
printf("Hello World!\n");
printf("arg test %d\n", func1(argc));
return 0;
}
テストコード
ut_main.c
#include <stdio.h>
#include <dlfcn.h>
#include <stdbool.h>
/* test function prototype */
typedef bool (*Test)(void);
#define main __original_main
#include "main.c"
#undef main
#define UT_ASSERT(f) {if (!(f)) {printf("%s:%u: '%s' is NG\n", __FILE__,__LINE__,#f);return false;}}
bool test001(void)
{
UT_ASSERT(func1(1) == 1);
return true;
}
bool test002(void)
{
UT_ASSERT(func1(0) == 1);
return true;
}
bool test003(void)
{
UT_ASSERT(func1(-1) == 0);
return true;
}
int main(int argc, char *argv[])
{
Test t;
bool ret;
int i;
char func_name[100];
unsigned int count_ok = 0, count = 0;
for (i = 0; i < 100; i++) {
sprintf(func_name, "test%03d", i);
t = (Test) dlsym(RTLD_DEFAULT, func_name);
if (t != NULL) {
count++;
ret = (*t)();
if (ret)
count_ok++;
else
printf("test NG %s\n", func_name);
}
}
printf("result %u/%u \n", count_ok, count);
}
Makefile
all: main
#TARGETS
PROGRAM = main
OBJS = main.o
TEST = ut_main
TEST_OBJS = ut_main.o
# ENVIRONMENT
CC ?= gcc
CFLAGS ?= -Wall -O2
# suffixes
.SUFFIXES: .c .o
# target
$(PROGRAM): $(OBJS)
$(CC) -o $(PROGRAM) $^
# サフィックスルール
.c.o:
$(CC) $(CFLAGS) -c $<
# ファイル削除用ターゲット
.PHONY: clean
clean:
$(RM) $(PROGRAM) $(OBJS) $(TEST) $(TEST_OBJS)
.PHONY: test
test: $(TEST)
./$(TEST)
$(TEST): $(TEST_OBJS)
$(CC) -o $(TEST) $^
実行例
MAC OS X(10.12.4)
% make test
cc -Wall -O2 -c ut_main.c
cc -o ut_main ut_main.o
./ut_main
ut_main.c:24: 'func1(0) == 1' is NG
test NG test002
result 2/3
技術的な話
- dlsymを使ってテスト用関数の名前(文字列)から関数ポインタを取得する。test0xxという名前で適当に追加すれば実行される。dlsymはposix準拠(*1)のはずなので移植性も問題ない(と思いたい)
- テスト対象を#include することで、staticメンバにもアクセスできるようにする。
- Makefile内でtestというターゲット名を作り、コンパイルしたテストコードをそのまま実行(emacs内での実行を想定、NG箇所に飛べるように出力形式を合わせる)
参考
1: https://linuxjm.osdn.jp/html/LDP_man-pages/man3/dlsym.3.html