[Test] unit test framework - Problems with static object creation during test case destruction

Here is the scenario: A test case implemented as a class and the class uses boost::serialization in the destructor to serialize some objects. On windows, with both VC7.1 and VC8, I would get access violations and runtime-error - pure virtual function call. Turned out it is because test cases are not destroyed until the unit test framework singleton instance goes away which happens only after the main returns. boost::serialization uses extensively function scope static objects and these objects are instantiated during program termination in this particular case. There are some issues AFAICS: 1. function-scope static objects deletion order 2. At least with MSVC, atexit is called when a function-scope static object is created. It seems that the implementation does not support calling atexit when doexit is running. I took a quick glance at the standard but did not find anything that mentions when atexit can be called. I was able to resolve the problem by forcing the deletion and recreation of the static unit test framework singleton object but I am not sure if there will be other consequences. Thanks, Sean

"Sean Huang" <huangsean@hotmail.com> wrote in message news:BAY102-DAV1177B755E23C045FE1092BA3870@phx.gbl...
Here is the scenario: A test case implemented as a class and the class uses boost::serialization in the destructor to serialize some objects. On windows, with both VC7.1 and VC8, I would get access violations and runtime-error - pure virtual function call. Turned out it is because test cases are not destroyed until the unit test framework singleton instance goes away which happens only after the main returns. boost::serialization uses extensively function scope static objects and these objects are instantiated during program termination in this particular case. There are some issues AFAICS: 1. function-scope static objects deletion order 2. At least with MSVC, atexit is called when a function-scope static object is created. It seems that the implementation does not support calling atexit when doexit is running. I took a quick glance at the standard but did not find anything that mentions when atexit can be called.
I was able to resolve the problem by forcing the deletion and recreation of the static unit test framework singleton object but I am not sure if there will be other consequences.
Above information does not give me a clear picture of what is going on. Could you please post some example code, so I could comment more intelligently. Gennadiy

----- Original Message ----- From: "Gennadiy Rozental" <gennadiy.rozental@thomson.com>
"Sean Huang" <huangsean@hotmail.com> wrote in message news:BAY102-DAV1177B755E23C045FE1092BA3870@phx.gbl...
Here is the scenario: A test case implemented as a class and the class uses boost::serialization in the destructor to serialize some objects. On windows, with both VC7.1 and VC8, I would get access violations and runtime-error - pure virtual function call. Turned out it is because test cases are not destroyed until the unit test framework singleton instance goes away which happens only after the main returns. boost::serialization uses extensively function scope static objects and these objects are instantiated during program termination in this particular case. There are some issues AFAICS: 1. function-scope static objects deletion order 2. At least with MSVC, atexit is called when a function-scope static object is created. It seems that the implementation does not support calling atexit when doexit is running. I took a quick glance at the standard but did not find anything that mentions when atexit can be called.
I was able to resolve the problem by forcing the deletion and recreation of the static unit test framework singleton object but I am not sure if there will be other consequences.
Above information does not give me a clear picture of what is going on. Could you please post some example code, so I could comment more intelligently.
Here is an example: #include <map> #include <iostream> #include <string> class T { public: T() { std::cout << "In T's constructor" << std::endl; } ~T() { std::cout << "In T's destructor" << std::endl; } }; class Test { public: void Func( void ) { static T staticObj; } ~Test() { Func(); } }; static Test test; int main(int argc, char* argv[]) { return 0; } T's destructor will not be called. If there are too many static objects like this (as in the case of boost::archive and boost::serialization), the program crashes with access violations because the CRT tries to realloc the function list used by atexit(). Hope this clarifies things a little. Sean

"Sean Huang" <huangsean@hotmail.com> wrote in message news:BAY102-DAV363FF7185FDF2B8806438A3840@phx.gbl...
----- Original Message ----- From: "Gennadiy Rozental" <gennadiy.rozental@thomson.com>
"Sean Huang" <huangsean@hotmail.com> wrote in message news:BAY102-DAV1177B755E23C045FE1092BA3870@phx.gbl...
Here is the scenario: A test case implemented as a class and the class uses boost::serialization in the destructor to serialize some objects. On windows, with both VC7.1 and VC8, I would get access violations and runtime-error - pure virtual function call. Turned out it is because test cases are not destroyed until the unit test framework singleton instance goes away which happens only after the main returns. boost::serialization uses extensively function scope static objects and these objects are instantiated during program termination in this particular case. There are some issues AFAICS: 1. function-scope static objects deletion order 2. At least with MSVC, atexit is called when a function-scope static object is created. It seems that the implementation does not support calling atexit when doexit is running. I took a quick glance at the standard but did not find anything that mentions when atexit can be called.
I was able to resolve the problem by forcing the deletion and recreation of the static unit test framework singleton object but I am not sure if there will be other consequences.
Above information does not give me a clear picture of what is going on. Could you please post some example code, so I could comment more intelligently.
Here is an example:
...
T's destructor will not be called. If there are too many static objects like this (as in the case of boost::archive and boost::serialization), the program crashes with access violations because the CRT tries to realloc the function list used by atexit().
Hope this clarifies things a little.
I still do not see Boost.Test references. Could you please give an example that shows the Boost Test involvement. Gennadiy

