进化的测试

关注软件测试,白盒测试,自动化测试,性能测试

敏捷开发中开展自动化测试的经验

上周在51testing上看到一个问题,题目是在敏捷开发中,自动化测试应该如何开展呢?当时在论坛上回复了一下,现在放到博客,稍微调整一下。

首先,敏捷开发并不是部分同学想象中的那样,没有文档没有需求,开发来了就干,干几个月就丢给客户一个版本让他们用去。我们公司一般6个星期是一个release周期,在这6个星期里面,可以做的事情是非常多的。

  • 需求,需求通常来自于PM,在一个release周期的开始,QA通常没太多事情需要做,比较轻松,这个时候一个比较重要的工作就是跟PM沟通当前release里面的一些feature的情况。在这个时候,QA可以做一些自动化测试的准备。例如在某个release周期里,我知道在接下来的测试当中我需要频繁地比较CSV文件,那么作为QA就应该在项目还不是很紧张的时候,就开始准备自动化测试的脚本,例如刚才说的这个CSV文件比较工作。
  • 开始开发,如果公司是实时TDD开发,那么这个时候QA可以做的事情大概有2个,帮助开发写单元测试用例,并且实施自动化测试(主要是单元测试),另一个是review(虽然不是自动化测试的内容)。如果不是采用TDD开发,那么QA做的事情跟上一个阶段的做的差不多。
  • 正式提交测试,OK,这个时候是我们QA比较忙的时候,有可能出现几个情况,1. 跟我的预想一样,我真的需要一个CSV文件比较工作,并且只需要这一个工具,并且我已经完成了,那么就可以进行测试了。2. 可能有一些新的自动化测试需求跑出来了,例如每天晚上自动比较几万个CSV文件并且把测试结果发给相关的人,这时候作为QA,在考虑资源允许的情况下,应该尽早完成这个工具,而不是每天晚上爬起来看结果。
  • 发布完毕以后,回过头来看工具,是否有值得改进的地方,是否能够改进一下就能够给整个Team使用。

以上算是一个release周期里面的一些微观的工作,宏观上来说需要做点什么事情呢?

现在提到的敏捷开发,都有一个很突出的特点,就是产品快速交付给客户,为了快速交付这个目的,公司里面每个团队都作出了努力,那么具体到QA团队,肯定就是要在保持测试质量得到保证的前提下,尽可能地缩减测试所需要的时间,使得产品按时按质交付。要达到这个目的,总靠一些AD-HOC的工作(例如刚才提到的突然写个CSV比较工具)是不可能达到要求的,那应该如何进行呢?

敏捷开发也是开发,产品不是孙悟空,不会某一天就从石头里面爆出来了。在产品开发的前期(例如0.1, 0.2版本之类),尽可能地想办法搭建一个自动化回归测试的框架,这个框架的特点有:1. 快速完成回归测试; 2.能够快速地添加测试用例并且跑起来;3.能够随着产品的演化而不断改进(不能是那种用1~2个release就要扔的东西);4.维护的成本要低(在一个release周期里面如果自动化测试需求有变化,不应该需要超过1个星期的时间才能改好,当然翻天覆地的变化除外)

综上所述, 我认为在敏捷开发里面的自动化测试是有2条路线,并且这2条路是并行的,缺一不可

  • 至少一个自动化回归测试框架,保证release前能够对产品进行覆盖较为全面的回归测试
  • 工作中*不断地*开发自动化测试工具,提高自己的生产率

以上两点的目的很简单,就是要在保持产品质量处于一个较高水平的情况下,帮助公司尽可能地快速交付新版本的产品。

内部自动化测试交流有感

上周公司组织了一个交流会,主题是关于自动化测试,这个已经在公司引起高层们足够重视的话题,说是交流会,其实我更觉得是个成果展示会,本人代表CORE QA跟大家分享了一下我们组内自动化测试的一些情况,并且在做的过程中的一些经验。我是第一个,下面是VI的自动化测试,VI主要是跟Video播放器结合的比较紧密,最后是UI同事的介绍。我从头到尾都参与,所以说说的我感受吧。

