Opened 6 years ago

Closed 6 years ago

#12712 closed Patches (fixed)

BOOST_AUTO_TEST_SUITE: Generate unique names by using __COUNTER__

Reported by: ki.stfu@… Owned by: Raffi Enficiaud
Milestone: Boost 1.64.0 Component: test
Version: Boost Development Trunk Severity: Problem
Keywords: Cc: raffi.enficiaud@…, ki.stfu@…

Description

There is a problem with non-unique names of <test_suite_name>_registrar in BOOST_AUTO_TEST_SUITE when joining multiple source files into one file using #include:

I have a project with huge number of tests in different files and it takes around 10 minutes to compile. The project structure is like:

.../test/data/foo.cpp: ` #include "stdafx.h"

BOOST_AUTO_TEST_SUITE(data) BOOST_AUTO_TEST_SUITE(foo)

<my test cases>

BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() `

.../test/data/bar.cpp: ` #include "stdafx.h"

BOOST_AUTO_TEST_SUITE(data) BOOST_AUTO_TEST_SUITE(bar)

<my test cases>

BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() `

To reduce the compilation time I included all sources into one file and after that I need to compile only that file (so it works much faster): .../test/_all.cpp: ` #include "stdafx.h"

#include "data/foo.cpp" #include "data/bar.cpp" and so on...

`

But when I'm compiling this file I get a multiple definitions of "data_registrar3" (3 is a substituted LINE).

I think we can use COUNTER if it's supported (it's not a standard feature but most of compilers support it) so that it will fix the problem.

Here is my patch: ` $ diff unit_test_suite.hpp.original unit_test_suite.hpp -uar --- unit_test_suite.hpp.original 2016-12-26 11:01:33.272912600 +0300 +++ unit_test_suite.hpp 2016-12-26 11:00:37.171138300 +0300 @@ -24,6 +24,11 @@

#include <boost/test/detail/pp_variadic.hpp>

+#if defined(COUNTER) +#define BOOST_TEST_UNIQUE_NUMBER COUNTER +#else +#define BOOST_TEST_UNIQUE_NUMBER LINE +#endif

@@ -120,7 +125,7 @@

#define BOOST_AUTO_TEST_SUITE_END() \

-BOOST_AUTO_TU_REGISTRAR( BOOST_JOIN( end_suite, LINE ) )( 1 ); \ +BOOST_AUTO_TU_REGISTRAR( BOOST_JOIN( end_suite, BOOST_TEST_UNIQUE_NUMBER ) )( 1 ); \

} \ //

@@ -298,7 +303,7 @@

#define BOOST_TEST_DECORATOR( D ) \ static boost::unit_test::decorator::collector const& \

-BOOST_JOIN(decorator_collector,LINE) = D; \ +BOOST_JOIN(decorator_collector,BOOST_TEST_UNIQUE_NUMBER) = D; \

//

@@ -322,7 +327,7 @@

#define BOOST_AUTO_TU_REGISTRAR( test_name ) \ static boost::unit_test::ut_detail::auto_test_unit_registrar \

-BOOST_JOIN( BOOST_JOIN( test_name, _registrar ), LINE ) \ +BOOST_JOIN( BOOST_JOIN( test_name, _registrar ), BOOST_TEST_UNIQUE_NUMBER ) \

// #define BOOST_AUTO_TC_INVOKER( test_name ) BOOST_JOIN( test_name, _invoker ) #define BOOST_AUTO_TC_UNIQUE_ID( test_name ) BOOST_JOIN( test_name, _id )

`

Change History (16)

comment:1 by ki.stfu@…, 6 years ago

Oops. I fixed its style:


There is a problem with non-unique names of <test_suite_name>_registrar in BOOST_AUTO_TEST_SUITE when joining multiple source files into one file using #include:

I have a project with huge number of tests in different files and it takes around 10 minutes to compile. The project structure is like:

.../test/data/foo.cpp:

#include "stdafx.h"

BOOST_AUTO_TEST_SUITE(data)
BOOST_AUTO_TEST_SUITE(foo)

<my test cases>

BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()

.../test/data/bar.cpp:

#include "stdafx.h"

BOOST_AUTO_TEST_SUITE(data)
BOOST_AUTO_TEST_SUITE(bar)

<my test cases>

BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()

To reduce the compilation time I included all sources into one file and after that I need to compile only that file (so it works much faster):

.../test/_all.cpp:

#include "stdafx.h"

#include "data/foo.cpp"
#include "data/bar.cpp"
and so on...

But when I'm compiling this file I get a multiple definitions of data_registrar3 (3 is a substituted __LINE__).

