当前位置:Gxlcms > 数据库问题 > leveldb单元测试之宏定义源码剖析

leveldb单元测试之宏定义源码剖析

时间:2021-07-01 10:21:17 帮助过:26人阅读

main(int argc, char** argv) { return leveldb::test::RunAllTests(); }

RunAllTests() 定义

int RunAllTests() {
  int num = 0;
  if (tests != NULL) {
    for (int i = 0; i < tests->size(); i++) {
      const Test& t = (*tests)[i];
      fprintf(stderr, "==== Test %s.%s\n", t.base, t.name);
      (*t.func)();
      ++num;
    }
  }
  fprintf(stderr, "==== PASSED %d tests\n", num);
  return 0;
}

Test 定义

struct Test {
  const char* base;
  const char* name;
  void (*func)();
};

看其中最简单的一个 TEST 的代码

TEST(DBTest, Empty) {
  ASSERT_TRUE(db_ != NULL);
  ASSERT_EQ("NOT_FOUND", Get("foo"));
}

看 TEST 定义,是一个较为复杂的宏定义

#define TCONCAT(a,b) TCONCAT1(a,b)
#define TCONCAT1(a,b) a##b

#define TEST(base,name)                                                 class TCONCAT(_Test_,name) : public base {                               public:                                                                  void _Run();                                                            static void _RunIt() {                                                    TCONCAT(_Test_,name) t;                                                 t._Run();                                                             }                                                                     };                                                                      bool TCONCAT(_Test_ignored_,name) =                                       ::leveldb::test::RegisterTest(#base, #name, &TCONCAT(_Test_,name)::_RunIt); void TCONCAT(_Test_,name)::_Run()

// Register the specified test.  Typically not used directly, but
// invoked via the macro expansion of TEST.
extern bool RegisterTest(const char* base, const char* name, void (*func)());

在宏定义中 # 表示将后面的参数替换成字符串,如:#abc 为 "abc"。 ## 表示粘连符,即将 a 和 b 连成一个字符串,比如:a = abc, b = 123, a##b 为 abc123。#define 为预处理命令,定义了一个标识符及一个串,在源程序中每次遇到该标识符时,均以定义的串代换它。预编译期间进行宏替换:

class _Test_Empty : public DBTest {
 public:
  void _Run();
  static void _RunIt() {
    _Test_Empty t;
    t._Run();
  }
};
bool _Test_ignored_name = 
  ::leveldb::test::RegisterTest("DBTest", "Empty", &_Test_Empty::_RunIt);  // 为全局变量,在 main() 函数运行前执行
void _Test_Empty::_Run() {
  ASSERT_TRUE(db_ != NULL);
  ASSERT_EQ("NOT_FOUND", Get("foo"));
}

RegisterTest() 定义,这个是注册测试用例的作用,tests 是一个全局 std::vector<Test>指针,存储测试用例。

bool RegisterTest(const char* base, const char* name, void (*func)()) {
  if (tests == NULL) {
    tests = new std::vector<Test>;
  }
  Test t;
  t.base = base;
  t.name = name;
  t.func = func;
  tests->push_back(t);
  return true;
}

以上的代码实现其实非常巧妙,主要目的是用 TEST(xxx, yyy) {} 来定义一段单元测试代码 。思路是这样的,TEST(xxx, yyy)其实定义的是一个宏 ,{} 后面是要测试的代码,其实就是就把他当成一个函数,每声明一个宏就可以定义了一个函数并注册到全局的 tests 中去。这个过程中会定义一个 xxxyyy 的一个专属的测试用例类,而用宏定义实现这个过程主要是为了减少代码的冗余。因为测试用例非常多,如果对每个测试用例都专门编码定义一个类,那代码的冗余是让人无法接受的。其实这种做法也可以借鉴到其他地方来达到减少代码冗余的效果。

 

leveldb单元测试之宏定义源码剖析

标签:测试用例   func   个人   test   extern   处理   c中   call   i++   

人气教程排行