基于版本对比的Debug

基于版本对比的Debug

前传

搜肠刮肚,费了很大的劲想这几个月来改过的bug,还真没有哪次调试可以拿出来作为故事和大家共享。唯一一个有点意思的bug,是由于使用对象的指针来比较级别,造成程序的计算结果变来变去。实际上应该使用对象的ID来比较,这样可以保证计算结果的唯一性。只是当初(去年12月左右吧)加班加的头晕眼花,迷迷糊糊的把对象的指针和对象的ID之间画了个等号。由于需要通过ID来比较级别的情况实在是太罕见了,所以过了大半年才被发现。不过这个Debug的过程就比较平淡了:首先写了一堆log,然后跟踪了一下某个特定ID对象的计算过程,这个bug就这么解决了。说白了,就是写log + 耐心的Step + 时间。这个故事写给大家看,hehe,实在是...,所以写了一半就写不下去了。

现在言归正传吧。

关于题目

这个题目,来自于Martin Fowler的一篇blog(DiffDebugging)MF在这个blog里,结合亲身的经历,讲述了如何运用Diff工具结合版本控制来“Debug”的过程。其实这样的过程大家可能都在不自觉中使用过很多次,不过直到读了这篇blog,我才真正对此种Debug的方式有一个清晰的认识,真有种醍醐灌顶的感觉。

我的问题

由于更改GCL7GGJ9的共享代码,GCL7工程量计算的自动回归测试已经3天没有运行了(总是编译失败,没把我烦死)。今天一运行,果然是问题多多。问题多咱不怕,因为这是意料之中的事。大部分错误都是一眼都能看出来的。不过有一个问题却有点异常——梁的计算似乎出现了不应该的错误——预制梁的体积算错了,因为预制梁不和其他构件发生扣减关系,所以我们所做的改动是不应该对预制梁产生影响的。什么原因呢?

Debug

SourceSafe比较了一下BeamCalculator.pas(负责梁计算的单元)的历史,这三天来共做了三处改动:

1.        预制梁计算用新的中心线计算类替换了原来的中心线计算函数(liuhq

2.        梁扣板的规则提取了一个专门的类(zhouf)

3.        修改了非预制梁的边线、中心线初始化过程。 (liuwa)

 

改动2首先被排除,因为预制梁不会扣板。改动3也明显被排除,因为预制梁的计算不会调用这个初始化过程。

改动1的嫌疑最大(呵呵,这个好像很明显啊。不过我还是喜欢先把不可能情况排除,这样可以集中精力于一点)。但是,改动1所作的改动是非常少的,仅仅是修改了一个函数调用而已,如下:

 

原来:dLength := TCSeg2D.Length(FBeamEDO.LinearEDOInfo.MidLine);

现在:dLength := TCSeg2D.Length(FBeamEDO.MidLine);

 

LinearEDOInfo.MidLineMidLine之间的区别也仅仅是调用不同的中心线计算方法。并且,新的中心线计算方法的结果应该和旧有的中心线计算方法产生相同的计算结果。

问题肯定出在计算中心线的StructuralEDO.pas单元!马上得了三天前的StructuralEDO.pasBuildAll,然后运行自动回归测试。这次回归的结果肯定一致了,我美滋滋的边想边喝了口茶。

什么?对比结果还是不一致?

我简直不敢相信自己的眼睛,问题居然不在中心线的计算方法上!不可能——这是我得第一反应。我Get了三天前BeamCalculator.pas和最新的StrutualEDO.pas,再次BuildAll,运行自动回归测试。

对比结果一致!

我定了定神,仔细的考虑了一会儿。

现在已经可以确定,不是中心线计算的问题,那么唯一的可能有问题的还是上面那行简单的代码。可是那么一行简单的变动会有什么问题呢?会不会是其他单元的变动造成的影响呢?从分析来看是不会的,但是我需要一个明证。一个简单的证明胜过所有的分析。

我再一次Get了三天前的BeamCalculator.pasStruatualEDO.pas,并且保持其他单元都是Last Version,然后BuildAll,执行自动回归测试。

对比结果一致!

很明白了,问题就是出在BeamCalculator.pas。但是我还是不知道是什么导致的错误,那一行简单的变动不可能会出错。难道变动2和变动3在某些情况下对计算产生了影响?我逐一将这三天来BeamCalculator.pas所作的改动Get到本地,并且运行自动回归测试。结果表明只有改动1发生后的版本才会出现错误,而改动2和改动3确实和这个计算无关。

 

那么到底那里出了问题?

我似乎陷入了“Impossible Puzzle[1]之中,那么应该是我的假设本身有问题,我应改从当前的假设中跳出来。到底哪一个假设出了问题呢?

我最大的假设是什么?

——现在唯一未经证明的假设应该是自动回归测试的结果——我假设以前的计算结果是正确的。

如果以前的计算结果本来是错误的会怎样?我找到出错的梁仔细的分析它的结算结果,很奇怪,现在的结果似乎是正确地啊?而以前的那个结果,怎么像是错的呢?

为什么我们要采用新的中心线计算方法?

——因为原来的计算方法有缺陷。

为什么从代码变动中看到,只有预制梁需要修改而其他的梁都不需要呢?

——因为非预制梁在很久以前就采用了新的中心线计算方法了!

^_^,我豁然开朗:梁的中心线的计算很久之前就改了,我们现在只是改动了它的初始化过程和调用方法,本不应该对计算结果产生影响。但是,很久以前改动中心线的时候遗漏了预制梁的计算。从那时起,预制梁一直都在使用旧的中心线计算方法,而非预制梁却一直使用新的中心线计算方法。错误的种子其实在那次遗漏的修改中就已经埋下,但是由于LinearEDOInfo一直没有从源代码中删除,而预制梁又很少被用到(也并非测试的重点所在),所以直到今天要彻底删掉LinearEDOInfo的时候,错误才“忽然”冒了出来,狠狠的咬了可怜的我一口。

555....,以前改梁的代码不是我改的啊.....

到此结束

无论怎么说,问题还是解决了。自始至终,我没有用DelphiDebugger。写到这里我忽然想,Martin FowlerDiffDebugging为题,除了字面上版本对比(Diff)的意思以外,是不是也希望我们能够看到这种Debug方式与传统上的Debug方式之间的Diff呢?

说是实在的,自从有了自动回归测试,我编程序的方式慢慢的发生了改变,越来越依赖于这个自动回归了。当然,我们的自动回归还不是真正的单元测试,但是它给了我很多TDD的感觉,给了我Refactoring时所必需的保护。也正是从这一点一滴开始,我才真正爱上了编程序这工作。

 

我的下一个目标,是要用“真正”的UnitTestTDD来彻底的改变我的日常工作!

特别致谢

特别感谢我的开发经理岳亮,是他加班加点编写了GCL7的自动回归测试工具,让它从我的一个想法变成一个强大的工具。

1.         我翻译为“不可能迷题”。The Pragmatic Programmer, P212.中文版为《程序员修炼之道》

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值