我现在正在使用Boost Unit Test为我的项目执行单元测试。每次运行单元测试时,都会出现内存堆栈问题。我调试了BOOST库的源代码,我发现问题来自于调用unit_test_suite.hpp文件中的以下代码:
void
traverse_test_tree( test_unit_id id, test_tree_visitor& V )
{
global_i = global_i + 1;
std::cout<<global_i<<std::endl;
if( ut_detail::test_id_2_unit_type( id ) == tut_case )
traverse_test_tree( framework::get<test_case>( id ), V );
else
traverse_test_tree( framework::get<test_suite>( id ), V );
}
我从VC10获得的错误信息是:
Unhandled exception at 0x779815de in TestApplication.exe: 0xC00000FD: Stack overflow.
我想知道测试程序有什么问题。谢谢!
编辑根据我查看代码的建议,会发生非常奇怪的事情:如果测试套件在与main()相同的程序中定义,则可以正常工作;但是,如果测试套件来自.dll,则会发生错误。我列出以下代码来说明我的问题:
boost::unit_test::test_suite* main_global_test_suite;
void Hellotestdll()
{
int i= 1;
int j= 2;
BOOST_CHECK(i == j);
}
boost::unit_test::test_suite* get_abc_test_suite()
{
test_suite* ts = BOOST_TEST_SUITE( "unit_geometric" );
ts->add( BOOST_TEST_CASE( &Hellotestdll ) );
return ts;
}
int main( int argc, char* argv[] )
{
try
{
/**
* Step 1. obtain options
*/
char* optionLine[1024];
int len;
len = obtain_options(optionLine, argc, argv);
/**
* Step 2. perform unit test based on user's options
*/
int test_status=0;
main_global_test_suite = get_abc_test_suite();
test_status = unit_test_main(run_global_test_suite, len, optionLine);
return test_status;
}
catch(std::exception& e)
{
std::cout << e.what() << std::endl;
return 1;
}
catch (const std::string& s)
{
std::cout << s << std::endl;
return 1;
}
catch (...)
{
return 1;
}
}
以上代码非常有效。但是,如果测试套件来自.dll,例如:
// dll_header.h
namespace abc
{
ABC_EXPORT boost::unit_test::test_suite* get_geometric_test_suite();
}
// dll_header.cpp
namespace abc
{
using namespace boost;
using namespace boost::unit_test;
void Hellotestdllabc()
{
int i= 1;
int j= 2;
BOOST_CHECK(i == j);
}
boost::unit_test::test_suite* get_abc_test_suite()
{
test_suite* ts = BOOST_TEST_SUITE( "unit_abc" );
ts->add( BOOST_TEST_CASE( &Hellotestdllabc ) );
return ts;
}
}
然后,如果我使用以下代码调用此测试套件:
int main( int argc, char* argv[] )
{
............
/**
* Step 2. perform unit test based on user's options
*/
int test_status=0;
main_global_test_suite = abc::get_abc_test_suite();
test_status = unit_test_main(run_global_test_suite, len, optionLine);
return test_status;
}
会发生恼人的堆栈溢出错误。
问题总结
(1) boost dll with MDd (Succeed)
如果我将boost单元测试库(使用定义-DBOOST_ALL_DYN_LINK -DBOOST_TEST_NO_MAIN -DBOOST_TEST_DYN_LINK -DBOOST_ALL_NO_LIB
)和正在运行的可执行程序与相同的动态运行时库(多线程调试DLL(MDd))链接起来,它将起作用。< / p>
(2) boost dll with MTd (Failed)
如果boost单元测试库(定义为-DBOOST_ALL_DYN_LINK -DBOOST_TEST_NO_MAIN -DBOOST_TEST_DYN_LINK -DBOOST_ALL_NO_LIB
)和正在运行的可执行程序被编译并链接到相同的静态运行时库(Multi-thred Debu(MTd)),我将有一个崩溃,但崩溃与我上面报道的崩溃不同:
(3) boost static lib with MDd (Failed)
如果将boost构建为静态库(具有-DBOOST_TEST_NO_MAIN -DBOOST_ALL_NO_LIB
的定义),则boost库和可执行程序都使用相同的动态运行时库(MDd)构建。将发生以下崩溃:
(4) boost static lib with MTd (Failed)
如果将boost构建为静态库(定义为-DBOOST_TEST_NO_MAIN -DBOOST_ALL_NO_LIB
),则boost库和可执行程序都使用相同的静态运行时库(MTd)构建。将发生以下崩溃:
答案 0 :(得分:2)
ABC_EXPORT boost::unit_test::test_suite* get_geometric_test_suite();
单元测试的目的是尽早发现代码中的问题。这很有效,只是你早期发现问题非常。太早甚至不能让单元测试正常运行。
返回指向C ++对象的指针的DLL中的函数通常是个问题。当C ++对象的布局完全匹配编译器在编译DLL和EXE时所做的假设时,它才会出现好结果。并且对象存在于两个模块都可以访问的堆上,因为DLL创建了对象,并且您的EXE需要删除它。
要允许正确删除对象,DLL和EXE 必须共享相同的CRT版本。使用/ MT构建程序时会遇到麻烦,要求提供CRT的静态版本。相关的编译器设置是C / C ++,代码生成,运行时库设置。您的Debug配置必须使用/ MDd,您的Release配置必须使用/ MD。对于EXE和DLL项目,以及编译时的Boost库。如果它是/ MTd和/ MT,那么DLL将有自己的CRT副本链接到它,并将使用自己的堆来分配。 EXE无法正确删除该对象,因为它使用另一个堆。无论如何,这样做会产生不确定的行为。任何事情都可能发生,当你在比XP更新的Windows版本上运行你的程序时,你往往会很幸运。当你运行附加调试器的单元测试时,Vista和up将使用调试堆,它会在注意到传递给:: operator delete的指针无效时调用断点。请务必让链接器自动找到要链接的正确Boost .lib,不要自己强制使用。
对象布局更可能是您的问题,不幸的是,诊断起来要困难得多。通过使用完全相同的编译器设置构建EXE和DLL,可以避免麻烦。附加要求是它们必须与用于构建Boost库的设置相匹配。这当然是困难的部分,需要时间机器。特别是_HAS_ITERATOR_DEBUGGING宏是一个麻烦制造者,像std :: vector这样的基本STL类将具有不同的布局,这取决于该宏的值。
我意识到这很模糊,但问题上没有足够的信息来真正诊断这个问题。您可以执行的一项非常基本的检查是将返回的boost :: unit_test :: test_suite指针放在监视表达式中。如果您在步入Boost代码时突然看到该对象的成员发生了更改,那么您知道您有一个对象布局问题。接下来发生的事情是高度不可预测的,堆栈溢出肯定是可能的。另一种诊断是使用Debug + Windows + Registers窗口。当您跳过功能时,请确保ESP寄存器值稳定。