Oct 13
Writing a custom check macro for the BOOST test library
In this article I demonstrate how to write your own check macros for the boost test library (not guaranteed to work in the future)
Full source code for this example can be downloaded here.
The problem
For the purposes of this article I would like to verify that a collection is monotonically increasing. There are a couple ways I can do this. The first is to just do it inline in every test:
... if(begin != end){ IteratorType previous = begin; IteratorType current = begin; current++; for(;current != end; current++){ BOOST_CHECK_LT(*previous, *current); } } ...
This approach is tedious and does not generate good error messages. Each failure generates an independent error message that doesn’t give any context such as the collection being checked or the index at which the error was found.
The second approach is to make a function:
template bool check_monotonically_increasing(Iterator begin, Iterator end){ ... } ... ITOS_CHECK(check_monotonically_increasing(begin, end));
This approach is less tedious, but now the error message only tells you the check failed without any context as to where and why. So clearly what we really want here is something like BOOST_CHECK_EQUAL_COLLECTIONS.
FTM_*_COLLECTION_MONO_INCREASE
The solution is to define a set of macros called FTM_*_COLLECTION_MONO_INCREASE that can be used as follows:
... FTM_CHECK_COLLECTION_MONO_INCREASE(begin, end); ...
Where are the BOOST_* macros?
All of the code I looked at to create my own MACRO was located in boost/test_tools.hpp . This is the best place to start looking when you want to write your own.
Defining Your Test as a Functor
The first step to defining a custom test macro is to implement it as a functor. If you do now know what a functor is please refer to this article.
In this case, the functor must return a value of the type boost::test_tools::predicate_result. predicate_result combines a Boolean status with, when appropriate, an error message that will be displayed on failure.
namespace fm{namespace test{namespace detail{ struct check_mono_increase_func_frwd{ template<typename ForwardIteratorA, typename ForwardIteratorB> boost::test_tools::predicate_result operator()(ForwardIteratorA begin, ForwardIteratorB end) const{ boost::test_tools::predicate_result result = true; std::size_t pos = 0; if(begin == end) return result; ForwardIteratorA previous = begin; ForwardIteratorA current = begin; current++; while(current != end){ if(*previous > *current){ result = false; result.message() << "\n(position: " << pos << "): "; result.message() << *previous << " > " << *current; } previous = current; current++; pos++; } return result; } }; }}}
Creating Your Macros
If you look in test_tools.hpp there are a number of different macros used to implement the various check macros. The key to this step is finding an example that is close to what you want and modifying it. BOOST_* macros are the most straight forward and BOOST_*_EXCEPTION and BOOST_*_EQUAL_COLLECTIONS provide examples of performing more complicated tasks.
In this case I’m going to essentially copy BOOST_*_EQUALS as follows:
#define FTM_WARN_COLLECTION_MONO_INCREASE(Begin, End) \ BOOST_CHECK_WITH_ARGS_IMPL(::ftm::test::detail::check_mono_increase_func_frwd(), \ "FTM_WARN_COLLECTION_MONO_INCREASE", \ WARN, CHECK_PRED_WITH_ARGS, (Begin)(End)); #define FTM_CHECK_COLLECTION_MONO_INCREASE(Begin, End) \ BOOST_CHECK_WITH_ARGS_IMPL(::ftm::test::detail::check_mono_increase_func_frwd(), "FTM_CHECK_COLLECTION_MONO_INCREASE", \ CHECK, CHECK_PRED_WITH_ARGS, (Begin)(End)); #define FTM_REQUIRE_COLLECTION_MONO_INCREASE(Begin, End) \ BOOST_CHECK_WITH_ARGS_IMPL(::ftm::test::detail::check_mono_increase_func_frwd(), "FTM_REQUIRE_COLLECTION_MONO_INCREASE", \ REQUIRE, CHECK_PRED_WITH_ARGS, (Begin)(End));
Using the Macro
Now we can simply use the FTM macros like we’d use any of the BOOST test macros
BOOST_AUTO_TEST_CASE(test_increase){ int test_array[] = {1,2,3,67,32,17,44,12}; FTM_CHECK_COLLECTION_MONO_INCREASE(test_array, test_array + 8); }
Final Thoughts
It’s worth noting that although adding your own boost test library macro can make unit tests a lot easier to write for your development team, the API doesn’t give me a lot of confidence that it was intended to be extended in this manner. I’m not sure what guarantee there is, if any, that what works in this release will work in the next.
Michael Smit is a software engineer in Maryland who currently develops software for spacecraft, spacecraft simulators, and spacecraft control systems.The Author
No Comments
Leave a comment