Code coverage: why the red end marker (End If, End Try, ...)

I am using MS-Test with Visual Studio 2010 and Visual Basic.

In the following function, Code Coverage tells me that there is one uncontrolled block, and the line with "End Try" is red (see http://lts.cr/BVvP ):

Private Function GetLatestVersionInfoForAsync() Try Return GetLatestVersionInfo() Catch ex As Exception RaiseEvent UnhandledAsyncException(Me, New UnhandledExceptionEventArgs(ex, False)) Return New VersionInfo() With {.ExceptionOccoured = True, .Exception = ex} End Try End Function 

So why is this β€œEnd Try” line an uncovered (red) block (the same thing happens with β€œEnd If” at the end of the function)?

Another question: is there any resource that explains the different colors in the code coverage results (blue is clear, but I saw yellow, dark and light red, ...).

Thanks!

+4
source share
4 answers

In addition to Daniel's point on the points of the sequence, it is worth looking at this further. If we take a simple function that repeats what you do

 07 Function Method() As String 08 Try 09 Return "" 10 Catch ex As Exception 11 Return "" 12 End Try 13 End Function 

In Debug, we get the following points in the sequence (I use OpenCover for this)

 <SequencePoints> <SequencePoint offset="0" ordinal="0" uspid="261" vc="0" ec="32" el="7" sc="5" sl="7"/> <SequencePoint offset="1" ordinal="1" uspid="262" vc="0" ec="12" el="8" sc="9" sl="8"/> <SequencePoint offset="2" ordinal="2" uspid="263" vc="0" ec="22" el="9" sc="13" sl="9"/> <SequencePoint offset="19" ordinal="3" uspid="264" vc="0" ec="30" el="10" sc="9" sl="10"/> <SequencePoint offset="20" ordinal="4" uspid="265" vc="0" ec="22" el="11" sc="13" sl="11"/> <SequencePoint offset="40" ordinal="5" uspid="266" vc="0" ec="16" el="12" sc="9" sl="12"/> <SequencePoint offset="41" ordinal="6" uspid="267" vc="0" ec="17" el="13" sc="5" sl="13"/> </SequencePoints> 

(where sl = start row, el = end row, sc = start column, ec = end column and offset = shift IL in decimal)

However, this only makes sense when you look at IL

 .method public static string Method () cil managed { // Method begins at RVA 0x272c // Code size 43 (0x2b) .maxstack 2 .locals init ( [0] string Method, [1] class [mscorlib]System.Exception ex ) IL_0000: nop IL_0001: nop .try { IL_0002: ldstr "" IL_0007: stloc.0 IL_0008: leave.s IL_0029 IL_000a: leave.s IL_0028 } // end .try catch [mscorlib]System.Exception { IL_000c: dup IL_000d: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::SetProjectError(class [mscorlib]System.Exception) IL_0012: stloc.1 IL_0013: nop IL_0014: ldstr "" IL_0019: stloc.0 IL_001a: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::ClearProjectError() IL_001f: leave.s IL_0029 IL_0021: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::ClearProjectError() IL_0026: leave.s IL_0028 } // end handler IL_0028: nop IL_0029: ldloc.0 IL_002a: ret } // end of method Module1::Method 

Now that you see that the End Try line you are worried about is only marked as falling if you click on the IL command at offset 40 (IL_0028), however, when you look at IL, I cannot see how you ever ( leave.s is a small jump, such as an instruction that is used to exit try / catch / finally blocks), and if you follow the code, you will see that you always reach leave.s , which jumps to IL_0029.

The release of IL changes

 .method public static string Method () cil managed { // Method begins at RVA 0x2274 // Code size 30 (0x1e) .maxstack 2 .locals init ( [0] string Method, [1] class [mscorlib]System.Exception ex ) .try { IL_0000: ldstr "" IL_0005: stloc.0 IL_0006: leave.s IL_001c } // end .try catch [mscorlib]System.Exception { IL_0008: dup IL_0009: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::SetProjectError(class [mscorlib]System.Exception) IL_000e: stloc.1 IL_000f: ldstr "" IL_0014: stloc.0 IL_0015: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::ClearProjectError() IL_001a: leave.s IL_001c } // end handler IL_001c: ldloc.0 IL_001d: ret } // end of method Module1::Method 

as well as sequence points

 <SequencePoints> <SequencePoint offset="0" ordinal="0" uspid="33" vc="0" ec="22" el="9" sc="13" sl="9"/> <SequencePoint offset="15" ordinal="1" uspid="34" vc="0" ec="22" el="11" sc="13" sl="11"/> <SequencePoint offset="28" ordinal="2" uspid="35" vc="0" ec="17" el="13" sc="5" sl="13"/> </SequencePoints> 

This way you are free anyway, since now you will never see that your try / catch lines marked closed

So let's try changing your code, as Hans suggested, and return to debugging (because that's where you will get coverage from usually)

 15 Function Method2() As String 16 Dim x As String 17 Try 18 x = "" 19 Catch ex As Exception 20 x = "" 21 End Try 22 Return x 23 End Function 

Let's look at the points of the sequence again

 <SequencePoints> <SequencePoint offset="0" ordinal="0" uspid="268" vc="0" ec="33" el="15" sc="5" sl="15"/> <SequencePoint offset="1" ordinal="1" uspid="269" vc="0" ec="12" el="17" sc="9" sl="17"/> <SequencePoint offset="2" ordinal="2" uspid="270" vc="0" ec="19" el="18" sc="13" sl="18"/> <SequencePoint offset="17" ordinal="3" uspid="271" vc="0" ec="30" el="19" sc="9" sl="19"/> <SequencePoint offset="18" ordinal="4" uspid="272" vc="0" ec="19" el="20" sc="13" sl="20"/> <SequencePoint offset="31" ordinal="5" uspid="273" vc="0" ec="16" el="21" sc="9" sl="21"/> <SequencePoint offset="32" ordinal="6" uspid="274" vc="0" ec="17" el="22" sc="9" sl="22"/> <SequencePoint offset="36" ordinal="7" uspid="275" vc="0" ec="17" el="23" sc="5" sl="23"/> </SequencePoints> 

and IL

 .method public static string Method2 () cil managed { // Method begins at RVA 0x282c // Code size 38 (0x26) .maxstack 2 .locals init ( [0] string Method2, [1] string x, [2] class [mscorlib]System.Exception ex ) IL_0000: nop IL_0001: nop .try { IL_0002: ldstr "" IL_0007: stloc.1 IL_0008: leave.s IL_001f } // end .try catch [mscorlib]System.Exception { IL_000a: dup IL_000b: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::SetProjectError(class [mscorlib]System.Exception) IL_0010: stloc.2 IL_0011: nop IL_0012: ldstr "" IL_0017: stloc.1 IL_0018: call void [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.ProjectData::ClearProjectError() IL_001d: leave.s IL_001f } // end handler IL_001f: nop IL_0020: ldloc.1 IL_0021: stloc.0 IL_0022: br.s IL_0024 IL_0024: ldloc.0 IL_0025: ret } // end of method Module1::Method2 

So, in order for your End Try be closed, we need line 21 to be deleted, and this will be offset 31 (IL_001F), and since we can see the leave.s instructions go to that point, so now this the line will be marked as covered.

So, both Hans and Daniel are true, and I hope this explains why

+4
source

Before the control passes the End Try string, it reaches the Return string and exits the function. So (as far as the code is concerned), you will never reach that line. Not that in this case it was a problem.

The workaround was to keep this Version in one temporary variable and return it after completion. Guess (I'm used to C #, not VB):

 Private Function GetLatestVersionInfoForAsync() Dim vi As VersionInfo Try vi = GetLatestVersionInfo() Catch ex As Exception RaiseEvent UnhandledAsyncException(Me, New UnhandledExceptionEventArgs(ex, False)) vi = New VersionInfo() With {.ExceptionOccoured = True, .Exception = ex} End Try Return vi End Function 
+3
source

Your assembly's PDB file contains information on which IL statements correspond to the source code line (s). This piece of information is called a sequence point. But not every line of your code exactly matches one point in the sequence. Your test coverage is calculated based on the points in the sequence, so it may happen that the lines of your code look uncovered, although they were executed during the test.

+2
source

I have never used MS-Test, but it will mark "New VersionInfo ()" as unchecked.

0
source

Source: https://habr.com/ru/post/1391830/


All Articles