与没有instrument过的IL代码相比,被instrument的代码是多出了上面用灰色标识的部分,它们就是真正用来标记哪些代码被执行的。仔细数数正好是 7 段,每一段标识了一个block划分的开始,数组的索引值(例如:IL_0010: ldc.i8 0x1 )给每个block从 1 到 7 进行了编号。当这些标识block代码被执行,则代表它们所标识真正被测试代码一定被执行到,代码覆盖收集的监听程序,会时刻监听和收集这些标记代码的执行情况,并由此生成最终的覆盖报告。再对照上篇博客多提到的block的定义 - a single entry point, a single exit point, and a set of instructions that are all run in sequence - 仔细检查一下,确实是这样每一个block都是只有一个唯一入口和一个为出口,block标记到大都是加载在br.s、brtrue.s、ble.s等分支跳转语句前面。
对于GetInteger()而言,最有意思就要数 if( arg1 >0 && arg2 < 0 ),别看只有一行代码,但由于条件与操作&&的存在,在IL级这一行代码时间上是被划分4个block的,如上面的粗体代码所示,这些代码并不是很难理解。这里出个小问题:对于GetInteger()函数,测试用例(arg1 =1, arg2 = -1),能够对 if( arg1 >0 && arg2 < 0 ) 行进行完全覆盖吗?答案:不能,因它漏掉了仅有一条IL指令(IL_005b: ldc.i4.1)的哪个block,随意仍是部分覆盖。要想达到对该if行的完全覆盖,最少需要两个用例, 例如:(arg1 = -1, arg1 =-1)和 (arg1 =1, arg2 = -1)。
最后需要提示一下:Reflector工具可以将IL代码反编译为C#等语言代码,这样阅读起来会更方便一些,但是有一些instrument过的IL,Reflector反编译的结果可能会丢失一些block划分信息。例如:GetInteger()的发编译结果如下。其中,Block#2和#3并没有显示在代码中体现出来,所以在有些情况下,直接阅读IL代码能更准确把握block的划分情况。
view plaincopy to clipboardprint? |
相关链接: