10

假设我有这个代码:

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。

我想过要有一个全局函数、向量和一个宏。
该宏将简单地调用该函数,将源文件名和代码行作为参数传递,并且该函数将通过将宏传递的信息插入到向量中来将其标记为“已检查”。

问题是我不会看到任何关于没有“检查”的路径。
知道我该怎么做吗?如何在编译时“注册”一行代码,所以在运行时我可以看到它还没有“检查”?

我希望我很清楚。

4

6 回答 6

8

通常,编译器会提供覆盖实用程序(例如gcov )。但是请注意,他们通常只会为您提供 C0 保险。IE

  • C0 - 每行至少执行一次。请注意,a ? b : c即使只使用了一个分支,它也会被标记为已执行。
  • C1 - 每个分支至少执行一次。
  • C2 - 每条路径至少执行一次

因此,即使您的测试显示 100% 的 C0 覆盖率,您也可能无法捕获代码中的每条路径——而且您可能没有时间去做(路径数量相对于分支呈指数增长)。但是,最好知道您有 10% C2 还是 70% C2(或 0.1% C2)。

于 2010-06-01T00:53:05.273 回答
2

很多时候,您的编译器会提供一个实用程序来进行这种代码覆盖率分析。例如,GCC 有gcov实用程序。

于 2010-06-01T00:40:29.497 回答
2

您需要一个代码覆盖程序(gcov、bulseye、开发合作伙伴)和单元测试(unittest++、cppunit 等)。您编写将测试该功能的测试。

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

然后在这种情况下,单元测试不仅检查完整性(尽管它们仍然这样做),而且还测试覆盖率。

于 2010-06-01T09:28:36.713 回答
1

试用SD C++ TestCoverage以获得与 VisualStudio 兼容的测试覆盖工具。我相信它实际上也会告诉你a?b:c 的测试覆盖率。

于 2010-06-04T12:47:47.177 回答
1

问题是我不会看到任何关于没有“检查”的路径。

如果这意味着换句话说,您不仅要查找实际执行的代码点集,还要查找已按预期执行以某种方式“标记”以最终报告差异的代码点集,我可能有一个非常危险的解决方案。它适用于我的 MSVC 2010 和 2013。

方法是利用静态变量的预程序启动初始化,但由于所有代码点都在函数中,因此必须以某种方式将“静态锚点”放在那里,因此,c++延迟初始化静态的特性必须克服函数变量。

这似乎可以通过通过带有静态成员变量 (progloc_) 的模板类 (X) 添加间接来执行每个模板参数的初始化,而模板参数又是传输所需信息的包装器结构 (_.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_);

ProgLocation-ctor 中使用的 S__LINE__ 技巧来自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;
    }
};

也许有很多与这种方法相关的问题,我还没有看到,这对于您的情况来说是不切实际的,正如其他人指出的那样,在大多数情况下,使用静态代码分析工具是更好的解决方案。

编辑:

刚刚发现之前在另一个上下文中讨论过提供的解决方案:

gcc 中模板的非延迟静态成员初始化

于 2013-12-20T00:22:22.013 回答
0

您可以使用FILELINE预处理器指令:

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

只需将 TRACE(msg) 宏插入到您要跟踪的位置的代码中,并使用您的自定义消息,然后编写您的 MyTraceNotify 函数。

void MyTraceNotify(const char *msg, const char *filename, ULONG line)
{
    /* Put your code here... */    
}
于 2010-06-01T02:13:47.267 回答