案例 1
【案例描述】
某交换类产品BAM后台管理系统使用了注册表存储后台系统数据自动备份时间;在对数据自动备份进行系统测试时考虑到注册表中数值的任意性,很有可能被测系统没有对注册表中存储的值作相应的合法性检测,从而有可能对系统产生不良影响。
【处理过程】
通过审查源程序及实际验证,发现果然存在问题。BAM有关数据自动备份程序程序在得到该项值后只做了简单处理,没有对时间进行合法性验证。若自动备份操作发生在前后台通讯的高峰期或者是在设置数据需要对数据库进行操作时,对系统可能会产生重大影响。
【结 论】
系统使用注册表中的数据同样要进行各种情况下的测试,包括合法的和不合法的数据设置。
【思考与启示】
系统的数据输入有多种渠道,不仅包括人机命令、系统INI文件,还包括注册表等其它途径;在测试时对各种情况都要进行全方位的测试,从而保证系统的可靠性。
案例 2
【案例描述】
设计规定07的TSS板是不支持数字用户内,外线强测的,在对数字用户内,外线测试正常后,有意强测一正在通话的数字用户,却发现返回报告:
用户外线测试
------------
测试时间 = 1999-06-03 16:48:06
号首集 = 0
用户号码 = 6540136
BAM测试状态 = 正常
主机测试状态 = 正常
AVAG = 0.08
AVBG = 0.29
AVAB = -0.21
DVAG = 0.62
DVBG = 0.62
DVAB = 0.00
RAG = >10M
RBG = >10M
RAB = >10M
RL = >10M
CAG = 0.001
CBG = 0.001
CAB = 0.001
结论 = 断线
结论断线肯定是错误的,测试期间查询TSS状态,显示TSS空闲,可见主机返回的报告是错误的。
【处理过程】
修改命令ADD RTSTI对应的存储过程,问题解决。
【结 论】
用户外线测试主机没有判用户是否为数字用户。
【思考与启示】
测试一项功能,既要测试系统提供的功能要满足要求,系统没有提供的功能看是否误执行了,导致错误的结论。
-
-
-
xilinxue 发表于 2008/11/7 13:17:28
一般开发人员认为函数的参数很简单,但在实际设计和调用函数时,很容易犯一些参数方面的错误。下面就一些例子进行一些分析,希望能引起大家的重视。
1、函数设计中,使用函数内局部变量保存下次函数重新调用时需要的保留值。
void SlaverDownLoadProc( WORD wMsgLength , void *pTempMsgAddr )
{
void *pTempTargetAddr ;SSLAVERLOADMSG *pSTempSlaverLoadMsg = ( SSLAVERLOADMSG * )pTempMsgAddr;
if ( COMMON_BOARD_LOAD_PROGRAM == pSTempSlaverLoadMsg->m_ucCmd )
pTempTargetAddr = ( void * )SDRAM_LOAD_PROG_START_ADDR ;
#ifndef MMX /* MMX板的数据不用加载,用备份方式*/
else if ( COMMON_BOARD_LOAD_FPGA == pSTempSlaverLoadMsg->m_ucCmd )
pTempTargetAddr = ( void * )SDRAM_FPGA_START_ADDR ;
...
case BEGIN_LOAD :
if ( ( LOAD_MIDDLE_FRAME == pSTempSlaverLoadMsg->m_ucLoadCmd )
&&( ( dwRecFrameNum % 0xFF ) == pSTempSlaverLoadMsg->m_ucOrderNo )
)
{
/* 序号和帧的命令字都是合法的,保存BUFFER */
memcpy( ( BYTE * )pTempTargetAddr , ( BYTE * )pSTempSlaverLoadMsg + 5 , wMsgLength - 5 ) ;
/* 5 = m_ucBoardType + m_ucCmd + m_ucSerialNo + m_ucLoadCmd + m_ucOrderNo */
dwRecFrameNum ++ ;
...
这里每收到一帧都要调用此函数,而每次进入时,函数将pTempTargetAddr赋值为两个固定值中的一个,导致收到的新帧将上一次的帧数据覆盖。显然,函数将本应作为全局变量的参数pTempTargetAddr错误地定义为局部变量。
2、当函数的输入参数较多,并且互相之间有联系时,是较易出错的地方。
在测试串口任务时,我们发现一个错误:当加入新用户时,当用户名输入到四十一个字符时,程序死掉。经过调试跟踪,发现是在input函数中调用GetString,但是在对第二个参数赋值时,没有搞清楚参数之间的关系。第一个参数(传入的指针)指向一长度为40字符的字符串,而在对第二个参数时,错误地将字符串长度设置为41。导致串口接收第41个字符时,程序越界操作,导致死机。下面是函数GetString的声明:
signed long GetString
(
char * szString, /* OUT: 字符串指针 */
WORD wSize /* IN: 指定的字符串长度 */
)
1
当然有关函数参数的错误不止这些,例如未在模块接口函数内部检验传入参数的合法性、指针参数的有效性、参数未赋值就使用、参数类型不匹配或考虑不周导致上溢下溢等。这些都是编程者容易出错的地方,亦是我们大家在走读代码时,需要特别注意的地方。
引用 xilinxue 2008/11/7 13:17:28 发表于2楼的内容
-
-
-
-
xilinxue 发表于 2008/11/7 13:17:44
在交换机的V5协议测试中,有一个相当常用又相当简单的测试项目,“交换机对某个V5接口发起主备倒换命令”。这在所有的V5测试中都会很顺利地通过的项目,在以前这个项目也测试过许多次,也从来没有遇到过异常情况。3楼 回复本楼
可是在一次测试中却遇到麻烦,在交换机侧输入了参数“模块号”、“V5接口号”、“逻辑C通道ID”之后,发起主备链路倒换的命令,操作的结果居然是“无效的V5端口”。
这种提示令人感到很诧异,因为从AN侧能够正常发起主备链路倒换,从LE侧也能正常发起该V5端口的指定链路倒换,而且系统也完全正常,可见数据配置并没有错误。排除了人为的错误之后,我把重点放到了这三个输入参数上:“模块号”,“V5接口号”均是非常常规的数值,应该没有问题;“逻辑C通道ID”数值比较大,但是仍然在协议规定的65535之内,应该为有效值。想到常规配置数据时“逻辑C通道ID”值一直配得比较小,或许问题就出在此。
于是把“逻辑C通道ID”值改小,再作同样的操作,操作成功。到此很显然是这个参数的有效值范围定义有误,再细细检查,发现它只在单字节范围内有效,必然是该参数定义的类型有误。原因是:在函数OAM_Swap_Communication_Link(_UC v5_interface,_UC logic_c_id)里将逻辑C通道定义为字符型导致,改为_US型即解决。
推而广之,在终端的功能测试中,对输入参数值的测试,应该尽量覆盖所有的有效值。在正常情况下,如果输入值在为有效值,则应该得出正确的结果;如果输入值为非法值,则系统应该给出错误提示,并且不予执行。 对于参数值有效性的判断,特别应该注意一些特殊值和临界值,比如在字节和双字节处等等。
引用 xilinxue 2008/11/7 13:17:44 发表于3楼的内容
-
-
-
-
xilinxue 发表于 2008/11/7 13:18:04
现象与分析:4楼 回复本楼
在回归“载频配置表及跳频数据表中有零频点或重复频点时,MA编码不正确”问题单时。发现当载频配置表及跳频数据表中有重复频点或零频点时,带跳频的呼叫不成功。
这时首先考虑系统消息发的是否正确,观察系统消息一,发现所带的CA表没有错误,已经过滤掉了重复频点和零频点。
在考虑指配命令所带的MA值是否正确,结果发现MA编码也是正确的,也已经过滤掉重复频点和零频点。
经过以上初步分析以后,开始怀疑给基站下配置时是否也正确过滤了无效频点。然后查阅代码,发现果然 BTSM在对基站初始化设置载频属性及设置信道属性时,没有对载频配置表及跳频数据表中频点的有效性做检查,以致于表中出现非法频点时跳频呼叫不成功。
回顾与反思:
这个问题的发现并没有用什么特殊的分析方法,只不过是一般的“排除法”,而且这个问题也不是隐藏的非常之深,但是我觉得这个问题的发现暴露了我们在开发过程中的一个问题。那就是:如何实现各个模块之间的有效沟通,避免因为某一模块的修改而引起其他模块的连锁反映。
本来RR和BTSM都没有考虑重复频点和零频点的情况,大家都寄托与数据配置的正确,却也相安无事。RR首先增加了对非法频点的过滤,本来是件好事,使我们的系统变的更加坚强,但是BTSM并不知道这一变化,于是在错误数据面前束手无策,反而起到了相反的效果。
公司常说“下一道工序是上一道工序的上帝”,是不是可以引申为“其他相关模块是本模块的上帝”。试想如果各个模块多想想自己的改动是否给别人带来不良影响,多及时了解一下其他模块的最新进展。那么此类问题将不再会发生。
引用 xilinxue 2008/11/7 13:18:04 发表于4楼的内容
-
-
-
-
xilinxue 发表于 2008/11/7 13:18:26
现象与分析:
在动态数据配置测试增加小区时,发现小区不能正常初始化。观察ABIS_BTSM接口跟踪窗口,没有关于初始化流程的相关消息。经过分析发现是调用函数。
BTSM_make_send_site_config(_UC site_no , _UC ObjectClass , _US BTS_no ,_UC TRX_no , _UC OperMode)
{
………
//判断合法性
if ((site_no >= MAX_SITE_NUM)
|| (BTS_no >= MAX_BTS_NUM)
|| (TRX_no >= MAX_TRX_A_BTS))
{
ASSERT(FALSE);
return (FALSE);
}
………
}
由于进行小区级的操作调用该函数时,TRX_NO以0xFF带入,则在函数合法性检查时就会返回。从而引发该错误。回顾与反思:
这是一个比较普通的错误,但是回顾它产生和解决的过程,觉得很有启发,也引起了我对编程规范的一些思考。可以说该错误的引起是与编程规范有关,本来函数 BTSM_make_send_site_config()没有对 BTS_no和TRX_no 的合法性判断,在代码审查时,我们考虑到这不符合编程规范中的“检查所有参数输入的有效性”这一条,于是提出请开发人员增加对参数的有效性检查。
但是我们和开发人员都忽略的一个问题,那就是,该函数被不同级别的对象调用,当进行小区级操作时,TRX_NO以0xFF带入,则在函数合法性检查时就会返回,引起上述错误。
让我们再回头看看这个函数,发现依然不符合编程规范,至少违反了“不要设计多用途面面俱到的函数”这一条。
查看我们的代码,类似的问题还有不少,而且诸如:函数入口参数不加有效性检查、函数返回值不加处理等问题也可以找到。这些问题就象一颗颗隐型炸弹,在稍微不注意时就会带来严重后果。
正如前文所述,测试和开发人员都会因为“代码熟悉程度”、“代码编制时间太久有所遗忘”等诸如此类的原因而忽略一些问题。这时,良好的程序风格和编程规范就会成为一把强有力的保护伞。试想如果本文所提到的函数从设计到开发都严格按照规范来进行,那么这个问题就没有产生的土壤,如果我们的每一段代码的编写都严格遵守规范,那么我们的系统将变的有多么坚不可摧。
引用 xilinxue 2008/11/7 13:18:26 发表于5楼的内容
-