1 #ifndef CORELIB___TEST_BOOST__HPP
2 #define CORELIB___TEST_BOOST__HPP
3 
4 /*  $Id: test_boost.hpp 631032 2021-05-12 14:09:52Z ivanov $
5  * ===========================================================================
6  *
7  *                            PUBLIC DOMAIN NOTICE
8  *               National Center for Biotechnology Information
9  *
10  *  This software/database is a "United States Government Work" under the
11  *  terms of the United States Copyright Act.  It was written as part of
12  *  the author's official duties as a United States Government employee and
13  *  thus cannot be copyrighted.  This software/database is freely available
14  *  to the public for use. The National Library of Medicine and the U.S.
15  *  Government have not placed any restriction on its use or reproduction.
16  *
17  *  Although all reasonable efforts have been taken to ensure the accuracy
18  *  and reliability of the software and data, the NLM and the U.S.
19  *  Government do not and cannot warrant the performance or results that
20  *  may be obtained by using this software or data. The NLM and the U.S.
21  *  Government disclaim all warranties, express or implied, including
22  *  warranties of performance, merchantability or fitness for any particular
23  *  purpose.
24  *
25  *  Please cite the author in any work or product based on this material.
26  *
27  * ===========================================================================
28  *
29  * Author:  Pavel Ivanov
30  *
31  */
32 
33 /// @file test_boost.hpp
34 ///   Utility stuff for more convenient using of Boost.Test library.
35 ///
36 /// This header must be included before any Boost.Test header
37 /// (if you have any).
38 
39 #ifdef BOOST_CHECK
40 #  error "test_boost.hpp should be included before any Boost.Test header"
41 #endif
42 
43 
44 #include <corelib/expr.hpp>
45 #include <corelib/ncbiapp.hpp>
46 #include <corelib/request_ctx.hpp>
47 #include <corelib/ncbisys.hpp>
48 #include <corelib/ncbimtx.hpp>
49 
50 
51 // Keep Boost's inclusion of <limits> from breaking under old WorkShop versions.
52 #if defined(numeric_limits)  &&  defined(NCBI_NUMERIC_LIMITS)
53 #  undef numeric_limits
54 #endif
55 
56 // BOOST_AUTO_TEST_MAIN should not be defined - it is in test_boost library
57 #ifdef BOOST_AUTO_TEST_MAIN
58 #  undef BOOST_AUTO_TEST_MAIN
59 #endif
60 
61 #ifdef NCBI_COMPILER_MSVC
62 #  pragma warning(push)
63 // 'class' : class has virtual functions, but destructor is not virtual
64 #  pragma warning(disable: 4265)
65 #endif
66 
67 #include <boost/version.hpp>
68 #include <boost/test/unit_test.hpp>
69 #if BOOST_VERSION >= 105900
70 #  include <boost/test/tools/floating_point_comparison.hpp>
71 #else
72 #  include <boost/test/floating_point_comparison.hpp>
73 #endif
74 #include <boost/test/framework.hpp>
75 #include <boost/test/execution_monitor.hpp>
76 #include <boost/test/parameterized_test.hpp>
77 #include <boost/test/results_collector.hpp>
78 
79 #if BOOST_VERSION >= 105600
80 #  include <boost/core/ignore_unused.hpp>
81 #endif
82 
83 #include <boost/preprocessor/tuple/rem.hpp>
84 #include <boost/preprocessor/repeat.hpp>
85 #include <boost/preprocessor/array/elem.hpp>
86 #include <boost/preprocessor/arithmetic/inc.hpp>
87 
88 #include <common/boost_skew_guard.hpp>
89 
90 #ifdef NCBI_COMPILER_MSVC
91 #  pragma warning(pop)
92 #endif
93 
94 
95 // Redefine some Boost macros to make them more comfortable and fit them into
96 // the framework.
97 #undef BOOST_CHECK_THROW_IMPL
98 #undef BOOST_CHECK_NO_THROW_IMPL
99 #ifdef BOOST_FIXTURE_TEST_CASE_WITH_DECOR
100 #  undef BOOST_FIXTURE_TEST_CASE_WITH_DECOR
101 #else
102 #  undef BOOST_FIXTURE_TEST_CASE
103 #endif
104 #undef BOOST_PARAM_TEST_CASE
105 
106 
107 /// Check that current boost test case passed (no exceptions or assertions)
108 /// on the moment of calling this method.
109 /// This can be useful to ignore some unit code if (any) previos checks fails.
BOOST_CURRENT_TEST_PASSED()110 inline bool BOOST_CURRENT_TEST_PASSED()
111 {
112     ::boost::unit_test::test_case::id_t id = ::boost::unit_test::framework::current_test_case().p_id;
113     ::boost::unit_test::test_results rc = ::boost::unit_test::results_collector.results(id);
114     return rc.passed();
115 }
116 
117 
118 #if BOOST_VERSION >= 105900
119 #  if BOOST_VERSION >= 106000
120 #    define BOOST_CHECK_THROW_IMPL_EX( S, E, P, postfix, TL, guard )    \
121 do {                                                                    \
122     try {                                                               \
123         BOOST_TEST_PASSPOINT();                                         \
124         S;                                                              \
125         guard;                                                          \
126         BOOST_TEST_TOOL_IMPL( 2, false, "exception " BOOST_STRINGIZE(E) \
127                               " expected but not raised",               \
128                               TL, CHECK_MSG, _ );                       \
129     } catch( E const& ex ) {                                            \
130         ::boost::ignore_unused( ex );                                   \
131         guard;                                                          \
132         BOOST_TEST_TOOL_IMPL( 2, P,                                     \
133                               "exception \"" BOOST_STRINGIZE( E )       \
134                               "\" raised as expected" postfix,          \
135                               TL, CHECK_MSG, _  );                      \
136     } catch (...) {                                                     \
137         guard;                                                          \
138         BOOST_TEST_TOOL_IMPL( 2, false,                                 \
139                               "an unexpected exception was thrown by "  \
140                               BOOST_STRINGIZE( S ),                     \
141                               TL, CHECK_MSG, _ );                       \
142     }                                                                   \
143 } while( ::boost::test_tools::tt_detail::dummy_cond() )                 \
144 /**/
145 
146 #    define BOOST_THROW_AFFIX ""
147 #    define BOOST_EXCEPTION_AFFIX(P)                            \
148     ": validation on the raised exception through predicate \"" \
149     BOOST_STRINGIZE(P) "\""
150 
151 #  else
152 #    define BOOST_CHECK_THROW_IMPL_EX( S, E, P, prefix, TL, guard )     \
153 do {                                                                    \
154     try {                                                               \
155         BOOST_TEST_PASSPOINT();                                         \
156         S;                                                              \
157         guard;                                                          \
158         BOOST_TEST_TOOL_IMPL( 2, false, "exception " BOOST_STRINGIZE(E) \
159                               " is expected",                           \
160                               TL, CHECK_MSG, _ );                       \
161     } catch( E const& ex ) {                                            \
162         ::boost::unit_test::ut_detail::ignore_unused_variable_warning( ex ); \
163         guard;                                                          \
164         BOOST_TEST_TOOL_IMPL( 2, P, prefix BOOST_STRINGIZE( E ) " is caught", \
165                               TL, CHECK_MSG, _  );                      \
166     } catch (...) {                                                     \
167         guard;                                                          \
168         BOOST_TEST_TOOL_IMPL( 2, false,                                 \
169                               "an unexpected exception was thrown by "  \
170                               BOOST_STRINGIZE( S ),                     \
171                               TL, CHECK_MSG, _ );                       \
172     }                                                                   \
173 } while( ::boost::test_tools::tt_detail::dummy_cond() )                 \
174 /**/
175 #  endif
176 
177 #  define BOOST_CHECK_NO_THROW_IMPL_EX( S, TL, guard )                  \
178 do {                                                                    \
179     try {                                                               \
180         S;                                                              \
181         guard;                                                          \
182         BOOST_TEST_TOOL_IMPL( 2, true, "no exceptions thrown by "       \
183                               BOOST_STRINGIZE( S ),                     \
184                               TL, CHECK_MSG, _ );                       \
185     } catch (std::exception& ex) {                                      \
186         guard;                                                          \
187         BOOST_TEST_TOOL_IMPL( 2, false, "an std::exception was thrown by " \
188                               BOOST_STRINGIZE( S ) " : " << ex.what(),  \
189                               TL, CHECK_MSG, _ );                       \
190     } catch( ... ) {                                                    \
191         guard;                                                          \
192         BOOST_TEST_TOOL_IMPL( 2, false, "a nonstandard exception thrown by " \
193                               BOOST_STRINGIZE( S ),                     \
194                               TL, CHECK_MSG, _ );                       \
195     }                                                                   \
196 } while( ::boost::test_tools::tt_detail::dummy_cond() )                 \
197 /**/
198 #else
199 #  define BOOST_CHECK_THROW_IMPL_EX( S, E, P, prefix, TL, guard )        \
200 try {                                                                    \
201     BOOST_TEST_PASSPOINT();                                              \
202     S;                                                                   \
203     guard;                                                               \
204     BOOST_CHECK_IMPL( false, "exception " BOOST_STRINGIZE( E )           \
205                              " is expected", TL, CHECK_MSG ); }          \
206 catch( E const& ex ) {                                                   \
207     boost::unit_test::ut_detail::ignore_unused_variable_warning( ex );   \
208     guard;                                                               \
209     BOOST_CHECK_IMPL( P, prefix BOOST_STRINGIZE( E ) " is caught",       \
210                       TL, CHECK_MSG );                                   \
211 }                                                                        \
212 catch (...) {                                                            \
213     guard;                                                               \
214     BOOST_CHECK_IMPL(false, "an unexpected exception was thrown by "     \
215                             BOOST_STRINGIZE( S ),                        \
216                      TL, CHECK_MSG);                                     \
217 }                                                                        \
218 /**/
219 
220 #  define BOOST_CHECK_NO_THROW_IMPL_EX( S, TL, guard )                       \
221 try {                                                                        \
222     S;                                                                       \
223     guard;                                                                   \
224     BOOST_CHECK_IMPL( true, "no exceptions thrown by " BOOST_STRINGIZE( S ), \
225                       TL, CHECK_MSG );                                       \
226 }                                                                            \
227 catch (std::exception& ex) {                                                 \
228     guard;                                                                   \
229     BOOST_CHECK_IMPL( false, "an std::exception was thrown by "              \
230                              BOOST_STRINGIZE( S ) " : " << ex.what(),        \
231                       TL, CHECK_MSG);                                        \
232 }                                                                            \
233 catch( ... ) {                                                               \
234     guard;                                                                   \
235     BOOST_CHECK_IMPL( false, "a nonstandard exception thrown by "            \
236                              BOOST_STRINGIZE( S ),                           \
237                       TL, CHECK_MSG );                                       \
238 }                                                                            \
239 /**/
240 #endif
241 
242 #ifndef BOOST_THROW_AFFIX // 1.59 or older
243 #  define BOOST_THROW_AFFIX "exception "
244 #  define BOOST_EXCEPTION_AFFIX(P) "incorrect exception "
245 #endif
246 
247 #  define BOOST_CHECK_THROW_IMPL( S, E, P, affix, TL ) \
248     BOOST_CHECK_THROW_IMPL_EX( S, E, P, affix, TL, )
249 #  define BOOST_CHECK_THROW_IMPL_MT_SAFE( S, E, P, affix, TL )                \
250     BOOST_CHECK_THROW_IMPL_EX( S, E, P, affix, TL,                            \
251                                NCBI_NS_NCBI::CFastMutexGuard _ncbitest_guard( \
252                                    NCBI_NS_NCBI::g_NcbiTestMutex) )
253 
254 #define BOOST_WARN_THROW_MT_SAFE( S, E ) \
255     BOOST_CHECK_THROW_IMPL_MT_SAFE( S, E, true, BOOST_THROW_AFFIX, WARN )
256 #define BOOST_CHECK_THROW_MT_SAFE( S, E ) \
257     BOOST_CHECK_THROW_IMPL_MT_SAFE( S, E, true, BOOST_THROW_AFFIX, CHECK )
258 #define BOOST_REQUIRE_THROW_MT_SAFE( S, E ) \
259     BOOST_CHECK_THROW_IMPL_MT_SAFE( S, E, true, BOOST_THROW_AFFIX, REQUIRE )
260 
261 #define BOOST_WARN_EXCEPTION_MT_SAFE( S, E, P )                              \
262     BOOST_CHECK_THROW_IMPL_MT_SAFE( S, E, P( ex ), BOOST_EXCEPTION_AFFIX(P), \
263                                     WARN )
264 #define BOOST_CHECK_EXCEPTION_MT_SAFE( S, E, P )                             \
265     BOOST_CHECK_THROW_IMPL_MT_SAFE( S, E, P( ex ), BOOST_EXCEPTION_AFFIX(P), \
266                                     CHECK )
267 #define BOOST_REQUIRE_EXCEPTION_MT_SAFE( S, E, P )                           \
268     BOOST_CHECK_THROW_IMPL_MT_SAFE( S, E, P( ex ), BOOST_EXCEPTION_AFFIX(P), \
269                                     REQUIRE )
270 
271 #define BOOST_CHECK_NO_THROW_IMPL( S, TL ) \
272     BOOST_CHECK_NO_THROW_IMPL_EX( S, TL, )
273 #define BOOST_CHECK_NO_THROW_IMPL_MT_SAFE( S, TL )                      \
274     BOOST_CHECK_NO_THROW_IMPL_EX( S, TL,                                \
275                                   NCBI_NS_NCBI::CFastMutexGuard         \
276                                   _ncbitest_guard(                      \
277                                       NCBI_NS_NCBI::g_NcbiTestMutex) )
278 
279 #define BOOST_WARN_NO_THROW_MT_SAFE( S ) \
280     BOOST_CHECK_NO_THROW_IMPL_MT_SAFE( S, WARN )
281 #define BOOST_CHECK_NO_THROW_MT_SAFE( S ) \
282     BOOST_CHECK_NO_THROW_IMPL_MT_SAFE( S, CHECK )
283 #define BOOST_REQUIRE_NO_THROW_MT_SAFE( S ) \
284     BOOST_CHECK_NO_THROW_IMPL_MT_SAFE( S, REQUIRE )
285 
286 
287 #if BOOST_VERSION >= 104200
288 #  define NCBI_BOOST_LOCATION()  , boost::execution_exception::location()
289 #else
290 #  define NCBI_BOOST_LOCATION()
291 #endif
292 
293 #ifdef BOOST_FIXTURE_TEST_CASE_NO_DECOR
294 #  if BOOST_VERSION < 106900
295 BEGIN_SCOPE(boost)
296 BEGIN_SCOPE(unit_test)
297 BEGIN_SCOPE(decorator)
298 typedef collector collector_t;
299 END_SCOPE(decorator)
300 END_SCOPE(unit_test)
301 END_SCOPE(boost)
302 #  endif
303 #  define NCBI_BOOST_DECORATOR_ARG \
304     , boost::unit_test::decorator::collector_t::instance()
305 #else
306 #  define NCBI_BOOST_DECORATOR_ARG
307 #endif
308 
309 #ifdef BOOST_FIXTURE_TEST_CASE_NO_DECOR
310 #  define BOOST_FIXTURE_TEST_CASE_WITH_DECOR( test_name, F, decorators ) \
311 struct test_name : public F { void test_method(); };                    \
312                                                                         \
313 static void BOOST_AUTO_TC_INVOKER( test_name )()                        \
314 {                                                                       \
315     NCBI_NS_NCBI::CDiagContext& dctx = NCBI_NS_NCBI::GetDiagContext();  \
316     NCBI_NS_NCBI::CRequestContext& rctx = dctx.GetRequestContext();     \
317     rctx.SetRequestID();                                                \
318     NCBI_NS_NCBI::CRequestContextGuard_Base rg(&rctx);                  \
319     dctx.PrintRequestStart().Print("test_name", #test_name);            \
320     BOOST_TEST_CHECKPOINT('"' << #test_name << "\" fixture entry.");    \
321     test_name t;                                                        \
322     BOOST_TEST_CHECKPOINT('"' << #test_name << "\" entry.");            \
323     try {                                                               \
324         t.test_method();                                                \
325     }                                                                   \
326     catch (NCBI_NS_NCBI::CException& ex) {                              \
327         ERR_POST("Uncaught exception in \""                             \
328                  << boost::unit_test                                    \
329                          ::framework::current_test_case().p_name        \
330                  << "\"" << ex);                                        \
331         char* msg = NcbiSysChar_strdup(ex.what());                      \
332         NCBI_NS_NCBI::CNcbiTestMemoryCleanupList::GetInstance()->Add(msg); \
333         throw boost::execution_exception(                               \
334                 boost::execution_exception::cpp_exception_error,        \
335                 msg                                                     \
336                 NCBI_BOOST_LOCATION() );                                \
337     }                                                                   \
338     BOOST_TEST_CHECKPOINT('"' << #test_name << "\" exit.");             \
339 }                                                                       \
340                                                                         \
341 struct BOOST_AUTO_TC_UNIQUE_ID( test_name ) {};                         \
342                                                                         \
343 static ::NCBI_NS_NCBI::SNcbiTestRegistrar                               \
344 BOOST_JOIN( BOOST_JOIN( test_name, _registrar ), __LINE__ ) (           \
345     boost::unit_test::make_test_case(                                   \
346         &BOOST_AUTO_TC_INVOKER( test_name ), #test_name,                \
347         __FILE__, __LINE__ ),                                           \
348     ::NCBI_NS_NCBI::SNcbiTestTCTimeout<                                 \
349         BOOST_AUTO_TC_UNIQUE_ID( test_name )>::instance()->value(),     \
350     decorators );                                                       \
351                                                                         \
352 void test_name::test_method()                                           \
353 /**/
354 #else
355 #  define BOOST_FIXTURE_TEST_CASE( test_name, F )                       \
356 struct test_name : public F { void test_method(); };                    \
357                                                                         \
358 static void BOOST_AUTO_TC_INVOKER( test_name )()                        \
359 {                                                                       \
360     NCBI_NS_NCBI::CDiagContext& dctx = NCBI_NS_NCBI::GetDiagContext();  \
361     NCBI_NS_NCBI::CRequestContext& rctx = dctx.GetRequestContext();     \
362     rctx.SetRequestID();                                                \
363     NCBI_NS_NCBI::CRequestContextGuard_Base rg(&rctx);                  \
364     dctx.PrintRequestStart().Print("test_name", #test_name);            \
365     test_name t;                                                        \
366     try {                                                               \
367         t.test_method();                                                \
368     }                                                                   \
369     catch (NCBI_NS_NCBI::CException& ex) {                              \
370         ERR_POST("Uncaught exception in \""                             \
371                  << boost::unit_test                                    \
372                          ::framework::current_test_case().p_name        \
373                  << "\"" << ex);                                        \
374         char* msg = NcbiSysChar_strdup(ex.what());                      \
375         NCBI_NS_NCBI::CNcbiTestMemoryCleanupList::GetInstance()->Add(msg); \
376         throw boost::execution_exception(                               \
377                 boost::execution_exception::cpp_exception_error,        \
378                 msg                                                     \
379                 NCBI_BOOST_LOCATION() );                                \
380     }                                                                   \
381 }                                                                       \
382                                                                         \
383 struct BOOST_AUTO_TC_UNIQUE_ID( test_name ) {};                         \
384                                                                         \
385 static ::NCBI_NS_NCBI::SNcbiTestRegistrar                               \
386 BOOST_JOIN( BOOST_JOIN( test_name, _registrar ), __LINE__ ) (           \
387     boost::unit_test::make_test_case(                                   \
388         &BOOST_AUTO_TC_INVOKER( test_name ), #test_name ),              \
389     boost::unit_test::ut_detail::auto_tc_exp_fail<                      \
390         BOOST_AUTO_TC_UNIQUE_ID( test_name )>::instance()->value(),     \
391     ::NCBI_NS_NCBI::SNcbiTestTCTimeout<                                 \
392         BOOST_AUTO_TC_UNIQUE_ID( test_name )>::instance()->value() );   \
393                                                                         \
394 void test_name::test_method()                                           \
395 /**/
396 #endif
397 
398 #define BOOST_PARAM_TEST_CASE( function, begin, end )                       \
399     ::NCBI_NS_NCBI::NcbiTestGenTestCases( function,                         \
400                                           BOOST_TEST_STRINGIZE( function ), \
401                                           (begin), (end) )                  \
402 /**/
403 
404 /// Set timeout value for the test case created using auto-registration
405 /// facility.
406 #define BOOST_AUTO_TEST_CASE_TIMEOUT(test_name, n)                      \
407 struct BOOST_AUTO_TC_UNIQUE_ID( test_name );                            \
408                                                                         \
409 static struct BOOST_JOIN( test_name, _timeout_spec )                    \
410 : ::NCBI_NS_NCBI::                                                      \
411   SNcbiTestTCTimeout<BOOST_AUTO_TC_UNIQUE_ID( test_name ) >             \
412 {                                                                       \
413     BOOST_JOIN( test_name, _timeout_spec )()                            \
414     : ::NCBI_NS_NCBI::                                                  \
415       SNcbiTestTCTimeout<BOOST_AUTO_TC_UNIQUE_ID( test_name ) >( n )    \
416     {}                                                                  \
417 } BOOST_JOIN( test_name, _timeout_spec_inst );                          \
418 /**/
419 
420 /// Automatic registration of the set of test cases based on some function
421 /// accepting one parameter. Set of parameters used to call that function is
422 /// taken from iterator 'begin' which is incremented until it reaches 'end'.
423 ///
424 /// @sa BOOST_PARAM_TEST_CASE
425 #define BOOST_AUTO_PARAM_TEST_CASE( function, begin, end )               \
426     BOOST_AUTO_TU_REGISTRAR(function) (                                  \
427                             BOOST_PARAM_TEST_CASE(function, begin, end)  \
428                             NCBI_BOOST_DECORATOR_ARG)                    \
429 /**/
430 
431 #define BOOST_TIMEOUT(M)                                        \
432     do {                                                        \
433         throw boost::execution_exception(                       \
434                 boost::execution_exception::timeout_error, M    \
435                 NCBI_BOOST_LOCATION());                         \
436     } while (0)                                                 \
437 /**/
438 
439 
440 #  define BOOST_TEST_TOOL_PASS_ARG_ONLY( r, _, arg ) , arg
441 
442 #if BOOST_VERSION >= 105900
443 
444 #  define NCBITEST_CHECK_IMPL_EX(frwd_type, P, check_descr, TL, CT, ARGS)    \
445     BOOST_CHECK_NO_THROW_IMPL(                                               \
446         BOOST_TEST_TOOL_IMPL(frwd_type, P, check_descr, TL, CT, ARGS), TL)
447 
448 #  define NCBITEST_CHECK_IMPL(P, check_descr, TL, CT)                        \
449     NCBITEST_CHECK_IMPL_EX(2, P, check_descr, TL, CT, _)
450 
451 #  define NCBITEST_CHECK_WITH_ARGS_IMPL(P, check_descr, TL, CT, ARGS)        \
452     NCBITEST_CHECK_IMPL_EX(0, ::boost::test_tools::tt_detail::P(),           \
453                            check_descr, TL, CT, ARGS)
454 
455 #  define NCBITEST_CHECK_IMPL_MT_SAFE(P, check_descr, TL, CT)             \
456     do {                                                                  \
457         bool _ncbitest_value;                                             \
458         NCBI_NS_NCBI::CFastMutexGuard _ncbitest_guard                     \
459             (NCBI_NS_NCBI::eEmptyGuard);                                  \
460         BOOST_CHECK_NO_THROW_IMPL_EX(_ncbitest_value = (P);, TL,          \
461             _ncbitest_guard.Guard(NCBI_NS_NCBI::g_NcbiTestMutex));        \
462         BOOST_TEST_TOOL_IMPL(2, _ncbitest_value, check_descr, TL, CT, _); \
463     } while( ::boost::test_tools::tt_detail::dummy_cond() )
464 
465 #  define BOOST_PP_BOOL_00 0
466 
467 #  define BOOST_TEST_TOOL_PASS_PRED00( P, ARGS ) P
468 
469 #  define BOOST_TEST_TOOL_PASS_ARGS00( ARGS )                           \
470     BOOST_PP_SEQ_FOR_EACH( BOOST_TEST_TOOL_PASS_ARG_ONLY, _, ARGS )
471 
472 #  define NCBITEST_CHECK_WITH_2_ARGS_IMPL_MT_SAFE(P, descr, TL, CT, A1, A2) \
473     do {                                                                    \
474         std::decay<decltype(A1)>::type _ncbitest_value1;                    \
475         std::decay<decltype(A2)>::type _ncbitest_value2;                    \
476         NCBI_NS_NCBI::CFastMutexGuard  _ncbitest_guard                      \
477             (NCBI_NS_NCBI::eEmptyGuard);                                    \
478         BOOST_CHECK_NO_THROW_IMPL_EX(                                       \
479             _ncbitest_value1 = (A1); _ncbitest_value2 = (A2);, TL,          \
480             _ncbitest_guard.Guard(NCBI_NS_NCBI::g_NcbiTestMutex));          \
481         BOOST_TEST_TOOL_IMPL(00, ::boost::test_tools::tt_detail::P(),       \
482                              descr, TL, CT,                                 \
483                              (_ncbitest_value1)(BOOST_STRINGIZE(A1))        \
484                              (_ncbitest_value2)(BOOST_STRINGIZE(A2)));      \
485     } while( ::boost::test_tools::tt_detail::dummy_cond() )
486 
487 #  define BOOST_CHECK_IMPL_MT_SAFE( P, check_descr, TL, CT )                \
488     do {                                                                    \
489         bool _ncbitest_value = (P);                                         \
490         NCBI_NS_NCBI::CFastMutexGuard _ncbitest_guard                       \
491             (NCBI_NS_NCBI::g_NcbiTestMutex);                                \
492         BOOST_TEST_TOOL_IMPL( 2, _ncbitest_value, check_descr, TL, CT, _ ); \
493     } while( ::boost::test_tools::tt_detail::dummy_cond() )
494 
495 #  define BOOST_CHECK_WITH_2_ARGS_IMPL_MT_SAFE(P, descr, TL, CT, A1, A2) \
496     do {                                                                 \
497         auto _ncbitest_value1 = A1;                                      \
498         auto _ncbitest_value2 = A2;                                      \
499         NCBI_NS_NCBI::CFastMutexGuard _ncbitest_guard                    \
500             (NCBI_NS_NCBI::g_NcbiTestMutex);                             \
501         BOOST_TEST_TOOL_IMPL(00, ::boost::test_tools::tt_detail::P(),    \
502                              descr, TL, CT,                              \
503                              (_ncbitest_value1)(BOOST_STRINGIZE(A1))     \
504                              (_ncbitest_value2)(BOOST_STRINGIZE(A2)));   \
505     } while( ::boost::test_tools::tt_detail::dummy_cond() )
506 
507 #  define BOOST_CLOSE_IMPL_MT_SAFE( L, R, T, TL )                             \
508     do {                                                                      \
509         auto _ncbitest_l = L;                                                 \
510         auto _ncbitest_r = R;                                                 \
511         auto _ncbitest_t = ::boost::math::fpc::percent_tolerance(T);          \
512         NCBI_NS_NCBI::CFastMutexGuard _ncbitest_guard                         \
513             (NCBI_NS_NCBI::g_NcbiTestMutex);                                  \
514         BOOST_TEST_TOOL_IMPL(                                                 \
515             00, ::boost::test_tools::check_is_close_t(), "", TL, CHECK_CLOSE, \
516             (_ncbitest_l)(BOOST_STRINGIZE(L))(_ncbitest_r)(BOOST_STRINGIZE(R))\
517             (_ncbitest_t)(""));  \
518     } while ( ::boost::test_tools::tt_detail::dummy_cond() )
519 
520 #  define BOOST_EQUAL_COLLECTIONS_IMPL_MT_SAFE( LB, LE, RB, RE, TL ) \
521     do {                                                             \
522         NCBI_NS_NCBI::CFastMutexGuard _ncbitest_guard                \
523             (NCBI_NS_NCBI::g_NcbiTestMutex);                         \
524         BOOST_TEST_TOOL_IMPL(                                        \
525             1, ::boost::test_tools::tt_detail::equal_coll_impl(),    \
526             "", TL, CHECK_EQUAL_COLL, (LB)(LE)(RB)(RE) );            \
527     } while( ::boost::test_tools::tt_detail::dummy_cond() )
528 
529 #else
530 
531 #  define NCBITEST_CHECK_IMPL(P, check_descr, TL, CT)                        \
532     BOOST_CHECK_NO_THROW_IMPL(BOOST_CHECK_IMPL(P, check_descr, TL, CT), TL)
533 
534 #  define NCBITEST_CHECK_WITH_ARGS_IMPL(P, check_descr, TL, CT, ARGS)        \
535     BOOST_CHECK_NO_THROW_IMPL(BOOST_CHECK_WITH_ARGS_IMPL(                    \
536     ::boost::test_tools::tt_detail::P(), check_descr, TL, CT, ARGS), TL)
537 
538 #  define NCBITEST_CHECK_IMPL_MT_SAFE(P, check_descr, TL, CT)           \
539     do {                                                                \
540         bool _ncbitest_value;                                           \
541         NCBI_NS_NCBI::CFastMutexGuard _ncbitest_guard                   \
542             (NCBI_NS_NCBI::eEmptyGuard);                                \
543         BOOST_CHECK_NO_THROW_IMPL_EX(_ncbitest_value = (P), TL,         \
544             _ncbitest_guard.Guard(NCBI_NS_NCBI::g_NcbiTestMutex));      \
545         BOOST_CHECK_IMPL(_ncbitest_value, check_descr, TL, CT);         \
546     } while ( ::boost::test_tools::dummy_cond )
547 
548 #  define NCBITEST_CHECK_WITH_2_ARGS_IMPL_MT_SAFE(P, descr, TL, CT, A1, A2)   \
549     do {                                                                      \
550         std::decay<decltype(A1)>::type _ncbitest_value1;                      \
551         std::decay<decltype(A2)>::type _ncbitest_value2;                      \
552         NCBI_NS_NCBI::CFastMutexGuard  _ncbitest_guard                        \
553             (NCBI_NS_NCBI::eEmptyGuard);                                      \
554         BOOST_CHECK_NO_THROW_IMPL_EX(                                         \
555             _ncbitest_value1 = (A1); _ncbitest_value2 = (A2);, TL,            \
556             _ncbitest_guard.Guard(NCBI_NS_NCBI::g_NcbiTestMutex));            \
557         /* BOOST_TEST_PASSPOINT(); */ /* redundant */                         \
558         BOOST_TEST_TOOL_IMPL( check_frwd,                                     \
559                               ::boost::test_tools::tt_detail::P(),            \
560                               descr, TL, CT )                                 \
561             BOOST_PP_SEQ_FOR_EACH( BOOST_TEST_TOOL_PASS_ARG_ONLY, '_',        \
562                                    (_ncbitest_value1)(BOOST_STRINGIZE(A1))    \
563                                    (_ncbitest_value2)(BOOST_STRINGIZE(A2)))); \
564     } while ( ::boost::test_tools::dummy_cond )
565 
566 #  define BOOST_CHECK_IMPL_MT_SAFE( P, check_descr, TL, CT )      \
567     do {                                                          \
568         bool _ncbitest_value = (P);                               \
569         NCBI_NS_NCBI::CFastMutexGuard _ncbitest_guard             \
570             (NCBI_NS_NCBI::g_NcbiTestMutex);                      \
571         BOOST_CHECK_IMPL( _ncbitest_value, check_descr, TL, CT ); \
572     } while ( ::boost::test_tools::dummy_cond )
573 
574 #  define BOOST_CHECK_WITH_2_ARGS_IMPL_MT_SAFE(P, descr, TL, CT, A1, A2)      \
575     do {                                                                      \
576         auto _ncbitest_value1 = A1;                                           \
577         auto _ncbitest_value2 = A2;                                           \
578         NCBI_NS_NCBI::CFastMutexGuard _ncbitest_guard                         \
579             (NCBI_NS_NCBI::g_NcbiTestMutex);                                  \
580         BOOST_TEST_PASSPOINT();                                               \
581         BOOST_TEST_TOOL_IMPL( check_frwd,                                     \
582                               ::boost::test_tools::tt_detail::P(),            \
583                               descr, TL, CT )                                 \
584             BOOST_PP_SEQ_FOR_EACH( BOOST_TEST_TOOL_PASS_ARG_ONLY, '_',        \
585                                    (_ncbitest_value1)(BOOST_STRINGIZE(A1))    \
586                                    (_ncbitest_value2)(BOOST_STRINGIZE(A2)))); \
587     } while ( ::boost::test_tools::dummy_cond )
588 
589 #  define BOOST_CLOSE_IMPL_MT_SAFE( L, R, T, TL )                             \
590     do {                                                                      \
591         auto _ncbitest_l = L;                                                 \
592         auto _ncbitest_r = R;                                                 \
593         auto _ncbitest_t = ::boost::test_tools::percent_tolerance(T);         \
594         NCBI_NS_NCBI::CFastMutexGuard _ncbitest_guard                         \
595             (NCBI_NS_NCBI::g_NcbiTestMutex);                                  \
596         BOOST_TEST_PASSPOINT();                                               \
597         BOOST_TEST_TOOL_IMPL(check_frwd, ::boost::test_tools::check_is_close, \
598                              "", TL, CHECK_CLOSE)                             \
599             BOOST_PP_SEQ_FOR_EACH(BOOST_TEST_TOOL_PASS_ARG_ONLY, '_',         \
600                                   (_ncbitest_l)(BOOST_STRINGIZE(L))           \
601                                   (_ncbitest_r)(BOOST_STRINGIZE(R))           \
602                                   (_ncbitest_t)("")));                        \
603     } while ( ::boost::test_tools::dummy_cond )
604 
605 #  define BOOST_EQUAL_COLLECTIONS_IMPL_MT_SAFE( LB, LE, RB, RE, TL ) \
606     do {                                                             \
607         NCBI_NS_NCBI::CFastMutexGuard _ncbitest_guard                \
608             (NCBI_NS_NCBI::g_NcbiTestMutex);                         \
609         BOOST_EQUAL_COLLECTIONS_IMPL( LB, LE, RB, RE, TL );          \
610     } while( ::boost::test_tools::dummy_cond )
611 
612 #endif
613 
614 // Several analogs to BOOST_* macros that make simultaneous checking of
615 // NO_THROW and some other condition
616 #define NCBITEST_WARN(P)      NCBITEST_CHECK_IMPL( (P), BOOST_TEST_STRINGIZE( P ), WARN,    CHECK_PRED )
617 #define NCBITEST_CHECK(P)     NCBITEST_CHECK_IMPL( (P), BOOST_TEST_STRINGIZE( P ), CHECK,   CHECK_PRED )
618 #define NCBITEST_REQUIRE(P)   NCBITEST_CHECK_IMPL( (P), BOOST_TEST_STRINGIZE( P ), REQUIRE, CHECK_PRED )
619 
620 #define NCBITEST_WARN_MT_SAFE(P)                                       \
621     NCBITEST_CHECK_IMPL_MT_SAFE( (P), BOOST_TEST_STRINGIZE( P ), WARN, \
622                                  CHECK_PRED )
623 #define NCBITEST_CHECK_MT_SAFE(P)                                       \
624     NCBITEST_CHECK_IMPL_MT_SAFE( (P), BOOST_TEST_STRINGIZE( P ), CHECK, \
625                                  CHECK_PRED )
626 #define NCBITEST_REQUIRE_MT_SAFE(P)                                       \
627     NCBITEST_CHECK_IMPL_MT_SAFE( (P), BOOST_TEST_STRINGIZE( P ), REQUIRE, \
628                                  CHECK_PRED )
629 
630 
631 #define NCBITEST_WARN_MESSAGE( P, M )    NCBITEST_CHECK_IMPL( (P), M, WARN,    CHECK_MSG )
632 #define NCBITEST_CHECK_MESSAGE( P, M )   NCBITEST_CHECK_IMPL( (P), M, CHECK,   CHECK_MSG )
633 #define NCBITEST_REQUIRE_MESSAGE( P, M ) NCBITEST_CHECK_IMPL( (P), M, REQUIRE, CHECK_MSG )
634 
635 #define NCBITEST_WARN_MESSAGE_MT_SAFE( P, M ) \
636     NCBITEST_CHECK_IMPL_MT_SAFE( (P), M, WARN,    CHECK_MSG )
637 #define NCBITEST_CHECK_MESSAGE_MT_SAFE( P, M ) \
638     NCBITEST_CHECK_IMPL_MT_SAFE( (P), M, CHECK,   CHECK_MSG )
639 #define NCBITEST_REQUIRE_MESSAGE_MT_SAFE( P, M ) \
640     NCBITEST_CHECK_IMPL_MT_SAFE( (P), M, REQUIRE, CHECK_MSG )
641 
642 
643 #define NCBITEST_WARN_EQUAL( L, R ) \
644     NCBITEST_CHECK_WITH_ARGS_IMPL( equal_impl_frwd, "", WARN,    CHECK_EQUAL, (L)(R) )
645 #define NCBITEST_CHECK_EQUAL( L, R ) \
646     NCBITEST_CHECK_WITH_ARGS_IMPL( equal_impl_frwd, "", CHECK,   CHECK_EQUAL, (L)(R) )
647 #define NCBITEST_REQUIRE_EQUAL( L, R ) \
648     NCBITEST_CHECK_WITH_ARGS_IMPL( equal_impl_frwd, "", REQUIRE, CHECK_EQUAL, (L)(R) )
649 
650 #define NCBITEST_WARN_EQUAL_MT_SAFE( L, R )                             \
651     NCBITEST_CHECK_WITH_2_ARGS_IMPL_MT_SAFE( equal_impl_frwd, "", WARN, \
652                                              CHECK_EQUAL, L, R )
653 #define NCBITEST_CHECK_EQUAL_MT_SAFE( L, R )                             \
654     NCBITEST_CHECK_WITH_2_ARGS_IMPL_MT_SAFE( equal_impl_frwd, "", CHECK, \
655                                              CHECK_EQUAL, L, R )
656 #define NCBITEST_REQUIRE_EQUAL_MT_SAFE( L, R )                             \
657     NCBITEST_CHECK_WITH_2_ARGS_IMPL_MT_SAFE( equal_impl_frwd, "", REQUIRE, \
658                                              CHECK_EQUAL, L, R )
659 
660 
661 #define NCBITEST_WARN_NE( L, R ) \
662     NCBITEST_CHECK_WITH_ARGS_IMPL( ne_impl, "", WARN,    CHECK_NE, (L)(R) )
663 #define NCBITEST_CHECK_NE( L, R ) \
664     NCBITEST_CHECK_WITH_ARGS_IMPL( ne_impl, "", CHECK,   CHECK_NE, (L)(R) )
665 #define NCBITEST_REQUIRE_NE( L, R ) \
666     NCBITEST_CHECK_WITH_ARGS_IMPL( ne_impl, "", REQUIRE, CHECK_NE, (L)(R) )
667 
668 #define NCBITEST_WARN_NE_MT_SAFE( L, R )                        \
669     NCBITEST_CHECK_WITH_2_ARGS_IMPL_MT_SAFE( ne_impl, "", WARN, \
670                                              CHECK_NE, L, R )
671 #define NCBITEST_CHECK_NE_MT_SAFE( L, R )                        \
672     NCBITEST_CHECK_WITH_2_ARGS_IMPL_MT_SAFE( ne_impl, "", CHECK, \
673                                              CHECK_NE, L, R )
674 #define NCBITEST_REQUIRE_NE_MT_SAFE( L, R )                        \
675     NCBITEST_CHECK_WITH_2_ARGS_IMPL_MT_SAFE( ne_impl, "", REQUIRE, \
676                                              CHECK_NE, L, R )
677 
678 // MT-safe variants of commonly used standard Boost macros
679 #define BOOST_WARN_MT_SAFE( P ) \
680     BOOST_CHECK_IMPL_MT_SAFE( (P), BOOST_TEST_STRINGIZE(P), WARN, CHECK_PRED )
681 #define BOOST_CHECK_MT_SAFE( P ) \
682     BOOST_CHECK_IMPL_MT_SAFE( (P), BOOST_TEST_STRINGIZE(P), CHECK, CHECK_PRED )
683 #define BOOST_REQUIRE_MT_SAFE( P )                                   \
684     BOOST_CHECK_IMPL_MT_SAFE( (P), BOOST_TEST_STRINGIZE(P), REQUIRE, \
685                               CHECK_PRED )
686 
687 #define BOOST_WARN_MESSAGE_MT_SAFE( P, M ) \
688     BOOST_CHECK_IMPL_MT_SAFE( (P), M, WARN, CHECK_MSG )
689 #define BOOST_CHECK_MESSAGE_MT_SAFE( P, M ) \
690     BOOST_CHECK_IMPL_MT_SAFE( (P), M, CHECK, CHECK_MSG )
691 #define BOOST_REQUIRE_MESSAGE_MT_SAFE( P, M ) \
692     BOOST_CHECK_IMPL_MT_SAFE( (P), M, REQUIRE, CHECK_MSG )
693 
694 #define BOOST_WARN_EQUAL_MT_SAFE( L, R )                             \
695     BOOST_CHECK_WITH_2_ARGS_IMPL_MT_SAFE( equal_impl_frwd, "", WARN, \
696                                           CHECK_EQUAL, L, R )
697 #define BOOST_CHECK_EQUAL_MT_SAFE( L, R )                             \
698     BOOST_CHECK_WITH_2_ARGS_IMPL_MT_SAFE( equal_impl_frwd, "", CHECK, \
699                                           CHECK_EQUAL, L, R )
700 #define BOOST_REQUIRE_EQUAL_MT_SAFE( L, R )                             \
701     BOOST_CHECK_WITH_2_ARGS_IMPL_MT_SAFE( equal_impl_frwd, "", REQUIRE, \
702                                           CHECK_EQUAL, L, R )
703 
704 #define BOOST_WARN_NE_MT_SAFE( L, R ) \
705     BOOST_CHECK_WITH_2_ARGS_IMPL_MT_SAFE( ne_impl, "", WARN,   CHECK_NE, L, R )
706 #define BOOST_CHECK_NE_MT_SAFE( L, R ) \
707     BOOST_CHECK_WITH_2_ARGS_IMPL_MT_SAFE( ne_impl, "", CHECK,  CHECK_NE, L, R )
708 #define BOOST_REQUIRE_NE_MT_SAFE( L, R ) \
709     BOOST_CHECK_WITH_2_ARGS_IMPL_MT_SAFE(ne_impl, "", REQUIRE, CHECK_NE, L, R )
710 
711 #define BOOST_ERROR_MT_SAFE( M ) BOOST_CHECK_MESSAGE_MT_SAFE( false, M )
712 #define BOOST_FAIL_MT_SAFE( M )  BOOST_REQUIRE_MESSAGE_MT_SAFE( false, M )
713 
714 #define BOOST_TEST_MESSAGE_MT_SAFE( M )               \
715     do {                                              \
716         NCBI_NS_NCBI::CFastMutexGuard _ncbitest_guard \
717             (NCBI_NS_NCBI::g_NcbiTestMutex);          \
718         BOOST_TEST_MESSAGE( M );                      \
719     } while (false)
720 
721 #define BOOST_WARN_CLOSE_MT_SAFE( L, R, T ) \
722     BOOST_CLOSE_IMPL_MT_SAFE( L, R, T, WARN )
723 #define BOOST_CHECK_CLOSE_MT_SAFE( L, R, T ) \
724     BOOST_CLOSE_IMPL_MT_SAFE( L, R, T, CHECK )
725 #define BOOST_REQUIRE_CLOSE_MT_SAFE( L, R, T ) \
726     BOOST_CLOSE_IMPL_MT_SAFE( L, R, T, REQUIRE )
727 
728 #define BOOST_WARN_EQUAL_COLLECTIONS_MT_SAFE( LB, LE, RB, RE ) \
729     BOOST_EQUAL_COLLECTIONS_IMPL_MT_SAFE( LB, LE, RB, RE, WARN )
730 #define BOOST_CHECK_EQUAL_COLLECTIONS_MT_SAFE( LB, LE, RB, RE ) \
731     BOOST_EQUAL_COLLECTIONS_IMPL_MT_SAFE( LB, LE, RB, RE, CHECK )
732 #define BOOST_REQUIRE_EQUAL_COLLECTIONS_MT_SAFE( LB, LE, RB, RE ) \
733     BOOST_EQUAL_COLLECTIONS_IMPL_MT_SAFE( LB, LE, RB, RE, REQUIRE )
734 
735 // Ensure Clang analysis tools (notably scan-build) recognize that a failed
736 // {BOOST,NCBITEST}_REQUIRE* call will bail.
737 #ifdef __clang_analyzer__
738 #  undef  BOOST_REQUIRE
739 #  define BOOST_REQUIRE(P)               _ALWAYS_ASSERT(P)
740 #  undef  BOOST_REQUIRE_MT_SAFE
741 #  define BOOST_REQUIRE_MT_SAFE(P)       BOOST_REQUIRE(P)
742 #  undef  BOOST_REQUIRE_MESSAGE
743 #  define BOOST_REQUIRE_MESSAGE(P, M)    _ALWAYS_ASSERT((FORMAT(M), (P)))
744 #  undef  BOOST_REQUIRE_MESSAGE_MT_SAFE
745 #  define BOOST_REQUIRE_MESSAGE_MT_SAFE(P, M) BOOST_REQUIRE_MESSAGE(P, M)
746 #  undef  BOOST_REQUIRE_EQUAL
747 #  define BOOST_REQUIRE_EQUAL(L, R)      _ALWAYS_ASSERT((L) == (R))
748 #  undef  BOOST_REQUIRE_EQUAL_MT_SAFE
749 #  define BOOST_REQUIRE_EQUAL_MT_SAFE(L, R) BOOST_REQUIRE_EQUAL(L, R)
750 #  undef  BOOST_REQUIRE_NE
751 #  define BOOST_REQUIRE_NE(L, R)         _ALWAYS_ASSERT((L) != (R))
752 #  undef  BOOST_REQUIRE_NE_MT_SAFE
753 #  define BOOST_REQUIRE_NE_MT_SAFE(L, R) BOOST_REQUIRE_NE(L, R)
754 #  undef  BOOST_REQUIRE_NO_THROW
755 #  define BOOST_REQUIRE_NO_THROW(S) try { S; } catch (...) { _ALWAYS_TROUBLE; }
756 #  undef  BOOST_REQUIRE_NO_THROW_MT_SAFE
757 #  define BOOST_REQUIRE_NO_THROW_MT_SAFE(S) BOOST_REQUIRE_NO_THROW(S)
758 #  undef  BOOST_REQUIRE_THROW
759 #  define BOOST_REQUIRE_THROW(S, E) \
760     do { try { S; } catch (E const&) { break; } _ALWAYS_TROUBLE; } while(false)
761 #  undef  BOOST_REQUIRE_THROW_MT_SAFE
762 #  define BOOST_REQUIRE_THROW_MT_SAFE(S, E) BOOST_REQUIRE_THROW(S, E)
763 #  undef  NCBITEST_REQUIRE
764 #  define NCBITEST_REQUIRE(P) BOOST_REQUIRE_NO_THROW(_ALWAYS_ASSERT(P))
765 #  undef  NCBITEST_REQUIRE_MT_SAFE
766 #  define NCBITEST_REQUIRE_MT_SAFE(P)    NCBITEST_REQUIRE(P)
767 #  undef  NCBITEST_REQUIRE_MESSAGE
768 #  define NCBITEST_REQUIRE_MESSAGE(P, M) NCBITEST_REQUIRE((FORMAT(M), (P)))
769 #  undef  NCBITEST_REQUIRE_MESSAGE_MT_SAFE
770 #  define NCBITEST_REQUIRE_MESSAGE_MT_SAFE(P, M) NCBITEST_REQUIRE_MESSAGE(P, M)
771 #  undef  NCBITEST_REQUIRE_EQUAL
772 #  define NCBITEST_REQUIRE_EQUAL(L, R)   NCBITEST_REQUIRE((L) == (R))
773 #  undef  NCBITEST_REQUIRE_EQUAL_MT_SAFE
774 #  define NCBITEST_REQUIRE_EQUAL_MT_SAFE(L, R) NCBITEST_REQUIRE_EQUAL(L, R)
775 #  undef  NCBITEST_REQUIRE_NE
776 #  define NCBITEST_REQUIRE_NE(L, R)      NCBITEST_REQUIRE((L) != (R))
777 #  undef  NCBITEST_REQUIRE_NE_MT_SAFE
778 #  define NCBITEST_REQUIRE_NE_MT_SAFE(L, R) NCBITEST_REQUIRE_NE(L, R)
779 // Used but left as is: BOOST_REQUIRE_CLOSE, BOOST_REQUIRE_EQUAL_COLLECTIONS.
780 // Irrelevant: BOOST_REQUIRE_CTX (custom, based on BOOST_REQUIRE),
781 //             BOOST_REQUIRE_CUTPOINT (custom, based on throw).
782 // Used only in BOOST_CHECK_... form: GE, GT, LT, SMALL.
783 #endif
784 
785 
786 /** @addtogroup Tests
787  *
788  * @{
789  */
790 
791 
792 BEGIN_NCBI_SCOPE
793 
794 
795 /// Macro for introducing function initializing argument descriptions for
796 /// tests. This function will be called before CNcbiApplication will parse
797 /// command line arguments. So it will parse command line using descriptions
798 /// set by this function. Also test framework will react correctly on such
799 /// arguments as -h, -help or -dryrun (the last will just print list of unit
800 /// tests without actually executing them). The parameter var_name is a name
801 /// for variable of type CArgDescriptions* that can be used inside function
802 /// to set up argument descriptions. Usage of this macro is like this:<pre>
803 /// NCBITEST_INIT_CMDLINE(my_args)
804 /// {
805 ///     my_args->SetUsageContext(...);
806 ///     my_args->AddPositional(...);
807 /// }
808 /// </pre>
809 ///
810 #define NCBITEST_INIT_CMDLINE(var_name)                     \
811     NCBITEST_AUTOREG_PARAMFUNC(eTestUserFuncCmdLine,        \
812                                CArgDescriptions* var_name,  \
813                                NcbiTestGetArgDescrs)
814 
815 
816 /// Macro for introducing initialization function which will be called before
817 /// tests execution and only if tests will be executed (if there's no command
818 /// line parameter -dryrun or --do_not_test) even if only select number of
819 /// tests will be executed (if command line parameter --run_test=... were
820 /// given). If any of these initialization functions will throw an exception
821 /// then tests will not be executed. The usage of this macro:<pre>
822 /// NCBITEST_AUTO_INIT()
823 /// {
824 ///     // initialization function body
825 /// }
826 /// </pre>
827 /// Arbitrary number of initialization functions can be defined. They all will
828 /// be called before tests but the order of these callings is not defined.
829 ///
830 /// @sa NCBITEST_AUTO_FINI
831 ///
832 #define NCBITEST_AUTO_INIT()  NCBITEST_AUTOREG_FUNCTION(eTestUserFuncInit)
833 
834 
835 /// Macro for introducing finalization function which will be called after
836 /// actual tests execution even if only select number of tests will be
837 /// executed (if command line parameter --run_test=... were given). The usage
838 /// of this macro:<pre>
839 /// NCBITEST_AUTO_FINI()
840 /// {
841 ///     // finalization function body
842 /// }
843 /// </pre>
844 /// Arbitrary number of finalization functions can be defined. They all will
845 /// be called after tests are executed but the order of these callings is not
846 /// defined.
847 ///
848 /// @sa NCBITEST_AUTO_INIT
849 ///
850 #define NCBITEST_AUTO_FINI()  NCBITEST_AUTOREG_FUNCTION(eTestUserFuncFini)
851 
852 
853 /// Macro for introducing function which should initialize configuration
854 /// conditions parser. This parser will be used to evaluate conditions for
855 /// running tests written in configuration file. So you should set values for
856 /// all variables that you want to participate in those expressions. Test
857 /// framework automatically adds all OS*, COMPILER* and DLL_BUILD variables
858 /// with the values of analogous NCBI_OS*, NCBI_COMPILER* and NCBI_DLL_BUILD
859 /// macros. The usage of this macro:<pre>
860 /// NCBITEST_INIT_VARIABLES(my_parser)
861 /// {
862 ///    my_parser->AddSymbol("var_name1", value_expr1);
863 ///    my_parser->AddSymbol("var_name2", value_expr2);
864 /// }
865 /// </pre>
866 /// Arbitrary number of such functions can be defined.
867 ///
868 #define NCBITEST_INIT_VARIABLES(var_name)              \
869     NCBITEST_AUTOREG_PARAMFUNC(eTestUserFuncVars,      \
870                                CExprParser* var_name,  \
871                                NcbiTestGetIniParser)
872 
873 
874 /// Macro for introducing function which should initialize dependencies
875 /// between test units and some hard coded (not taken from configuration file)
876 /// tests disablings. All function job can be done by using NCBITEST_DISABLE,
877 /// NCBITEST_DEPENDS_ON and NCBITEST_DEPENDS_ON_N macros in conjunction with
878 /// some conditional statements maybe. The usage of this macro:<pre>
879 /// NCBITEST_INIT_TREE()
880 /// {
881 ///     NCBITEST_DISABLE(test_name11);
882 ///
883 ///     NCBITEST_DEPENDS_ON(test_name22, test_name1);
884 ///     NCBITEST_DEPENDS_ON_N(test_name33, N, (test_name1, ..., test_nameN));
885 /// }
886 /// </pre>
887 /// Arbitrary number of such functions can be defined.
888 ///
889 /// @sa NCBITEST_DISABLE, NCBITEST_DEPENDS_ON, NCBITEST_DEPENDS_ON_N
890 ///
891 #define NCBITEST_INIT_TREE()  NCBITEST_AUTOREG_FUNCTION(eTestUserFuncDeps)
892 
893 
894 /// Unconditionally disable test case. To be used inside function introduced
895 /// by NCBITEST_INIT_TREE.
896 ///
897 /// @param test_name
898 ///   Name of the test as a bare text without quotes. Name can exclude test_
899 ///   prefix if function name includes one and class prefix if it is class
900 ///   member test case.
901 ///
902 /// @sa NCBITEST_INIT_TREE
903 ///
904 #define NCBITEST_DISABLE(test_name)                               \
905     NcbiTestDisable(NcbiTestGetUnit(BOOST_STRINGIZE(test_name)))
906 
907 
908 /// Add dependency between test test_name and dep_name. This dependency means
909 /// if test dep_name is failed during execution or was disabled by any reason
910 /// then test test_name will not be executed (will be skipped).
911 /// To be used inside function introduced by NCBITEST_INIT_TREE.
912 ///
913 /// @param test_name
914 ///   Name of the test as a bare text without quotes. Name can exclude test_
915 ///   prefix if function name includes one and class prefix if it is class
916 ///   member test case.
917 /// @param dep_name
918 ///   Name of the test to depend on. Name can be given with the same
919 ///   assumptions as test_name.
920 ///
921 /// @sa NCBITEST_INIT_TREE, NCBI_TEST_DEPENDS_ON_N
922 ///
923 #define NCBITEST_DEPENDS_ON(test_name, dep_name)                    \
924     NcbiTestDependsOn(NcbiTestGetUnit(BOOST_STRINGIZE(test_name)),  \
925                       NcbiTestGetUnit(BOOST_STRINGIZE(dep_name)))
926 
927 
928 /// Add dependency between test test_name and several other tests which names
929 /// given in the list dep_names_array. This dependency means if any of the
930 /// tests in list dep_names_array is failed during execution or was disabled
931 /// by any reason then test test_name will not be executed (will be skipped).
932 /// To be used inside function introduced by NCBITEST_INIT_TREE. Macro is
933 /// equivalent to use NCBI_TEST_DEPENDS_ON several times for each test in
934 /// dep_names_array.
935 ///
936 /// @param test_name
937 ///   Name of the test as a bare text without quotes. Name can exclude test_
938 ///   prefix if function name includes one and class prefix if it is class
939 ///   member test case.
940 /// @param N
941 ///   Number of tests in dep_names_array
942 /// @param dep_names_array
943 ///   Names of tests to depend on. Every name can be given with the same
944 ///   assumptions as test_name. Array should be given enclosed in parenthesis
945 ///   like (test_name1, ..., test_nameN) and should include exactly N elements
946 ///   or preprocessor error will occur during compilation.
947 ///
948 /// @sa NCBITEST_INIT_TREE, NCBI_TEST_DEPENDS_ON
949 ///
950 #define NCBITEST_DEPENDS_ON_N(test_name, N, dep_names_array)        \
951     BOOST_PP_REPEAT(N, NCBITEST_DEPENDS_ON_N_IMPL,                  \
952                     (BOOST_PP_INC(N), (test_name,                   \
953                         BOOST_PP_TUPLE_REM(N) dep_names_array)))    \
954     (void)0
955 
956 
957 /// Set of macros to manually add test cases that cannot be created using
958 /// BOOST_AUTO_TEST_CASE. To create such test cases you should have a function
959 /// (that can accept up to 3 parameters) and use one of macros below inside
960 /// NCBITEST_INIT_TREE() function. All function parameters are passed by value.
961 ///
962 /// @sa NCBITEST_INIT_TREE, BOOST_AUTO_PARAM_TEST_CASE
963 #define NCBITEST_ADD_TEST_CASE(function)                            \
964     boost::unit_test::framework::master_test_suite().add(           \
965         boost::unit_test::make_test_case(                           \
966                             boost::bind(function),                  \
967                             BOOST_TEST_STRINGIZE(function)          \
968                                         )               )
969 #define NCBITEST_ADD_TEST_CASE1(function, param1)                   \
970     boost::unit_test::framework::master_test_suite().add(           \
971         boost::unit_test::make_test_case(                           \
972                             boost::bind(function, (param1)),        \
973                             BOOST_TEST_STRINGIZE(function)          \
974                                         )               )
975 #define NCBITEST_ADD_TEST_CASE2(function, param1, param2)               \
976     boost::unit_test::framework::master_test_suite().add(               \
977         boost::unit_test::make_test_case(                               \
978                             boost::bind(function, (param1), (param2)),  \
979                             BOOST_TEST_STRINGIZE(function)              \
980                                         )               )
981 #define NCBITEST_ADD_TEST_CASE3(function, param1, param2, param3)                \
982     boost::unit_test::framework::master_test_suite().add(                        \
983         boost::unit_test::make_test_case(                                        \
984                             boost::bind(function, (param1), (param2), (param3)), \
985                             BOOST_TEST_STRINGIZE(function)                       \
986                                         )               )
987 
988 
989 /// Disable execution of all tests in current configuration. Call to the
990 /// function is equivalent to setting GLOBAL = true in ini file.
991 /// Globally disabled tests are shown as DIS by check scripts
992 /// (called via make check).
993 /// Function should be called only from NCBITEST_AUTO_INIT() or
994 /// NCBITEST_INIT_TREE() functions.
995 ///
996 /// @sa NCBITEST_AUTO_INIT, NCBITEST_INIT_TREE
997 ///
998 void NcbiTestSetGlobalDisabled(void);
999 
1000 
1001 /// Skip execution of all tests in current configuration.
1002 /// Globally skipped tests are shown as SKP by check scripts
1003 /// (called via make check).
1004 /// Function should be called only from NCBITEST_AUTO_INIT() or
1005 /// NCBITEST_INIT_TREE() functions.
1006 ///
1007 /// @sa NCBITEST_AUTO_INIT, NCBITEST_INIT_TREE
1008 ///
1009 void NcbiTestSetGlobalSkipped(void);
1010 
1011 
1012 /// Return current application instance. Similar to CNcbiApplication::Instance().
1013 ///
1014 CNcbiApplication* NcbiTestGetAppInstance(void);
1015 
1016 
1017 /// Wrapper to get the application's configuration parameters, accessible to read-write.
1018 /// We cannot use CNcbiApplication::GetConfig(), because it return read-only registry,
1019 /// and protected CNcbiApplication::GetRWConfig() is not accessible for unit tests directly.
1020 ///
1021 CNcbiRegistry& NcbiTestGetRWConfig(void);
1022 
1023 
1024 
1025 //////////////////////////////////////////////////////////////////////////
1026 // All API from this line below is for internal use only and is not
1027 // intended for use by any users. All this stuff is used by end-user
1028 // macros defined above.
1029 //////////////////////////////////////////////////////////////////////////
1030 
1031 #ifdef SYSTEM_MUTEX_INITIALIZER
1032 extern SSystemFastMutex g_NcbiTestMutex;
1033 #else
1034 extern CAutoInitializeStaticFastMutex g_NcbiTestMutex;
1035 #endif
1036 
1037 /// Helper macro to implement NCBI_TEST_DEPENDS_ON_N.
1038 #define NCBITEST_DEPENDS_ON_N_IMPL(z, n, names_array)               \
1039     NCBITEST_DEPENDS_ON(BOOST_PP_ARRAY_ELEM(0, names_array),        \
1040     BOOST_PP_ARRAY_ELEM(BOOST_PP_INC(n), names_array));
1041 
1042 
1043 /// Mark test case/suite as dependent on another test case/suite.
1044 /// If dependency test case didn't executed successfully for any reason then
1045 /// dependent test will not be executed. This rule has one exception: if test
1046 /// is requested to execute in command line via parameter "--run_test" and
1047 /// dependency was not requested to execute, requested test will be executed
1048 /// anyways.
1049 ///
1050 /// @param tu
1051 ///   Test case/suite that should be marked as dependent
1052 /// @param dep_tu
1053 ///   Test case/suite that will be "parent" for tu
1054 void NcbiTestDependsOn(boost::unit_test::test_unit* tu,
1055                        boost::unit_test::test_unit* dep_tu);
1056 
1057 /// Disable test unit.
1058 /// Disabled test unit will not be executed (as if p_enabled is set to false)
1059 /// but it will be reported in final Boost.Test report as disabled (as opposed
1060 /// to setting p_enabled to false when test does not appear in final
1061 /// Boost.Test report).
1062 void NcbiTestDisable(boost::unit_test::test_unit* tu);
1063 
1064 
1065 /// Type of user-defined function which will be automatically registered
1066 /// in test framework
1067 typedef void (*TNcbiTestUserFunction)(void);
1068 
1069 /// Types of functions that user can define
1070 enum ETestUserFuncType {
1071     eTestUserFuncInit,
1072     eTestUserFuncFini,
1073     eTestUserFuncCmdLine,
1074     eTestUserFuncVars,
1075     eTestUserFuncDeps,
1076     eTestUserFuncFirst = eTestUserFuncInit,
1077     eTestUserFuncLast  = eTestUserFuncDeps
1078 };
1079 
1080 /// Registrar of all user-defined functions
1081 void RegisterNcbiTestUserFunc(TNcbiTestUserFunction func,
1082                               ETestUserFuncType     func_type);
1083 
1084 /// Class for implementing automatic registration of user functions
1085 struct SNcbiTestUserFuncReg
1086 {
SNcbiTestUserFuncRegSNcbiTestUserFuncReg1087     SNcbiTestUserFuncReg(TNcbiTestUserFunction func,
1088                          ETestUserFuncType     func_type)
1089     {
1090         RegisterNcbiTestUserFunc(func, func_type);
1091     }
1092 };
1093 
1094 /// Get pointer to parser which will be used for evaluating conditions written
1095 /// in configuration file
1096 CExprParser* NcbiTestGetIniParser(void);
1097 
1098 /// Get ArgDescriptions object which will be passed to application for parsing
1099 /// command line arguments.
1100 CArgDescriptions* NcbiTestGetArgDescrs(void);
1101 
1102 /// Get pointer to test unit by its name which can be partial, i.e. without
1103 /// class prefix and/or test_ prefix if any. Throws an exception in case of
1104 /// name of non-existent test
1105 boost::unit_test::test_unit* NcbiTestGetUnit(CTempString test_name);
1106 
1107 
1108 /// Helper macros for unique identifiers
1109 #define NCBITEST_AUTOREG_FUNC(type)  \
1110                       BOOST_JOIN(BOOST_JOIN(Ncbi_, type),       __LINE__)
1111 #define NCBITEST_AUTOREG_OBJ     BOOST_JOIN(NcbiTestAutoObj,    __LINE__)
1112 #define NCBITEST_AUTOREG_HELPER  BOOST_JOIN(NcbiTestAutoHelper, __LINE__)
1113 
1114 #define NCBITEST_AUTOREG_FUNCTION(type)                                    \
1115 static void NCBITEST_AUTOREG_FUNC(type)(void);                             \
1116 static ::NCBI_NS_NCBI::SNcbiTestUserFuncReg                                \
1117 NCBITEST_AUTOREG_OBJ(&NCBITEST_AUTOREG_FUNC(type), ::NCBI_NS_NCBI::type);  \
1118 static void NCBITEST_AUTOREG_FUNC(type)(void)
1119 
1120 #define NCBITEST_AUTOREG_PARAMFUNC(type, param_decl, param_func)       \
1121 static void NCBITEST_AUTOREG_FUNC(type)(::NCBI_NS_NCBI::param_decl);   \
1122 static void NCBITEST_AUTOREG_HELPER(void)                              \
1123 {                                                                      \
1124     NCBITEST_AUTOREG_FUNC(type)(::NCBI_NS_NCBI::param_func());         \
1125 }                                                                      \
1126 static ::NCBI_NS_NCBI::SNcbiTestUserFuncReg                            \
1127 NCBITEST_AUTOREG_OBJ(&NCBITEST_AUTOREG_HELPER, ::NCBI_NS_NCBI::type);  \
1128 static void NCBITEST_AUTOREG_FUNC(type)(::NCBI_NS_NCBI::param_decl)
1129 
1130 /// Extension auto-registrar from Boost.Test that can automatically set the
1131 /// timeout for unit.
1132 struct SNcbiTestRegistrar
1133     : public boost::unit_test::ut_detail::auto_test_unit_registrar
1134 {
1135     typedef boost::unit_test::ut_detail::auto_test_unit_registrar TParent;
1136 
SNcbiTestRegistrarSNcbiTestRegistrar1137     SNcbiTestRegistrar(boost::unit_test::test_case* tc,
1138                        boost::unit_test::counter_t  exp_fail,
1139                        unsigned int                 timeout)
1140         : TParent(tc NCBI_BOOST_DECORATOR_ARG, exp_fail)
1141     {
1142         tc->p_timeout.set(timeout);
1143     }
1144 
1145 #ifdef BOOST_FIXTURE_TEST_CASE_WITH_DECOR
SNcbiTestRegistrarSNcbiTestRegistrar1146     SNcbiTestRegistrar(boost::unit_test::test_case*              tc,
1147                        unsigned int                              timeout,
1148                        boost::unit_test::decorator::collector_t& decorator)
1149         : TParent(tc, decorator)
1150     {
1151         tc->p_timeout.set(timeout);
1152     }
1153 #endif
1154 
SNcbiTestRegistrarSNcbiTestRegistrar1155     SNcbiTestRegistrar(boost::unit_test::test_case* tc,
1156                        boost::unit_test::counter_t  exp_fail)
1157         : TParent(tc NCBI_BOOST_DECORATOR_ARG, exp_fail)
1158     {}
1159 
1160 #ifndef BOOST_FIXTURE_TEST_CASE_WITH_DECOR
1161     explicit
SNcbiTestRegistrarSNcbiTestRegistrar1162     SNcbiTestRegistrar(boost::unit_test::const_string ts_name)
1163         : TParent(ts_name)
1164     {}
1165 #endif
1166 
1167     explicit
SNcbiTestRegistrarSNcbiTestRegistrar1168     SNcbiTestRegistrar(boost::unit_test::test_unit_generator const& tc_gen)
1169         : TParent(tc_gen NCBI_BOOST_DECORATOR_ARG)
1170     {}
1171 
1172     explicit
SNcbiTestRegistrarSNcbiTestRegistrar1173     SNcbiTestRegistrar(int n)
1174         : TParent(n)
1175     {}
1176 };
1177 
1178 
1179 /// Copy of auto_tc_exp_fail from Boost.Test to store the value of timeout
1180 /// for each test.
1181 template<typename T>
1182 struct SNcbiTestTCTimeout
1183 {
SNcbiTestTCTimeoutSNcbiTestTCTimeout1184     SNcbiTestTCTimeout() : m_value(0) {}
1185 
SNcbiTestTCTimeoutSNcbiTestTCTimeout1186     explicit SNcbiTestTCTimeout(unsigned int v)
1187         : m_value( v )
1188     {
1189         instance() = this;
1190     }
1191 
instanceSNcbiTestTCTimeout1192     static SNcbiTestTCTimeout*& instance()
1193     {
1194         static SNcbiTestTCTimeout  inst;
1195         static SNcbiTestTCTimeout* inst_ptr = &inst;
1196 
1197         return inst_ptr;
1198     }
1199 
valueSNcbiTestTCTimeout1200     unsigned int value() const { return m_value; }
1201 
1202 private:
1203     // Data members
1204     unsigned    m_value;
1205 };
1206 
1207 
1208 /// Special generator of test cases for function accepting one parameter.
1209 /// Generator differs from the one provided in Boost.Test in names assigned to
1210 /// generated test cases. NCBI.Test library requires all test names to be
1211 /// unique.
1212 template<typename ParamType, typename ParamIter>
1213 class CNcbiTestParamTestCaseGenerator
1214     : public boost::unit_test::test_unit_generator
1215 {
1216 public:
1217 #if BOOST_VERSION >= 105900
1218     typedef boost::function<void (ParamType)> TTestFunc;
1219 #else
1220     typedef boost::unit_test::callback1<ParamType> TTestFunc;
1221 #endif
1222 
CNcbiTestParamTestCaseGenerator(TTestFunc const & test_func,boost::unit_test::const_string name,ParamIter par_begin,ParamIter par_end)1223     CNcbiTestParamTestCaseGenerator(TTestFunc const&               test_func,
1224                                     boost::unit_test::const_string name,
1225                                     ParamIter                      par_begin,
1226                                     ParamIter                      par_end)
1227         : m_TestFunc(test_func),
1228           m_Name(boost::unit_test::ut_detail::normalize_test_case_name(name)),
1229           m_ParBegin(par_begin),
1230           m_ParEnd(par_end),
1231           m_CaseIndex(0)
1232     {
1233         m_Name += "_";
1234     }
1235 
~CNcbiTestParamTestCaseGenerator()1236     virtual ~CNcbiTestParamTestCaseGenerator() {}
1237 
next() const1238     virtual boost::unit_test::test_unit* next() const
1239     {
1240         if( m_ParBegin == m_ParEnd )
1241             return NULL;
1242 
1243         string this_name(m_Name);
1244         this_name += NStr::IntToString(++m_CaseIndex);
1245 #if BOOST_VERSION >= 105900
1246         boost::unit_test::test_unit* res
1247             = new boost::unit_test::test_case
1248                 (this_name, boost::bind(m_TestFunc, *m_ParBegin));
1249 #else
1250         boost::unit_test::ut_detail::test_func_with_bound_param<ParamType>
1251                                     bound_test_func( m_TestFunc, *m_ParBegin );
1252         boost::unit_test::test_unit* res
1253                   = new boost::unit_test::test_case(this_name, bound_test_func);
1254 #endif
1255         ++m_ParBegin;
1256 
1257         return res;
1258     }
1259 
1260 private:
1261     // Data members
1262     TTestFunc         m_TestFunc;
1263     string            m_Name;
1264     mutable ParamIter m_ParBegin;
1265     ParamIter         m_ParEnd;
1266     mutable int       m_CaseIndex;
1267 };
1268 
1269 
1270 /// Helper functions to be used in BOOST_PARAM_TEST_CASE macro to create
1271 /// special test case generator.
1272 template<typename ParamType, typename ParamIter>
1273 inline CNcbiTestParamTestCaseGenerator<ParamType, ParamIter>
NcbiTestGenTestCases(typename CNcbiTestParamTestCaseGenerator<ParamType,ParamIter>::TTestFunc const & test_func,boost::unit_test::const_string name,ParamIter par_begin,ParamIter par_end)1274 NcbiTestGenTestCases(typename CNcbiTestParamTestCaseGenerator<ParamType, ParamIter>::TTestFunc const& test_func,
1275                      boost::unit_test::const_string                name,
1276                      ParamIter                                     par_begin,
1277                      ParamIter                                     par_end)
1278 {
1279     return CNcbiTestParamTestCaseGenerator<ParamType, ParamIter>(
1280                                         test_func, name, par_begin, par_end);
1281 }
1282 
1283 template<typename ParamType, typename ParamIter>
1284 inline CNcbiTestParamTestCaseGenerator<
1285                     typename boost::remove_const<
1286                             typename boost::remove_reference<ParamType>::type
1287                                                 >::type, ParamIter>
NcbiTestGenTestCases(void (* test_func)(ParamType),boost::unit_test::const_string name,ParamIter par_begin,ParamIter par_end)1288 NcbiTestGenTestCases(void (*test_func)(ParamType),
1289                      boost::unit_test::const_string name,
1290                      ParamIter                      par_begin,
1291                      ParamIter                      par_end )
1292 {
1293     typedef typename boost::remove_const<
1294                         typename boost::remove_reference<ParamType>::type
1295                                         >::type             param_value_type;
1296     return CNcbiTestParamTestCaseGenerator<param_value_type, ParamIter>(
1297                                         test_func, name, par_begin, par_end);
1298 }
1299 
1300 
1301 /////////////////////////////////////////////////////////////////////////////
1302 ///
1303 /// CNcbiTestMemoryCleanupList -- Define a list of pointers to free at exit.
1304 ///
1305 /// Not really necessary for tests, but used to avoid memory leaks and
1306 /// corresponding reports by memory checkers like Valgrind.
1307 
1308 class CNcbiTestMemoryCleanupList
1309 {
1310 public:
1311     static CNcbiTestMemoryCleanupList* GetInstance();
1312     ~CNcbiTestMemoryCleanupList();
1313     void Add(void* ptr);
1314 private:
1315     std::list<void*> m_List;
1316 };
1317 
1318 
1319 END_NCBI_SCOPE
1320 
1321 
1322 /* @} */
1323 
1324 #endif  /* CORELIB___TEST_BOOST__HPP */
1325