As part of my unit testing I want to ensure code coverage of the tests. The aim is to place something like REQUIRE_TEST
macros somewhere in the code and check whether all of these were called.
void foo(bool b) {
if (b) {
REQUIRE_TEST
...
} else {
REQUIRE_TEST
...
}
}
void main() {
foo(true);
output_all_missed_REQUIRE_macros();
}
Ideally the output would include source-file and line of the macro.
My initial idea was to have the macros create static objects that would register themselves in some map and later check whether all of these were called
#define REQUIRE_TEST \
do { \
static ___RequiredTest requiredTest(__func__, __FILE__, __LINE__);\
(void)requiredTest;\
___RequiredTest::increaseCounter(__func__, __FILE__, __LINE__);\
} while(false)
but the static object are only created when the code is called the first time. So the map only contains functions that are also counted in the next line - missing REQUIRE_TEST macros are not found. the __attribute__((used))
is ignored in this case.
The gcc has a nice attribute __attribute__((constructor))
, but apparently chooses to ignore it when placed here (following code instead of the static object)
struct teststruct { \
__attribute__((constructor)) static void bla() {\
___RequiredTest::register(__func__, __FILE__, __LINE__); \
} \
};\
as well as for
[]() __attribute__((constructor)) { \
___RequiredTest::register(__func__, __FILE__, __LINE__); \
};\
The only way out I can think of now is a) manually (or via script) analyse the code outside of the regular compilation (uargh) or b) use the __COUNTER__
macro to count the macros - but then I would not know which specific REQUIRE_TEST macros were not called... (and everything breaks if somebody else decides to use the __COUNTER__
macro as well...)
Are there any decent solutions to this problem? What am I missing? It would be nice to have a macro that appends the current line and file so some preprocessor variable whenever it is called - but that is not possible, right? Are there any other ways to register something to be executed before
main()
that can be done within a function body?
An ugly but simple approach is for REQUIRE_TEST
to use __LINE__
or __COUNTER__
to construct the name of a unique file-scope static object which it refers to, and which will cause a compiler error if it hasn't been declared. You then need to manually declare all such objects up front, one for each REQUIRE_TEST
- but at least you get a compile error if you haven't done so.
Hey, I said it was ugly!
Jeremy gave me the right idea to have a closer look at the sections. His answer works - but only without inline
functions. With some more research I was able to find the following solution which is just as much gcc dependent (due to the name of the section) but more flexible (and works in inline functions). The macro is now as follows:
#define REQUIRE_TEST \
do { \
struct ___a{ static void rt() {\
___RequiredTest::register_test(__FILE__, __LINE__);\
} };\
static auto ___rtp __attribute__((section(".init_array"))) = &___a::rt; \
(void) ___rtp; \
___RequiredTest::increase_counter(__FILE__, __LINE__); \
} while(false)
Placing the function pointer in the section .init_array
actually places it in the list of initialization functions that are being called before main. This way it can be assured, that the locally defined function is being called before main.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With