1 //  (C) Copyright Gennadiy Rozental 2001.
2 //  Distributed under the Boost Software License, Version 1.0.
3 //  (See accompanying file LICENSE_1_0.txt or copy at
4 //  http://www.boost.org/LICENSE_1_0.txt)
5 
6 //  See http://www.boost.org/libs/test for the library home page.
7 //
8 //!@file
9 //!@brief test case family based on data generator
10 // ***************************************************************************
11 
12 #ifndef BOOST_TEST_DATA_TEST_CASE_HPP_102211GER
13 #define BOOST_TEST_DATA_TEST_CASE_HPP_102211GER
14 
15 // Boost.Test
16 #include <boost/test/data/config.hpp>
17 #include <boost/test/data/dataset.hpp>
18 #include <boost/test/data/for_each_sample.hpp>
19 #include <boost/test/tree/test_unit.hpp>
20 
21 // Boost
22 #include <boost/preprocessor/repetition/enum_params.hpp>
23 #include <boost/preprocessor/repetition/enum_binary_params.hpp>
24 #include <boost/preprocessor/repetition/repeat_from_to.hpp>
25 
26 #include <boost/preprocessor/variadic/to_seq.hpp>
27 #include <boost/preprocessor/variadic/size.hpp>
28 #include <boost/preprocessor/cat.hpp>
29 #include <boost/preprocessor/seq/for_each_i.hpp>
30 #include <boost/preprocessor/seq/for_each.hpp>
31 #include <boost/preprocessor/seq/enum.hpp>
32 #include <boost/preprocessor/control/iif.hpp>
33 #include <boost/preprocessor/comparison/equal.hpp>
34 
35 #include <boost/bind.hpp>
36 #include <boost/type_traits/is_copy_constructible.hpp>
37 
38 #include <boost/test/tools/detail/print_helper.hpp>
39 #include <boost/test/utils/string_cast.hpp>
40 
41 #include <list>
42 #include <string>
43 
44 #include <boost/test/detail/suppress_warnings.hpp>
45 
46 #if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) \
47    && !defined(BOOST_TEST_DATASET_MAX_ARITY)
48 # define BOOST_TEST_DATASET_MAX_ARITY 10
49 #endif
50 
51 //____________________________________________________________________________//
52 
53 namespace boost {
54 namespace unit_test {
55 namespace data {
56 
57 namespace ds_detail {
58 
59 // ************************************************************************** //
60 // **************                     seed                     ************** //
61 // ************************************************************************** //
62 
63 struct seed {
64     template<typename DataSet>
65     typename data::result_of::make<DataSet>::type
operator ->*boost::unit_test::data::ds_detail::seed66     operator->*( DataSet&& ds ) const
67     {
68         return data::make( std::forward<DataSet>( ds ) );
69     }
70 };
71 
72 
73 #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && \
74     !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) && \
75     !defined(BOOST_NO_CXX11_DECLTYPE) && \
76     !defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES) && \
77     !defined(BOOST_NO_CXX11_SMART_PTR)
78 
79 #define BOOST_TEST_DATASET_VARIADIC
80 template <class T>
81 struct parameter_holder {
82     std::shared_ptr<T> value;
83 
parameter_holderboost::unit_test::data::ds_detail::parameter_holder84     parameter_holder(T && value_)
85         : value(std::make_shared<T>(std::move(value_)))
86     {}
87 
operator T const&boost::unit_test::data::ds_detail::parameter_holder88     operator T const&() const {
89         return *value;
90     }
91 };
92 
93 template <class T>
94 parameter_holder<typename std::remove_reference<T>::type>
boost_bind_rvalue_holder_helper_impl(T && value,boost::false_type)95 boost_bind_rvalue_holder_helper_impl(T&& value, boost::false_type /* is copy constructible */) {
96     return parameter_holder<typename std::remove_reference<T>::type>(std::forward<T>(value));
97 }
98 
99 template <class T>
boost_bind_rvalue_holder_helper_impl(T && value,boost::true_type)100 T&& boost_bind_rvalue_holder_helper_impl(T&& value, boost::true_type /* is copy constructible */) {
101     return std::forward<T>(value);
102 }
103 
104 template <class T>
boost_bind_rvalue_holder_helper(T && value)105 auto boost_bind_rvalue_holder_helper(T&& value)
106   -> decltype(boost_bind_rvalue_holder_helper_impl(
107                   std::forward<T>(value),
108                   typename boost::is_copy_constructible<typename std::remove_reference<T>::type>::type()))
109 {
110     // need to use boost::is_copy_constructible because std::is_copy_constructible is broken on MSVC12
111     return boost_bind_rvalue_holder_helper_impl(
112               std::forward<T>(value),
113               typename boost::is_copy_constructible<typename std::remove_reference<T>::type>::type());
114 }
115 
116 #endif
117 
118 
119 // ************************************************************************** //
120 // **************                 test_case_gen                ************** //
121 // ************************************************************************** //
122 
123 template<typename TestCase,typename DataSet>
124 class test_case_gen : public test_unit_generator {
125 public:
126     // Constructor
127 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
test_case_gen(const_string tc_name,const_string tc_file,std::size_t tc_line,DataSet && ds)128     test_case_gen( const_string tc_name, const_string tc_file, std::size_t tc_line, DataSet&& ds )
129     : m_dataset( std::forward<DataSet>( ds ) )
130     , m_generated( false )
131     , m_tc_name( ut_detail::normalize_test_case_name( tc_name ) )
132     , m_tc_file( tc_file )
133     , m_tc_line( tc_line )
134     , m_tc_index( 0 )
135     {}
test_case_gen(test_case_gen && gen)136     test_case_gen( test_case_gen&& gen )
137     : m_dataset( std::move( gen.m_dataset ) )
138     , m_generated( gen.m_generated )
139     , m_tc_name( gen.m_tc_name )
140     , m_tc_file( gen.m_tc_file )
141     , m_tc_line( gen.m_tc_line )
142     , m_tc_index( gen.m_tc_index )
143     , m_test_cases( std::move(gen.m_test_cases) )
144     {}
145 #else
146     test_case_gen( const_string tc_name, const_string tc_file, std::size_t tc_line, DataSet const& ds )
147     : m_dataset( ds )
148     , m_generated( false )
149     , m_tc_name( ut_detail::normalize_test_case_name( tc_name ) )
150     , m_tc_file( tc_file )
151     , m_tc_line( tc_line )
152     , m_tc_index( 0 )
153     {}
154 #endif
155 
156 public:
next() const157     virtual test_unit* next() const
158     {
159         if(!m_generated) {
160             data::for_each_sample( m_dataset, *this );
161             m_generated = true;
162         }
163 
164         if( m_test_cases.empty() )
165             return 0;
166 
167         test_unit* res = m_test_cases.front();
168         m_test_cases.pop_front();
169 
170         return res;
171     }
172 
173 
174 #if !defined(BOOST_TEST_DATASET_VARIADIC)
175     // see BOOST_TEST_DATASET_MAX_ARITY to increase the default supported arity
176     // there is also a limit on boost::bind
177 #define TC_MAKE(z,arity,_)                                                              \
178     template<BOOST_PP_ENUM_PARAMS(arity, typename Arg)>                                 \
179     void    operator()( BOOST_PP_ENUM_BINARY_PARAMS(arity, Arg, const& arg) ) const     \
180     {                                                                                   \
181         m_test_cases.push_back( new test_case( genTestCaseName(), m_tc_file, m_tc_line, \
182            boost::bind( &TestCase::template test_method<BOOST_PP_ENUM_PARAMS(arity,Arg)>,\
183            BOOST_PP_ENUM_PARAMS(arity, arg) ) ) );                                      \
184     }                                                                                   \
185 
186     BOOST_PP_REPEAT_FROM_TO(1, BOOST_TEST_DATASET_MAX_ARITY, TC_MAKE, _)
187 #else
188     template<typename ...Arg>
189     void    operator()(Arg&& ... arg) const
190     {
191         m_test_cases.push_back(
192             new test_case( genTestCaseName(),
193                            m_tc_file,
194                            m_tc_line,
195                            std::bind( &TestCase::template test_method<Arg...>,
196                                       boost_bind_rvalue_holder_helper(std::forward<Arg>(arg))...)));
197     }
198 #endif
199 
200 private:
genTestCaseName() const201     std::string  genTestCaseName() const
202     {
203         return "_" + utils::string_cast(m_tc_index++);
204     }
205 
206     // Data members
207     DataSet                         m_dataset;
208     mutable bool                    m_generated;
209     std::string                     m_tc_name;
210     const_string                    m_tc_file;
211     std::size_t                     m_tc_line;
212     mutable std::size_t             m_tc_index;
213     mutable std::list<test_unit*>   m_test_cases;
214 };
215 
216 //____________________________________________________________________________//
217 
218 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
219 template<typename TestCase,typename DataSet>
220 boost::shared_ptr<test_unit_generator> //test_case_gen<TestCase,DataSet>
make_test_case_gen(const_string tc_name,const_string tc_file,std::size_t tc_line,DataSet && ds)221 make_test_case_gen( const_string tc_name, const_string tc_file, std::size_t tc_line, DataSet&& ds )
222 {
223     return boost::shared_ptr<test_unit_generator>(new test_case_gen<TestCase,DataSet>( tc_name, tc_file, tc_line, std::forward<DataSet>(ds) ));
224 }
225 #else
226 template<typename TestCase,typename DataSet>
227 test_case_gen<TestCase,DataSet>
make_test_case_gen(const_string tc_name,const_string tc_file,std::size_t tc_line,DataSet const & ds)228 make_test_case_gen( const_string tc_name, const_string tc_file, std::size_t tc_line, DataSet const& ds )
229 {
230     return test_case_gen<TestCase,DataSet>( tc_name, tc_file, tc_line, ds );
231 }
232 #endif
233 
234 //____________________________________________________________________________//
235 
236 } // namespace ds_detail
237 
238 // ************************************************************************** //
239 // **************             BOOST_DATA_TEST_CASE             ************** //
240 // ************************************************************************** //
241 
242 #define BOOST_DATA_TEST_CASE_PARAM(r, _, i, param)  (BOOST_PP_CAT(Arg, i) const& param)
243 #define BOOST_DATA_TEST_CONTEXT(r, _, param)  << BOOST_STRINGIZE(param) << " = " << boost::test_tools::tt_detail::print_helper(param) << "; "
244 
245 #define BOOST_DATA_TEST_CASE_PARAMS( params )                           \
246     BOOST_PP_SEQ_ENUM(                                                  \
247         BOOST_PP_SEQ_FOR_EACH_I(BOOST_DATA_TEST_CASE_PARAM, _, params)) \
248 /**/
249 
250 #define BOOST_DATA_TEST_CASE_IMPL(arity, F, test_name, dataset, params) \
251 struct BOOST_PP_CAT(test_name, case) : public F {                       \
252     template<BOOST_PP_ENUM_PARAMS(arity, typename Arg)>                 \
253     static void test_method( BOOST_DATA_TEST_CASE_PARAMS( params ) )    \
254     {                                                                   \
255         BOOST_TEST_CONTEXT( ""                                          \
256             BOOST_PP_SEQ_FOR_EACH(BOOST_DATA_TEST_CONTEXT, _, params))  \
257         {                                                               \
258           BOOST_TEST_CHECKPOINT('"' << #test_name << "\" fixture ctor");\
259           BOOST_PP_CAT(test_name, case) t;                              \
260           BOOST_TEST_CHECKPOINT('"'                                     \
261               << #test_name << "\" fixture setup");                     \
262           boost::unit_test::setup_conditional(t);                       \
263           BOOST_TEST_CHECKPOINT('"' << #test_name << "\" test entry");  \
264           t._impl(BOOST_PP_SEQ_ENUM(params));                           \
265           BOOST_TEST_CHECKPOINT('"'                                     \
266               << #test_name << "\" fixture teardown");                  \
267           boost::unit_test::teardown_conditional(t);                    \
268           BOOST_TEST_CHECKPOINT('"' << #test_name << "\" fixture dtor");\
269         }                                                               \
270     }                                                                   \
271 private:                                                                \
272     template<BOOST_PP_ENUM_PARAMS(arity, typename Arg)>                 \
273     void _impl(BOOST_DATA_TEST_CASE_PARAMS( params ));                  \
274 };                                                                      \
275                                                                         \
276 BOOST_AUTO_TEST_SUITE( test_name,                                       \
277                        *boost::unit_test::decorator::stack_decorator()) \
278                                                                         \
279 BOOST_AUTO_TU_REGISTRAR( BOOST_PP_CAT(test_name, case) )(               \
280     boost::unit_test::data::ds_detail::make_test_case_gen<              \
281                                       BOOST_PP_CAT(test_name, case)>(   \
282           BOOST_STRINGIZE( test_name ),                                 \
283           __FILE__, __LINE__,                                           \
284           boost::unit_test::data::ds_detail::seed{} ->* dataset ),      \
285     boost::unit_test::decorator::collector_t::instance() );             \
286                                                                         \
287 BOOST_AUTO_TEST_SUITE_END()                                             \
288                                                                         \
289     template<BOOST_PP_ENUM_PARAMS(arity, typename Arg)>                 \
290     void BOOST_PP_CAT(test_name, case)::_impl(                          \
291                                 BOOST_DATA_TEST_CASE_PARAMS( params ) ) \
292 /**/
293 
294 #define BOOST_DATA_TEST_CASE_WITH_PARAMS( F, test_name, dataset, ... )  \
295     BOOST_DATA_TEST_CASE_IMPL( BOOST_PP_VARIADIC_SIZE(__VA_ARGS__),     \
296                                F, test_name, dataset,                   \
297                                BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__) )  \
298 /**/
299 #define BOOST_DATA_TEST_CASE_NO_PARAMS( F, test_name, dataset )         \
300     BOOST_DATA_TEST_CASE_WITH_PARAMS( F, test_name, dataset, sample )   \
301 /**/
302 
303 #if BOOST_PP_VARIADICS_MSVC
304 
305 #define BOOST_DATA_TEST_CASE( ... )                                     \
306     BOOST_PP_CAT(                                                       \
307     BOOST_PP_IIF(BOOST_PP_EQUAL(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__),2), \
308                      BOOST_DATA_TEST_CASE_NO_PARAMS,                    \
309                      BOOST_DATA_TEST_CASE_WITH_PARAMS) (                \
310                         BOOST_AUTO_TEST_CASE_FIXTURE, __VA_ARGS__), )   \
311 /**/
312 
313 #define BOOST_DATA_TEST_CASE_F( F, ... )                                \
314     BOOST_PP_CAT(                                                       \
315     BOOST_PP_IIF(BOOST_PP_EQUAL(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__),2), \
316                      BOOST_DATA_TEST_CASE_NO_PARAMS,                    \
317                      BOOST_DATA_TEST_CASE_WITH_PARAMS) (                \
318                         F, __VA_ARGS__), )                              \
319 /**/
320 
321 #else
322 
323 #define BOOST_DATA_TEST_CASE( ... )                                     \
324     BOOST_PP_IIF(BOOST_PP_EQUAL(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__),2), \
325                      BOOST_DATA_TEST_CASE_NO_PARAMS,                    \
326                      BOOST_DATA_TEST_CASE_WITH_PARAMS) (                \
327                         BOOST_AUTO_TEST_CASE_FIXTURE, __VA_ARGS__)      \
328 /**/
329 
330 #define BOOST_DATA_TEST_CASE_F( F, ... )                                \
331     BOOST_PP_IIF(BOOST_PP_EQUAL(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__),2), \
332                      BOOST_DATA_TEST_CASE_NO_PARAMS,                    \
333                      BOOST_DATA_TEST_CASE_WITH_PARAMS) (                \
334                         F, __VA_ARGS__)                                 \
335 /**/
336 #endif
337 
338 } // namespace data
339 } // namespace unit_test
340 } // namespace boost
341 
342 #include <boost/test/detail/enable_warnings.hpp>
343 
344 #endif // BOOST_TEST_DATA_TEST_CASE_HPP_102211GER
345 
346