您的位置:首页 > 其它

关于leveldb源码整理之二

2017-07-22 01:56 190 查看

前言

上一节介绍了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)) {
bc72

ss_ << " failed: " << x << (" " #op " ") << y;
ok_ = false;
}
return *this;
}


我们暂时没看传入的a、b到底是啥,我们可以确定是一个string类型的东西,然后判断他们俩是不是一样,这样的话,不一样,输出failed,

那么我们就看真正的a、b到底是啥。这才是这个测试的重头戏,我们准备好开启真的征程了吗?

后记

本来想一篇文章写完,可是发现内容偏多,还是下一篇继续。下一篇继续看下关于数据库键值的编码部分
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  源码
相关文章推荐