I think we can use __COUNTER__ if it's supported (it's not a standard feature but most of compilers support it) so that it will fix the problem.

Here is my patch:

$ diff unit_test_suite.hpp.original unit_test_suite.hpp -uar
--- unit_test_suite.hpp.original        2016-12-26 11:01:33.272912600 +0300
+++ unit_test_suite.hpp 2016-12-26 11:00:37.171138300 +0300
@@ -24,6 +24,11 @@

 #include <boost/test/detail/pp_variadic.hpp>

+#if defined(__COUNTER__)
+#define BOOST_TEST_UNIQUE_NUMBER __COUNTER__
+#else
+#define BOOST_TEST_UNIQUE_NUMBER __LINE__
+#endif

 //____________________________________________________________________________//

@@ -120,7 +125,7 @@
 // ************************************************************************** //

 #define BOOST_AUTO_TEST_SUITE_END()                                     \
-BOOST_AUTO_TU_REGISTRAR( BOOST_JOIN( end_suite, __LINE__ ) )( 1 );      \
+BOOST_AUTO_TU_REGISTRAR( BOOST_JOIN( end_suite, BOOST_TEST_UNIQUE_NUMBER ) )( 1 );      \
 }                                                                       \
 /**/

@@ -298,7 +303,7 @@

 #define BOOST_TEST_DECORATOR( D )                                       \
 static boost::unit_test::decorator::collector const&                    \
-BOOST_JOIN(decorator_collector,__LINE__) = D;                           \
+BOOST_JOIN(decorator_collector,BOOST_TEST_UNIQUE_NUMBER) = D;                           \
 /**/

 // ************************************************************************** //
@@ -322,7 +327,7 @@

 #define BOOST_AUTO_TU_REGISTRAR( test_name )                    \
 static boost::unit_test::ut_detail::auto_test_unit_registrar    \
-BOOST_JOIN( BOOST_JOIN( test_name, _registrar ), __LINE__ )     \
+BOOST_JOIN( BOOST_JOIN( test_name, _registrar ), BOOST_TEST_UNIQUE_NUMBER )     \
 /**/
 #define BOOST_AUTO_TC_INVOKER( test_name )      BOOST_JOIN( test_name, _invoker )
 #define BOOST_AUTO_TC_UNIQUE_ID( test_name )    BOOST_JOIN( test_name, _id )

comment:2 by ki.stfu@…, 6 years ago

Friendly ping.

comment:3 by ki.stfu@…, 6 years ago

Friendly ping.

comment:4 by ki.stfu@…, 6 years ago

Version: Boost 1.62.0Boost Development Trunk

comment:5 by Raffi Enficiaud, 6 years ago

Owner: changed from Gennadiy Rozental to Raffi Enficiaud

comment:6 by Raffi Enficiaud, 6 years ago

Hi there,

Would you mind checking the current state of boost.test without the #include "stdafx.h" precompiled header? I looks weird to me that those registrar end-up at the same line.

comment:7 by ki.stfu@…, 6 years ago

Cc: ki.stfu@… added

stdafx.h doesn't play any special role in this case because I tested it on Linux with clang. So it can be renamed or even removed, it doesn't matter.

The fact is that __LINE__ is expanded to the current line number and if you have multiple files which define variables on the same line using this naming style then you will get an error.

You can reproduce it by following:

user@u14:~$ ls
_all.cpp  bar.cpp  foo.cpp
user@u14:~$ cat foo.cpp
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(data)
BOOST_AUTO_TEST_SUITE(foo)

BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
user@u14:~$ cat bar.cpp
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(data)
BOOST_AUTO_TEST_SUITE(bar)

BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END()
user@u14:~$ cat _all.cpp
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