CORE这边测试的特点就是,针对MRM系统的后台进行测试,肩带来说就是模拟各种跟后台打交道的“程序”的工作,进行测试。我们测试有以下特点:

  1. 直接跟后台程序交互,基本没有现成的开源或者商业工具可以支持自动化测试快速开展
  2. 测试验证结果大多数是后台的输入,也就是前台或者是第三方系统的输入,所以验证的方法不能简单地观察输出结果,同时需要知道后台的输出拿到别的系统能否正常工作
  3. 牵涉到数据迁移或者数据重处理的时候,QA需要直接读取生产环境的数据进行校验

由于以上特点,所以我们的自动化测试85%都是自己开发工具来做,常用的脚本语言是Python,经常用到的一些模块包括读取MySQL的MySQLdb;csv模块;re模块;总得原则就是把重复性强,容易引入错误的工作都写成小工具。并且尽可能使用已有的成熟的库,而不是自己重复发明轮子。例如我们的前端页面使用了web.py轻量级框架,JSON库。到目前为止,我自己感觉我们的自动化测试还是做的不错的,主要是以下几点

  1. 简单。说起自动化测试,可能有部分人,或者说是外行的人吧,都觉得这个东西非常酷,人只要倒杯咖啡看着电脑执行测试就好了。但是其实实用有效的自动化测试并不是说看起来有多酷,而是这个东西能把人从重复劳动中解放出来。
  2. 强大。我刚到公司的时候,已经有600多个回归测试跑在自动化框架上,我当时就觉得已经挺不错的,因为这个自动化测试是由大概4~5个不同的人做的,我以前在MySpace的时候SOA大概有300个CASE,不过那都是我一个人做的,相比较而言FreeWheel应该是更好。
  3. 持续改进。虽然我刚到公司的时候自动化测试已经存在并且也算是行之有效,但是任何系统都是有可改进的空间的,我把前端UI改了一下,很高兴可以帮助大家缩短了找问题的时间
  4. 全面。基本上所有的模块都有自己的一堆自动化测试工具。

引用一句我非常喜欢的英语:So far so good。那下面我想做什么事情呢?

  1. 自动化测试其实不是测试,只是重复运行测试用例而已。真正的测试用的是脑,而不是工具,工具只是辅助我们的工作的
  2. 自动化测试是危险的,不要看到所有回归测试都通过了,就高枕无忧
  3. 手动测试才是根本
  4. 希望能给大家灌输一些思想,如果发现自己在重复做一件事情,那么应该停下来,想想有什么办法能够让自己停止重复,尽可能自己解决问题,培养自己的动手能力
  5. 看看有没有一些开源工作能让现在的工作做的更加好

下面说说对VI TEAM自动化测试介绍的一点感觉吧,VI和CORE有点儿相似,就是都是用的自己开发的自动化工具,而没用应用了太多开源工具,我个人觉得这里面原因有2个

  1. VI的测试面向Video播放器的SDK,也是一个后台,所以也没有太多现成的工具
  2. 用户怎么用我们的SDK?就是调用接口,跟CORE面对的问题相似

估计由于经常跟XML打交道,所以VI的自动化测试用到很多XML文件作为配置。由于隔行如隔山,所以没有看懂里面的一些玄机,总的来说就是跟我们CORE有点相似。

我们CORE和VI一样,这些工具如果跳出了这个公司,基本上就不能应用到其他地方,这也是对整个系统来说的底层部分做自动化测试的特点:高度定制化,通用性低,自己开发居多

最后就是UI的介绍,终于等到一个看得懂的啦。

UI那边就是大量使用开源工具,这个也是很有道理的

  1. UI的自动化测试实施难度比后台程序的自动化要大
  2. 现有的UI自动化测试非常丰富

那我们的UI是怎么做的呢?首先UI的同事用了一个持续集成的工具hudson作为一个颗粒度比较粗的测试用例管理工具,hudson作为自动化测试的主心骨,QA们可以在hudson上触发自动化测试的运行,运行完了以后可以看到测试结果,并且,利用了hudson的分布式结构,由多个测试机来执行测试,达到了很好的资源调配。对浏览器的控制方面,用了Selenium,会上没有问UI是否利用了Selenium的多浏览器支持,从演示上来看应该只做的Firefox的。他们的分工很明确,分了专门做功能测试的QA和专门做自动化测试工具开发的SDET,SDET主要是负责写RUBY代码,封装并且暴露了一些通用的方法给QA使用,并且同时使用了Cucumber作为一个DSL,QA是用Cucumber来做自动化测试的一些描述,Cucumber的作用就是对功能测试的QA屏蔽了底层RUBY脚本,对上就是“翻译”功能测试QA的意图,“翻译”成RUBY。说一下我觉得的优点:

  1. 分开了自动化测试工具开发和自动化测试实施
  2. 使用了大量开源工具,提高效率
  3. 而且都是业界常用工具,对以后跳槽帮助不小(嘿嘿)
  4. One click automation (只需要点一下hudson)

