Покрытие кода (путь выполнения кода C++ )

скажем, у меня есть этот код:

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

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

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

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

надеюсь, я ясно выразился.

6 ответов


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

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

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


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


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

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

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


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


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

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

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

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

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

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

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

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

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

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__ - трюк, который используется в Проглокации-ctor происходит от вот на так.

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

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

non-deferred-static-member-initialization-for-templates-in-gcc