Покрытие кода (путь выполнения кода c ++) - PullRequest
9 голосов
/ 01 июня 2010

Допустим, у меня есть этот код:

int function(bool b)
{
    // execution path 1
    int ret = 0;
    if(b)
    {
        // execution path 2
        ret = 55;
    }
    else
    {
        // execution path 3
        ret = 120;
    }
    return ret;
}

Мне нужен какой-то механизм для проверки того, что код прошел по любому возможному пути, т.е. пути выполнения 1, 2 и 3 в приведенном выше коде.

Я думал о наличии глобальной функции, вектора и макроса.
Этот макрос просто вызывает эту функцию, передавая в качестве параметров имя исходного файла и строку кода, и эта функция помечает это как «проверено», вставляя в вектор информацию, которую передал макрос.

Проблема в том, что я не вижу ничего о путях, которые не "проверяют".
Есть идеи, как мне это сделать? Как «зарегистрировать» строку кода во время компиляции, чтобы во время выполнения я мог видеть, что она еще не «проверяла»?

Надеюсь, я в порядке.

Ответы [ 6 ]

7 голосов
/ 01 июня 2010

Обычно утилиты покрытия (такие как gcov ) поставляются с компилятором. Тем не менее, обратите внимание, что они обычно дают вам только покрытие C0. * Т.е. 1003 *

  • C0 - каждая строка выполняется хотя бы один раз. Обратите внимание, что a ? b : c помечается как выполненное, даже если использовалась только одна ветвь.
  • C1 - каждая ветвь выполняется хотя бы один раз.
  • C2 - каждый путь выполняется хотя бы один раз

Таким образом, даже если ваши тесты показывают 100% -ное покрытие C0, вы можете не уловить каждый путь в коде - и, вероятно, у вас нет времени на это (количество путей растет по экспоненте относительно ветвей). Тем не менее, полезно знать, есть ли у вас 10% C2 или 70% C2 (или 0,1% C2).

2 голосов
/ 01 июня 2010

Вам нужна программа покрытия кода (gcov, bullseye, dev partner) и модульное тестирование (unittest ++, cppunit и т. Д.). Вы пишете тест, который проверит эту функцию.

TEST( UnitTestFunction )
{
    CHECK( function(true) == 55 );
    CHECK( function(false) == 120 );
}

Тогда модульные тесты в этом случае не только проверяют целостность (хотя они все еще делают), но также проверяют наличие покрытия.

2 голосов
/ 01 июня 2010

Довольно часто в ваш компилятор входит утилита для такого рода анализа покрытия кода. Например, в GCC есть утилита gcov .

1 голос
/ 04 июня 2010

Попробуйте SD C ++ TestCoverage для VisualStudio-совместимого инструмента покрытия тестов. Я полагаю, что на самом деле это скажет вам о тестовом покрытии a? B: c тоже.

0 голосов
/ 20 декабря 2013

Проблема в том, что я не вижу ничего о путях, которые не "проверяют".

Если это означает, другими словами, что вы ищете не только набор кодовых точек, которые фактически выполняются, но также и набор кодовых точек, которые каким-то образом «помечены», как ожидается, будут выполнены, чтобы, возможно, наконец сообщить Разница, у меня может быть очень опасное решение. Это работает для меня на MSVC 2010 и 2013.

Подход состоит в том, чтобы использовать инициализацию статических переменных до запуска программы, но поскольку все точки кода находятся в функциях и, следовательно, «точка статического анкер» должна быть каким-то образом помещена туда, поэтому функция c ++ в отложенная инициализация статических переменных функций должна быть преодолена.

Это представляется возможным путем добавления косвенного обращения через класс шаблона (X) со статической переменной-членом (progloc_), чтобы обеспечить инициализацию для параметра шаблона, который, в свою очередь, является структурой-оберткой, которая транспортирует необходимую информацию (_. FILE ._ "в строке" _. LINE ._).

Если сложить это вместе, самый важный код для достижения этого может выглядеть следующим образом:

template <class T> class X {
public:
    static T progloc_;
};
template <class T> T X<T>::progloc_;

#define TRACE_CODE_POINT \
    struct ProgLocation { \
    public: \
        std::string loc_; \
        ProgLocation() : loc_(std::string(__FILE__ " at line " S__LINE__)) \
        { \
            TestFw::CodePoints::Test::imHere(loc_); \
        } \
    }; \
    TestFw::CodePoints::X<ProgLocation> dummy; \
    TestFw::CodePoints::Test::iGotCalled(dummy.progloc_.loc_);

Трюк S__LINE__, который используется в ProgLocation - ctor, взят из здесь, на SO .

#define S(x) #x
#define S_(x) S(x)
#define S__LINE__ S_(__LINE__)

Для отслеживания используется следующее:

class Test
{
private:
    typedef std::set<std::string> TFuncs;
    static TFuncs registeredFunctions;
    static TFuncs calledFunctions;
public:
    static int imHere(const std::string fileAndLine)
    {
        assert(registeredFunctions.find(fileAndLine) == registeredFunctions.end());
        registeredFunctions.insert(fileAndLine);
        return 0;
    }
    static void iGotCalled(const std::string fileAndLine)
    {
        if (calledFunctions.find(fileAndLine) == calledFunctions.end())
            calledFunctions.insert(fileAndLine);
    }
    static void report()
    {
        for (TFuncs::const_iterator rfIt = registeredFunctions.begin(); rfIt != registeredFunctions.end(); ++rfIt)
            if (calledFunctions.find(*rfIt) == calledFunctions.end())
                std::cout << (*rfIt) << " didn't get called" << std::endl;
    }
};

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

EDIT:

Только что обнаружил, что предоставленное решение обсуждалось ранее в другом контексте:

неотсроченный статический член-инициализация-для-шаблоны-в-НКУ

0 голосов
/ 01 июня 2010

Вы можете использовать директивы препроцессора FILE и LINE :

#define TRACE(msg) MyTraceNotify(msg,__FILE__,__LINE__)

Просто вставьте макрос TRACE (msg) в ваш код в местах, которые вы хотите отслеживать, с вашим собственным сообщением и напишите свою функцию MyTraceNotify.

void MyTraceNotify(const char *msg, const char *filename, ULONG line)
{
    /* Put your code here... */    
}
...