一些工具带来的制约

  1. 一次只能运行一批测试,不能重跑单个测试
  2. 个人觉得使用XPATH作为对象的识别并不是一个好的选择

总得来说大家都各有特色,并且都做得挺好,并且都有不少可以提高的空间。多点交流的确能带来不少灵感。

用Python对体积较大的CSV文件进行比较的经验

最近的工作总是跟数据打交道,需要经常比较一些CSV文件,这些CSV文件其实都需要被LOAD到数据库里面,所以也就是一堆堆的数据文件需要比较。暂时没有发现有比较好用的现成的CSV比较工具,自己动手用Python做了一个凑合能用的。思想比较简单,就是把CSV文件的内容读取出来,保存为一个list,然后把2个CSV文件所生成的list进行对比。有个特殊的需求,就是对于CSV文件中一些肯定不一样的列,例如process date这样的字段,是需要跳过的。由于本地生成的CSV文件比较小,刚开始没有注意到如果文件太大的话会占用很多的内存。最开始的版本是:

def readcsv2list(filename, rows):
    fileobj = open(filename, 'rb')
    csvreader = csv.reader(fileobj)
    retlist = []
    for row in csvreader:
        clist = []
        selected_rows = [ic for ic in range(len(row)) if ic not in rows]
        for c in selected_rows:
            clist.append(row[c])
        retlist.append(clist)
 
    fileobj.close()
    return retlist

后来用这个脚本比较生产环境数据的时候就遇到问题了,其中最大的一个数据文件大概是1.5GB,这只是文件大小,把文件转成list以后所占用的内存会翻几倍(这个很容易理解,整数1在文件里面站1个字节,放到list里面就要4个字节了)。一下子把机器的内存用光了。随后找了一下文档,csv.reader是没有一个方法可以指定一次读取若干行数据的。后来就利用file object有一个readline()方法,通过一个参数来控制一次读取多少行的记录,从而达到控制内存使用量的目的。需要的注意的点有:1. 在读完若干行数据以后,需要获取一下当前这个file object的位置,Python提供了.tell()方法来获取这个值;2. 读取文件的时候需要知道上一会读到什么地方了,并且从那里继续往下读,用到了.seek()方法;3. readline()方法在读到文件末尾的时候只会返回一个空字符,所以需要对这个空字符做一点处理。

def readcsv2list(filename, rows, last_position, max_line):
    fileobj = open(filename, 'rb')
    fileobj.seek(last_position)
    datalines = []
    for i in range(max_line):
        line_itme = fileobj.readline()
        if len(line_itme) > 0:
            datalines.append(line_itme)
        else:
            break
 
    csvreader = csv.reader(datalines)
    retlist = []
    for row in csvreader:
        clist = []
        selected_rows = [ic for ic in range(len(row)) if ic not in rows]
        for c in selected_rows:
            clist.append(row[c])
        retlist.append(clist)
 
    current_position = fileobj.tell()
    fileobj.close()
    return retlist, current_position

Python,尤其是低版本(例如我们用的2.4.3),对于在程序里面显式地del一些变量(通常是个大list之类),是不会立刻释放内存的,所以对于处理数据量比较大的case的时候就需要特别注意内存的使用。参考文章:
Python Memory Management
Why doesn’t Python release the memory when I delete a large object?

代码中的注释

我第一个实习的公司,是一个美资公司,在印度设立研发中心可能都已经有超过10年的经验了,那时候一些前辈们告诉我,印度人写的代码可能不如中国人那么聪明,但是他们的注释实在是非常详细,有时候甚至达到了1:1的比例,试想想,100行代码就有100行注释,这是多么的恐怖啊。

经常听到会有人抱怨道,怎么这段代码没有注释啊,这是为什么这样写的啊,如此云云。仿佛没有注释,这个世界就不转了。类似的事情也经常发生在QA们的身边,只不过注释换成了文档。