#include "foo.cpp"
#include "bar.cpp"
user@u14:~$ clang++ _all.cpp
In file included from _all.cpp:5:
./bar.cpp:3:1: error: redefinition of 'data_registrar3'
BOOST_AUTO_TEST_SUITE(data)
^
/usr/include/boost/test/unit_test_suite.hpp:45:73: note: expanded from macro 'BOOST_AUTO_TEST_SUITE'
namespace suite_name {                                                  \
                                                                        ^
/usr/include/boost/test/unit_test_suite.hpp:209:62: note: expanded from macro '\
BOOST_AUTO_TU_REGISTRAR'
static boost::unit_test::ut_detail::auto_test_unit_registrar BOOST_JOIN( BOOST_JOIN( test_name, _registrar ), __LINE__ )
                                                             ^
/usr/include/boost/config/suffix.hpp:608:28: note: expanded from macro 'BOOST_JOIN'
#define BOOST_JOIN( X, Y ) BOOST_DO_JOIN( X, Y )
                           ^
/usr/include/boost/config/suffix.hpp:609:31: note: expanded from macro 'BOOST_DO_JOIN'
#define BOOST_DO_JOIN( X, Y ) BOOST_DO_JOIN2(X,Y)
                              ^
/usr/include/boost/config/suffix.hpp:610:32: note: expanded from macro 'BOOST_DO_JOIN2'
#define BOOST_DO_JOIN2( X, Y ) X##Y
                               ^
<scratch space>:114:1: note: expanded from here
data_registrar3
^
./foo.cpp:3:1: note: previous definition is here
BOOST_AUTO_TEST_SUITE(data)
^
/usr/include/boost/test/unit_test_suite.hpp:45:73: note: expanded from macro 'BOOST_AUTO_TEST_SUITE'
namespace suite_name {                                                  \
                                                                        ^
/usr/include/boost/test/unit_test_suite.hpp:209:62: note: expanded from macro '\
BOOST_AUTO_TU_REGISTRAR'
static boost::unit_test::ut_detail::auto_test_unit_registrar BOOST_JOIN( BOOST_JOIN( test_name, _registrar ), __LINE__ )
                                                             ^
/usr/include/boost/config/suffix.hpp:608:28: note: expanded from macro 'BOOST_JOIN'
#define BOOST_JOIN( X, Y ) BOOST_DO_JOIN( X, Y )
                           ^
/usr/include/boost/config/suffix.hpp:609:31: note: expanded from macro 'BOOST_DO_JOIN'
#define BOOST_DO_JOIN( X, Y ) BOOST_DO_JOIN2(X,Y)
                              ^
/usr/include/boost/config/suffix.hpp:610:32: note: expanded from macro 'BOOST_DO_JOIN2'
#define BOOST_DO_JOIN2( X, Y ) X##Y
                               ^
<scratch space>:96:1: note: expanded from here
data_registrar3
^

comment:8 by Raffi Enficiaud, 6 years ago

Thanks for the clarification. The problem with __COUNTER__ is that it is not a standard macro, I would rather not include that. Also since those registrar are static, there is no clash in having the exact same declarations (in terms of lines) in 2 different compilation units, if you do not include everything in one place.

That is to me a really specific use case, and also has the following drawbacks:

  • each time a .cpp changes, a very big block is recompiled
  • you do not benefit from potential accelerations of the build system ( builds)

I am not sure the benefits of doing it the way you do outweigh the problems (maybe you have numbers). I would rather suggest a nasty patch: you can specify a line number with the #line preprocessor directive, which may be used in conjunction with __COUNTER__ in your case to lower the risks for clashes.

comment:9 by ki.stfu@…, 6 years ago

I don't think I'm doing anything illegal and in my case the game is worth the candle.

__COUNTER__ is supported by major compilers (MSVC, Clang, GCC) and is already used in Boost library. Moreover, it suits better for generating unique names than __LINE__.

So why we can't define BOOST_TEST_UNIQUE_NUMBER macro, which refers to __COUNTER__ or (if it's not available to) __LINE__?

comment:10 by Raffi Enficiaud, 6 years ago

I understand that this is properly handled by major compilers, but not all of them. Maybe there is a way to know if this macro is supported. If so, it would be possible to integrate that, if not then it will be a won't fix.

comment:11 by ki.stfu@…, 6 years ago

How about the way suggested above?

` #if defined(COUNTER) #define BOOST_TEST_UNIQUE_NUMBER COUNTER #else #define BOOST_TEST_UNIQUE_NUMBER LINE #endif `

comment:12 by ki.stfu@…, 6 years ago

Ugh... I meant

#if defined(__COUNTER__)
#define BOOST_TEST_UNIQUE_NUMBER __COUNTER__
#else
#define BOOST_TEST_UNIQUE_NUMBER __LINE__
#endif

comment:13 by Raffi Enficiaud, 6 years ago

Ok, then I'll check with that, I'll keep you updated.

comment:14 by Raffi Enficiaud, 6 years ago

Milestone: To Be DeterminedBoost 1.64.0
Status: newassigned

Would you please give a try to the branch topic/12712-several-test-suite-decl-in-same-comp-unit ?

Thanks,

comment:15 by ki.stfu@…, 6 years ago

Yes, it works!

comment:16 by Raffi Enficiaud, 6 years ago

Resolution: fixed
Status: assignedclosed

Merged to master, rev a2b73f5d7568e69162dfac213fab87eea0021ec5

Note: See TracTickets for help on using tickets.