1 前言
在一个团队进行软件开发的过程中,一般都会制定一个大家共同遵守的编码规范,程序员遵循良好的编码规范写程序有很多好处:
1.有助于程序的维护,降低软件生命周期成本,符合项目管理的规律;
2.使团队中相关人员的流动对项目的影响尽可能小,有利于项目的控制与管理
3.提高程序的可读性,有利于相关设计人员交流,提高软件质量
4.有利于形成可管理,可重用的团队后备资源
5.有利于软件工程相关产品元素的SCM
并且,通过建立代码编写规范,可以提高程序的可靠性、可读性、可修改性、可维护性、一致性,保证程序代码的质量,继续软件开发成果,充分利用资源。提高程序的可继续性,使开发人员之间的工作成果可以共享。这是遵守规范进行代码编写是程序员的基本素质。
如果对编码规范进行人肉检查,无疑是非常费时费力,效果不好。使用自动化的工作来进行检查显然是一个更好的执行方法。为了这个目标,我们就研究了下使用C++ Test进行代码静态检查。
C++Test是一个C/C++自动单元测试工具,自动测试C/C++类、函数或部件,自动生成测试用例、测试驱动程序或桩调用,无需手工编写。C++Test能够自动测试代码构造(白盒测试)、测试代码的功能性(黑盒测试)和维护代码的完整性(回归测试),并提供自动覆盖率测试。同时C++Test还是一个C/C++编程规范自动检查工具,它内置了800多条业界规则,同时可以图形化地定制自己的规则,C++ Test进行静态测试(自动编程规范检查)有以下优点:
1.Pattern Matching(自动代码走查)
2.内置800多条业界有名的C/C++规则(大量规则来自于多家世界著名电信公司的编程规范,以及世界权威的编程规范资料),可实现自动的图形化代码编程规范检查3.使用RuleWizard图形化建立自定义代码规则的功能。客户可以很方便建立并保存一整套自己的代码编程规范(团队经验),从而保证团队经验不会因为人员变动而流失。与此同时避免了编程人员为了保存团队的代码编程规范而浪费大量时间人工编写script Bug Detective(Flow Analysis)
4.Hot Spot技术(“热点”技术)。C++Test通过自带的“热点”包,用回溯的方式自动模拟运行程序中的执行路径,从而精确高速地找到程序存在问题的位置;例如,我们不允许程序中存在“/0”的运算部分,由此凡是程序中“/var”的部分都有可能因为var是0而造成程序逻辑错误(其中“/var”的型态就称为程序中的“热点”),此时,C++Test会自动搜索程序中所有“/var”的运算型态,并回溯运行程序中的执行路径,找到所有致使var变成0的路径,从而精确地找到程序中的错误。
2. 定制规则
C++ Test内置了300多个静态测试项,一般的情况下其实也就够用了。但是如果有特殊的测试要求,动辄几十K的代码,用眼睛看,脑子想,太辛苦,也不“安全”。如果我们用C++Test图形化的RuleWizard,结合公司的编码规范来制定规则,一来方便,高效,二来可以节约人工检查所带来的不必要的成本。Rule的创建有2种方式,一种是手工写规则,让RuleWizard去帮你生成规则(最好别用autocreate,因为C++Test的智能化还很欠缺)。我这里主要重点放在自己写规则方面,下面介绍基本步骤:
2.1 C++Test的基本设置
首先使用C++Test静态规则集检查必须要进行一些简单的设置
新建工程:选择VS6.0或者VS.NET工程文件,或者打开一个已有的工程(是原来已经建立的C++Test工程,后缀名为*.cpf)
配置工程,引入需要包含的头文件文件夹 .
测试选项的筛选,Enable Coding Standards(静态测试),Enable Unit Testing(单元测试),选择静态测试,去掉单元测试的选项。
#p#副标题#e#2.2 默认规则
C++Test内置了800多条规则包括Effective C++,Security等各个方面的C++规则,应该能够满足大多数公司编码规范,启用某某规则只要勾选checkbox即可。另外,如果不会写规则的话,也可以参看默认的规则和C++Test的manual在\Parasoft\C++Test\manuals\rulewizard文件夹下。
2.3 制作规则
首先介绍一下基本规则的制作方法
在出现的对话框中Dictionary选择C,C++,还有一个选项是C++Test一般用于C++文本规则的制作,Rule Creation选择By Node,Auto-Create用于手动书写代码而自动生成规则,但是自动生成的规则往往不尽人意,所以一般不用。Node Selections选择if,因为此次书写的规则跟if有关,完成了点击ok.
以上制作的规则是在逻辑表达式中常量应该放==号的左边,这样可以避免漏写一个“=”号后造成分支判断出现永真或者永假式即:
以上条件判断永远只会走 return (ERROR)这个分支。
#p#副标题#e#2.4 Rulewizard定义的基本组成部分
Node:规则的基本组成部分,通过Node你可以很清楚的知道它的功能。比如表达式a=b、变量的类型测试分为:
parentNode:规则中的主分支Node或者次分支的主Node.内容可以是表达式、变量、函数等
childNode:规则的组成单元。
Commands:用来在Node和Nodes之间建立关联关系。形象一点就是点中一个Node然后右键显示出的快捷菜单的上半部分。如下图:
介绍一下主要的Command
Collector:集合。这个概念更像数学中的集合而不是Java中的广义集合。满足一定条件的数据或者方法或者变量的全体。比如,所有被声明过的变量,如下图
isDecl是用来返回前边的node是不是一个声明。那么body的membervariable返回了所有的包涵指定类型变量得语句(包括 变量付值、声明、判断等)那么这个图被理解为,所有不是声明得语句内使用的变量的集合。
没有被声明的变量,如下图。
这里的意思是所有作为声明语句的变量的集合。
那么通常情况下,A集合和B集合应该是相等的,也就是说所有被声明过的变量(B集合内容)应该都被使用/赋值/判断(A集合的内容)。
Node Set:Node对Node集合的处理。分为Union(合并)、Intersection(交叉)、Difference(差值,左差/右差)、Xor(异或)。如下图:
如果 满足这个规则,那么输出相应得警告信息。
Output:如果满足check的条件,向用户返回一个消息(箭头表示)
#p#副标题#e#2.5 导入已制作的规则
选择已制作规则的文件夹,添加完毕后启用
2.6 运行已制作的规则
当导入制作好的规则后,点击toolbar上的运行button(三角形的按钮)即可对工程进行静态测试。
运行完毕后,出现出错信息,并且出错的行号和出错的文件也被指明出来了
以上是针对规则全局常量命名必须大写:
#p#副标题#e#3 使用Python制作复杂规则
Python作为时下流行的脚本语言之一,以其超强的适应性,超大的功能性,超凡的扩容性,超常的简易性,超强的功能性等著称。它几乎无所不能。内核小巧,但却拥有足够的基本程序块用于设计大部分应用软件。而且在某些情况下该程序语言还可以扩充与 C,C++ 和 Java 语言并用,因此没有它编不了的程序。 Python 解释器还带有极为强大的补充模块库,用于扩充语言能力,进行网络通讯、文本处理和规则表达式匹配。
C++ Test结合其对的Python脚本的支持,所以可扩展性非常强大。至于Python语言的语法本身,本文就不过多阐述,请参看《Python编程金典》等Python相关书籍。
3.1 C++Test添加Python脚本有两种方式
1. 作为输出组件添加(右键单击Node,点Create Output> Method),这种方式主要用于一些规则中需要输出特殊的变量,而用RuleWizard中的Create Output>Display无法实现。例如要输出某个Node的名字,只能通过添加Python脚本来实现:
2. 作为单独组件添加(右键单击Node,点Create Method> Node or Create Method> Boolean),这种方式可以控制整个规则的行为,返回值0代表有效值,返回值1代表出错的情况。
3.2 Python Method
C++Test创建的Python Method可以有0个,1个或者2个参数,第一个参数的类型是NodeProvider,第二个参数的类型是RuleContext ,以下Python Method是符合规范的:
当一个规则被创建时,它会匹配源文件的上下文语法解释Tree,上面说的Node的参数NodeProvider,也就是对语法解释Tree包装出来的一个对象,我们就可以通过它来访问语法解释Tree的任何属性。
而上面所说的RuleContext,你可以存储一些数据在它里面,而其他规则也可以访问这些数据,这意味着几条规则之间有一块共享区域,彼此之间可以通信。例如有些规
范一条规则不能实现,需要多条规则结合起来才能实现,那么就要用到RuleContext.Python 脚本书写的规则还有一下性质:
1. Python Method的名字没有限制
2. Boolean methods的返回值0代表有效,1代表无效
3. Python Method的语法必须符合Python规范,如果创建的Output Method的语法有错误,则它的输出箭头会变为红色。
4. Python Method修改后实时生效
3.3 主要API模块
为了使用Python脚本,C++Test提供了一系列的API,主要分为三大模块:1. NodeProvider,对Python上下文语法解释Tree的封装2. RuleContext,可以对当前规则的上下文进行访问。如:a. 访问上下文中的容器,当前规则上下文中有List A时,任何时候,我们都可以用context.getList ("A"),对集合A进行访问。
b. 生成出错信息,我们可以用context.report ("Violation detected"),生成出错信息,改信息显示在C++Test的Message视窗中。
3. EnforcerContext,跨规则的访问上下文,可以多条规则联合起来实现更强大的功能。以下Python脚本示例跨规则的访问:
主要用到函数executeRule(),该函数返回1代表RuleEnforcerContext检测当指定文件名的规则,返回0代表没有检测到指定文件名的规则。
Python脚本的具体用法请参照Parasoft\C++Test\manuals\rulewizard\python_scripting_api.htm
#p#副标题#e#3.4 示例
以上用python脚本创建函数ConpareVarName
来避免函数内有仅有以大小写区分的变量名,即:不允许出现仅依靠大小写进行区分的标识符。
「理由」
避免混淆,产生对程序理解和维护的困难。
「事例」
不正确的命名
INT32 i32ExtAttr = 0;INT32 i32Extattr = 0;
4 总结
使用自动工具进行编码规范的检查效率很高,而且能保证质量。C++ Test 的静态代码规则制作也比较容易,功能也很强大。唯一的不足可能就是,C++ Test本身的运行速度稍微有点慢,而且比较占内存。(貌似是Java写的,呵呵)。总的来讲还是很不错的,值得推广和。