刚毕业的时候做白盒测试,现在回想起来,那时候的我测试的代码大部分都是不包含注释的,不过我测试起来并没有太大的困难,总结一下,应该有以下几点原因:

  1. 有意义的函数名、变量名。函数的命名让人一看就大概知道在做什么,例如PostBlog就是发布一篇博客,如果遇到一个叫SaveProfile的方法但做的却是加好友,那我想再多的注释我也会头晕
  2. 代码不会说谎。根据经验,如果一段代码理解起来很费劲,那么通常里面都会隐藏着问题。代码就是最好的注释,一些过时的注释,设置会对阅读代码的人产生误导
  3. 充分的沟通。虽然游走于几个项目组,跟不同的开发人员打交道,但是每当遇到问题的时候总会主动跟相关的人沟通,一个活生生的人坐在那里不问,却迷信什么文档,这真是本末倒置

注释,能不写就别写,实在要写,写WHY而不是WHAT。

联系一下最近在Team内写的一个新的回归测试工具,里面基本没有注释,希望过几个月以后,自己还能够看看代码就知道当时那段代码为什么这样写。

DSL与自动化测试 – 用Python实现简单的DSL

自动化测试,一个在测试领域中被广为熟知,也是被谈论最多的概念之一。DSL (Domain Specific Language),一种高度抽象,用于某个特定领域下编程语言。软件测试在大多数情况下都是对某个特定行业的软件系统进行测试,所以这两者应该可以很好的结合起来,事实上也是这样的,QTP里面的keyword view,其实就是DSL的一个实现。DSL一般可以分为两个大的类型,分别是External DSL 和 Internal DSL (引用自Martin Fowler)。External DSL 一般来说是跟其实现语言不一样的 DSL,常见的External DSL 有:SQL和XML配置文件;而Internal DSL 一般来说就是该DSL使用某个现成的编程语言(就是所谓的host language),然后对host language进行一些改造而成。

我们在测试中会遇到很多问题,其中一些问题,几乎是所有公司所有团队都会遇到的,例如测试覆盖率不够,测试的时间不够等等。面对这些问题,自动化测试自然而然地成为解决这些问题的首选方法。但是自动化测试真的就是银弹麽?不见得!以前曾经在ASP.NET QA 的博客中给他们留言,请教过关于自动化测试的事情,我记得其中有一个回复是说,在某个release中过度地使用自动化测试,一切东西都想实现自动化测试,而忽略了产品本身的功能、特性的关注,结果就是超高的自动化测试覆盖率,但是很差的产品质量。大家都去实现自动化测试了,谁来做功能点的覆盖呢?某些领域的专家(SME),他们可能对测试技术是一无所知的,要把这些领域专家和测试实施结合起来,DSL就是一个比较好的桥梁。

我在工作中遇到的问题是,我需要测试一个类似UV(独立用户访问数)统计的系统,统计UV的方法其实就是根据_uid cookie的值来判断这个用户在某段时间内访问过我们的系统多少次,访问了哪些站点,进行了什么样的行为。其中有2个地方比较麻烦,第一就是在测试过程中要不断地拷贝cookie,这样拷来拷去两三次以后很容易就混乱,出错;第二就是需要记录访问哪些站点,这些站点都只是ID,也是需要不断地修改请求,测试时间长了也是很容易出错。所以我就打算在原来的测试工具基础上,实现一个简单的Internal DSL。先看成品:

@tc
def uniq_inventory_case01():
    test= testTool()
    test.user('a').view('asset55100002').anetwork('55100').onsite('site55100503').snetwork('55100').dnetwork('55100').times(1).go()
    test.user('b').view('asset55100002').anetwork('55100').onsite('site55100503').snetwork('55100').dnetwork('55100').times(2).go()
    test.user('b').view('asset55100002').anetwork('55100').onsite('site55100504_noad').snetwork('55100').dnetwork('55100').times(4).go()

实例化一个testTool对象,然后就是指定哪个用户:user(‘a’)或者user(‘b’),看的视频的ID:view(‘asset55100002′),这个视频属于哪个CRO呢?anetwork(’55100′);放在哪个网站呢?onsite(‘site55100503′);网站是谁的呢?snetwork(’55100′);谁是分发者呢?dnetwork(’55100′);看了多少次呢?times(4);最后一个有点儿丑陋的go()。

