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-2025 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"
|