Покрытие кода: почему маркер конца красный (End If, End Try, ...) - PullRequest
4 голосов
/ 19 января 2012

Я использую MS-Test с Visual Studio 2010 и Visual Basic.

В следующей функции Coverage кода сообщает мне, что есть один непроверенный блок и строка с «End Try» красная (см. 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

Итак, почему эта строка "End Try" является непокрытым (красным) блоком (то же самое происходит с "End If" в конце функции)?

Другой вопрос, который у меня есть: есть лиресурс, который объясняет различные цвета в результатах покрытия кода (синий ясно, но я видел желтый, темный и светло-красный, ...).

Спасибо!

Ответы [ 4 ]

4 голосов
/ 20 января 2012

В дополнение к пункту Даниэля о точках последовательности стоит посмотреть на это подробнее. Если мы возьмем простую функцию, которая повторяет то, что вы делаете

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

В Debug мы получаем следующие точки последовательности (для этого я использую OpenCover )

<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>

(где sl = начальная строка, el = конечная строка, sc = начальный столбец, ec = конечный столбец и смещение = смещение IL в десятичном формате)

Однако это имеет смысл только тогда, когда вы смотрите на 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

Теперь, как вы видите, линия End Try, о которой вы беспокоитесь, будет помечена как ударная, только если вы нажмете инструкцию IL со смещением 40 (IL_0028), однако, когда вы посмотрите на произведенный IL, я не смогу увидеть, как вы когда-либо получить из-за нечетного IL (leave.s - это маленькая инструкция перехода, используемая для выхода из блоков try / catch / finally), и если вы будете следовать коду, вы увидите, что вы всегда достигнете leave.s, который переходит к IL_0029 первый.

В релизе IL изменяется

.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

и точки последовательности тоже

<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>

Таким образом, вы проигрываете в любом случае, так как теперь вы никогда не увидите, чтобы ваши линии try / catch отмечены как покрытые

Итак, давайте попробуем изменить ваш код в соответствии с предложением Ханса и вернемся к отладке (потому что именно с этого места вы обычно будете выполнять покрытие)

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

Снова смотрим точки последовательности

<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>

и ИЛ

.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

Итак, чтобы ваше End Try было покрыто, нам нужна строка 21, которая будет смещена на 31 (IL_001F), и, как мы видим, обе команды leave.s переходят к этой точке, так что теперь эта линия будет отмечена как покрытая .

Так что и Ганс, и Даниэль правы, и я надеюсь, что вышеизложенное объясняет, почему

3 голосов
/ 19 января 2012

Перед тем, как управление пройдет эту линию End Try, оно достигает линии Return и выходит из функции. Таким образом (что касается покрытия кода), вы никогда не достигнете этой линии. Не то чтобы это была какая-то проблема в этом случае.

Обходной путь может заключаться в том, чтобы хранить это VersionInfo в одной временной переменной и возвращать после окончания попытки. Предположение (я привык к C #, а не к 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
2 голосов
/ 19 января 2012

Файл PDB вашей сборки содержит информацию о том, какие инструкции IL соответствуют какой строке (строкам) вашего исходного исходного кода.Эта часть информации называется точкой последовательности.Но не каждая строка в вашем коде точно соответствует одной точке последовательности.Ваше покрытие теста рассчитывается на основе точек последовательности, поэтому может случиться, что строки вашего кода окажутся непокрытыми, хотя они были выполнены во время вашего теста.

0 голосов
/ 19 января 2012

Я никогда не использовал MS-Test, но он будет помечать «New VersionInfo ()» как не отмеченный.

...