像这样子一句话里面N个方法连着用,就叫Method Chaining,Method Chaining通常可以让代码变得更加人性化,读起来更加容易。但是使用Method Chaining通常会遇到一个问题,就是很难判断就是到了哪个方法才是终结呢?是不是有些方法的调用是可选的,有些是必选的呢?其中一个解决方法就是我用到的,放一个.go()方法在最后,作为终结方法。要实现Method Chaining,其实只需要顶一个类,对于需要做连接的方法,最后都返回这个类的实例。例如:

def view(self, assetid):
    if assetid:     self.asset_id = assetid
    return self
 
def anetwork(self, networkid):
    if networkid:   self.a_network_id = networkid
    return self
 
def snetwork(self, networkid):
    if networkid:   self.s_network_id = networkid
    return self
 
def dnetwork(self, networkid):
    if networkid:   self.d_network_id = networkid
    return self
 
def onsite(self, sectionid):
    if sectionid:   self.site_section_id = sectionid
    return self
 
def times(self, times):
    if times>0:       self.request_times = times
    return self

最后一个终结方法go(),就做真正的处理

def go(self):
    if self.asset_id and self.site_section_id and self.times and self.a_network_id and self.s_network_id:
        self.prepareRequest()
        for i in range(self.request_times):
            self.sendRequest()
 
        self.cleanup()
    else:
        info = 'Required information missing, abort running.'
        logging.debug(info)
        print info

如果是实现一个External DSL 的话,的确难度不小;但是Internal DSL其实并不是很高深,也不是很难实现,在它的帮助下,可以把工作完成的更好,对自己以后维护测试用例也带来了不少方便。

TestLink不能连接BugZilla的解决办法

TestLink是一个基于Web的PHP开源测试管理系统,虽然用起来跟QC那些商业软件比起来不是那么爽,但是由于是开源、免费,所以越来越多的公司在用TestLink。BugZilla作为老牌的bug管理工具,同样有着很大的用户群。

TestLink有一个BugTracking的接口模块,可以使得TestLink可以与其他BugTracking系统集成。在集成的过程中发现TestLink提示错误:Bug ID does not exist on BTS(中文的话是’bug的ID在BTS中不存在!’),找到TestLink的代码文件bugAdd.php,找到下面这段代码块

if($args->bug_id != "")
{
	$msg = lang_get("error_wrong_BugID_format");
	if ($g_bugInterface->checkBugID($args->bug_id))
	{
		$msg = lang_get("error_bug_does_not_exist_on_bts");
		// 问题在这里
		if ($g_bugInterface->checkBugID_existence($args->bug_id))
		{ 	  
			if (write_execution_bug($db,$args->exec_id, $args->bug_id))
			{
				$msg = lang_get("bug_added");
				logAuditEvent(TLS("audit_executionbug_added",$args->bug_id),"CREATE",$args->exec_id,"executions");
			}
		}
	}
}

问题出在$g_bugInterface->checkBugID_existence($args->bug_id)这个方法中。在、TestLink的int_bugzilla.php文件中,并没有overload这个checkBugID_existence的方法,所以这个方法就会按照int_bugtracking.php中的默认实现,返回false。TestLink就会出现error_bug_does_not_exist_on_bts这个ERROR

解决这个问题很简单,就是在int_bugzilla.php中自己实现checkBugID_existence方法,简单的实现如下:

function checkBugID_existence($id)
{
	$status_ok = 0;
        //关键是下面这个Query bug id的语句,大家自己看看数据库是哪个表,根据实际情况自己修改	
	$query = "SELECT bug_id FROM bugs WHERE bug_id='" . $id ."'";
	$result = $this->dbConnection->exec_query($query);
	if ($result && ($this->dbConnection->num_rows($result) == 1))
	{
		$status_ok = 1;    
	}
	return $status_ok;
}

OK。在int_bugzilla.php文件中添加了 checkBugID_existence() 方法后,就可以从TestLink中直接把BugZilla里面的BUG ID和某个测试关联起来了。

年终总结,新年展望

2009年,毕业后的第二年,上半年在MySpace,下半年在FreeWheel。测试技术上,有1年多的白盒测试经验,不过由于工作调动,现在已经没有做了,不过还好,还没有离开代码。在新的单位里面主要跟以下东西打交道,数据仓库,ETL工具,报表脚本,自动化回归测试框架,在线广告的业务逻辑。

