我有一个用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
答案 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_F
,TEST_P
变体。因此,您可以部署这些
您的代码中的宏具有相同的约束和期望
适用于类定义。
有鉴于此,如果您在libcool
中定义了库cool.h
,则在cool.cpp
中实施
并希望gtest
单元测试,由测试程序tests
执行
这是在tests.cpp
中实现的,合理的是: -
cool_test.h
#include "cool.h"
在其中#include <gtest/gtest.h>
。 libcool
个测试用例#include "cool_test.h"
tests.cpp
,
tests.cpp
和libcool
libgtest
很明显,为什么你不会做OP所做的事情。你不会定义
tests.cpp
中cool.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();
}
编译并与libcool
,libcooler
,libcoolest
和libgtest