关于leveldb源码整理之二

前言

上一节介绍了makefile的问题,可是那个仅仅是编译出一个动态库,函数的流程根本毫无办法了解,这里只能找test,这里我们先找几个慢慢分析。

正文

我们基本上都是挨个看,找一些简单的test,慢慢进行。这里我们选取了dbformat_test这个作为我们的第一个文件,这个相对来说比较简单,可是有些东西还是比较复杂,我们一一道来。
找到main() 函数

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

是不是觉得挺崩溃的,我也是,我们来看看最终的函数调用,

int RunAllTests() {
  const char* matcher = getenv("LEVELDB_TESTS");

  int num = 0;
  if (tests != NULL) {
    for (size_t i = 0; i < tests->size(); i++) {
      const Test& t = (*tests)[i];
      if (matcher != NULL) {
        std::string name = t.base;
        name.push_back('.');
        name.append(t.name);
        if (strstr(name.c_str(), matcher) == NULL) {
          continue;
        }
      }
      fprintf(stderr, "==== Test %s.%s\n", t.base, t.name);
      (*t.func)();
      ++num;
    }
  }
  fprintf(stderr, "==== PASSED %d tests\n", num);
  return 0;
}

更蒙了,这里我们大概看下,大概就是输出tests中的一些东西,那么我们就记住这个变量,看下在哪里初始化,添加值,这里我们就暂时不管了。
继续看我们的dbformat_test.cc 这里代码很短,除了定义几个函数,都不会被调用,立马我们就会感觉是这俩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()

作为java出身的我,看到这些代码,我是崩溃的,不过好在,我还是看懂了,咱们可以吧这些东西转化成预处理后的东西,这里只拿第二个函数来转化:


TEST(FormatTest, InternalKeyShortestSuccessor) {
  ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek),
            ShortSuccessor(IKey("foo", 100, kTypeValue)));
  ASSERT_EQ(IKey("\xff\xff", 100, kTypeValue),
            ShortSuccessor(IKey("\xff\xff", 100, kTypeValue)));
}
//转化后
class _FormatTest_InternalKeyShortestSuccessor : public FormatTest{
public:                                                                
  void _Run();                                                          
  static void _RunIt() {
    _FormatTest_InternalKeyShortestSuccessor t;
    t.run();
    }
};
bool _FormatTest_ignored_InternalKeyShortestSuccessor = ::leveldb::test::RegisterTest("FormatTest", "InternalKeyShortestSuccessor", &_FormatTest_InternalKeyShortestSuccessor::_RunIt);
void _FormatTest_ignored_InternalKeyShortestSuccessor::_Run()
{
  ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek),
            ShortSuccessor(IKey("foo", 100, kTypeValue)));
  ASSERT_EQ(IKey("\xff\xff", 100, kTypeValue),
            ShortSuccessor(IKey("\xff\xff", 100, kTypeValue)));
}

是不是发现就是定义了一个类,然后,调用了一个函数,这个函数做了什么,我们来好好观察,

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;
}

很简单对吧,就是加入tests队列中,和我们开始想的一模一样。我们在回到开始看看到底干了啥:

for (size_t i = 0; i < tests->size(); i++) {
      const Test& t = (*tests)[i];
      if (matcher != NULL) {
        std::string name = t.base;
        name.push_back('.');
        name.append(t.name);
        if (strstr(name.c_str(), matcher) == NULL) {
          continue;
        }
      }

      fprintf(stderr, "==== Test %s.%s\n", t.base, t.name);
      (*t.func)();
      ++num;
    }

开始的东西基本可以漠视了,这里我们看看到输出名字,然后,调用传进来的函数指针,这些函数内容就是在源文件中的东西,如下

ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek),
            ShortSuccessor(IKey("foo", 100, kTypeValue)));
ASSERT_EQ(IKey("\xff\xff", 100, kTypeValue),
            ShortSuccessor(IKey("\xff\xff", 100, kTypeValue)));

搞了这么多事情,就为了这个,有没有好气的感觉,那么我们还是进入这些真实的测试,看看真实的leveldb的内容吧。

::leveldb::test::Tester(__FILE__, __LINE__).IsEq((a),(b))
//其实是调用了一个Tester的一个名字叫IsEq的方法,这个又是宏生成的,继续阅读,最后是调用了:
template <class X, class Y>                           
  Tester& IsEq(const X& x, const Y& y) {                
    if (! (x == y)) {                                   
      ss_ << " failed: " << x << (" " #op " ") << y;    
      ok_ = false;                                      
    }                                                   
    return *this;                                       
  }

我们暂时没看传入的a、b到底是啥,我们可以确定是一个string类型的东西,然后判断他们俩是不是一样,这样的话,不一样,输出failed,
那么我们就看真正的a、b到底是啥。这才是这个测试的重头戏,我们准备好开启真的征程了吗?

后记

本来想一篇文章写完,可是发现内容偏多,还是下一篇继续。下一篇继续看下关于数据库键值的编码部分

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值