请选择 进入手机版 | 继续访问电脑版

Redis中国用户组(CRUG)论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

搜索
热搜: 活动 交友 discuz
查看: 491|回复: 0

Redis源码分析(十)--- testhelp.h小型测试框架

[复制链接]
  • TA的每日心情
    开心
    2017-3-20 10:39
  • 签到天数: 90 天

    [LV.6]常住居民II

    358

    主题

    462

    帖子

    3652

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    3652

    最佳新人活跃会员宣传达人突出贡献优秀版主荣誉管理论坛元老

    发表于 2016-4-1 09:54:30 | 显示全部楼层 |阅读模式
    分析了一段时间的struct结构体的redis代码,越到最后越发现其实很多代码都是大同小异的。在struct包中还有1,2个文件还没分析,是关于set集合的一些东西,就放在下次分析好了,在选择下个分析的对象时,我考虑了一下,最后决定先把简单的test包下的东西看看一下,毕竟结构体这块有点复杂,所以这次分析个简单点的。test包下的文件并不多,代码量也很少,总共5个文件:
    1.memtest.c 内存检测
    2.redis_benchmark.c 用于redis性能测试的实现。
    3.redis_check_aof.c 用于更新日志检查的实现。
    4.redis_check_dump.c 用于本地数据库检查的实现。
    5.testhelp.c 一个C风格的小型测试框架。

    今天讨论3和5,先看看5,testhelp.c被解释为一个C风格的小型测试框架,看见这标题,一定想到这是一个很大的文件吧,都被称为测试框架了,其实不然,我贴出整个代码:
    1. /* 预处理 */  
    2. #ifndef __TESTHELP_H  
    3. #define __TESTHELP_H  
    4.   
    5. /* 开始时失败总数为0 */  
    6. int __failed_tests = 0;  
    7. /* 开始时测试总的例子数为0 */  
    8. int __test_num = 0;  
    9.   
    10. /* 宏定义测试方法,输入参数,输入描述语,判断的式子作为参数 */  
    11. /* 有完全体现了函数式编程的思想 */  
    12. #define test_cond(descr,_c) do { \  
    13.     __test_num++; printf("%d - %s: ", __test_num, descr); \  
    14.     //判断式子在此处调用  
    15.     if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \  
    16. } while(0);  
    17.   
    18. /* 测试报告在最结尾输出 */  
    19. #define test_report() do { \  
    20.     printf("%d tests, %d passed, %d failed\n", __test_num, \  
    21.                     __test_num-__failed_tests, __failed_tests); \  
    22.     if (__failed_tests) { \  
    23.         printf("=== WARNING === We have failed tests here...\n"); \  
    24.         exit(1); \  
    25.     } \  
    26. } while(0);  
    27.   
    28. #endif  
    复制代码

    调用形式也是极其简单:
    1. * test_cond("Check if 1 == 1", 1==1)  
    2. * test_cond("Check if 5 > 10", 5 > 10)  
    3. * test_report()  
    复制代码

    其实就是宏定义了一个判别表达式真假的方法,又用到了函数式编程的思想,把_c整个表达式的值传入了另一个函数中,就这么简单。好,下一个文件redis_check_aof.c用于日志检测的,必然和文件的操作相关的,同样列出里面的API:
    1. /* 方法API */  
    2. int consumeNewline(char *buf) /* 消除buf前面的换行符,即比较buf字符串中的前2个字符 */  
    3. int readLong(FILE *fp, char prefix, long *target) /* 从文件中读取long类型值 */  
    4. int readBytes(FILE *fp, char *target, long length) /* 从文件中读取字节 */  
    5. int readString(FILE *fp, char** target) /* 文件中读取字符串 */  
    6. int readArgc(FILE *fp, long *target) /* 文件中读取参数,首字符以“*”开头 */  
    7. off_t process(FILE *fp) /* 返回fp文件的偏移量 */  
    复制代码

    read的很多操作其实都是非常类似的,我就举出其中的一个read方法当做例子,就是纯粹文件的简单操作,熟悉C语言的同学一定很熟悉:
    1. /* 从文件中读取long类型值 */  
    2. int readLong(FILE *fp, char prefix, long *target) {  
    3.     char buf[128], *eptr;  
    4.     //定位到文件的读取位置  
    5.     epos = ftello(fp);  
    6.     //将文件中的内容读取到buf中  
    7.     if (fgets(buf,sizeof(buf),fp) == NULL) {  
    8.         //如果为空直接返回  
    9.         return 0;  
    10.     }  
    11.       
    12.     //如果读取到的首字符不等于预期值,则提示报错  
    13.     if (buf[0] != prefix) {  
    14.         ERROR("Expected prefix '%c', got: '%c'",buf[0],prefix);  
    15.         return 0;  
    16.     }  
    17.       
    18.     //将字符串值转成long类型值  
    19.     *target = strtol(buf+1,&eptr,10);  
    20.       
    21.     return consumeNewline(eptr);  
    22. }  
    复制代码

    里面有一个比较特别的方法,consumeNewline()消除换行符的方法:
    1. /* 消除buf前面的换行符,即比较buf字符串中的前2个字符 */  
    2. int consumeNewline(char *buf) {  
    3.     if (strncmp(buf,"\r\n",2) != 0) {  
    4.         //如果不是等于"\r\n",则提示出错  
    5.         ERROR("Expected \\r\\n, got: %02x%02x",buf[0],buf[1]);  
    6.         return 0;  
    7.     }  
    8.     return 1;  
    9. }  
    复制代码

    里面比较复杂的方法是off_t process(FILE *fp)获取文件偏移量的操作,这个方法是用来后面截断文件的操作,截断后面的空文件的部分:
    1. off_t pos = process(fp);  
    2.   
    3. //截断文件的操作,从问价头部到后面的偏移量,没有用的空间截去  
    4.             if (ftruncate(fileno(fp), pos) == -1) {  
    5.                 printf("Failed to truncate AOF\n");  
    6.                 exit(1);  
    7.             } else {  
    8.                 printf("Successfully truncated AOF\n");  
    9.             }  
    复制代码

    以上就是我所分析的内容了,非常简单,比起struct比,简单太多了,可以细细体会其中方法涉及的技巧,比如我们会考虑日志文件分析时会想到用consumeNewline()换行操作的设计吗


    转自:http://blog.csdn.net/androidlushangderen/article/details/40142545

    上一篇:Redis源码分析(九)--- t_list,t_string的分析
    下一篇:Redis源码分析(十一)--- memtest内存检测
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    阿里云
    阿里云

    Archiver|手机版|小黑屋|Redis中国用户组 ( 京ICP备15003959号

    GMT+8, 2017-4-27 21:05 , Processed in 0.111592 second(s), 32 queries .

    Powered by Discuz! X3.2

    © 2001-2013 Comsenz Inc.

    快速回复 返回顶部 返回列表