NCBI C++ ToolKit
test_boost.cpp
Go to the documentation of this file.
00001 /*  $Id: test_boost.cpp 53244 2012-03-05 14:23:25Z ivanovp $
00002  * ===========================================================================
00003  *
00004  *                            PUBLIC DOMAIN NOTICE
00005  *               National Center for Biotechnology Information
00006  *
00007  *  This software/database is a "United States Government Work" under the
00008  *  terms of the United States Copyright Act.  It was written as part of
00009  *  the author's official duties as a United States Government employee and
00010  *  thus cannot be copyrighted.  This software/database is freely available
00011  *  to the public for use. The National Library of Medicine and the U.S.
00012  *  Government have not placed any restriction on its use or reproduction.
00013  *
00014  *  Although all reasonable efforts have been taken to ensure the accuracy
00015  *  and reliability of the software and data, the NLM and the U.S.
00016  *  Government do not and cannot warrant the performance or results that
00017  *  may be obtained by using this software or data. The NLM and the U.S.
00018  *  Government disclaim all warranties, express or implied, including
00019  *  warranties of performance, merchantability or fitness for any particular
00020  *  purpose.
00021  *
00022  *  Please cite the author in any work or product based on this material.
00023  *
00024  * ===========================================================================
00025  *
00026  * Author:  Pavel Ivanov
00027  *
00028  * File Description:
00029  *   Implementation of special reporter for Boost.Test framework and utility
00030  *   functions for embedding it into the Boost.
00031  *
00032  */
00033 
00034 #include <ncbi_pch.hpp>
00035 #include <corelib/ncbicfg.h>
00036 #include <corelib/error_codes.hpp>
00037 #include <corelib/ncbienv.hpp>
00038 #include <corelib/ncbimisc.hpp>
00039 #include <corelib/ncbiapp.hpp>
00040 #include <corelib/ncbi_system.hpp>
00041 
00042 #ifndef BOOST_TEST_NO_LIB
00043 #  define BOOST_TEST_NO_LIB
00044 #endif
00045 #define BOOST_TEST_NO_MAIN
00046 #include <corelib/test_boost.hpp>
00047 
00048 #include <boost/preprocessor/cat.hpp>
00049 #include <boost/preprocessor/tuple/elem.hpp>
00050 #include <boost/preprocessor/tuple/eat.hpp>
00051 
00052 // On Mac OS X, some corelib headers end up pulling in system headers
00053 // that #define nil as a macro that ultimately expands to __null,
00054 // breaking Boost's internal use of a struct nil.
00055 #ifdef nil
00056 #  undef nil
00057 #endif
00058 #ifdef NCBI_COMPILER_MSVC
00059 #  pragma warning(push)
00060 // 'class' : class has virtual functions, but destructor is not virtual
00061 #  pragma warning(disable: 4265)
00062 // 'operator/operation' : unsafe conversion from 'type of expression' to 'type required'
00063 #  pragma warning(disable: 4191)
00064 #endif
00065 
00066 #include <boost/test/included/unit_test.hpp>
00067 #include <boost/test/results_collector.hpp>
00068 #include <boost/test/results_reporter.hpp>
00069 #include <boost/test/test_observer.hpp>
00070 #include <boost/test/unit_test_log.hpp>
00071 #include <boost/test/unit_test_log_formatter.hpp>
00072 #include <boost/test/output/plain_report_formatter.hpp>
00073 #include <boost/test/output/xml_report_formatter.hpp>
00074 #include <boost/test/output/compiler_log_formatter.hpp>
00075 #include <boost/test/output/xml_log_formatter.hpp>
00076 #include <boost/test/utils/xml_printer.hpp>
00077 #include <boost/test/detail/global_typedef.hpp>
00078 #include <boost/test/detail/unit_test_parameters.hpp>
00079 #include <boost/test/debug.hpp>
00080 
00081 #ifdef NCBI_COMPILER_MSVC
00082 #  pragma warning(pop)
00083 #endif
00084 
00085 #include <list>
00086 #include <vector>
00087 #include <set>
00088 #include <map>
00089 #include <string>
00090 
00091 
00092 #define NCBI_USE_ERRCODE_X  Corelib_TestBoost
00093 
00094 
00095 namespace but = boost::unit_test;
00096 
00097 
00098 BEGIN_NCBI_SCOPE
00099 
00100 const char* kTestsDisableSectionName = "UNITTESTS_DISABLE";
00101 const char* kTestsToFixSectionName = "UNITTESTS_TOFIX";
00102 const char* kTestsTimeoutSectionName = "UNITTESTS_TIMEOUT_MULT";
00103 const char* kTestConfigGlobalValue = "GLOBAL";
00104 
00105 #define DUMMY_TEST_FUNCTION_NAME  DummyTestFunction
00106 const char* kDummyTestCaseName    = BOOST_STRINGIZE(DUMMY_TEST_FUNCTION_NAME);
00107 
00108 const char* kTestResultPassed      = "passed";
00109 const char* kTestResultFailed      = "failed";
00110 const char* kTestResultTimeout     = "timeout";
00111 const char* kTestResultAborted     = "aborted";
00112 const char* kTestResultSkipped     = "skipped";
00113 const char* kTestResultDisabled    = "disabled";
00114 const char* kTestResultToFix       = "tofix";
00115 
00116 
00117 typedef but::results_reporter::format   TBoostRepFormatter;
00118 typedef but::unit_test_log_formatter    TBoostLogFormatter;
00119 typedef set<but::test_unit*>            TUnitsSet;
00120 typedef map<but::test_unit*, TUnitsSet> TUnitToManyMap;
00121 typedef map<string, but::test_unit*>    TStringToUnitMap;
00122 
00123 
00124 /// Reporter for embedding in Boost framework and adding non-standard
00125 /// information to detailed report given by Boost.
00126 class CNcbiBoostReporter : public TBoostRepFormatter
00127 {
00128 public:
00129     CNcbiBoostReporter(void);
00130     virtual ~CNcbiBoostReporter(void) {}
00131 
00132     /// Setup reporter tuned for printing report of specific format
00133     ///
00134     /// @param format
00135     ///   Format of the report
00136     void SetOutputFormat(but::output_format format);
00137 
00138     // TBoostRepFormatter interface
00139     virtual
00140     void results_report_start   (ostream& ostr);
00141     virtual
00142     void results_report_finish  (ostream& ostr);
00143     virtual
00144     void test_unit_report_start (but::test_unit const& tu, ostream& ostr);
00145     virtual
00146     void test_unit_report_finish(but::test_unit const& tu, ostream& ostr);
00147     virtual
00148     void do_confirmation_report (but::test_unit const& tu, ostream& ostr);
00149 
00150 private:
00151     /// Standard reporter from Boost for particular report format
00152     AutoPtr<TBoostRepFormatter>  m_Upper;
00153     /// If report is XML or not
00154     bool                         m_IsXML;
00155     /// Current indentation level in plain text report
00156     int                          m_Indent;
00157 };
00158 
00159 
00160 /// Logger for embedding in Boost framework and adding non-standard
00161 /// information to logging given by Boost.
00162 class CNcbiBoostLogger : public TBoostLogFormatter
00163 {
00164 public:
00165     CNcbiBoostLogger(void);
00166     virtual ~CNcbiBoostLogger(void) {}
00167 
00168     /// Setup logger tuned for printing log of specific format
00169     ///
00170     /// @param format
00171     ///   Format of the report
00172     void SetOutputFormat(but::output_format format);
00173 
00174     // TBoostLogFormatter interface
00175     virtual
00176     void log_start        (ostream& ostr, but::counter_t test_cases_amount);
00177     virtual
00178     void log_finish       (ostream& ostr);
00179     virtual
00180     void log_build_info   (ostream& ostr);
00181     virtual
00182     void test_unit_start  (ostream& ostr, but::test_unit const& tu);
00183     virtual
00184     void test_unit_finish (ostream& ostr, but::test_unit const& tu,
00185                                           unsigned long elapsed);
00186     virtual
00187     void test_unit_skipped(ostream& ostr, but::test_unit const& tu);
00188 #if BOOST_VERSION >= 104200
00189     virtual
00190     void log_exception    (ostream& ostr, but::log_checkpoint_data const& lcd,
00191                                           boost::execution_exception const& ex);
00192     // Next line is necessary for compiling with ICC and Boost 1.41.0 and up
00193     using TBoostLogFormatter::log_exception;
00194 #else
00195     virtual
00196     void log_exception    (ostream& ostr, but::log_checkpoint_data const& lcd,
00197                                           but::const_string explanation);
00198 #endif
00199     virtual
00200     void log_entry_start  (ostream& ostr, but::log_entry_data const& led,
00201                                           log_entry_types let);
00202     virtual
00203     void log_entry_value  (ostream& ostr, but::const_string value);
00204     // Next line is necessary for compiling with ICC and Boost 1.41.0 and up
00205     using TBoostLogFormatter::log_entry_value;
00206     virtual
00207     void log_entry_finish (ostream& ostr);
00208 
00209 private:
00210     /// Standard logger from Boost for particular report format
00211     AutoPtr<TBoostLogFormatter>  m_Upper;
00212     /// If report is XML or not
00213     bool                         m_IsXML;
00214 };
00215 
00216 
00217 /// Special observer to embed in Boost.Test framework to initialize test
00218 /// dependencies before they started execution.
00219 class CNcbiTestsObserver : public but::test_observer
00220 {
00221 public:
00222     virtual ~CNcbiTestsObserver(void) {}
00223 
00224     /// Method called before execution of all tests
00225     virtual void test_start(but::counter_t /* test_cases_amount */);
00226 
00227     /// Method called after execution of all tests
00228     virtual void test_finish(void);
00229 
00230     /// Method called before execution of each unit
00231     virtual void test_unit_start(but::test_unit const& tu);
00232 
00233     /// Method called after execution of each unit
00234     virtual void test_unit_finish(but::test_unit const& tu,
00235                                   unsigned long         elapsed);
00236 
00237     /// Method called when some exception was caught during execution of unit
00238     virtual void exception_caught(boost::execution_exception const& ex);
00239 
00240     virtual void test_unit_aborted(but::test_unit const& tu);
00241     virtual void assertion_result(bool passed);
00242 };
00243 
00244 
00245 /// Class that can walk through all tree of tests and register them inside
00246 /// CNcbiTestApplication.
00247 class CNcbiTestsCollector : public but::test_tree_visitor
00248 {
00249 public:
00250     virtual ~CNcbiTestsCollector(void) {}
00251 
00252     virtual void visit           (but::test_case  const& test );
00253     virtual bool test_suite_start(but::test_suite const& suite);
00254 };
00255 
00256 
00257 /// Element of tests tree. Used to make proper order between units to ensure
00258 /// that dependencies are executed earlier than dependents.
00259 class CNcbiTestTreeElement
00260 {
00261 public:
00262     /// Element represents one test unit
00263     CNcbiTestTreeElement(but::test_unit* tu);
00264     /// In destructor class destroys all its children
00265     ~CNcbiTestTreeElement(void);
00266 
00267     /// Get unit represented by the element
00268     but::test_unit* GetTestUnit(void);
00269 
00270     /// Add child element. Class acquires ownership on the child element and
00271     /// destroys it at the end of work.
00272     void AddChild(CNcbiTestTreeElement* element);
00273 
00274     /// Get parent element in tests tree. If this element represents master
00275     /// test suite then return NULL.
00276     CNcbiTestTreeElement* GetParent(void);
00277 
00278     /// Ensure good dependency of this element on "from" element. If
00279     /// dependency is not fulfilled well then ensure that "from" element will
00280     /// stand earlier in tests tree. Correct order is made in internal
00281     /// structures only. To make it in Boost tests tree you need to call
00282     /// FixUnitsOrder().
00283     ///
00284     /// @sa  FixUnitsOrder()
00285     void EnsureDep(CNcbiTestTreeElement* from);
00286 
00287     /// Fix order of unit tests in the subtree rooted in this element. Any
00288     /// action is taken only if during calls to EnsureDep() some wrong order
00289     /// was found.
00290     ///
00291     /// @sa EnsureDep()
00292     void FixUnitsOrder(void);
00293 
00294 private:
00295     /// Prohibit
00296     CNcbiTestTreeElement(const CNcbiTestTreeElement&);
00297     CNcbiTestTreeElement& operator= (const CNcbiTestTreeElement&);
00298 
00299     typedef vector<CNcbiTestTreeElement*> TElemsList;
00300     typedef set   <CNcbiTestTreeElement*> TElemsSet;
00301 
00302     /// Ensure that leftElem and rightElem (or element pointed by it_right
00303     /// inside m_Children) are in that very order: leftElem first, rightElem
00304     /// after that. leftElem and rightElem should be children of this element.
00305     void x_EnsureChildOrder(CNcbiTestTreeElement* leftElem,
00306                             CNcbiTestTreeElement* rightElem);
00307     void x_EnsureChildOrder(CNcbiTestTreeElement* leftElem,
00308                             size_t                idx_right);
00309 
00310     /// Add leftElem (rightElem) in the list of elements that should be
00311     /// "lefter" ("righter") in the tests tree.
00312     void x_AddToMustLeft(CNcbiTestTreeElement* elem,
00313                          CNcbiTestTreeElement* leftElem);
00314     void x_AddToMustRight(CNcbiTestTreeElement* elem,
00315                           CNcbiTestTreeElement* rightElem);
00316 
00317 
00318     /// Parent element in tests tree
00319     CNcbiTestTreeElement* m_Parent;
00320     /// Unit represented by the element
00321     but::test_unit*       m_TestUnit;
00322     /// If order of children was changed during checking dependencies
00323     bool                  m_OrderChanged;
00324     /// Children of the element in tests tree
00325     TElemsList            m_Children;
00326     /// Elements that should be "on the left" from this element in tests tree
00327     /// (should have less index in the parent's list of children).
00328     TElemsSet             m_MustLeft;
00329     /// Elements that should be "on the right" from this element in tests tree
00330     /// (should have greater index in the parent's list of children).
00331     TElemsSet             m_MustRight;
00332 };
00333 
00334 
00335 /// Class for traversing all Boost tests tree and building tree structure in
00336 /// our own accessible manner.
00337 class CNcbiTestsTreeBuilder : public but::test_tree_visitor
00338 {
00339 public:
00340     CNcbiTestsTreeBuilder(void);
00341     virtual ~CNcbiTestsTreeBuilder(void);
00342 
00343     virtual void visit            (but::test_case  const& test );
00344     virtual bool test_suite_start (but::test_suite const& suite);
00345     virtual void test_suite_finish(but::test_suite const& suite);
00346 
00347     /// Ensure good dependency of the tu test unit on tu_from test unit. If
00348     /// dependency is not fulfilled well then ensure that tu_from element will
00349     /// stand earlier in tests tree. Correct order is made in internal
00350     /// structures only. To make it in Boost tests tree you need to call
00351     /// FixUnitsOrder().
00352     ///
00353     /// @sa  FixUnitsOrder()
00354     void EnsureDep(but::test_unit* tu, but::test_unit* tu_from);
00355 
00356     /// Fix order of unit tests in the whole tree of tests. Any action is
00357     /// taken only if during calls to EnsureDep() some wrong order was found.
00358     ///
00359     /// @sa EnsureDep()
00360     void FixUnitsOrder(void);
00361 
00362 private:
00363     typedef map<but::test_unit*, CNcbiTestTreeElement*> TUnitToElemMap;
00364 
00365     /// Root element of the tests tree
00366     CNcbiTestTreeElement* m_RootElem;
00367     /// Element in tests tree representing started but not yet finished test
00368     /// suite, i.e. all test cases that will be visited now will for sure be
00369     /// from this test suite.
00370     CNcbiTestTreeElement* m_CurElem;
00371     /// Overall map of relations between test units and their representatives
00372     /// in elements tree.
00373     TUnitToElemMap        m_AllUnits;
00374 };
00375 
00376 
00377 /// Application for all unit tests
00378 class CNcbiTestApplication : public CNcbiApplication
00379 {
00380 public:
00381     CNcbiTestApplication(void);
00382 
00383     virtual void Init  (void);
00384     virtual int  Run   (void);
00385     virtual int  DryRun(void);
00386 
00387     /// Add user function
00388     void AddUserFunction(TNcbiTestUserFunction func,
00389                          ETestUserFuncType     func_type);
00390     /// Add dependency for test unit
00391     void AddTestDependsOn(but::test_unit* tu, but::test_unit* dep_tu);
00392     /// Set test as disabled by user
00393     void SetTestDisabled(but::test_unit* tu);
00394     /// Set flag that all tests globally disabled
00395     void SetGloballyDisabled(void);
00396     /// Set flag that all tests globally skipped
00397     void SetGloballySkipped(void);
00398 
00399     /// Initialize this application, main test suite and all test framework
00400     but::test_suite* InitTestFramework(int argc, char* argv[]);
00401     /// Get object with argument descriptions.
00402     /// Return NULL if it is not right time to fill in descriptions.
00403     CArgDescriptions* GetArgDescrs(void);
00404     /// Get parser evaluating configuration conditions.
00405     /// Return NULL if it is not right time to deal with the parser.
00406     CExprParser* GetIniParser(void);
00407 
00408     /// Save test unit in the collection of all tests.
00409     void CollectTestUnit(but::test_unit* tu);
00410     /// Get pointer to test case or test suite by its name.
00411     but::test_unit* GetTestUnit(CTempString test_name);
00412     /// Initialize already prepared test suite before running tests
00413     void InitTestsBeforeRun(void);
00414     /// Finalize test suite after running tests
00415     void FiniTestsAfterRun(void);
00416     /// Enable all necessary tests after execution but before printing report
00417     void ReEnableAllTests(void);
00418     /// Check the correct setting for unit timeout and check overall
00419     /// test timeout.
00420     void AdjustTestTimeout(but::test_unit* tu);
00421     /// Mark test case as failed due to hit of the timeout
00422     void SetTestTimedOut(but::test_case* tc);
00423     /// Register the fact of test failure
00424     void SetTestErrored(but::test_case* tc);
00425     /// Check if given test is marked as requiring fixing in the future
00426     bool IsTestToFix(const but::test_unit* tu);
00427 
00428     /// Get number of actually executed tests
00429     int GetRanTestsCount(void);
00430     /// Get number of tests that were failed but are marked to be fixed
00431     int GetToFixTestsCount(void);
00432     /// Get string representation of result of test execution
00433     string GetTestResultString(but::test_unit* tu);
00434     /// Get pointer to empty test case added to Boost for internal purposes
00435     but::test_case* GetDummyTest(void);
00436     /// Check if user initialization functions failed
00437     bool IsInitFailed(void);
00438 
00439     /// Check if there were any test errors
00440     bool HasTestErrors(void);
00441     /// Check if there were any timeouted tests
00442     bool HasTestTimeouts(void);
00443 
00444 private:
00445     typedef list<TNcbiTestUserFunction> TUserFuncsList;
00446 
00447 
00448     /// Setup our own reporter for Boost.Test
00449     void x_SetupBoostReporters(void);
00450     /// Call all user functions. Return TRUE if functions execution is
00451     /// successful and FALSE if come function thrown exception.
00452     bool x_CallUserFuncs(ETestUserFuncType func_type);
00453     /// Ensure that all dependencies stand earlier in tests tree than their
00454     /// dependents.
00455     void x_EnsureAllDeps(void);
00456     /// Set up real Boost.Test dependencies based on ones made by
00457     /// AddTestDependsOn().
00458     ///
00459     /// @sa AddTestDependsOn()
00460     void x_ActualizeDeps(void);
00461     /// Enable / disable tests based on application configuration file
00462     bool x_ReadConfiguration(void);
00463     /// Get number of tests which Boost will execute
00464     int x_GetEnabledTestsCount(void);
00465     /// Add empty test necesary for internal purposes
00466     void x_AddDummyTest(void);
00467     /// Initialize common for all tests parser variables
00468     /// (OS*, COMPILER* and DLL_BUILD)
00469     void x_InitCommonParserVars(void);
00470     /// Apply standard trimmings to test name and return resultant test name
00471     /// which will identify test inside the framework.
00472     string x_GetTrimmedTestName(const string& test_name);
00473     /// Enable / disable all tests known to application
00474     void x_EnableAllTests(bool enable);
00475     /// Collect names and pointers to all tests existing in master test suite
00476     void x_CollectAllTests();
00477     /// Calculate the value from configuration file
00478     bool x_CalcConfigValue(const string& value);
00479 
00480 
00481     /// Mode of running testing application
00482     enum ERunMode {
00483         fTestList   = 0x1,  ///< Only tests list is requested
00484         fDisabled   = 0x2,  ///< All tests are disabled in configuration file
00485         fInitFailed = 0x4   ///< Initialization user functions failed
00486     };
00487     typedef unsigned int TRunMode;
00488 
00489 
00490     /// If Run() was called or not
00491     ///
00492     /// @sa Run()
00493     bool                      m_RunCalled;
00494     /// Mode of running the application
00495     TRunMode                  m_RunMode;
00496     /// Lists of all user-defined functions
00497     TUserFuncsList            m_UserFuncs[eTestUserFuncLast
00498                                             - eTestUserFuncFirst + 1];
00499     /// Argument descriptions to be passed to SetArgDescriptions().
00500     /// Value is not null only during NCBITEST_INIT_CMDLINE() function
00501     AutoPtr<CArgDescriptions> m_ArgDescrs;
00502     /// Parser to evaluate expressions in configuration file.
00503     /// Value is not null only during NCBITEST_INIT_VARIABLES() function
00504     AutoPtr<CExprParser>      m_IniParser;
00505     /// List of all test units mapped to their names.
00506     TStringToUnitMap          m_AllTests;
00507     /// List of all disabled tests
00508     TUnitsSet                 m_DisabledTests;
00509     /// List of all tests which result is a timeout
00510     TUnitsSet                 m_TimedOutTests;
00511     /// List of all tests marked as in need of fixing in the future
00512     TUnitsSet                 m_ToFixTests;
00513     /// List of all dependencies for each test having dependencies
00514     TUnitToManyMap            m_TestDeps;
00515     /// Observer to make test dependencies and look for unit's timeouts
00516     CNcbiTestsObserver        m_Observer;
00517     /// Boost reporter - must be pointer because Boost.Test calls free() on it
00518     CNcbiBoostReporter*       m_Reporter;
00519     /// Boost logger - must be pointer because Boost.Test calls free() on it
00520     CNcbiBoostLogger*         m_Logger;
00521     /// Output stream for Boost.Test report
00522     ofstream                  m_ReportOut;
00523     /// Builder of internal accessible from library tests tree
00524     CNcbiTestsTreeBuilder     m_TreeBuilder;
00525     /// Empty test case added to Boost for internal perposes
00526     but::test_case*           m_DummyTest;
00527     /// Timeout for the whole test
00528     double                    m_Timeout;
00529     /// String representation for whole test timeout (real value taken from
00530     /// CHECK_TIMEOUT in Makefile).
00531     string                    m_TimeoutStr;
00532     /// Multiplicator for timeouts
00533     double                    m_TimeMult;
00534     /// Timer measuring elapsed time for the whole test
00535     CStopWatch                m_Timer;
00536     /// Timeout that was set in currently executing unit before adjustment
00537     ///
00538     /// @sa AdjustTestTimeout()
00539     unsigned int              m_CurUnitTimeout;
00540     /// Flag showing if there were some test errors
00541     bool                      m_HasTestErrors;
00542     /// Flag showing if there were some timeouted tests
00543     bool                      m_HasTestTimeouts;
00544 };
00545 
00546 
00547 inline
00548 CNcbiBoostReporter::CNcbiBoostReporter()
00549     : m_IsXML(false)
00550 {}
00551 
00552 inline void
00553 CNcbiBoostReporter::SetOutputFormat(but::output_format format)
00554 {
00555     if (format == but::XML) {
00556         m_IsXML = true;
00557         m_Upper = new but::output::xml_report_formatter();
00558     }
00559     else {
00560         m_IsXML = false;
00561         m_Upper = new but::output::plain_report_formatter();
00562     }
00563 }
00564 
00565 
00566 inline
00567 CNcbiBoostLogger::CNcbiBoostLogger(void)
00568 : m_IsXML(false)
00569 {}
00570 
00571 inline void
00572 CNcbiBoostLogger::SetOutputFormat(but::output_format format)
00573 {
00574     if (format == but::XML) {
00575         m_IsXML = true;
00576         m_Upper = new but::output::xml_log_formatter();
00577     }
00578     else {
00579         m_IsXML = false;
00580         m_Upper = new but::output::compiler_log_formatter();
00581     }
00582 }
00583 
00584 
00585 inline
00586 CNcbiTestTreeElement::CNcbiTestTreeElement(but::test_unit* tu)
00587     : m_Parent      (NULL),
00588       m_TestUnit    (tu),
00589       m_OrderChanged(false)
00590 {}
00591 
00592 CNcbiTestTreeElement::~CNcbiTestTreeElement(void)
00593 {
00594     ITERATE(TElemsList, it, m_Children) {
00595         delete *it;
00596     }
00597 }
00598 
00599 inline void
00600 CNcbiTestTreeElement::AddChild(CNcbiTestTreeElement* element)
00601 {
00602     m_Children.push_back(element);
00603     element->m_Parent = this;
00604 }
00605 
00606 void
00607 CNcbiTestTreeElement::x_EnsureChildOrder(CNcbiTestTreeElement* leftElem,
00608                                          size_t                idx_right)
00609 {
00610     size_t idx_left = 0;
00611     for (; idx_left < m_Children.size(); ++ idx_left) {
00612         if (m_Children[idx_left] == leftElem)
00613             break;
00614     }
00615     _ASSERT(idx_left < m_Children.size());
00616 
00617     if (idx_left < idx_right)
00618         return;
00619 
00620     m_OrderChanged = true;
00621     m_Children.erase(m_Children.begin() + idx_left);
00622     m_Children.insert(m_Children.begin() + idx_right, leftElem);
00623 
00624     ITERATE(TElemsSet, it, leftElem->m_MustLeft) {
00625         x_EnsureChildOrder(*it, idx_right);
00626         // If order is changed in the above call then leftElem will move to
00627         // the right and we need to change our index.
00628         while (m_Children[idx_right] != leftElem)
00629             ++idx_right;
00630     }
00631 }
00632 
00633 void
00634 CNcbiTestTreeElement::x_AddToMustLeft(CNcbiTestTreeElement* elem,
00635                                       CNcbiTestTreeElement* leftElem)
00636 {
00637     if (elem == leftElem) {
00638         NCBI_THROW(CCoreException, eCore,
00639                    FORMAT("Circular dependency found: '"
00640                           << elem->m_TestUnit->p_name.get()
00641                           << "' must depend on itself."));
00642     }
00643 
00644     elem->m_MustLeft.insert(leftElem);
00645 
00646     ITERATE(TElemsSet, it, elem->m_MustRight) {
00647         x_AddToMustLeft(*it, leftElem);
00648     }
00649 }
00650 
00651 void
00652 CNcbiTestTreeElement::x_AddToMustRight(CNcbiTestTreeElement* elem,
00653                                        CNcbiTestTreeElement* rightElem)
00654 {
00655     if (elem == rightElem) {
00656         NCBI_THROW(CCoreException, eCore,
00657                    FORMAT("Circular dependency found: '"
00658                           << elem->m_TestUnit->p_name.get()
00659                           << "' must depend on itself."));
00660     }
00661 
00662     elem->m_MustRight.insert(rightElem);
00663 
00664     ITERATE(TElemsSet, it, elem->m_MustLeft) {
00665         x_AddToMustRight(*it, rightElem);
00666     }
00667 }
00668 
00669 inline void
00670 CNcbiTestTreeElement::x_EnsureChildOrder(CNcbiTestTreeElement* leftElem,
00671                                          CNcbiTestTreeElement* rightElem)
00672 {
00673     x_AddToMustLeft(rightElem, leftElem);
00674     x_AddToMustRight(leftElem, rightElem);
00675 
00676     size_t idx_right = 0;
00677     for (; idx_right < m_Children.size(); ++idx_right) {
00678         if (m_Children[idx_right] == rightElem)
00679             break;
00680     }
00681     _ASSERT(idx_right < m_Children.size());
00682 
00683     x_EnsureChildOrder(leftElem, idx_right);
00684 }
00685 
00686 void
00687 CNcbiTestTreeElement::EnsureDep(CNcbiTestTreeElement* from)
00688 {
00689     TElemsList parents;
00690 
00691     CNcbiTestTreeElement* parElem = this;
00692     if (m_TestUnit->p_type != but::tut_suite) {
00693         parElem = m_Parent;
00694     }
00695     do {
00696         parents.push_back(parElem);
00697         parElem = parElem->m_Parent;
00698     }
00699     while (parElem != NULL);
00700 
00701     parElem = from;
00702     CNcbiTestTreeElement* fromElem = from;
00703     do {
00704         TElemsList::iterator it = find(parents.begin(), parents.end(), parElem);
00705         if (it != parents.end()) {
00706             break;
00707         }
00708         fromElem = parElem;
00709         parElem  = parElem->m_Parent;
00710     }
00711     while (parElem != NULL);
00712     _ASSERT(parElem);
00713 
00714     if (parElem == this) {
00715         NCBI_THROW(CCoreException, eCore,
00716                    FORMAT("Error in unit tests setup: dependency of '"
00717                           << m_TestUnit->p_name.get() << "' from '"
00718                           << from->m_TestUnit->p_name.get()
00719                           << "' can never be implemented."));
00720     }
00721 
00722     CNcbiTestTreeElement* toElem = this;
00723     while (toElem->m_Parent != parElem) {
00724         toElem = toElem->m_Parent;
00725     }
00726 
00727     parElem->x_EnsureChildOrder(fromElem, toElem);
00728 }
00729 
00730 void
00731 CNcbiTestTreeElement::FixUnitsOrder(void)
00732 {
00733     if (m_OrderChanged) {
00734         but::test_suite* suite = static_cast<but::test_suite*>(m_TestUnit);
00735         ITERATE(TElemsList, it, m_Children) {
00736             suite->remove((*it)->m_TestUnit->p_id);
00737         }
00738         ITERATE(TElemsList, it, m_Children) {
00739             suite->add((*it)->m_TestUnit);
00740         }
00741     }
00742 
00743     ITERATE(TElemsList, it, m_Children) {
00744         (*it)->FixUnitsOrder();
00745     }
00746 }
00747 
00748 inline but::test_unit*
00749 CNcbiTestTreeElement::GetTestUnit(void)
00750 {
00751     return m_TestUnit;
00752 }
00753 
00754 inline CNcbiTestTreeElement*
00755 CNcbiTestTreeElement::GetParent(void)
00756 {
00757     return m_Parent;
00758 }
00759 
00760 
00761 CNcbiTestsTreeBuilder::CNcbiTestsTreeBuilder(void)
00762     : m_RootElem(NULL),
00763       m_CurElem (NULL)
00764 {}
00765 
00766 CNcbiTestsTreeBuilder::~CNcbiTestsTreeBuilder(void)
00767 {
00768     delete m_RootElem;
00769 }
00770 
00771 bool
00772 CNcbiTestsTreeBuilder::test_suite_start(but::test_suite const& suite)
00773 {
00774     but::test_suite* nc_suite = const_cast<but::test_suite*>(&suite);
00775     if (m_RootElem) {
00776         CNcbiTestTreeElement* next_elem = new CNcbiTestTreeElement(nc_suite);
00777         m_CurElem->AddChild(next_elem);
00778         m_CurElem = next_elem;
00779     }
00780     else {
00781         m_RootElem = new CNcbiTestTreeElement(nc_suite);
00782         m_CurElem  = m_RootElem;
00783     }
00784 
00785     m_AllUnits[nc_suite] = m_CurElem;
00786 
00787     return true;
00788 }
00789 
00790 void
00791 CNcbiTestsTreeBuilder::test_suite_finish(but::test_suite const& suite)
00792 {
00793     _ASSERT(m_CurElem->GetTestUnit()
00794                             == &static_cast<const but::test_unit&>(suite));
00795     m_CurElem = m_CurElem->GetParent();
00796 }
00797 
00798 void
00799 CNcbiTestsTreeBuilder::visit(but::test_case const& test)
00800 {
00801     but::test_case* nc_test = const_cast<but::test_case*>(&test);
00802     CNcbiTestTreeElement* elem = new CNcbiTestTreeElement(nc_test);
00803     m_CurElem->AddChild(elem);
00804     m_AllUnits[nc_test] = elem;
00805 }
00806 
00807 inline void
00808 CNcbiTestsTreeBuilder::EnsureDep(but::test_unit* tu, but::test_unit* tu_from)
00809 {
00810     CNcbiTestTreeElement* elem = m_AllUnits[tu];
00811     CNcbiTestTreeElement* elem_from = m_AllUnits[tu_from];
00812     _ASSERT(elem  &&  elem_from);
00813 
00814     elem->EnsureDep(elem_from);
00815 }
00816 
00817 inline void
00818 CNcbiTestsTreeBuilder::FixUnitsOrder(void)
00819 {
00820     m_RootElem->FixUnitsOrder();
00821 }
00822 
00823 
00824 inline
00825 CNcbiTestApplication::CNcbiTestApplication(void)
00826     : m_RunCalled(false),
00827       m_RunMode  (0),
00828       m_DummyTest(NULL),
00829       m_Timeout  (0),
00830       m_TimeMult (1),
00831       m_Timer    (CStopWatch::eStart),
00832       m_HasTestErrors(false),
00833       m_HasTestTimeouts(false)
00834 {
00835     m_Reporter = new CNcbiBoostReporter();
00836     m_Logger   = new CNcbiBoostLogger();
00837 
00838     // Do not show warning about inaccessible configuration file
00839     SetDiagFilter(eDiagFilter_Post, "!(106.11)");
00840 }
00841 
00842 /// Application for unit tests
00843 static CNcbiTestApplication&
00844 s_GetTestApp(void)
00845 {
00846     static CNcbiTestApplication s_TestApp;
00847 
00848     return s_TestApp;
00849 }
00850 
00851 void
00852 CNcbiTestApplication::Init(void)
00853 {
00854     m_ArgDescrs = new CArgDescriptions();
00855     m_ArgDescrs->AddFlag("-help",
00856          "Print test framework related command line arguments");
00857 #ifndef NCBI_COMPILER_WORKSHOP
00858     m_ArgDescrs->AddOptionalKey("-run_test", "Filter",
00859          "Allows to filter which test units to run",
00860          CArgDescriptions::eString, CArgDescriptions::fMandatorySeparator);
00861 #endif
00862     m_ArgDescrs->AddFlag("dryrun",
00863                          "Do not actually run tests, "
00864                          "just print list of all available tests.");
00865     m_ArgDescrs->SetUsageContext(GetArguments().GetProgramBasename(),
00866                                  "NCBI unit test");
00867     if (!m_UserFuncs[eTestUserFuncCmdLine].empty())
00868         x_CallUserFuncs(eTestUserFuncCmdLine);
00869     SetupArgDescriptions(m_ArgDescrs.release());
00870 }
00871 
00872 int
00873 CNcbiTestApplication::Run(void)
00874 {
00875     m_RunCalled = true;
00876     return 0;
00877 }
00878 
00879 int
00880 CNcbiTestApplication::DryRun(void)
00881 {
00882     m_RunCalled = true;
00883     m_RunMode |= fTestList;
00884     but::results_reporter::set_level(but::DETAILED_REPORT);
00885     return 0;
00886 }
00887 
00888 inline void
00889 CNcbiTestApplication::AddUserFunction(TNcbiTestUserFunction func,
00890                                       ETestUserFuncType     func_type)
00891 {
00892     m_UserFuncs[func_type].push_back(func);
00893 }
00894 
00895 inline void
00896 CNcbiTestApplication::AddTestDependsOn(but::test_unit* tu,
00897                                        but::test_unit* dep_tu)
00898 {
00899     m_TestDeps[tu].insert(dep_tu);
00900 }
00901 
00902 inline void
00903 CNcbiTestApplication::SetTestDisabled(but::test_unit* tu)
00904 {
00905     if (but::runtime_config::test_to_run().empty()) {
00906         tu->p_enabled.set(false);
00907         m_DisabledTests.insert(tu);
00908     }
00909 }
00910 
00911 inline CArgDescriptions*
00912 CNcbiTestApplication::GetArgDescrs(void)
00913 {
00914     return m_ArgDescrs.get();
00915 }
00916 
00917 inline CExprParser*
00918 CNcbiTestApplication::GetIniParser(void)
00919 {
00920     return m_IniParser.get();
00921 }
00922 
00923 inline but::test_case*
00924 CNcbiTestApplication::GetDummyTest(void)
00925 {
00926     return m_DummyTest;
00927 }
00928 
00929 inline bool
00930 CNcbiTestApplication::IsInitFailed(void)
00931 {
00932     return (m_RunMode & fInitFailed) != 0;
00933 }
00934 
00935 string
00936 CNcbiTestApplication::x_GetTrimmedTestName(const string& test_name)
00937 {
00938     string new_name = test_name;
00939     SIZE_TYPE pos = NStr::FindCase(new_name, "::", 0, new_name.size(),
00940                                    NStr::eLast);
00941     if (pos != NPOS) {
00942         new_name = new_name.substr(pos + 2);
00943     }
00944 
00945     if(NStr::StartsWith(new_name, "test_", NStr::eNocase)) {
00946         new_name = new_name.substr(5);
00947     }
00948     else if(NStr::StartsWith(new_name, "test", NStr::eNocase)) {
00949         new_name = new_name.substr(4);
00950     }
00951 
00952     return new_name;
00953 }
00954 
00955 inline void
00956 CNcbiTestApplication::CollectTestUnit(but::test_unit* tu)
00957 {
00958     const string unit_name = x_GetTrimmedTestName(tu->p_name.get());
00959     if (unit_name == kDummyTestCaseName)
00960         return;
00961     string test_name(unit_name);
00962     int index = 0;
00963     for (;;) {
00964         but::test_unit*& tu_val = m_AllTests[test_name];
00965         if (!tu_val) {
00966             tu_val = tu;
00967             if (test_name != unit_name) {
00968                 LOG_POST_X(3, Info << "Duplicate name found: '" << unit_name
00969                                    << "' - renamed to '" << test_name << "'");
00970                 tu->p_name.set(test_name);
00971             }
00972             break;
00973         }
00974         test_name = unit_name;
00975         test_name += "_";
00976         test_name += NStr::IntToString(++index);
00977     }
00978 }
00979 
00980 inline void
00981 CNcbiTestApplication::x_EnsureAllDeps(void)
00982 {
00983     ITERATE(TUnitToManyMap, it, m_TestDeps) {
00984         but::test_unit* test = it->first;
00985         ITERATE(TUnitsSet, dep_it, it->second) {
00986             but::test_unit* dep_test = *dep_it;
00987             m_TreeBuilder.EnsureDep(test, dep_test);
00988         }
00989     }
00990 
00991     m_TreeBuilder.FixUnitsOrder();
00992 }
00993 
00994 inline void
00995 CNcbiTestApplication::x_ActualizeDeps(void)
00996 {
00997     ITERATE(TUnitToManyMap, it, m_TestDeps) {
00998         but::test_unit* test = it->first;
00999         if (!m_DisabledTests.count(test) && !test->p_enabled) {
01000             continue;
01001         }
01002 
01003         ITERATE(TUnitsSet, dep_it, it->second) {
01004             but::test_unit* dep_test = *dep_it;
01005             if (!m_DisabledTests.count(dep_test) && !dep_test->p_enabled) {
01006                 continue;
01007             }
01008 
01009             test->depends_on(dep_test);
01010         }
01011     }
01012 }
01013 
01014 /// Helper macro to check if NCBI preprocessor flag was defined empty or
01015 /// equal to 1.
01016 /// Macro expands to true if flag was defined empty or equal to 1 and to false
01017 /// if it was defined to something else or wasn't defined at all.
01018 #define IS_FLAG_DEFINED(flag)                                 \
01019     BOOST_PP_TUPLE_ELEM(2, 1, IS_FLAG_DEFINED_I(BOOST_PP_CAT(NCBI_, flag)))
01020 #define IS_VAR_DEFINED(var)                                   \
01021     BOOST_PP_TUPLE_ELEM(2, 1, IS_FLAG_DEFINED_I(var))
01022 #define IS_FLAG_DEFINED_I(flag)                               \
01023     (BOOST_PP_CAT(IS_FLAG_DEFINED_II_, flag) (), false)
01024 #define IS_FLAG_DEFINED_II_()                                 \
01025     BOOST_PP_NIL, true) BOOST_PP_TUPLE_EAT(2) (BOOST_PP_NIL
01026 #define IS_FLAG_DEFINED_II_1()                                \
01027     BOOST_PP_NIL, true) BOOST_PP_TUPLE_EAT(2) (BOOST_PP_NIL
01028 
01029 /// List of features that will be converted to unittest variables
01030 /// (checking testsuite environment variable $FEATURES).
01031 /// If you would like to add some new veriables here, please
01032 /// see Unix configure utility and Project Tree Builder for full
01033 /// list of supported values.
01034 /// @note
01035 ///    All non alphanumeric charecters in the names replaced with "_" symbol.
01036 static const char* s_NcbiFeatures[] = {
01037     // Features 
01038     "AIX",
01039     "BSD",
01040     "CompaqCompiler",
01041     "Cygwin",
01042     "CygwinMT",
01043     "DLL",
01044     "DLL_BUILD",
01045     "Darwin",
01046     "GCC",
01047     "ICC",
01048     "IRIX",
01049     "KCC",
01050     "Linux",
01051     "MIPSpro",
01052     "MSVC",
01053     "MSWin",
01054     "MT",
01055     "MacOS",
01056     "Ncbi_JNI",            // Ncbi-JNI
01057     "OSF",
01058     "PubSeqOS",
01059     "SRAT_internal",       // SRAT-internal
01060     "Solaris",
01061     "VisualAge",
01062     "WinMain",
01063     "WorkShop",
01064     "XCODE",
01065     "in_house_resources",  // in-house-resources
01066     "unix",
01067 
01068     // Packages
01069     "BZ2",
01070     "BerkeleyDB",
01071     "BerkeleyDB__",        // BerkeleyDB++
01072     "Boost_Regex",         // Boost.Regex
01073     "Boost_Spirit",        // Boost.Spirit
01074     "Boost_Test",          // Boost.Test
01075     "Boost_Test_Included", // Boost.Test.Included
01076     "Boost_Threads",       // Boost.Threads
01077     "C_Toolkit",           // C-Toolkit
01078     "CPPUNIT",
01079     "C_ncbi",
01080     "DBLib",
01081     "EXPAT",
01082     "FLTK",
01083     "FUSE",
01084     "Fast_CGI",            // Fast-CGI
01085     "FreeTDS",
01086     "FreeType",
01087     "GIF",
01088     "GLUT",
01089     "GNUTLS",
01090     "HDF5",
01091     "ICU",
01092     "JPEG",
01093     "LIBXML",
01094     "LIBXSLT",
01095     "LZO",
01096     "LocalBZ2",
01097     "LocalMSGMAIL2",
01098     "LocalNCBILS",
01099     "LocalPCRE",
01100     "LocalSSS",
01101     "LocalZ",
01102     "MAGIC",
01103     "MESA",
01104     "MUPARSER",
01105     "MySQL",
01106     "NCBILS2",
01107     "ODBC",
01108     "OECHEM",
01109     "OPENSSL",
01110     "ORBacus",
01111     "OpenGL",
01112     "PCRE",
01113     "PNG",
01114     "PYTHON",
01115     "PYTHON23",
01116     "PYTHON24",
01117     "PYTHON25",
01118     "SABLOT",
01119     "SGE",
01120     "SP",
01121     "SQLITE",
01122     "SQLITE3",
01123     "SQLITE3ASYNC",
01124     "SSSDB",
01125     "SSSUTILS",
01126     "Sybase",
01127     "SybaseCTLIB",
01128     "SybaseDBLIB",
01129     "TIFF",
01130     "UNGIF",
01131     "UUID",
01132     "XPM",
01133     "Xalan",
01134     "Xerces",
01135     "Z",
01136     "wx2_8",               // wx2.8
01137     "wxWidgets",
01138     "wxWindows",
01139 
01140     // Projects
01141     "algo",
01142     "app",
01143     "bdb",
01144     "cgi",
01145     "connext",
01146     "ctools",
01147     "dbapi",
01148     "gbench",
01149     "gui",
01150     "local_lbsm",
01151     "ncbi_crypt",
01152     "objects",
01153     "serial"
01154 };
01155 
01156 
01157 inline void
01158 CNcbiTestApplication::x_InitCommonParserVars(void)
01159 {
01160     m_IniParser->AddSymbol("COMPILER_Compaq",    IS_FLAG_DEFINED(COMPILER_COMPAQ));
01161     m_IniParser->AddSymbol("COMPILER_GCC",       IS_FLAG_DEFINED(COMPILER_GCC));
01162     m_IniParser->AddSymbol("COMPILER_ICC",       IS_FLAG_DEFINED(COMPILER_ICC));
01163     m_IniParser->AddSymbol("COMPILER_KCC",       IS_FLAG_DEFINED(COMPILER_KCC));
01164     m_IniParser->AddSymbol("COMPILER_MipsPro",   IS_FLAG_DEFINED(COMPILER_MIPSPRO));
01165     m_IniParser->AddSymbol("COMPILER_MSVC",      IS_FLAG_DEFINED(COMPILER_MSVC));
01166     m_IniParser->AddSymbol("COMPILER_VisualAge", IS_FLAG_DEFINED(COMPILER_VISUALAGE));
01167     m_IniParser->AddSymbol("COMPILER_WorkShop",  IS_FLAG_DEFINED(COMPILER_WORKSHOP));
01168 
01169     m_IniParser->AddSymbol("OS_AIX",             IS_FLAG_DEFINED(OS_AIX));
01170     m_IniParser->AddSymbol("OS_BSD",             IS_FLAG_DEFINED(OS_BSD));
01171     m_IniParser->AddSymbol("OS_Cygwin",          IS_FLAG_DEFINED(OS_CYGWIN));
01172     m_IniParser->AddSymbol("OS_MacOSX",          IS_FLAG_DEFINED(OS_DARWIN));
01173     m_IniParser->AddSymbol("OS_Irix",            IS_FLAG_DEFINED(OS_IRIX));
01174     m_IniParser->AddSymbol("OS_Linux",           IS_FLAG_DEFINED(OS_LINUX));
01175     m_IniParser->AddSymbol("OS_MacOS",           IS_FLAG_DEFINED(OS_MAC));
01176     m_IniParser->AddSymbol("OS_Windows",         IS_FLAG_DEFINED(OS_MSWIN));
01177     m_IniParser->AddSymbol("OS_Tru64",           IS_FLAG_DEFINED(OS_OSF1));
01178     m_IniParser->AddSymbol("OS_Solaris",         IS_FLAG_DEFINED(OS_SOLARIS));
01179     m_IniParser->AddSymbol("OS_Unix",            IS_FLAG_DEFINED(OS_UNIX));
01180 
01181     m_IniParser->AddSymbol("PLATFORM_Bits32",    NCBI_PLATFORM_BITS == 32);
01182     m_IniParser->AddSymbol("PLATFORM_Bits64",    NCBI_PLATFORM_BITS == 64);
01183 
01184     m_IniParser->AddSymbol("PLATFORM_BigEndian",     IS_VAR_DEFINED(WORDS_BIGENDIAN));
01185     m_IniParser->AddSymbol("PLATFORM_LittleEndian", !IS_VAR_DEFINED(WORDS_BIGENDIAN));
01186 
01187     m_IniParser->AddSymbol("BUILD_Dll",          IS_FLAG_DEFINED(DLL_BUILD));
01188     m_IniParser->AddSymbol("BUILD_Static",      !IS_FLAG_DEFINED(DLL_BUILD));
01189 
01190     m_IniParser->AddSymbol("BUILD_Debug",        IS_VAR_DEFINED(_DEBUG));
01191     m_IniParser->AddSymbol("BUILD_Release",     !IS_VAR_DEFINED(_DEBUG));
01192 
01193 
01194     // Add variables based on features available in the build
01195 
01196     string features_str = NCBI_GetBuildFeatures();
01197     if (features_str.empty()) {
01198         return;
01199     }
01200     // Split $FEATURES to tokens
01201     list<string> features_list;
01202     NStr::Split(features_str, " ", features_list);
01203     // Convert list<> to set<> to speed up a search
01204     typedef set<string> TFeatures;
01205     TFeatures features;
01206     // For all features
01207     ITERATE(list<string>, it, features_list) {
01208         // Replace all non alphanumeric characters in the names with "_".
01209         // Ignore negative features (with first "-" characters)
01210         string f = *it;
01211         if (f[0] != '-') {
01212             NON_CONST_ITERATE (string, fit, f) {
01213                 if (!isalnum((unsigned char)(*fit))) {
01214                     *fit = '_';
01215                 }
01216             }
01217             // Add feature name
01218             features.insert(f);
01219         }
01220     }
01221     // Add FEATURE_* variables
01222     for (size_t i = 0; i < sizeof(s_NcbiFeatures) / sizeof(s_NcbiFeatures[0]); i++) {
01223         string name("FEATURE_");
01224         name += s_NcbiFeatures[i];
01225         TFeatures::const_iterator it = features.find(s_NcbiFeatures[i]);
01226         bool found = (it != features.end());
01227         m_IniParser->AddSymbol(name.c_str(), found);
01228     }
01229 }
01230 
01231 inline bool
01232 CNcbiTestApplication::x_CalcConfigValue(const string& value)
01233 {
01234     m_IniParser->Parse(value.c_str());
01235     const CExprValue& expr_res = m_IniParser->GetResult();
01236 
01237     if (expr_res.GetType() == CExprValue::eBOOL  &&  !expr_res.GetBool())
01238         return false;
01239 
01240     return true;
01241 }
01242 
01243 
01244 void
01245 DUMMY_TEST_FUNCTION_NAME(void)
01246 {
01247     if (s_GetTestApp().IsInitFailed()) {
01248         but::results_collector.test_unit_aborted(
01249                                         *s_GetTestApp().GetDummyTest());
01250     }
01251 }
01252 
01253 
01254 void
01255 CNcbiTestApplication::SetGloballyDisabled(void)
01256 {
01257     m_RunMode |= fDisabled;
01258 
01259     // This should certainly go to the output. So we can use only printf,
01260     // nothing else.
01261     printf("All tests are disabled in current configuration.\n"
01262            " (for autobuild scripts: NCBI_UNITTEST_DISABLED)\n");
01263 }
01264 
01265 void
01266 CNcbiTestApplication::SetGloballySkipped(void)
01267 {
01268     m_RunMode |= fDisabled;
01269 
01270     // This should certainly go to the output. So we can use only printf,
01271     // nothing else.
01272     printf("Tests cannot be executed in current configuration "
01273                                                     "and will be skipped.\n"
01274            " (for autobuild scripts: NCBI_UNITTEST_SKIPPED)\n");
01275 }
01276 
01277 inline void
01278 CNcbiTestApplication::x_AddDummyTest(void)
01279 {
01280     if (!m_DummyTest) {
01281         m_DummyTest = BOOST_TEST_CASE(&DUMMY_TEST_FUNCTION_NAME);
01282         but::framework::master_test_suite().add(m_DummyTest);
01283     }
01284 }
01285 
01286 inline bool
01287 CNcbiTestApplication::x_ReadConfiguration(void)
01288 {
01289     m_IniParser = new CExprParser(CExprParser::eDenyAutoVar);
01290     x_InitCommonParserVars();
01291     if (!x_CallUserFuncs(eTestUserFuncVars))
01292         return false;
01293 
01294     const IRegistry& registry = s_GetTestApp().GetConfig();
01295     list<string> reg_entries;
01296     registry.EnumerateEntries(kTestsDisableSectionName, &reg_entries);
01297 
01298     // Disable tests ...
01299     ITERATE(list<string>, it, reg_entries) {
01300         const string& test_name = *it;
01301         string reg_value = registry.Get(kTestsDisableSectionName, test_name);
01302 
01303         if (test_name == kTestConfigGlobalValue) {
01304             if (x_CalcConfigValue(reg_value)) {
01305                 SetGloballyDisabled();
01306             }
01307             continue;
01308         }
01309 
01310         but::test_unit* tu = GetTestUnit(test_name);
01311         if (tu) {
01312             if (x_CalcConfigValue(reg_value)) {
01313                 SetTestDisabled(tu);
01314             }
01315         }
01316         else {
01317             ERR_POST_X(2, Warning << "Invalid test case name: '"
01318                                   << test_name << "'");
01319         }
01320     }
01321 
01322     reg_entries.clear();
01323     registry.EnumerateEntries(kTestsToFixSectionName, &reg_entries);
01324     // Put tests into "to-fix" list
01325     ITERATE(list<string>, it, reg_entries) {
01326         const string& test_name = *it;
01327         string reg_value = registry.Get(kTestsToFixSectionName, test_name);
01328 
01329         but::test_unit* tu = GetTestUnit(test_name);
01330         if (tu) {
01331             if (x_CalcConfigValue(reg_value)) {
01332                 m_ToFixTests.insert(tu);
01333             }
01334         }
01335         else {
01336             ERR_POST_X(4, Warning << "Invalid test case name: '"
01337                                   << test_name << "'");
01338         }
01339     }
01340 
01341     reg_entries.clear();
01342     registry.EnumerateEntries(kTestsTimeoutSectionName, &reg_entries);
01343     // Adjust timeouts of test units
01344     ITERATE(list<string>, it, reg_entries) {
01345         const string& test_name = *it;
01346         string reg_value = registry.Get(kTestsTimeoutSectionName, test_name);
01347 
01348         but::test_unit* tu = GetTestUnit(test_name);
01349         if (tu) {
01350             list<CTempString> koef_lst;
01351             NStr::Split(reg_value, ";", koef_lst);
01352             ITERATE(list<CTempString>, it_koef, koef_lst) {
01353                 CTempString koef_str, koef_cond;
01354                 if (NStr::SplitInTwo(*it_koef, ":", koef_str, koef_cond)) {
01355                     if (x_CalcConfigValue(koef_cond)) {
01356                         double koef = NStr::StringToDouble(koef_str,
01357                                                 NStr::fAllowLeadingSpaces
01358                                                 | NStr::fAllowTrailingSpaces);
01359                         tu->p_timeout.set(Uint4(tu->p_timeout.get() * koef));
01360                         break;
01361                     }
01362                 }
01363                 else {
01364                     ERR_POST_X(6, "Bad format of TIMEOUT_MULT string: '"
01365                                   << reg_value << "'");
01366                     break;
01367                 }
01368             }
01369         }
01370         else {
01371             ERR_POST_X(5, Warning << "Invalid test case name: '"
01372                                   << test_name << "'");
01373         }
01374     }
01375 
01376     return true;
01377 }
01378 
01379 void
01380 CNcbiTestApplication::x_EnableAllTests(bool enable)
01381 {
01382     ITERATE(TStringToUnitMap, it, m_AllTests) {
01383         but::test_unit* tu = it->second;
01384         if (tu->p_type == but::tut_case) {
01385             tu->p_enabled.set(enable);
01386 
01387             /*
01388             For full correctness this functionality should exist but it
01389             can't be made now. So if test suite will be disabled by user
01390             then it will not be possible to get list of tests inside this
01391             suite to be included in the report.
01392 
01393             if (enable  &&  tu->p_type == but::tut_suite) {
01394                 but::results_collector.results(tu->p_id).p_skipped = false;
01395             }
01396             */
01397         }
01398     }
01399 }
01400 
01401 inline void
01402 CNcbiTestApplication::InitTestsBeforeRun(void)
01403 {
01404     bool need_run = !(m_RunMode & (fTestList + fDisabled));
01405     if (need_run  &&  !x_CallUserFuncs(eTestUserFuncInit)) {
01406         m_RunMode |= fInitFailed;
01407         need_run = false;
01408     }
01409     // fDisabled property can be changed in initialization functions
01410     if (m_RunMode & fDisabled)
01411         need_run = false;
01412 
01413     if (need_run) {
01414         x_EnsureAllDeps();
01415         x_ActualizeDeps();
01416     }
01417     else {
01418         x_EnableAllTests(false);
01419 
01420         if (m_RunMode & fInitFailed) {
01421             x_AddDummyTest();
01422         }
01423     }
01424 }
01425 
01426 inline void
01427 CNcbiTestApplication::FiniTestsAfterRun(void)
01428 {
01429     x_CallUserFuncs(eTestUserFuncFini);
01430 }
01431 
01432 inline void
01433 CNcbiTestApplication::ReEnableAllTests(void)
01434 {
01435     x_EnableAllTests(true);
01436 
01437     // Disabled tests can accidentally become not included in full list if
01438     // they were disabled in initialization
01439     ITERATE(TUnitsSet, it, m_DisabledTests) {
01440         (*it)->p_enabled.set(true);
01441     }
01442 }
01443 
01444 inline void
01445 CNcbiTestApplication::SetTestTimedOut(but::test_case* tc)
01446 {
01447     // If equal then it's real timeout, if not then it's just this unit hit
01448     // the whole test timeout.
01449     if (tc->p_timeout.get() == m_CurUnitTimeout) {
01450         m_TimedOutTests.insert(tc);
01451     }
01452     m_HasTestTimeouts = true;
01453 }
01454 
01455 inline void
01456 CNcbiTestApplication::SetTestErrored(but::test_case* tc)
01457 {
01458     if (m_TimedOutTests.find(tc) == m_TimedOutTests.end())
01459         m_HasTestErrors = true;
01460 }
01461 
01462 void
01463 CNcbiTestApplication::AdjustTestTimeout(but::test_unit* tu)
01464 {
01465     m_CurUnitTimeout = tu->p_timeout.get();
01466     unsigned int new_timeout = (unsigned int)(m_CurUnitTimeout * m_TimeMult);
01467 
01468     if (m_Timeout != 0) {
01469         double elapsed = m_Timer.Elapsed();
01470         if (m_Timeout <= elapsed) {
01471             CNcbiEnvironment env;
01472             printf("Maximum execution time of %s seconds is exceeded",
01473                    m_TimeoutStr.c_str());
01474             throw but::test_being_aborted();
01475         }
01476         new_timeout = (unsigned int)(m_Timeout - elapsed);
01477     }
01478     if (m_CurUnitTimeout == 0  ||  m_CurUnitTimeout > new_timeout) {
01479         tu->p_timeout.set(new_timeout);
01480     }
01481 }
01482 
01483 string
01484 CNcbiTestApplication::GetTestResultString(but::test_unit* tu)
01485 {
01486     string result;
01487     const but::test_results& tr = but::results_collector.results(tu->p_id);
01488 
01489     if (m_DisabledTests.count(tu) != 0  ||  (m_RunMode & fDisabled))
01490         result = kTestResultDisabled;
01491     else if (m_TimedOutTests.count(tu) != 0)
01492         result = kTestResultTimeout;
01493     else if (!tr.passed()  &&  m_ToFixTests.find(tu) != m_ToFixTests.end())
01494         result = kTestResultToFix;
01495     else if (tr.p_aborted)
01496         result = kTestResultAborted;
01497     else if (tr.p_assertions_failed.get() > tr.p_expected_failures.get()
01498              ||  tr.p_test_cases_failed.get()
01499                         + tr.p_test_cases_aborted.get() > 0)
01500     {
01501         result = kTestResultFailed;
01502     }
01503     else if ((m_RunMode & fTestList)  ||  tr.p_skipped)
01504         result = kTestResultSkipped;
01505     else if( tr.passed() )
01506         result = kTestResultPassed;
01507     else
01508         result = kTestResultFailed;
01509 
01510     return result;
01511 }
01512 
01513 int
01514 CNcbiTestApplication::GetRanTestsCount(void)
01515 {
01516     int result = 0;
01517     ITERATE(TStringToUnitMap, it, m_AllTests) {
01518         but::test_unit* tu = it->second;
01519         if (tu->p_type.get() != but::tut_case)
01520             continue;
01521 
01522         string str = GetTestResultString(tu);
01523         if (str != kTestResultDisabled  &&  str != kTestResultSkipped)
01524             ++result;
01525     }
01526     return result;
01527 }
01528 
01529 int
01530 CNcbiTestApplication::GetToFixTestsCount(void)
01531 {
01532     int result = 0;
01533     ITERATE(TUnitsSet, it, m_ToFixTests) {
01534         if (!but::results_collector.results((*it)->p_id).passed())
01535             ++result;
01536     }
01537     return result;
01538 }
01539 
01540 inline bool
01541 CNcbiTestApplication::IsTestToFix(const but::test_unit* tu)
01542 {
01543     return m_ToFixTests.find(const_cast<but::test_unit*>(tu))
01544                                                         != m_ToFixTests.end();
01545 }
01546 
01547 inline void
01548 CNcbiTestApplication::x_SetupBoostReporters(void)
01549 {
01550     but::output_format format = but::runtime_config::report_format();
01551 
01552     CNcbiEnvironment env;
01553     string is_autobuild = env.Get("NCBI_AUTOMATED_BUILD");
01554     if (! is_autobuild.empty()) {
01555         // There shouldn't be any message box in the automated build mode
01556         SuppressSystemMessageBox(fSuppress_All);
01557 
01558         format = but::XML;
01559         but::results_reporter::set_level(but::DETAILED_REPORT);
01560 
01561         string boost_rep = env.Get("NCBI_BOOST_REPORT_FILE");
01562         if (! boost_rep.empty()) {
01563             m_ReportOut.open(boost_rep.c_str());
01564             if (m_ReportOut.good()) {
01565                 but::results_reporter::set_stream(m_ReportOut);
01566             }
01567             else {
01568                 ERR_POST("Error opening Boost.Test report file '"
01569                          << boost_rep << "'");
01570             }
01571         }
01572     }
01573 
01574     m_Reporter->SetOutputFormat(format);
01575     but::results_reporter::set_format(m_Reporter);
01576 
01577     m_Logger->SetOutputFormat(but::runtime_config::log_format());
01578     but::unit_test_log.set_formatter(m_Logger);
01579 }
01580 
01581 static const char*
01582 s_GetUserFuncName(ETestUserFuncType func_type)
01583 {
01584     switch (func_type) {
01585     case eTestUserFuncInit:
01586         return "NCBITEST_AUTO_INIT()";
01587     case eTestUserFuncFini:
01588         return "NCBITEST_AUTO_FINI()";
01589     case eTestUserFuncCmdLine:
01590         return "NCBITEST_INIT_CMDLINE()";
01591     case eTestUserFuncVars:
01592         return "NCBITEST_INIT_VARIABLES()";
01593     case eTestUserFuncDeps:
01594         return "NCBITEST_INIT_TREE()";
01595     default:
01596         return NULL;
01597     }
01598 }
01599 
01600 bool
01601 CNcbiTestApplication::x_CallUserFuncs(ETestUserFuncType func_type)
01602 {
01603     ITERATE(TUserFuncsList, it, m_UserFuncs[func_type]) {
01604         try {
01605             (*it)();
01606         }
01607         catch (CException& e) {
01608             ERR_POST_X(1, "Exception in " << s_GetUserFuncName(func_type) << ": " << e);
01609             throw;
01610             //return false;
01611         }
01612         catch (exception& e) {
01613             ERR_POST_X(1, "Exception in " << s_GetUserFuncName(func_type) << ": " << e.what());
01614             throw;
01615             //return false;
01616         }
01617     }
01618 
01619     return true;
01620 }
01621 
01622 inline but::test_unit*
01623 CNcbiTestApplication::GetTestUnit(CTempString test_name)
01624 {
01625     TStringToUnitMap::iterator it = m_AllTests.find(
01626                                             x_GetTrimmedTestName(test_name));
01627     if (it == m_AllTests.end()) {
01628         NCBI_THROW(CCoreException, eInvalidArg,
01629                    "Test unit '" + (string)test_name + "' not found.");
01630     }
01631 
01632     return it->second;
01633 }
01634 
01635 inline void
01636 CNcbiTestApplication::x_CollectAllTests(void)
01637 {
01638     m_AllTests.clear();
01639     CNcbiTestsCollector collector;
01640     but::traverse_test_tree(but::framework::master_test_suite(), collector);
01641 }
01642 
01643 inline int
01644 CNcbiTestApplication::x_GetEnabledTestsCount(void)
01645 {
01646     but::test_case_counter tcc;
01647     but::traverse_test_tree(but::framework::master_test_suite(), tcc);
01648     return tcc.p_count;
01649 }
01650 
01651 but::test_suite*
01652 CNcbiTestApplication::InitTestFramework(int argc, char* argv[])
01653 {
01654     // Do not detect memory leaks using msvcrt - this information is useless
01655     boost::debug::detect_memory_leaks(false);
01656     boost::debug::break_memory_alloc(0);
01657 
01658     x_SetupBoostReporters();
01659     but::framework::register_observer(m_Observer);
01660 
01661     // TODO: change this functionality to use only -dryrun parameter
01662     for (int i = 1; i < argc; ++i) {
01663         if (NStr::CompareCase(argv[i], "--do_not_run") == 0) {
01664             m_RunMode |= fTestList;
01665             but::results_reporter::set_level(but::DETAILED_REPORT);
01666 
01667             for (int j = i + 1; j < argc; ++j) {
01668                 argv[j - 1] = argv[j];
01669             }
01670             --argc;
01671         }
01672     }
01673 
01674     CNcbiEnvironment env;
01675     m_TimeoutStr = env.Get("NCBI_CHECK_TIMEOUT");
01676     if (!m_TimeoutStr.empty()) {
01677         m_Timeout = NStr::StringToDouble(m_TimeoutStr, NStr::fConvErr_NoThrow);
01678     }
01679     if (m_Timeout == 0) {
01680         m_Timer.Stop();
01681     }
01682     else {
01683         m_Timeout = min(max(0.0, m_Timeout - 3), 0.9 * m_Timeout);
01684     }
01685     m_TimeMult = NCBI_GetCheckTimeoutMult();
01686 
01687     if (AppMain(argc, argv) == 0 && m_RunCalled) {
01688         x_CollectAllTests();
01689 
01690         but::traverse_test_tree(but::framework::master_test_suite(),
01691                                 m_TreeBuilder);
01692 
01693         // We do not read configuration if particular tests were given in
01694         // command line
01695         if (x_CallUserFuncs(eTestUserFuncDeps)
01696             &&  (!but::runtime_config::test_to_run().empty()
01697                  ||  x_ReadConfiguration()))
01698         {
01699             // Call should be doubled to support manual adding of
01700             // test cases inside NCBITEST_INIT_TREE().
01701             x_CollectAllTests();
01702             if (x_GetEnabledTestsCount() == 0) {
01703                 SetGloballyDisabled();
01704                 x_AddDummyTest();
01705             }
01706 #ifdef NCBI_COMPILER_WORKSHOP
01707             else if (!but::runtime_config::test_to_run().empty()) {
01708                 printf("Parameter --run_test is not supported in current configuration\n");
01709                 x_EnableAllTests(false);
01710                 x_AddDummyTest();
01711             }
01712 #endif
01713 
01714             return NULL;
01715         }
01716     }
01717 
01718     // This path we'll be if something have gone wrong
01719     x_CollectAllTests();
01720     x_EnableAllTests(false);
01721 
01722     return NULL;
01723 }
01724 
01725 inline bool
01726 CNcbiTestApplication::HasTestErrors(void)
01727 {
01728     return m_HasTestErrors;
01729 }
01730 
01731 inline bool
01732 CNcbiTestApplication::HasTestTimeouts(void)
01733 {
01734     return m_HasTestTimeouts;
01735 }
01736 
01737 void
01738 CNcbiTestsCollector::visit(but::test_case const& test)
01739 {
01740     s_GetTestApp().CollectTestUnit(const_cast<but::test_case*>(&test));
01741 }
01742 
01743 bool
01744 CNcbiTestsCollector::test_suite_start(but::test_suite const& suite)
01745 {
01746     s_GetTestApp().CollectTestUnit(const_cast<but::test_suite*>(&suite));
01747     return true;
01748 }
01749 
01750 
01751 void
01752 CNcbiTestsObserver::test_start(but::counter_t /* test_cases_amount */)
01753 {
01754     s_GetTestApp().InitTestsBeforeRun();
01755 }
01756 
01757 void
01758 CNcbiTestsObserver::test_finish(void)
01759 {
01760     s_GetTestApp().FiniTestsAfterRun();
01761 }
01762 
01763 void
01764 CNcbiTestsObserver::test_unit_start(but::test_unit const& tu)
01765 {
01766     s_GetTestApp().AdjustTestTimeout(const_cast<but::test_unit*>(&tu));
01767 }
01768 
01769 void
01770 CNcbiTestsObserver::test_unit_finish(but::test_unit const& tu,
01771                                      unsigned long         elapsed)
01772 {
01773     unsigned long timeout = tu.p_timeout.get();
01774     // elapsed comes in microseconds
01775     if (timeout != 0  &&  timeout < elapsed / 1000000) {
01776         boost::execution_exception ex(
01777                boost::execution_exception::timeout_error, "Timeout exceeded"
01778                NCBI_BOOST_LOCATION());
01779         but::framework::exception_caught(ex);
01780     }
01781 
01782     but::test_results& tr = but::s_rc_impl().m_results_store[tu.p_id];
01783     if (!tr.passed()  &&  s_GetTestApp().IsTestToFix(&tu)) {
01784         static_cast<but::readwrite_property<bool>& >(
01785             static_cast<but::class_property<bool>& >(
01786                                             tr.p_skipped)).set(true);
01787         static_cast<but::readwrite_property<but::counter_t>& >(
01788             static_cast<but::class_property<but::counter_t>& >(
01789                                             tr.p_assertions_failed)).set(0);
01790     }
01791 }
01792 
01793 void
01794 CNcbiTestsObserver::exception_caught(boost::execution_exception const& ex)
01795 {
01796     if (ex.code() == boost::execution_exception::timeout_error) {
01797         s_GetTestApp().SetTestTimedOut(const_cast<but::test_case*>(
01798                                        &but::framework::current_test_case()));
01799     }
01800     else {
01801         s_GetTestApp().SetTestErrored(const_cast<but::test_case*>(
01802                                       &but::framework::current_test_case()));
01803     }
01804 }
01805 
01806 void
01807 CNcbiTestsObserver::test_unit_aborted(but::test_unit const& tu)
01808 {
01809     s_GetTestApp().SetTestErrored((but::test_case*)&tu);
01810 }
01811 
01812 void
01813 CNcbiTestsObserver::assertion_result(bool passed)
01814 {
01815     if (!passed) {
01816         s_GetTestApp().SetTestErrored(const_cast<but::test_case*>(
01817                                       &but::framework::current_test_case()));
01818     }
01819 }
01820 
01821 
01822 void
01823 CNcbiBoostReporter::results_report_start(ostream& ostr)
01824 {
01825     m_Indent = 0;
01826     s_GetTestApp().ReEnableAllTests();
01827 
01828     m_Upper->results_report_start(ostr);
01829 }
01830 
01831 void
01832 CNcbiBoostReporter::results_report_finish(ostream& ostr)
01833 {
01834     m_Upper->results_report_finish(ostr);
01835     if (m_IsXML) {
01836         ostr << endl;
01837     }
01838 }
01839 
01840 void
01841 CNcbiBoostReporter::test_unit_report_start(but::test_unit const&  tu,
01842                                            ostream&               ostr)
01843 {
01844     if (tu.p_name.get() == kDummyTestCaseName)
01845         return;
01846 
01847     string descr = s_GetTestApp().GetTestResultString(
01848                                         const_cast<but::test_unit*>(&tu));
01849 
01850     if (m_IsXML) {
01851         ostr << '<' << (tu.p_type == but::tut_case ? "TestCase" : "TestSuite")
01852              << " name"   << but::attr_value() << tu.p_name.get()
01853              << " result" << but::attr_value() << descr;
01854 
01855         ostr << '>';
01856     }
01857     else {
01858         ostr << std::setw( m_Indent ) << ""
01859             << "Test " << (tu.p_type == but::tut_case ? "case " : "suite " )
01860             << "\"" << tu.p_name << "\" " << descr;
01861 
01862         ostr << '\n';
01863         m_Indent += 2;
01864     }
01865 }
01866 
01867 void
01868 CNcbiBoostReporter::test_unit_report_finish(but::test_unit const&  tu,
01869                                             std::ostream&          ostr)
01870 {
01871     if (tu.p_name.get() == kDummyTestCaseName)
01872         return;
01873 
01874     m_Indent -= 2;
01875     m_Upper->test_unit_report_finish(tu, ostr);
01876 }
01877 
01878 void
01879 CNcbiBoostReporter::do_confirmation_report(but::test_unit const&  tu,
01880                                            std::ostream&          ostr)
01881 {
01882     m_Upper->do_confirmation_report(tu, ostr);
01883 }
01884 
01885 
01886 void
01887 CNcbiBoostLogger::log_start(ostream& ostr, but::counter_t test_cases_amount)
01888 {
01889     m_Upper->log_start(ostr, test_cases_amount);
01890 }
01891 
01892 void
01893 CNcbiBoostLogger::log_finish(ostream& ostr)
01894 {
01895     m_Upper->log_finish(ostr);
01896     if (!m_IsXML) {
01897         ostr << "Executed " << s_GetTestApp().GetRanTestsCount()
01898              << " test cases";
01899         int to_fix = s_GetTestApp().GetToFixTestsCount();
01900         if (to_fix != 0) {
01901             ostr << " (" << to_fix << " to fix)";
01902         }
01903         ostr << "." << endl;
01904     }
01905 }
01906 
01907 void
01908 CNcbiBoostLogger::log_build_info(ostream& ostr)
01909 {
01910     m_Upper->log_build_info(ostr);
01911 }
01912 
01913 void
01914 CNcbiBoostLogger::test_unit_start(ostream& ostr, but::test_unit const& tu)
01915 {
01916     m_Upper->test_unit_start(ostr, tu);
01917 }
01918 
01919 void
01920 CNcbiBoostLogger::test_unit_finish(ostream& ostr, but::test_unit const& tu,
01921                                    unsigned long elapsed)
01922 {
01923     m_Upper->test_unit_finish(ostr, tu, elapsed);
01924 }
01925 
01926 void
01927 CNcbiBoostLogger::test_unit_skipped(ostream& ostr, but::test_unit const& tu)
01928 {
01929     m_Upper->test_unit_skipped(ostr, tu);
01930 }
01931 
01932 #if BOOST_VERSION >= 104200
01933 void
01934 CNcbiBoostLogger::log_exception(ostream& ostr, but::log_checkpoint_data const& lcd,
01935                                 boost::execution_exception const& ex)
01936 {
01937     m_Upper->log_exception(ostr, lcd, ex);
01938 }
01939 #else
01940 void
01941 CNcbiBoostLogger::log_exception(ostream& ostr, but::log_checkpoint_data const& lcd,
01942                                 but::const_string explanation)
01943 {
01944     m_Upper->log_exception(ostr, lcd, explanation);
01945 }
01946 #endif
01947 
01948 void
01949 CNcbiBoostLogger::log_entry_start(ostream& ostr, but::log_entry_data const& led,
01950                                   log_entry_types let)
01951 {
01952     m_Upper->log_entry_start(ostr, led, let);
01953 }
01954 
01955 void
01956 CNcbiBoostLogger::log_entry_value(ostream& ostr, but::const_string value)
01957 {
01958     m_Upper->log_entry_value(ostr, value);
01959 }
01960 
01961 void
01962 CNcbiBoostLogger::log_entry_finish(ostream& ostr)
01963 {
01964     m_Upper->log_entry_finish(ostr);
01965 }
01966 
01967 
01968 void
01969 RegisterNcbiTestUserFunc(TNcbiTestUserFunction func,
01970                          ETestUserFuncType     func_type)
01971 {
01972     s_GetTestApp().AddUserFunction(func, func_type);
01973 }
01974 
01975 void
01976 NcbiTestDependsOn(but::test_unit* tu, but::test_unit* dep_tu)
01977 {
01978     s_GetTestApp().AddTestDependsOn(tu, dep_tu);
01979 }
01980 
01981 void
01982 NcbiTestDisable(but::test_unit* tu)
01983 {
01984     s_GetTestApp().SetTestDisabled(tu);
01985 }
01986 
01987 void
01988 NcbiTestSetGlobalDisabled(void)
01989 {
01990     s_GetTestApp().SetGloballyDisabled();
01991 }
01992 
01993 void
01994 NcbiTestSetGlobalSkipped(void)
01995 {
01996     s_GetTestApp().SetGloballySkipped();
01997 }
01998 
01999 CExprParser*
02000 NcbiTestGetIniParser(void)
02001 {
02002     return s_GetTestApp().GetIniParser();
02003 }
02004 
02005 CArgDescriptions*
02006 NcbiTestGetArgDescrs(void)
02007 {
02008     return s_GetTestApp().GetArgDescrs();
02009 }
02010 
02011 but::test_unit*
02012 NcbiTestGetUnit(CTempString test_name)
02013 {
02014     return s_GetTestApp().GetTestUnit(test_name);
02015 }
02016 
02017 
02018 END_NCBI_SCOPE
02019 
02020 
02021 using namespace but;
02022 
02023 /// Global initialization function called from Boost framework
02024 test_suite*
02025 init_unit_test_suite(int argc, char* argv[])
02026 {
02027     return NCBI_NS_NCBI::s_GetTestApp().InitTestFramework(argc, argv);
02028 }
02029 
02030 // This main() is mostly a copy from Boost's unit_test_main.ipp
02031 int
02032 main(int argc, char* argv[])
02033 {
02034     int result = boost::exit_success;
02035 
02036     try {
02037         framework::init( &init_unit_test_suite, argc, argv );
02038 
02039         if( !runtime_config::test_to_run().is_empty() ) {
02040             test_case_filter filter( runtime_config::test_to_run() );
02041 
02042             traverse_test_tree( framework::master_test_suite().p_id, filter );
02043         }
02044 
02045         framework::run();
02046 
02047         // Let's try to make report in case of any error after all catches.
02048         //results_reporter::make_report();
02049 
02050         if (!runtime_config::no_result_code()) {
02051             result = results_collector.results( framework::master_test_suite().p_id ).result_code();
02052             if (!NCBI_NS_NCBI::s_GetTestApp().HasTestErrors()
02053                 &&  NCBI_NS_NCBI::s_GetTestApp().HasTestTimeouts())
02054             {
02055                 // This should certainly go to the output. So we can use only
02056                 // printf, nothing else.
02057                 printf("There were no test failures, only timeouts.\n"
02058                        " (for autobuild scripts: NCBI_UNITTEST_TIMEOUTS_BUT_NO_ERRORS)\n");
02059             }
02060         }
02061     }
02062 #if BOOST_VERSION >= 104200
02063     catch( framework::nothing_to_test const& ) {
02064         result = boost::exit_success;
02065     }
02066 #endif
02067     catch( framework::internal_error const& ex ) {
02068         results_reporter::get_stream() << "Boost.Test framework internal error: " << ex.what() << std::endl;
02069         
02070         result = boost::exit_exception_failure;
02071     }
02072     catch( framework::setup_error const& ex ) {
02073         results_reporter::get_stream() << "Test setup error: " << ex.what() << std::endl;
02074         
02075         result = boost::exit_exception_failure;
02076     }
02077     catch( std::exception const& ex ) {
02078         results_reporter::get_stream() << "Test framework error: " << ex.what() << std::endl;
02079 
02080         result = boost::exit_exception_failure;
02081     }
02082     catch( ... ) {
02083         results_reporter::get_stream() << "Boost.Test framework internal error: unknown reason" << std::endl;
02084         
02085         result = boost::exit_exception_failure;
02086     }
02087 
02088     results_reporter::make_report();
02089 
02090     NCBI_NS_NCBI::GetDiagContext().SetExitCode(result);
02091     return result;
02092 }
Modified on Wed May 23 12:58:40 2012 by modify_doxy.py rev. 337098