LoginSignup
6
6

More than 5 years have passed since last update.

単体テストのカバレッジを取る

Last updated at Posted at 2017-05-06

単体テスト環境を作ってみる
http://qiita.com/tksmiura/items/f783cab446a4a1075bba

の続き

  • とりあえずgcovを使う
  • 'make gcov'と打つだけでカバレッジが取れるようにしたい。

実装結果


all: main

#TARGETS
PROGRAM = main
SRCS = main.c
TEST = ut_main
TEST_SRCS = ut_main.c
GCOV_TEST = ut_main_gcov

# ENVIRONMENT
CC ?= gcc
CFLAGS ?= -Wall -O2

# suffixes
.SUFFIXES: .c .o .go

# target
$(PROGRAM): $(SRCS:.c=.o)
    $(CC) -o $(PROGRAM) $^

# サフィックスルール
.c.o:
    $(CC) $(CFLAGS) -c $<

.c.go:
    $(CC) $(CFLAGS) --coverage -o $@ -c $<

# ファイル削除用ターゲット
.PHONY: clean
clean:
    $(RM) $(PROGRAM) $(SRCS:.c=.o) $(TEST) $(TEST_SRCS:.c=.o) $(GCOV_TEST) *.gcda *.gcno *.gcov *.go

.PHONY: test
test: $(TEST)
    ./$(TEST)

$(TEST): $(TEST_SRCS:.c=.o)
    $(CC) -o $(TEST) $^

.PHONY: gcov
gcov: $(GCOV_TEST)
    ./$(GCOV_TEST)
    gcov -b $(TEST_SRCS:.c=.gcda)

$(GCOV_TEST): $(TEST_SRCS:.c=.go)
    $(CC) $(CFLAGS) --coverage -o $(GCOV_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);
}

実行結果

MAC OS X(10.12.4)

% make gcov
cc -Wall -O2 --coverage -o ut_main.go -c ut_main.c
cc -Wall -O2 --coverage -o ut_main_gcov ut_main.go
./ut_main_gcov
result 3/3 
gcov -b ut_main.gcda
File './main.c'
Lines executed:57.14% of 7
Branches executed:100.00% of 2
Taken at least once:100.00% of 2
No calls
./main.c:creating 'main.c.gcov'

File 'ut_main.c'
Lines executed:95.24% of 21
Branches executed:100.00% of 12
Taken at least once:66.67% of 12
No calls
ut_main.c:creating 'ut_main.c.gcov'

技術的な話

  • makeで別オプションのビルドをするために、実行ファイル名を変えるのと合わせ、独自の拡張子(*.go)を導入、ルールを追加(あっ、go言語と被る?)、リンカはどうせマジックナンバーで見ているので勝手にリンクしてくれるだろう(保証なし)
  • make時にgcovを掛けるところまで実行
  • テストコードのカバレッジが出てしまうのもイマイチ
6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6