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


    随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, "33[31m%s:%d:%s33[0m ", 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 ", t.base, t.name); (*t.func)(); ++num; } } fprintf(stderr, "====33[32m PASSED %d tests33[0m ", 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)

    得到的测试结果: 失败

     
  • 相关阅读:
    java前端学习步骤
    安装Sublime Text 3插件的方法(转自Rising的博文)
    LibSVM学习详细说明
    class 2-3 小项目练习
    class 2-2 小项目练习
    class 2-1 小项目练习
    class 1-1 python开发环境配置
    Class
    class 10 文件和异常
    class
  • 原文地址:https://www.cnblogs.com/leogn/p/7604246.html
Copyright © 2020-2023  润新知