在FreeWheel已经4个月了,工作开始上手,新的公司同事的素质都非常高,就我们Core来说吧,清华北大的占了一大半,第一次跟那么多非常聪明的人一起工作,有点兴奋,压力。公司发展的势头不错,我从进公司到现在,短短四个月,ad servering的流量应该翻了5倍吧。明年应该会增长的更加快,cool!

明年工作上主要还是集中在数据库相关技术,自动化回归测试框架,Python应该是主要的编程语言。

keep learning…

自动化测试中的sleep

最近在修改公司现有的一个自动化测试框架,里面用了很多time.sleep()方法,看着不是很爽,为什么我觉得sleep方法在自动化测试中不应该过多的使用呢,我甚至觉得应该尽可能避免sleep方法的使用,sleep可以作为增加自动化测试稳定性的手段,但是不能依赖sleep来让自动化系统稳定。

举个例子,如果一个UI的自动化测试,需要等待某个页面load完成以后才进行操作,那么需要对那个页面是否已经Load完成进行判断,而不应该sleep(x),x是一个magic number,有时候1、2秒就足以,有时候它却不知道有多大,因为已经超时了!那如果我们在check页面状态之前做一个短时间的sleep,那么在某些场合下可以增加这个自动化测试的稳定性,但是最终整个自动化测试的脚本是不会依赖于这个sleep的语句来达到稳定的。

在做自动化测试的时候,最常见的两种判断就是1. 某程序已经成功启动,某页面已经加载完毕。 2. 某程序已经正常关闭,某服务已经顺利停止。

回到实际的工作,我要判断被测的程序是否已经正常启动,可以用系统提供的一些工具,或者调用一些接口,例如SNMP命令,或者是调用一下Lua脚本等,如果他们都返回我们期望的数据,那么可以认为程序已经成功启动了。反之,如果前面的这些命令出错了,那么我也可以认为程序已经是关闭了的。

要实现自动化测试,就必须要让测试代码每时每刻都掌握着被测系统的状态,sleep方法会让自动化测试脚本的行为变得诡异

谁来保证测试工作的质量

在一个公司或者是某个组织里面,测试人员扮演的角色通常都会被认为是软件质量的保证者,把关者,仿佛经过测试的产品都是没有任何缺陷的。但事实大家都知道,即使经过多么“完美”测试的产品,总免不了在发布以后还会发现或多或少的问题。

以前在MySpace做测试的时候,主要是写代码来测试一些接口、模块等。这样就会出现一个问题,我用一段程序A去测试层序B,那么测试代码也是程序,如何保证程序A的正确性呢?OK,我们可以写一个程序C去测试程序A,由此来保障程序A的正确性,但是程序C的正确性又由谁来保证呢?who watches the watchers?当时只有我一个人负责白盒测试,最多也就是让开发帮忙看看。其实对于一段程序,只要写的足够简单,那么就可以认为这段简单的程序的正确性是能得到保证的。所以我一直都给自己强调,单元测试的代码不要写的复杂,尽可能不用判断,让测试代码顺序执行下去。

但是对于功能测试来说,怎么样才能尽可能地保证测试的方法,测试的数据,测试的覆盖率是能达到某项标准的呢?在这次MRM 2.9 Release的测试过程中,我们引入了peer review的做法,一个功能点一般会以ticket的形式存在,每个人拿到ticket的时候首先自己设计测试用例,包括测试数据的准备,用什么样的方法等等。然后找另外一个同事来review自己的用例。这样做的好处有:

  1. 强迫自己有一个较为系统的测试用例设计,因为这个是需要给同事看,并且让别人看懂的
  2. 同事之间的knowledge share在不知不觉中就达到
  3. 两个人的review总是比一大群人坐到会议室里面要有效,帮助提高测试覆盖率,尽可能避免测试盲点
  4. 互相监督

Python多行注释技巧

Python语言本身是没有注释多行的支持的,如果需要注册多行,可以用一个取巧的方法,就是把需要注释的代码块用三个括号括起来,赋值为一个永远都不会使用的字符串变量,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
__devilcomment = '''
if bCmpLog == "True":
        self.appendAdsLogToCmpLog("")
 
if bCmpBinaryLog == "True":
        self.appendBinaryAdsLogToCmpLog(res)
 
if bCompareResp == "True":
    self.appendResponseToCmpLog(response_strs)
 
print "move new and debug logs"
self.tools.move (src_db,dst_db)
'''