Покрытие кода (путь выполнения кода 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