码迷,mamicode.com
首页 > 数据库 > 详细

leveldb 阅读笔记 (2) 简易测试框架

时间:2017-09-27 22:28:47      阅读:181      评论:0      收藏:0      [点我收藏+]

标签:使用   level   main   byte   orm   编写   col   eal   http   

随leveldb一起开源的代码中,还包括一些测试程序, 发现这些测试程序都使用了一些公共的部分代码,很容易编写多个测试用例,自动运行,还能生成测试报告。原来这就是一个简单的测试框架啊,非常实用,实现也很美观,因此记下来。

自动化测试中的必不可少的过程,是需要针对不同的输入条件自动执行测试对象程序,比较输出结果和预期答案,并且提供测试报告, 而使用leveldb中的测试框架实现这些是件很方便的事情。

 

测试过程中的断言:

    每使用一个断言都会产生一个 Tester 的临时对象,断言的时候还允许附加额外的输出信息。

    该对象销毁时会判断断言是否成功,如果断言失败,会自动输出此次断言的文件位置以及失败原因,然后退出程序,结束测试。

实现代码如下:

// An instance of Tester is allocated to hold temporary state during
// the execution of an assertion.
class Tester {
 private:
  bool ok_;             // 断言是否成功
  const char* fname_;      // 文件名
  int line_;         // 行数, 与文件名 一起定位断言失败的位置
  std::stringstream ss_;   // 断言失败时输出的具体错误信息

 public:
  Tester(const char* f, int l)
      : ok_(true), fname_(f), line_(l) {
  }
 // 析构时判断断言结果
~Tester() { if (!ok_) { fprintf(stderr, "\033[31m%s:%d:%s\033[0m\n", fname_, line_, ss_.str().c_str()); exit(1); } } Tester& Is(bool b, const char* msg) { if (!b) { ss_ << " Assertion failure " << msg; ok_ = false; } return *this; } Tester& IsOk(const Status& s) { if (!s.ok()) { ss_ << " " << s.ToString(); ok_ = false; } return *this; } #define BINARY_OP(name,op) \ template <class X, class Y> Tester& name(const X& x, const Y& y) { if (! (x op y)) { ss_ << " failed: " << x << (" " #op " ") << y; ok_ = false; } return *this; } BINARY_OP(IsEq, ==) BINARY_OP(IsNe, !=) BINARY_OP(IsGe, >=) BINARY_OP(IsGt, >) BINARY_OP(IsLe, <=) BINARY_OP(IsLt, <) #undef BINARY_OP // Attach the specified value to the error message if an error has occurred
 // 允许附加额外的信息 template <class V> Tester& operator<<(const V& value) { if (!ok_) { ss_ << " " << value; } return *this; } };
// 一些方便使用的宏,常见断言语句
#define ASSERT_TRUE(c) ::leveldb::test::Tester(__FILE__, __LINE__).Is((c), #c) #define ASSERT_OK(s) ::leveldb::test::Tester(__FILE__, __LINE__).IsOk((s)) #define ASSERT_EQ(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsEq((a),(b)) #define ASSERT_NE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsNe((a),(b)) #define ASSERT_GE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGe((a),(b)) #define ASSERT_GT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGt((a),(b)) #define ASSERT_LE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLe((a),(b)) #define ASSERT_LT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLt((a),(b))

 

测试对象的管理与自动执行:

struct Test {       // 保存单个测试对象信息
  const char* base;
  const char* name;
  void (*func)();   // 指向执行入口函数
};
std::vector<Test>* tests;   // 保存所有的测试对象

// 注册一个测试对象
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; }
// 运行所有测试对象, 也可根据环境变量只运行指定的测试对象
// 如果定义了 LEVELDB_TESTS 这个环境变量,只会运行那些名字中包含了LEVELDB_TESTS的值的测试单例。
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, "====\033[32m PASSED %d tests\033[0m\n", num); return 0; } #define TCONCAT(a,b) TCONCAT1(a,b) // 字符拼接 #define TCONCAT1(a,b) a##b
/*
使用这个宏可以定义、自动注册测试用例
  base 是基类名
TCONCAT(_Test_ignored_,name) 是一个静态变量, 因此它的初始化可以优先于main函数运行,完成注册
  void TCONCAT(_Test_,name)::_Run() 后面接测试用例的实现
*/
#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()                        // 

 

例如对上一篇内存分配器做简单测试:

namespace leveldb {

class ArenaTest { };

// 定义测试1 TEST(ArenaTest, Empty) { Arena arena; }
// 定义测试2 TEST(ArenaTest, Simple) { std::vector
<std::pair<size_t, char*> > allocated; Arena arena; const int N = 100000; size_t bytes = 0; Random rnd(301); for (int i = 0; i < N; i++) { size_t s; if (i % (N / 10) == 0) { s = i; } else { s = rnd.OneIn(4000) ? rnd.Uniform(6000) : (rnd.OneIn(10) ? rnd.Uniform(100) : rnd.Uniform(20)); } if (s == 0) { // Our arena disallows size 0 allocations. s = 1; } char* r; if (rnd.OneIn(10)) { r = arena.AllocateAligned(s); } else { r = arena.Allocate(s); } for (size_t b = 0; b < s; b++) { // Fill the "i"th allocation with a known bit pattern r[b] = i % 256; } bytes += s; allocated.push_back(std::make_pair(s, r)); ASSERT_GE(arena.MemoryUsage(), bytes); if (i > N/10) { ASSERT_LE(arena.MemoryUsage(), bytes * 1.10); // 断言按上面的申请内存方式,浪费的量不超过10% } } for (size_t i = 0; i < allocated.size(); i++) { size_t num_bytes = allocated[i].first; const char* p = allocated[i].second; for (size_t b = 0; b < num_bytes; b++) { // Check the "i"th allocation for the known bit pattern ASSERT_EQ(int(p[b]) & 0xff, i % 256);      // 断言与之前填写的内容一致,既分配的内存没有发生重叠、错乱。 } } } } // namespace leveldb int main(int argc, char** argv) { return leveldb::test::RunAllTests(); // 运行上面两个测试用例 }

测试结果:  通过

技术分享

假如我们要求内存浪费量不超过5%, 将第一句断言稍作修改:  

ASSERT_LE(arena.MemoryUsage(), bytes * 1.05)

得到的测试结果: 失败

技术分享

 

 

leveldb 阅读笔记 (2) 简易测试框架

标签:使用   level   main   byte   orm   编写   col   eal   http   

原文地址:http://www.cnblogs.com/leogn/p/7604246.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!