PythonでC言語の単体テストを書く

  • 21
    Like
  • 0
    Comment

C言語のテストをPythonのunittestを使って単体テストを書く方法についてです。動機は、コンパイル回数をできるだけ減らすことです。この方法を取ると、テストを書くときのコンパイルは不要になります(速度的なことや、戻り値が複雑な場合どうなるかは分かりませんが)。

まずC言語のコードは次のようなものとします。

//main.c
#include<stdio.h>
int add(int a, int b);
struct Pair {
  int l;
  int r;
};
int
main(void) {
  printf("%d + %d = %d\n", 3, 4, add(3, 4));
  return (0);
}
int add(int a, int b) {
  return a + b;
}
struct Pair  make_pair(int l, int r) {
  struct Pair p;
  p.l = l;
  p.r = r;
  return p;
}

次にgdbから呼び出すpythonコードは次のようにします。

#debug.py
import unittest

class Test(unittest.TestCase):
  def assertEqual_(self, a, expr):
    self.assertEqual(a, gdb.parse_and_eval(expr))
  def test_aaaa(self):
    self.assertEqual_(42, 'add(30, 12)')
  def test_bbb(self):
    self.assertEqual_(0, 'add(0, 0)')
  def test_ccc(self):
    p = gdb.parse_and_eval("make_pair(3, 4)")
    self.assertEqual("{l = 3, r = 4}", str(p))


gdb.execute('break main')
gdb.execute('run')
unittest.main()

C言語のコードをこのpythonコードでテストするには、次のようにします。

$ gcc -g3 main.c
$ gdb -q -x ./debug.py a.out

標準出力は次のようになります。これでPythonでC言語で書いた関数を再コンパイル無しでテストを追加、修正することが可能になります。

Reading symbols from a.out...done.
Breakpoint 1 at 0x804841c: file main.c, line 9.

Breakpoint 1, main () at main.c:9
9     printf("%d + %d = %d\n", 3, 4, add(3, 4));
...
----------------------------------------------------------------------
Ran 3 tests in 0.003s

OK

冒頭にも書いたように、関数の戻り値が複雑な場合、特に構造体の取り扱いをもっと良くできないかと思います。C言語の方で、オブジェクトの内容を文字列に変換するような関数を用意するのが現実的かもしれません。

バージョンについて

  • Ubuntu 16.04 i386
  • gcc 5.4.0
  • GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1