Modify

Ticket #12712 (closed Patches: fixed)

Opened 4 months ago

Last modified 2 months ago

BOOST_AUTO_TEST_SUITE: Generate unique names by using __COUNTER__

Reported by: ki.stfu@… Owned by: renficiaud
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 )

`

Attachments

Change History

comment:1 Changed 4 months ago by ki.stfu@…

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 Changed 4 months ago by ki.stfu@…

Friendly ping.

comment:3 Changed 3 months ago by ki.stfu@…

Friendly ping.

comment:4 Changed 3 months ago by ki.stfu@…

  • Version changed from Boost 1.62.0 to Boost Development Trunk

comment:5 Changed 3 months ago by renficiaud

  • Owner changed from rogeeff to renficiaud

comment:6 Changed 3 months ago by renficiaud

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 Changed 3 months ago by ki.stfu@…

  • 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 Changed 3 months ago by renficiaud

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 Changed 3 months ago by ki.stfu@…

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 Changed 3 months ago by renficiaud

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 Changed 3 months ago by ki.stfu@…

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 Changed 3 months ago by ki.stfu@…

Ugh... I meant

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

comment:13 Changed 3 months ago by renficiaud

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

comment:14 Changed 3 months ago by renficiaud

  • Status changed from new to assigned
  • Milestone changed from To Be Determined to Boost 1.64.0

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

Thanks,

comment:15 Changed 3 months ago by ki.stfu@…

Yes, it works!

comment:16 Changed 2 months ago by renficiaud

  • Status changed from assigned to closed
  • Resolution set to fixed

Merged to master, rev a2b73f5d7568e69162dfac213fab87eea0021ec5

View

Add a comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
The resolution will be deleted. Next status will be 'reopened'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.