----- Original Message ----- From: "Gennadiy Rozental" <gennadiy.rozental@thomson.com> Newsgroups: gmane.comp.lib.boost.devel Sent: Thursday, June 22, 2006 5:12 PM Subject: Re: [Test] unit test framework - Problemswithstaticobjectcreation during test case destruction
I still do not see Boost.Test references. Could you please give an example that shows the Boost Test involvement.
class T { public: T() { std::cout << "In T's constructor" << std::endl; } ~T() { std::cout << "In T's destructor" << std::endl; } }; class Test { public: void Func( void ) { static T staticObj; } void Run( void ) { } ~Test() { Func(); } }; boost::unit_test::test_suite* init_unit_test_suite( int argc, char* argv[] ) { test_suite* master = BOOST_TEST_SUITE( "Test" ); boost::shared_ptr< Test > test( new Test ); test_case * testCase = BOOST_CLASS_TEST_CASE( &Test::Run, test ); return master; }

"Sean Huang" <huangsean@hotmail.com> wrote in message news:BAY102-DAV1766E6447086FCFD5171BCA37B0@phx.gbl...
----- Original Message ----- From: "Gennadiy Rozental" <gennadiy.rozental@thomson.com> Newsgroups: gmane.comp.lib.boost.devel Sent: Thursday, June 22, 2006 5:12 PM Subject: Re: [Test] unit test framework - Problemswithstaticobjectcreation
during test case destruction
I still do not see Boost.Test references. Could you please give an
example
that shows the Boost Test involvement.
class T { public: T() { std::cout << "In T's constructor" << std::endl; } ~T() { std::cout << "In T's destructor" << std::endl; }
};
class Test { public: void Func( void ) { static T staticObj; } void Run( void ) { } ~Test() { Func(); }
};
boost::unit_test::test_suite* init_unit_test_suite( int argc, char* argv[] ) {
test_suite* master = BOOST_TEST_SUITE( "Test" ); boost::shared_ptr< Test > test( new Test );
test_case * testCase = BOOST_CLASS_TEST_CASE( &Test::Run, test ); return master; }
I see. I guess I could release test class instance once test case is done. But that would prevent anyone from running several different tests with the same test tree (once I support interactive based testing it will be possible). Gennadiy

"Gennadiy Rozental" <gennadiy.rozental@thomson.com> wrote in message news:e7iipp$6mu$1@sea.gmane.org...
I see. I guess I could release test class instance once test case is done. But that would prevent anyone from running several different tests with the same test tree (once I support interactive based testing it will be possible).
My workaround is to force the deletion and reconstruction of s_frk_impl() before main returns. The following is what I added: s_frk_impl().~framework_impl(); // reconstruct object so it can be deleted new ( & ( s_frk_impl() ) ) framework_impl(); Do you see any problems with this approach? Ideally, a destructible singleton can be used to avoid such hacks. Sean

"Sean Huang" <huangsean@hotmail.com> wrote in message news:e7jnce$sh7$1@sea.gmane.org...
"Gennadiy Rozental" <gennadiy.rozental@thomson.com> wrote in message news:e7iipp$6mu$1@sea.gmane.org...
I see. I guess I could release test class instance once test case is done. But that would prevent anyone from running several different tests with the same test tree (once I support interactive based testing it will be possible).
My workaround is to force the deletion and reconstruction of s_frk_impl() before main returns. The following is what I added:
s_frk_impl().~framework_impl();
// reconstruct object so it can be deleted
new ( & ( s_frk_impl() ) ) framework_impl();
Do you see any problems with this approach? Ideally, a destructible singleton can be used to avoid such hacks.
IMO It's not a framework responsibility. If you need to workaround some compiler deficiencies use global fixture and do all necessary work in it's destructor. Gennadiy
participants (2)
-
Gennadiy Rozental
-
Sean Huang