Блоки в кодовом покрытии с VS2010 - PullRequest
7 голосов
/ 10 февраля 2011

Я запускаю код C ++, чтобы получить результаты покрытия кода, как в этом посте .

#include <iostream>
using namespace std;

int testfunction(int input)
{
    if (input > 0) {
        return 1;
    }
    else {
        return 0;
    }
}

int main()
{
    testfunction(-1);
    testfunction(1);
}

enter image description here

Результат покрытия кода говорит о том, что в main () есть три блока, а в функции testfunction () - четыре. Что означает блок? Как там блоки 3/4 в основной / тестовой функции?

ДОБАВЛЕНО

Когда я изменил код следующим образом,

int main()
{
    testfunction(1);
    testfunction(1);
}

или следующим образом

int main()
{
    testfunction(-1);
    testfunction(-1);
}

У меня есть этот результат.

enter image description here

И, похоже, у testfunction() есть четыре блока.

  1. функция ввода
  2. если блок
  3. еще блок
  4. состояние

Я получил подсказки от этого поста .

Ответы [ 2 ]

6 голосов
/ 11 февраля 2011

Технический термин для блока в покрытии кода: базовый блок .Для шпаргалки непосредственно из записи в Википедии :

Код в базовом блоке имеет одну точку входа, что означает, что ни один код внутри него не является назначением инструкции перехода где-либо в программеи имеет одну точку выхода, то есть только последняя инструкция может заставить программу начать выполнение кода в другом базовом блоке.В этих условиях всякий раз, когда выполняется первая инструкция в базовом блоке, остальные инструкции обязательно выполняются ровно один раз, по порядку.

Базовый блок важен в покрытии кода, потому что мы можем вставить датчик в начале основного блока.Когда этот зонд попадет, мы знаем, что все следующие инструкции в этом базовом блоке будут выполнены (из-за свойств базового блока).

К сожалению, с компиляторами (и особенно с оптимизацией) этоНе всегда очевидно, как исходный код отображается на основные блоки.Самый простой способ узнать это - посмотреть на сгенерированную сборку.Например, давайте посмотрим на ваш исходный main & testfunction:

Для main я вижу сборку ниже (с чередованием с исходным источником).Аналогично тому, что Питер делает здесь , я отметил, где начинаются базовые блоки.

int main()
{
013B2D20  push        ebp                       <--- Block 0 (initial)
013B2D21  mov         ebp,esp  
013B2D23  sub         esp,40h  
013B2D26  push        ebx  
013B2D27  push        esi  
013B2D28  push        edi  
    testfunction(-1);
013B2D29  push        0FFFFFFFFh  
013B2D2B  call        testfunction (013B10CDh)  
013B2D30  add         esp,4                     <--- Block 1 (due to call)
    testfunction(1);
013B2D33  push        1  
013B2D35  call        testfunction (013B10CDh)  
013B2D3A  add         esp,4                     <--- Block 2 (due to call)
}
013B2D3D  xor         eax,eax  
013B2D3F  pop         edi  
013B2D40  pop         esi  
013B2D41  pop         ebx  
013B2D42  mov         esp,ebp  
013B2D44  pop         ebp  
013B2D45  ret  

Мы видим, что main имеет три основных блока: один начальный блок и два другихиз-за вызовов функций.Глядя на код, это кажется разумным.testfunction немного сложнее.Просто глядя на источник, кажется, что есть три блока:

  1. Запись в тест функции и логики (input > 0)
  2. Условие истинной ветви (return 1)
  3. Условие ложной ветви (return 0)

Однако из-за фактической сгенерированной сборки существует четыре блока.Я предполагаю, что вы создали свой код с отключенной оптимизацией.Когда я собираюсь с VS2010 в конфигурации Debug (оптимизация отключена), я вижу следующую разборку для testfunction:

int testfunction(int input)
{
013B2CF0  push        ebp                         <--- Block 0 (initial)
013B2CF1  mov         ebp,esp  
013B2CF3  sub         esp,40h  
013B2CF6  push        ebx  
013B2CF7  push        esi  
013B2CF8  push        edi  
    if (input > 0) {
013B2CF9  cmp         dword ptr [input],0  
013B2CFD  jle         testfunction+18h (013B2D08h)  
        return 1;
013B2CFF  mov         eax,1                        <--- Block 1 (due to jle branch)
013B2D04  jmp         testfunction+1Ah (013B2D0Ah)  
    }
    else {
013B2D06  jmp         testfunction+1Ah (013B2D0Ah) <--- Not a block (unreachable code)
        return 0;
013B2D08  xor         eax,eax                      <--- Block 2 (due to jmp branch @ 013B2D04)
    }
}
013B2D0A  pop         edi                          <--- Block 3 (due to being jump target from 013B2D04)
013B2D0B  pop         esi  
013B2D0C  pop         ebx  
013B2D0D  mov         esp,ebp  
013B2D0F  pop         ebp  
013B2D10  ret  

Здесь у нас есть четыре блока:

  1. запись в функцию
  2. Условие истинной ветви
  3. Условие ложной ветви
  4. Общая функция epilog (очистка стека и возврат)

Если бы компилятор продублировал эпилог функции как в условиях true, так и в условии false ответвлений, вы бы увидели только три блока.Также, что интересно, компилятор вставил ложную инструкцию jmp в 013B2D06.Поскольку это недоступный код, он не рассматривается как базовый блок.

В целом весь этот анализ является излишним, поскольку общая метрика покрытия кода скажет вам, что вам нужно знать.Этот ответ был просто для того, чтобы подчеркнуть, почему количество блоков не всегда очевидно или что ожидается.

2 голосов
/ 10 февраля 2011

Согласно MSDN на Обзор данных покрытия кода :

Данные покрытия кода рассчитываются для кодовых блоков, строк кода и частичных строк, если они выполняются тестомзапустить. Блок кода - это кодовый путь с одной точкой входа, одной точкой выхода и набором инструкций, которые выполняются в последовательности .Блок кода заканчивается, когда он достигает точки принятия решения, такой как новый блок условных операторов, вызов функции, выброс исключения, ввод, выход, попытка, перехват или конструкция finally.

Основной блок:

  • Запись метода
  • testfunction
  • testfunction

Testfunction block:

  • Запись метода
  • If / Else
  • Return
  • Вызов метода
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...