Branch data Line data Source code
1 : : // ***************************************************************************** 2 : : /*! 3 : : \file src/Main/UnitTest.cpp 4 : : \copyright 2012-2015 J. Bakosi, 5 : : 2016-2018 Los Alamos National Security, LLC., 6 : : 2019-2021 Triad National Security, LLC., 7 : : 2022-2024 J. Bakosi 8 : : All rights reserved. See the LICENSE file for details. 9 : : \brief UnitTest's Charm++ main chare and main(). 10 : : \details UnitTest's Charm++ main chare and main(). This file contains 11 : : the definition of the Charm++ main chare, equivalent to main() in Charm++- 12 : : land, running the serial and Charm++ unit tests as well as the ordinary 13 : : main() function, running the MPI unit test suite. 14 : : */ 15 : : // ***************************************************************************** 16 : : 17 : : #include <map> 18 : : #include <vector> 19 : : #include <string> 20 : : #include <iostream> 21 : : #include <utility> 22 : : #include <cstddef> 23 : : 24 : : #include "XystConfig.hpp" 25 : : 26 : : #ifdef XYST_AMPI 27 : : #include "NoWarning/mpi.hpp" 28 : : #endif 29 : : 30 : : #include "NoWarning/tut_runner.hpp" 31 : : 32 : : #include "NoWarning/tutsuite.decl.h" 33 : : #include "NoWarning/unittest.decl.h" 34 : : 35 : : #include "Timer.hpp" 36 : : #include "Exception.hpp" 37 : : #include "Init.hpp" 38 : : #include "ProcessException.hpp" 39 : : #include "UnitTestConfig.hpp" 40 : : #include "TUTConfig.hpp" 41 : : #include "QuietCerr.hpp" 42 : : 43 : : #if defined(__clang__) 44 : : #pragma clang diagnostic push 45 : : #pragma clang diagnostic ignored "-Wmissing-variable-declarations" 46 : : #endif 47 : : 48 : : //! \brief Charm handle to the main proxy, facilitates call-back to finalize, 49 : : //! etc., must be in global scope, unique per executable 50 : : CProxy_Main mainProxy; 51 : : 52 : : #if defined(__clang__) 53 : : #pragma clang diagnostic pop 54 : : #endif 55 : : 56 : : //! UnitTest declarations and definitions 57 : : namespace unittest { 58 : : 59 : : //! Global-scope data. Initialized by the main chare and distibuted to all PEs 60 : : //! by the Charm++ runtime system. Though semantically not const, all these 61 : : //! global data should be considered read-only. See also 62 : : //! http://charm.cs.illinois.edu/manuals/html/charm++/manual.html. The data 63 : : //! below is global-scope because they must be available to all PEs which could 64 : : //! be on different machines. 65 : : 66 : : #if defined(__clang__) 67 : : #pragma clang diagnostic push 68 : : #pragma clang diagnostic ignored "-Wmissing-variable-declarations" 69 : : #endif 70 : : 71 : : //! Template Unit Test test runner 72 : : tut::test_runner_singleton g_runner; 73 : : 74 : : //! Test suite Charm++ proxy facilitating call-back to unit test suite by 75 : : //! individual unit tests spawning Charm++ chares 76 : : CProxy_TUTSuite g_suiteProxy; 77 : : 78 : : //! UnitTest executable name. So that FileParser's unit tests can access a file 79 : : //! for opening. 80 : : std::string g_executable; 81 : : 82 : : //! Max number of tests in every group 83 : : int g_maxTestsInGroup = tut::MAX_TESTS_IN_GROUP; 84 : : 85 : : #if defined(__clang__) 86 : : #pragma clang diagnostic pop 87 : : #endif 88 : : 89 : : //! Pack/Unpack test runner. This Pack/Unpack method (re-)creates the 90 : : //! test runner singleton on all processing elements. Therefore we circumvent 91 : : //! Charm's usual pack/unpack for this type, and thus sizing does not make 92 : : //! sense: sizing is a no-op. We could initialize the stack in UnitTestDriver's 93 : : //! constructor and let this function re-create the runner only when unpacking, 94 : : //! but that leads to repeating the same code twice: once in UnitTestDriver's 95 : : //! constructor, once here. Another option is to use this pack/unpack routine to 96 : : //! both initially create (when packing) and to re-create (when unpacking) the 97 : : //! runner, which eliminates the need for pre-creating the object in 98 : : //! UnitTestDriver's constructor and therefore eliminates the repeated code. 99 : : //! This explains the guard for sizing: the code below is called for packing 100 : : //! only (in serial) and packing and unpacking (in parallel). 101 : : inline void operator|( PUP::er& p, tut::test_runner_singleton& runner ) 102 : : { if (!p.isSizing()) runner = tut::test_runner_singleton(); } 103 : : 104 : : } // unittest:: 105 : : 106 : : #ifdef XYST_AMPI 107 : : //! Main function for Charm++'s AMPI 108 : : //! \note If this is not defined, Charm++ does not wait for CkExit(). 109 : : int main( int, char** ) { 110 : : MPI_Init( nullptr, nullptr ); 111 : : return 0; 112 : : } 113 : : #endif 114 : : 115 : : //! \brief Charm++ main chare for the unit test suite executable, unittest. 116 : : //! \details Note that this object should not be in a namespace. 117 : : class Main : public CBase_Main { 118 : : 119 : : public: 120 : : //! \brief Constructor 121 : : //! \details UnitTest's main chare constructor is the entry point of the 122 : : //! program, called by the Charm++ runtime system. The constructor does 123 : : //! basic initialization steps, e.g., parser the command-line, prints out 124 : : //! some useful information to screen, and instantiates 125 : : //! a driver. Since Charm++ is fully asynchronous, the constructor 126 : : //! usually spawns asynchronous objects and immediately exits. Thus in the 127 : : //! body of the main chare constructor we fire up an 'execute' chare, 128 : : //! which then calls back to Main::execute(). Finishing the main chare 129 : : //! constructor the Charm++ runtime system then starts the 130 : : //! network-migration of all global-scope data (if any). The execute chare 131 : : //! calling back to Main::execute() signals the end of the migration of 132 : : //! the global-scope data. Then we are ready to execute the driver. Since 133 : : //! the unit test suite is parallel and asynchronous, its driver fires up 134 : : //! additional Charm++ chare objects which then call back to 135 : : //! Main::finalize() at some point in the future when all work has been 136 : : //! finished. finalize() then exits by calling Charm++'s CkExit(), 137 : : //! shutting down the runtime system. 138 : : //! \see http://charm.cs.illinois.edu/manuals/html/charm++/manual.html 139 : 4 : explicit Main( CkArgMsg* msg ) 140 : 4 : try : 141 [ + - ][ + - ]: 4 : m_timer(1) [ + - ] 142 : : { 143 [ + - ]: 4 : tk::setSignalHandlers(); 144 : : // Parse command line 145 : : unittest::ctr::Config cfg; 146 [ + - ]: 4 : cfg.cmdline( msg->argc, msg->argv ); 147 [ + - ]: 2 : tk::echoHeader( tk::HeaderType::UNITTEST ); 148 [ + - ][ + - ]: 2 : tk::echoBuildEnv( msg->argv[0] ); 149 [ + - ]: 2 : tk::echoRunEnv(msg->argc, msg->argv, cfg.get< tag::quiescence >()); 150 : : delete msg; 151 : : mainProxy = thisProxy; 152 [ + - ]: 2 : unittest::g_executable = msg->argv[0]; 153 [ + + ]: 2 : if (cfg.get< tag::quiescence >()) { 154 [ + - ]: 2 : CkStartQD( CkCallback( CkIndex_Main::quiescence(), thisProxy ) ); 155 : : } 156 [ + - ]: 2 : m_timer.emplace_back(); 157 : : // Create suite proxy (does not yet spawn tests) 158 : : unittest::g_suiteProxy = 159 [ + - ][ - - ]: 2 : unittest::CProxy_TUTSuite::ckNew( cfg.get< tag::group >(), 0 ); 160 : : // Fire up an asynchronous execute object, which when created will call 161 : : // back to this->execute(). This ensures that from this->execute() 162 : : // global-scope read-only data is already migrated and available to 163 : : // everyone. 164 [ + - ]: 2 : CProxy_execute::ckNew(); 165 : : // Quiet std::cerr 166 [ + - ]: 2 : tk::CProxy_QuietCerr::ckNew(); 167 [ - - ]: 2 : } catch (...) { tk::processExceptionCharm(); } 168 : : 169 : : //! Execute tests, global scope data have been migrated and available 170 : 2 : void execute() { 171 : : try { 172 [ + - ][ + - ]: 2 : m_timestamp.emplace_back("Migrate global-scope data", m_timer[1].hms()); 173 : : // Spawn tests 174 [ + - ]: 2 : unittest::g_suiteProxy.run(); 175 [ - - ]: 0 : } catch (...) { tk::processExceptionCharm(); } 176 : 2 : } 177 : : 178 : : //! Normal exit 179 : : void finalize( bool pass ) { 180 [ - - ][ - - ]: 2 : tk::finalize( m_timer, m_timestamp, pass ); 181 : 0 : } 182 : : 183 : : //! Entry method triggered when quiescence is detected 184 [ - - ][ - - ]: 0 : [[noreturn]] void quiescence() { Throw( "Quiescence detected" ); } [ - - ][ - - ] [ - - ][ - - ] [ - - ] 185 : : 186 : : private: 187 : : std::vector< tk::Timer > m_timer; //!< Timers 188 : : //! Time stamps in h:m:s with labels 189 : : std::vector< std::pair< std::string, tk::Timer::Watch > > m_timestamp; 190 : : }; 191 : : 192 : : //! \brief Charm++ chare execute 193 : : //! \details By the time this object is constructed, the Charm++ runtime system 194 : : //! has finished migrating all global-scoped read-only objects which happens 195 : : //! after the main chare constructor has finished. 196 : : class execute : public CBase_execute { 197 [ + - ]: 2 : public: explicit execute() { mainProxy.execute(); } 198 : : }; 199 : : 200 : : #include "NoWarning/unittest.def.h"