这两天是产品beta1以后进行的新一轮bug扫荡,beta1以前产品的bug降低到了大家非常满意的水平。但是beta1以后QA Team发飚,bug总数biu的一下又上升到人均20个上下,真是让人恼火。不过幸好大部分是为了发布beta1而回归出来的UI方面的bug,不过今天却发现被逮到一个非常隐蔽的控件初始状态考虑不足的bug

这个bug发生在一个纯脚本的TreeView控件上面,其异常表现为:当使用键盘Up&Down键移动TreeView上面被选中的Item时,IE报告currentNode为空的脚本错误。关于这个功能的详细设计,可以参看本文,示例效果为:
   
    上图中的node就是程序中的currentNode,Up&Down操作就是根据这个node来进行计算并移动的。在TreeView控件中,为了处理TreeView节点被多选时找到一个确切的currentNode,在每次选择操作时,我在TreeView实例的全局cache中纪录了一个叫m_LastSelected的值,保存最后一次选择动作中的第一个节点。如果是单选就是被选中的节点本身,如果是多选,就是多选时第一个被选中的节点。

Tree.__ContainerOnKeyDown = function()
{
    // . . .
    if ( innerCache.m_Selecteds.m_Count == 0 )
    {
        return;
    }
    var currentNode = innerCache.m_LastSelected;
    // . . .
}

Review上面这段代码,取currentNode的地方似乎是已经做了安全检查,可是怎么还是出错了呢?其实除了本身程序有错,上面这个校验代码也是不对的,我检查的是m_Selecteds这个集合里面有没有东西,然后却是用m_LastSelected赋的值,这之间就可能出现m_Selecteds和m_LastSelected不同步的问题。结果确实是m_Selecteds和m_LastSelected之间的关联出了问题!看下面的代码:

TreeNode.__ContentOnMouseDown = function()
{
    // . . .
    if ( (!evt.shiftKey && !evt.ctrlKey) || !objNode.Attributes('IsMultipleSelected') )
    {
        innerCache.UnselectAll();
        objNode.SetSelected(true);
        innerCache.m_LastSelected = objNode;
    }
    // . . .    
};

由于m_LastSelected和node的选取顺序有关系,它被实现在了鼠标选择TreeView节点的逻辑中,即每次节点被鼠标点选就会更新m_LastSelected的值。

这个逻辑似乎是没有什么问题,反正只要是选择了TreeView上的节点,就会有m_LastSelected的存在,我们也就会得到Up&Down操作的计步节点currentNode。可是就是这里漏掉了一个情况的考虑,如果被选中节点是在initialize我们的TreeView时,由调用代码来设置给节点的(示例如下)。那么由于实现时的疏忽,就出现了m_Selecteds非空,而m_LastSelected为空的情况。

var tree = new Tree();
var node = TreeNode('default node');
tree.Add(node);
tree.Show(/* treeview container element*/);
node.SetSelected(true);

SetSelected(isSelected)方法实现如下:

TreeNode.prototype.SetSelected = function(isSelected)
{
    var innerCache = this.m_Tree.m_InnerCache;
    if ( isSelected )
    {
        if ( !innerCache.m_Selecteds.Contains(this) ) 
        {
            innerCache.m_Selecteds.Add(this);
        }
    }
    else
    {
        innerCache.m_Selecteds.Remove(this);
    }
    if ( this.m_Selected != isSelected )
    {
        this.m_Selected = isSelected;
        if ( this.e_SelectedChanged )
        {
            this.e_SelectedChanged.Execute(isSelected);
        }
    }
    this.ApplyUIChange();
};

问题就是这里了,调用SetSelected选中了节点后,m_Selecteds这个集合是更新了,可是却没有设置m_LastSelected(由于Last Selected Node的定义,本身是不该在这里设置),这样就造成了m_Selecteds和m_LastSelected的不同步。从而出现了开始说到的,进行Up&Down操作结果出现currentNode为空的错误。

这个bug如果是Review代码可能细心能找出来,可是直接被QA通过黑合测试给提了出来,确实是相当相当不容易的。因为刚初始化好的TreeView是没有获得焦点的,这时不管怎么按Up&Down键都不会有反映,也就看起来并没有什么错误。如果使用鼠标点击一下TreeView内的任何区域,让其获得焦点,那么至少都会选中一个TreeNode,这样一来m_LastSelected也就有值了。那么这个如此隐蔽的bug是怎么发现的呢?这需要在页面载入并在TreeView生成后,立即使用Tab键把焦点移到TreeView的container元素上,这时候按Up&Down键,如果TreeView上面有初始花时被SetSelected(isSelected)选中的节点,那么就出错了

本文转自博客园鸟食轩的博客,原文链接:http://www.cnblogs.com/birdshome/,如需转载请自行联系原博主。

