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

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

被逮到一个初始状态考虑不周的BugTree.__ContainerOnKeyDown = function()
被逮到一个初始状态考虑不周的Bug{
被逮到一个初始状态考虑不周的Bug    
// . . .
被逮到一个初始状态考虑不周的Bug
    if ( innerCache.m_Selecteds.m_Count == 0 )
被逮到一个初始状态考虑不周的Bug    {
被逮到一个初始状态考虑不周的Bug        
return;
被逮到一个初始状态考虑不周的Bug    }
被逮到一个初始状态考虑不周的Bug    
var currentNode = innerCache.m_LastSelected;
被逮到一个初始状态考虑不周的Bug    
// . . .
被逮到一个初始状态考虑不周的Bug
}

    Review上面这段代码,取currentNode的地方似乎是已经做了安全检查,可是怎么还是出错了呢?其实除了本身程序有错,上面这个校验代码也是不对的,我检查的是m_Selecteds这个集合里面有没有东西,然后却是用m_LastSelected赋的值,这之间就可能出现m_Selecteds和m_LastSelected不同步的问题。结果确实是m_Selecteds和m_LastSelected之间的关联出了问题!看下面的代码:
被逮到一个初始状态考虑不周的BugTreeNode.__ContentOnMouseDown = function()
被逮到一个初始状态考虑不周的Bug{
被逮到一个初始状态考虑不周的Bug    
// . . .
被逮到一个初始状态考虑不周的Bug
    if ( (!evt.shiftKey && !evt.ctrlKey) || !objNode.Attributes('IsMultipleSelected') )
被逮到一个初始状态考虑不周的Bug    {
被逮到一个初始状态考虑不周的Bug        innerCache.UnselectAll();
被逮到一个初始状态考虑不周的Bug        objNode.SetSelected(
true);
被逮到一个初始状态考虑不周的Bug        innerCache.m_LastSelected 
= objNode;
被逮到一个初始状态考虑不周的Bug    }
被逮到一个初始状态考虑不周的Bug    
// . . .    
被逮到一个初始状态考虑不周的Bug
};
    由于m_LastSelected和node的选取顺序有关系,它被实现在了鼠标选择TreeView节点的逻辑中,即每次节点被鼠标点选就会更新m_LastSelected的值。

    这个逻辑似乎是没有什么问题,反正只要是选择了TreeView上的节点,就会有m_LastSelected的存在,我们也就会得到Up&Down操作的计步节点currentNode。可是就是这里漏掉了一个情况的考虑,如果被选中节点是在initialize我们的TreeView时,由调用代码来设置给节点的(示例如下)。那么由于实现时的疏忽,就出现了m_Selecteds非空,而m_LastSelected为空的情况。
被逮到一个初始状态考虑不周的Bugvar tree = new Tree();
被逮到一个初始状态考虑不周的Bug
var node = TreeNode('default node');
被逮到一个初始状态考虑不周的Bugtree.Add(node);
被逮到一个初始状态考虑不周的Bugtree.Show(/* treeview container element*/);
被逮到一个初始状态考虑不周的Bugnode.SetSelected(
true);

    SetSelected(isSelected)方法实现如下:
被逮到一个初始状态考虑不周的BugTreeNode.prototype.SetSelected = function(isSelected)
被逮到一个初始状态考虑不周的Bug{
被逮到一个初始状态考虑不周的Bug    
var innerCache = this.m_Tree.m_InnerCache;
被逮到一个初始状态考虑不周的Bug    
if ( isSelected )
被逮到一个初始状态考虑不周的Bug    {
被逮到一个初始状态考虑不周的Bug        
if ( !innerCache.m_Selecteds.Contains(this) ) 
被逮到一个初始状态考虑不周的Bug        {
被逮到一个初始状态考虑不周的Bug            innerCache.m_Selecteds.Add(
this);
被逮到一个初始状态考虑不周的Bug        }
被逮到一个初始状态考虑不周的Bug    }
被逮到一个初始状态考虑不周的Bug    
else
被逮到一个初始状态考虑不周的Bug    {
被逮到一个初始状态考虑不周的Bug        innerCache.m_Selecteds.Remove(
this);
被逮到一个初始状态考虑不周的Bug    }
被逮到一个初始状态考虑不周的Bug    
if ( this.m_Selected != isSelected )
被逮到一个初始状态考虑不周的Bug    {
被逮到一个初始状态考虑不周的Bug        
this.m_Selected = isSelected;
被逮到一个初始状态考虑不周的Bug        
if ( this.e_SelectedChanged )
被逮到一个初始状态考虑不周的Bug        {
被逮到一个初始状态考虑不周的Bug            
this.e_SelectedChanged.Execute(isSelected);
被逮到一个初始状态考虑不周的Bug        }
被逮到一个初始状态考虑不周的Bug    }
被逮到一个初始状态考虑不周的Bug    
this.ApplyUIChange();
被逮到一个初始状态考虑不周的Bug};
被逮到一个初始状态考虑不周的Bug
    问题就是这里了,调用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)选中的节点,那么就出错了被逮到一个初始状态考虑不周的Bug

相关文章:

  • 2021-11-29
  • 2022-12-23
  • 2022-12-23
  • 2021-11-07
  • 2022-12-23
  • 2021-07-07
  • 2022-12-23
  • 2021-07-29
猜你喜欢
  • 2022-12-23
  • 2021-04-19
  • 2021-08-21
  • 2021-04-29
  • 2021-08-24
  • 2021-11-30
  • 2021-11-22
相关资源
相似解决方案