GTest没有在单独的编译单元中找到测试

时间:2013-07-28 21:18:08

标签: googletest

我有一个用C ++编写的程序,其中一些子文件夹包含链接的库。有一个顶级的SConscript,它调用子文件夹/库中的SConscript文件。

在库cpp中,有一个GTest:

TEST(X, just_a_passing_test) {
EXPECT_EQ(true, true);
}

顶层程序源中有main(),它只调用GTests main,并且有另一个GTest:

int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

TEST(Dummy, should_pass){
EXPECT_EQ(true, true);
}

现在的问题是,当我运行程序时,GTest只在main.cpp源代码中运行测试。忽略库中的测试。现在,当我在main.cpp中的同一个库cpp中引用一个不相关的类时,它变得奇怪,没有任何副作用(例如'SomeClass foo;'),测试神奇地出现了。我已经尝试使用-O0和其他技巧来强制gcc不优化未调用的代码。我甚至尝试过Clang。 我怀疑它与GTest在编译期间测试发现的方式有关,但我找不到关于这个问题的任何信息。我相信它使用静态初始化,所以可能会有一些奇怪的排序。 任何帮助/信息非常感谢!

更新:在FAQ中发现了一个听起来像这个问题的部分,尽管它特指Visual C ++。其中包括一个技巧/黑客,强制编译器如果没有引用则不丢弃库。 它建议不要在库中放置测试,但这让我想知道你如何测试库,而不是每个都有可执行文件,使得快速运行它们很痛苦并且输出膨胀。 https://code.google.com/p/googletest/wiki/Primer#Important_note_for_Visual_C++_users

1 个答案:

答案 0 :(得分:15)

从场景设置中收集了gtest测试用例的库 缺失是在应用程序构建中静态链接。还有那个 GNU工具链正在使用中。

问题行为的原因很简单。考试 程序不包含对包含的库中任何内容的引用 TEST(X, just_a_passing_test)。因此,链接器不需要 来链接任何链接 来自该库的目标文件以链接该程序。所以它没有。所以 gtest运行时在可执行文件中找不到该测试,因为它不存在。

有助于理解GNU格式的静态库是一个存档 目标文件,用管家标题块和全局符号表装饰。

OP发现通过在程序中编码 ad hoc 引用 问题库中的任何公共符号,他都可以"神奇地"强迫它 测试用例进入程序。

没有魔力。为了满足对该公共符号的引用,链接器是 现在必须链接库中的目标文件 - 包含的目标文件 符号的定义。 OP赋予了库的制作 来自.cpp。因此,库中只有一个目标文件 包含测试用例的定义。用那个目标文件 联系,测试用例在程序中。

OP用编译器选项徒劳无功,从GCC切换到clang, 寻找一种更可敬的方式来实现同样的目的。编译器是 无关。 GCC或clang,它由系统链接器ld完成链接 (除非采取了不寻常的措施来取代它)。

是否有更可敬的方法让ld链接目标文件 静态库,即使程序引用该目标文件中没有符号?

有。假设问题程序是app,问题静态库是 libcool.a

然后,链接app的常用GCC命令行在相关内容中类似于此 分:

g++ -o app -L/path/to/the/libcool/archive -lcool

这会将命令行委托给ld,并附带其他链接器选项和 g++认为是自己发现的系统默认的库。

当链接器开始考虑-lcool时,它会发现这是一个请求 对于存档/path/to/the/libcool/archive/libcool.a。然后它会想 在这一点上,它是否仍然有任何尚未解决的符号参考 其定义在libcool.a中的目标文件中编译。如果有 任何,然后它会将这些目标文件链接到app。如果没有,那么它链接 没有来自libcool.a并传递。

但我们知道libcool.a中有符号定义 链接,即使app没有引用它们。在那种情况下,我们可以告诉 链接器链接来自libcool.a的目标文件,即使它们是。{ 没有引用。更确切地说,我们可以告诉g++告诉链接器这样做, 像这样:

g++ -o app -L/path/to/the/libcool/archive -Wl,--whole-archive -lcool -Wl,-no-whole-archive

这些-Wl,...选项告诉g++将选项...传递给ld--whole-archive 选项告诉ld链接后续归档中的所有目标文件,无论它们是什么 是否引用,直至另行通知。 -no-whole-archive告诉你 ld要停止这样做并像往常一样恢复业务。

看起来好像-Wl,-no-whole-archive是多余的,因为它是最后一件事 g++命令行。但事实并非如此。请记住,g++会附加系统默认库 在将其传递给ld之前,在幕后进行命令行。你肯定 当链接这些默认库时,不希望--whole-archive生效。 (链接将因多个定义错误而失败)。

将此解决方案应用于问题案例并TEST(X, just_a_passing_test) 将被执行,没有强迫程序进行一些无操作的黑客攻击 引用定义该测试的目标文件。

在一般情况下,此解决方案存在明显的缺点。如果它发生在库中 我们要强制链接一些未引用的目标文件包含一个 我们真正不需要的其他一些未引用的目标文件。 --whole-archive也将所有这些链接起来,而且他们只是在程序中臃肿。

--whole-archive解决方案可能比无操作引用更受尊重 黑客,但它不是可敬的。它甚至看起来可敬。

这里真正的解决方案就是做出合理的事情。如果你想要的话 链接器链接程序中某些内容的定义,然后不要保密 链接器。至少声明你所在的每个编译单元中的东西 期望使用它的定义。

gtest测试用例做合理的事情涉及到理解 像gtest这样的TEST(X, just_a_passing_test)宏扩展为类定义, 在这种情况下:

class X_just_a_passing_test_Test : public ::testing::Test {
public: 
    X_just_a_passing_test_Test() {} 
private: 
    virtual void TestBody(); 
    static ::testing::TestInfo* const test_info_ __attribute__ ((unused)); 
    X_just_a_passing_test_Test(X_just_a_passing_test_Test const &); 
    void operator=(X_just_a_passing_test_Test const &);
};

(加上test_info_的静态初始值设定项和TestBody()的定义。)

同样适用于TEST_FTEST_P变体。因此,您可以部署这些 您的代码中的宏具有相同的约束和期望 适用于类定义。

有鉴于此,如果您在libcool中定义了库cool.h,则在cool.cpp中实施 并希望gtest单元测试,由测试程序tests执行 这是在tests.cpp中实现的,合理的是: -

  • 编写标题文件cool_test.h
  • #include "cool.h"在其中
  • #include <gtest/gtest.h>
  • 然后在其中定义libcool个测试用例
  • {li> #include "cool_test.h" tests.cpp
  • 使用tests.cpplibcool
  • 编译并关联libgtest

很明显,为什么你不会做OP所做的事情。你不会定义 tests.cppcool.cpp所需的类,cool.cpp所需的 而不是tests.cpp

OP反对在库中定义测试用例的建议 这是因为:

  

你怎么测试库,没有每个库的可执行文件,    快速使他们痛苦。

根据经验,我建议保留gtest可执行文件的做法 每个要进行单元测试的库:使用普通的自动化工具快速运行它们是徒劳的 这样的make,每个库获得通过/失败判定要好得多 只是对一堆图书馆的判决。但如果你不想这样做,那里仍然没有什么 异议:

// tests.cpp
#include "cool_test.h"
#include "cooler_test.h"
#include "coolest_test.h"

int main(int argc, char** argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

编译并与libcoollibcoolerlibcoolestlibgtest

相关联