被逮到一个初始状态考虑不周的Bug相关推荐

  1. 一个历时五天的 Bug

    一个程序员在没有成长成为架构师之前,几乎都要跟 Bug为伴,程序员有很多时间都是花在了查找各种 Bug上. 我印象深刻的一个Bug, 是一个服务器网络框架无锁队列的 Bug .那个 Bug 连续查找了 ...

  2. 记录一个海思TOE的BUG

    原始引用地址: 记录一个海思TOE的BUG time: 2020.5.3 17:57 发现的过程 ​ 最近在做onvif开发时,有x86的验证的功能没有问题,移动到海思Hi3536上简单运行貌视也很正 ...

  3. 一个有关fstream类的bug

    一个有关fstream类的bug 近日写程序需要读出文件,对读出的内容作些修改,再写回到文件中. 突然发现一个莫名其妙的问题,写回去的时候居然在文件末尾增加了几个字 符.感到很不可思议.具体代码如下: ...

  4. 一个历时五天的 Bug,是如何被灭的?

    作者 | 大飞 本文经授权转大飞码字 一个程序员在没有成长成为架构师之前,几乎都要跟 Bug为伴,程序员有很多时间都是花在了查找各种 Bug上. 我印象深刻的一个Bug, 是一个服务器网络框架无锁队列 ...

  5. 一个多线程示例程序的BUG修复

    一个多线程示例程序的BUG修复 在<.NET 4.0面向对象编程漫谈 >的<应用篇>一书中,我介绍了一个使用"信号量(Semaphore) "同步对象模拟多 ...

  6. 软件测试一个星期没找到bug,一个月都没有发现bug,怎么办?

    昨天从51testing论坛看到一道题目,很有意思,如果是面试时突然提问,可能还真不太好回答. "给你一个软件,你测试了一个月都没有发现bug,这说明什么?你怎么办?" 当时看到这 ...

  7. CTO:再写if-else,逮着一个罚款1000!

    点击上方 好好学java ,选择 星标 公众号重磅资讯,干货,第一时间送达 今日推荐:分享一套基于SpringBoot和Vue的企业级中后台开源项目,这个项目有点哇塞!个人原创100W +访问量博客: ...

  8. 逮到一个阿里 10 年老 测试开发,聊过之后收益良多...

    老话说的好,这人呐,一单在某个领域鲜有敌手了,就会闲得蛋疼.前几天我在上班摸鱼刷CSDN的时候认识了一位阿里测试开发大佬,在阿里工作了10年,因为本人天赋比较高,平时工作也兢兢业业,现在企业内有一定的 ...

  9. 在知乎逮到一个腾讯10年老测试开发,聊过之后收益良多...

    老话说的好,这人呐,一单在某个领域鲜有敌手了,就会闲得蛋疼.前几天我在上班摸鱼刷知乎的时候认识了一位腾讯测试开发大佬,在腾讯工作了10年,因为本人天赋比较高,平时工作也兢兢业业,现在企业内有一定的地位 ...

最新文章

  1. Spring MVC 相关资料整理
  2. HTML5学习笔记(十四):变量作用域
  3. The Power of Ten – Rules for Developing Safety Critical Code
  4. python3扫盲系列-(3)
  5. aws cognito_AWS Cognito的用户管理—(2/3)核心功能
  6. Thinking in java之前
  7. 配置设备作为DHCP 服务器(基于全局地址池)
  8. [转载]Qt之中文显示(QMessageBox、QLineEdit右键菜单等)
  9. VC++实现标准型计算器步骤及源码
  10. 【图像处理】【去模糊】图像去模糊之初探--Single Image Motion Deblurring
  11. 让系统“飞”起来 读懂电脑虚拟内存常遇问题
  12. 专业化分类服务,引领IDC行业发展新模式
  13. linux笔记:shell编程-正则表达式
  14. 学以致用深入浅出数字信号处理 pdf_数字阵列雷达:零中频接收机的优缺点
  15. Word转html实现在线预览
  16. Android App 安全登录认证解决方案
  17. 基于网易云音乐API的无线音箱
  18. 《TypeScript》入门与精通-.d.ts描述文件的使用和详解
  19. 数据链路层——MAC地址欺骗及泛洪
  20. 关于CORBA实现的介绍

热门文章

  1. 细聊分布式ID生成方法-2
  2. Java RSA加密(一)--BCD输出
  3. 学号程序编辑 c语言,C语言程序设计实习报告 行编辑器 学生管理系统
  4. c语言递归汉诺塔次数,c语言递归解决汉诺塔参数变化的疑惑
  5. insert sort java_java插入排序 Insert sort实例
  6. ios10发邮件服务器拒绝,IOS10用户拒绝网络权限后,如何引导开启?
  7. Android让控件位于底部
  8. python用一行代码计算1~100的和(用reduce()和sum()内置函数实现)
  9. diy无感无刷电机霍尔安装_霍尔传感器常见的有那几个类别
  10. cfilefind 能找ftp 服务器上的文件夹吗?,将ftp目录映射为本地盘符