diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 1a7621fe8f..4b0daf21cc 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -37,6 +37,8 @@ The Axom project release numbers follow [Semantic Versioning](http://semver.org/ - Adds yapf as a Python formatter. - Quest: Adds fast GWN methods for STL/Triangulated STEP input and linearized NURBS Curve input which leverage error-controlled approximation and a spatial index (BVH). +- Slic: Adds new Slic macros that allow you to selectively print messages once per call-site. For example, + `SLIC_INFO_ONCE(msg)` and `SLIC_INFO_ROOT_IF_ONCE(EXP, msg)`. ### Changed - Primal: Axom's polygon clipping was modified to handle some corner cases. diff --git a/src/axom/quest/tests/quest_mesh_clipper.cpp b/src/axom/quest/tests/quest_mesh_clipper.cpp index c6f29506c3..0f9cb60d49 100644 --- a/src/axom/quest/tests/quest_mesh_clipper.cpp +++ b/src/axom/quest/tests/quest_mesh_clipper.cpp @@ -654,6 +654,7 @@ axom::klee::Geometry createGeom_CupMesh(sidre::DataStore& ds, const std::string& std::string proeFile = axom::utilities::filesystem::joinPath(AXOM_DATA_DIR, "quest/cup.proe"); reader.setFileName(proeFile); int readStatus = reader.read(); + AXOM_UNUSED_VAR(readStatus); SLIC_ASSERT(readStatus == 0); reader.getMesh(&tetMesh); const double extraScale = 1 / sqrt(3.0); // to ensure tetMesh remains inside mesh when rotated. diff --git a/src/axom/slic/docs/sphinx/sections/wrapping_slic_in_macros.rst b/src/axom/slic/docs/sphinx/sections/wrapping_slic_in_macros.rst index 19a2a327c7..75a470b116 100644 --- a/src/axom/slic/docs/sphinx/sections/wrapping_slic_in_macros.rst +++ b/src/axom/slic/docs/sphinx/sections/wrapping_slic_in_macros.rst @@ -111,38 +111,76 @@ functions are called. The table below details the built-in SLIC macros as well as some notes about when they are collective calls: -+----------------------------+------------------------------------------------+----------------------------------------------------------------------------+ -| Macro | Availability | Collective status | -+============================+================================================+============================================================================+ -| | ``SLIC_ASSERT`` | | Only available in debug configurations | | Collective by default. | -| | ``SLIC_ASSERT_MSG`` | | (i.e. when `AXOM_DEBUG` is defined). | | Collective after calling ``slic::enableAbortOnError()``. | -| | | | Not available in device code. | | No longer collective after calling ``slic::disableAbortOnError()``. | -+----------------------------+------------------------------------------------+----------------------------------------------------------------------------+ -| | ``SLIC_CHECK`` | | Only available in debug configurations | | Not collective by default. | -| | ``SLIC_CHECK_MSG`` | | (i.e. when `AXOM_DEBUG` is defined). | | Collective after ``slic::debug::checksAreErrors`` is set to ``true``, | -| | | | Not available in device code. | | defaults to ``false``. | -+----------------------------+------------------------------------------------+----------------------------------------------------------------------------+ -| | ``SLIC_DEBUG`` | | Only available in debug configurations | | Never | -| | ``SLIC_DEBUG_IF`` | | (i.e. when `AXOM_DEBUG` is defined) | | | -| | ``SLIC_DEBUG_ROOT`` | | | | | -| | ``SLIC_DEBUG_ROOT_IF`` | | | | | -+----------------------------+------------------------------------------------+----------------------------------------------------------------------------+ -| | ``SLIC_INFO`` | | Always | | Never | -| | ``SLIC_INFO_IF`` | | | | | -| | ``SLIC_INFO_ROOT`` | | | | | -| | ``SLIC_INFO_ROOT_IF`` | | | | | -| | ``SLIC_INFO_TAGGED`` | | | | | -+----------------------------+------------------------------------------------+----------------------------------------------------------------------------+ -| | ``SLIC_ERROR`` | | Always | | Collective by default. | -| | ``SLIC_ERROR_IF`` | | | | Collective after calling ``slic::enableAbortOnError()``. | -| | ``SLIC_ERROR_ROOT`` | | | | No longer collective after calling ``slic::disableAbortOnError()`` | -| | ``SLIC_ERROR_ROOT_IF`` | | | | | -+----------------------------+------------------------------------------------+----------------------------------------------------------------------------+ -| | ``SLIC_WARNING`` | | Always | | Not collective by default. | -| | ``SLIC_WARNING_IF`` | | | | Collective after calling ``slic::enableAbortOnWarning()``. | -| | ``SLIC_WARNING_ROOT`` | | | | No longer collective after calling ``slic::disableAbortOnWarning()`` | -| | ``SLIC_WARNING_ROOT_IF`` | | | | | -+----------------------------+------------------------------------------------+----------------------------------------------------------------------------+ +.. list-table:: SLIC macro availability and collective behavior + :header-rows: 1 + :widths: 28 34 38 + + * - Macro + - Availability + - Collective status + + * - ``SLIC_ASSERT`` + ``SLIC_ASSERT_MSG`` + - - Only available in debug configurations (i.e. when ``AXOM_DEBUG`` is defined) + - Not available in device code + - - Collective by default + - Collective after calling ``slic::enableAbortOnError()`` + - No longer collective after calling ``slic::disableAbortOnError()`` + + * - ``SLIC_CHECK`` + ``SLIC_CHECK_MSG`` + - - Only available in debug configurations (i.e. when ``AXOM_DEBUG`` is defined) + - Not available in device code + - - Not collective by default + - Collective after ``slic::debug::checksAreErrors`` is set to ``true``, defaults to ``false`` + + * - ``SLIC_DEBUG`` + ``SLIC_DEBUG_IF`` + ``SLIC_DEBUG_ROOT`` + ``SLIC_DEBUG_ROOT_IF`` + ``SLIC_DEBUG_PRINT_CONTAINER`` + ``SLIC_DEBUG_ONCE`` + ``SLIC_DEBUG_IF_ONCE`` + ``SLIC_DEBUG_ROOT_ONCE`` + ``SLIC_DEBUG_ROOT_IF_ONCE`` + ``SLIC_DEBUG_PRINT_CONTAINER_ONCE`` + - - Only available in debug configurations (i.e. when ``AXOM_DEBUG`` is defined) + - - Never + + * - ``SLIC_INFO`` + ``SLIC_INFO_IF`` + ``SLIC_INFO_ROOT`` + ``SLIC_INFO_ROOT_IF`` + ``SLIC_INFO_TAGGED`` + ``SLIC_INFO_ONCE`` + ``SLIC_INFO_IF_ONCE`` + ``SLIC_INFO_ROOT_ONCE`` + ``SLIC_INFO_ROOT_IF_ONCE`` + ``SLIC_INFO_TAGGED_ONCE`` + - - Always + - - Never + + * - ``SLIC_ERROR`` + ``SLIC_ERROR_IF`` + ``SLIC_ERROR_ROOT`` + ``SLIC_ERROR_ROOT_IF`` + - - Always + - - Collective by default + - Collective after calling ``slic::enableAbortOnError()`` + - No longer collective after calling ``slic::disableAbortOnError()`` + + * - ``SLIC_WARNING`` + ``SLIC_WARNING_IF`` + ``SLIC_WARNING_ROOT`` + ``SLIC_WARNING_ROOT_IF`` + ``SLIC_WARNING_ONCE`` + ``SLIC_WARNING_IF_ONCE`` + ``SLIC_WARNING_ROOT_ONCE`` + ``SLIC_WARNING_ROOT_IF_ONCE`` + - - Always + - - Not collective by default + - Collective after calling ``slic::enableAbortOnWarning()`` + - No longer collective after calling ``slic::disableAbortOnWarning()`` Doxygen generated API documentation on Macros can be found here: `SLIC Macros <../../../../../doxygen/html/slic__macros_8hpp.html>`_ @@ -157,6 +195,7 @@ Consider the following rules of thumb when choosing from the above logging macro (i.e. their messages will not get logged), while `SLIC_INFO` macros are always available. * The `SLIC_*_ROOT` variants can help reduce logging verbosity when called in an MPI application, especially if all MPI ranks are expected to have the same data (for example, if a value was broadcast from one rank to all the other ranks). +* The `SLIC_*_ONCE` variants can help reduce logging verbosity when only the first invocation at a call-site is necessary. .. ############################################################################# diff --git a/src/axom/slic/interface/slic_macros.hpp b/src/axom/slic/interface/slic_macros.hpp index 7975c95226..4cda60dc01 100644 --- a/src/axom/slic/interface/slic_macros.hpp +++ b/src/axom/slic/interface/slic_macros.hpp @@ -42,17 +42,7 @@ * \endcode * */ -#define SLIC_ERROR(msg) \ - do \ - { \ - std::ostringstream __oss; \ - __oss << msg; \ - axom::slic::logErrorMessage(__oss.str(), __FILE__, __LINE__); \ - if(axom::slic::isAbortOnErrorsEnabled()) \ - { \ - axom::slic::abort(); \ - } \ - } while(axom::slic::detail::false_value) +#define SLIC_ERROR(msg) SLIC_ERROR_IF(true, msg) /*! * \def SLIC_ERROR_IF( EXP, msg ) @@ -103,20 +93,7 @@ * \endcode * */ -#define SLIC_ERROR_ROOT(msg) \ - do \ - { \ - if(axom::slic::isRoot()) \ - { \ - std::ostringstream __oss; \ - __oss << msg; \ - axom::slic::logErrorMessage(__oss.str(), __FILE__, __LINE__); \ - if(axom::slic::isAbortOnErrorsEnabled()) \ - { \ - axom::slic::abort(); \ - } \ - } \ - } while(axom::slic::detail::false_value) +#define SLIC_ERROR_ROOT(msg) SLIC_ERROR_IF(axom::slic::isRoot(), msg) /*! * \def SLIC_ERROR_ROOT_IF( EXP, msg ) @@ -136,23 +113,7 @@ * \endcode * */ -#define SLIC_ERROR_ROOT_IF(EXP, msg) \ - do \ - { \ - if(EXP) \ - { \ - if(axom::slic::isRoot()) \ - { \ - std::ostringstream __oss; \ - __oss << msg; \ - axom::slic::logErrorMessage(__oss.str(), __FILE__, __LINE__); \ - if(axom::slic::isAbortOnErrorsEnabled()) \ - { \ - axom::slic::abort(); \ - } \ - } \ - } \ - } while(axom::slic::detail::false_value) +#define SLIC_ERROR_ROOT_IF(EXP, msg) SLIC_ERROR_IF((EXP) && (axom::slic::isRoot()), msg) ///@} @@ -187,17 +148,24 @@ * \endcode * */ -#define SLIC_WARNING(msg) \ - do \ - { \ - std::ostringstream __oss; \ - __oss << msg; \ - axom::slic::logWarningMessage(__oss.str(), __FILE__, __LINE__); \ - if(axom::slic::isAbortOnWarningsEnabled()) \ - { \ - axom::slic::abort(); \ - } \ - } while(axom::slic::detail::false_value) +#define SLIC_WARNING(msg) SLIC_WARNING_IF(true, msg) + +/*! + * \def SLIC_WARNING_ONCE( msg ) + * \brief Logs a warning message only once per call site. + * + * \param [in] msg user-supplied message + * + * \note The SLIC_WARNING_ONCE macro is always active. + * \note Aborts the application when `slic::enableAbortOnWarning()` + * + * Usage: + * \code + * SLIC_WARNING_ONCE( "my_val should always be positive" ); + * \endcode + * + */ +#define SLIC_WARNING_ONCE(msg) SLIC_DETAIL_LOG_IF_ONCE(SLIC_WARNING_IF, true, msg) /*! * \def SLIC_WARNING_IF( EXP, msg ) @@ -230,6 +198,24 @@ } \ } while(axom::slic::detail::false_value) +/*! + * \def SLIC_WARNING_IF_ONCE( EXP, msg ) + * \brief Logs a warning iff EXP is true, and only once per call site. + * + * \param [in] EXP user-supplied boolean expression. + * \param [in] msg user-supplied message. + * + * \note The SLIC_WARNING_IF_ONCE macro is always active. + * \note Aborts the application when `slic::enableAbortOnWarning()` + * + * Usage: + * \code + * SLIC_WARNING_IF_ONCE( (val < 0), "my_val should always be positive" ); + * \endcode + * + */ +#define SLIC_WARNING_IF_ONCE(EXP, msg) SLIC_DETAIL_LOG_IF_ONCE(SLIC_WARNING_IF, EXP, msg) + /*! * \def SLIC_WARNING_ROOT( msg ) * \brief Macro that logs given warning message only on root. @@ -247,20 +233,27 @@ * \endcode * */ -#define SLIC_WARNING_ROOT(msg) \ - do \ - { \ - if(axom::slic::isRoot()) \ - { \ - std::ostringstream __oss; \ - __oss << msg; \ - axom::slic::logWarningMessage(__oss.str(), __FILE__, __LINE__); \ - if(axom::slic::isAbortOnWarningsEnabled()) \ - { \ - axom::slic::abort(); \ - } \ - } \ - } while(axom::slic::detail::false_value) +#define SLIC_WARNING_ROOT(msg) SLIC_WARNING_IF(axom::slic::isRoot(), msg) + +/*! + * \def SLIC_WARNING_ROOT_ONCE( msg ) + * \brief Macro that logs given warning message only on root, and only once per call site. + * + * \param [in] msg user-supplied message. + * + * \note The SLIC_WARNING_ROOT_ONCE macro is always active. + * \note By default, all ranks are considered to be root. + * Must call `axom::slic::initialize(is_root={true|false})` + * or set via `axom::slic::setIsRoot({true|false})` to filter based on root. + * + * Usage: + * \code + * SLIC_WARNING_ROOT_ONCE( "A warning has occurred!" ); + * \endcode + * + */ +#define SLIC_WARNING_ROOT_ONCE(msg) \ + SLIC_DETAIL_LOG_IF_ONCE(SLIC_WARNING_IF, axom::slic::isRoot(), msg) /*! * \def SLIC_WARNING_ROOT_IF( EXP, msg ) @@ -280,23 +273,29 @@ * \endcode * */ -#define SLIC_WARNING_ROOT_IF(EXP, msg) \ - do \ - { \ - if(EXP) \ - { \ - if(axom::slic::isRoot()) \ - { \ - std::ostringstream __oss; \ - __oss << msg; \ - axom::slic::logWarningMessage(__oss.str(), __FILE__, __LINE__); \ - if(axom::slic::isAbortOnWarningsEnabled()) \ - { \ - axom::slic::abort(); \ - } \ - } \ - } \ - } while(axom::slic::detail::false_value) +#define SLIC_WARNING_ROOT_IF(EXP, msg) SLIC_WARNING_IF((EXP) && (axom::slic::isRoot()), msg) + +/*! + * \def SLIC_WARNING_ROOT_IF_ONCE( EXP, msg ) + * \brief Macro that logs given warning message only on root iff EXP is true, + * and only once per call site. + * + * \param [in] EXP user-supplied boolean expression. + * \param [in] msg user-supplied message. + * + * \note The SLIC_WARNING_ROOT_IF_ONCE macro is always active. + * \note By default, all ranks are considered to be root. + * Must call `axom::slic::initialize(is_root={true|false})` + * or set via `axom::slic::setIsRoot({true|false})` to filter based on root. + * + * Usage: + * \code + * SLIC_WARNING_ROOT_IF_ONCE( (val < 0), "my_val should always be positive" ); + * \endcode + * + */ +#define SLIC_WARNING_ROOT_IF_ONCE(EXP, msg) \ + SLIC_DETAIL_LOG_IF_ONCE(SLIC_WARNING_IF, (EXP) && (axom::slic::isRoot()), msg) ///@} @@ -334,20 +333,7 @@ * \endcode * */ - #define SLIC_ASSERT(EXP) \ - do \ - { \ - if(!(EXP)) \ - { \ - std::ostringstream __oss; \ - __oss << "Failed Assert: " << #EXP; \ - axom::slic::logErrorMessage(__oss.str(), __FILE__, __LINE__); \ - if(axom::slic::isAbortOnErrorsEnabled()) \ - { \ - axom::slic::abort(); \ - } \ - } \ - } while(axom::slic::detail::false_value) + #define SLIC_ASSERT(EXP) SLIC_ASSERT_MSG(EXP, "") /*! * \def SLIC_ASSERT_MSG( EXP, msg ) @@ -422,31 +408,7 @@ * \endcode * */ - #define SLIC_CHECK(EXP) \ - do \ - { \ - if(!(EXP)) \ - { \ - std::ostringstream __oss; \ - __oss << "Failed Check: " << #EXP; \ - if(axom::slic::debug::checksAreErrors) \ - { \ - axom::slic::logErrorMessage(__oss.str(), __FILE__, __LINE__); \ - if(axom::slic::isAbortOnErrorsEnabled()) \ - { \ - axom::slic::abort(); \ - } \ - } \ - else \ - { \ - axom::slic::logWarningMessage(__oss.str(), __FILE__, __LINE__); \ - if(axom::slic::isAbortOnWarningsEnabled()) \ - { \ - axom::slic::abort(); \ - } \ - } \ - } \ - } while(axom::slic::detail::false_value) + #define SLIC_CHECK(EXP) SLIC_CHECK_MSG(EXP, "") /*! * \def SLIC_CHECK_MSG( EXP, msg ) @@ -524,13 +486,23 @@ * \endcode * */ -#define SLIC_INFO(msg) \ - do \ - { \ - std::ostringstream __oss; \ - __oss << msg; \ - axom::slic::logMessage(axom::slic::message::Info, __oss.str(), __FILE__, __LINE__); \ - } while(axom::slic::detail::false_value) +#define SLIC_INFO(msg) SLIC_INFO_IF(true, msg) + +/*! + * \def SLIC_INFO_ONCE( msg ) + * \brief Logs an Info message only once per call site. + * + * \param [in] msg user-supplied message + * + * \note The SLIC_INFO_ONCE macro is always active. + * + * Usage: + * \code + * SLIC_INFO_ONCE( "informative text goes here" ); + * \endcode + * + */ +#define SLIC_INFO_ONCE(msg) SLIC_DETAIL_LOG_IF_ONCE(SLIC_INFO_IF, true, msg) /*! * \def SLIC_INFO_TAGGED( msg, tag ) @@ -555,6 +527,32 @@ axom::slic::logMessage(axom::slic::message::Info, __oss.str(), tag, __FILE__, __LINE__, false, true); \ } while(axom::slic::detail::false_value) +/*! + * \def SLIC_INFO_TAGGED_ONCE( msg, tag ) + * \brief Logs an Info message to a tagged stream only once per call site. + * + * \param [in] msg user-supplied message + * \param [in] tag user-supplied tag + * + * \note The SLIC_INFO_TAGGED_ONCE macro is always active. + * + * Usage: + * \code + * SLIC_INFO_TAGGED_ONCE("informative text goes here", "tag"); + * \endcode + * + */ +#define SLIC_INFO_TAGGED_ONCE(msg, tag) \ + do \ + { \ + static bool once = true; \ + if(once) \ + { \ + SLIC_INFO_TAGGED(msg, tag); \ + once = false; \ + } \ + } while(axom::slic::detail::false_value) + /*! * \def SLIC_INFO_IF( EXP, msg ) * \brief Logs an Info message iff EXP is true @@ -581,6 +579,23 @@ } \ } while(axom::slic::detail::false_value) +/*! + * \def SLIC_INFO_IF_ONCE( EXP, msg ) + * \brief Logs an Info message iff EXP is true, only once per call site. + * + * \param [in] EXP user-supplied boolean expression. + * \param [in] msg user-supplied message. + * + * \note The SLIC_INFO_IF_ONCE macro is always active. + * + * Usage: + * \code + * SLIC_INFO_IF_ONCE( (val < 0), "my_val should always be positive" ); + * \endcode + * + */ +#define SLIC_INFO_IF_ONCE(EXP, msg) SLIC_DETAIL_LOG_IF_ONCE(SLIC_INFO_IF, EXP, msg) + /*! * \def SLIC_INFO_ROOT( msg ) * \brief Logs an Info message if on root @@ -597,6 +612,22 @@ */ #define SLIC_INFO_ROOT(msg) SLIC_INFO_IF(axom::slic::isRoot(), msg) +/*! + * \def SLIC_INFO_ROOT_ONCE( msg ) + * \brief Logs an Info message if on root, only once per call site. + * + * \param [in] msg user-supplied message. + * + * \note The SLIC_INFO_ROOT_ONCE macro is always active. + * + * Usage: + * \code + * SLIC_INFO_ROOT_ONCE( "informative text goes here" ); + * \endcode + * + */ +#define SLIC_INFO_ROOT_ONCE(msg) SLIC_DETAIL_LOG_IF_ONCE(SLIC_INFO_IF, axom::slic::isRoot(), msg) + /*! * \def SLIC_INFO_ROOT_IF( EXP, msg ) * \brief Logs an Info message if on root and iff EXP is true @@ -614,6 +645,24 @@ */ #define SLIC_INFO_ROOT_IF(EXP, msg) SLIC_INFO_IF((EXP) && (axom::slic::isRoot()), msg) +/*! + * \def SLIC_INFO_ROOT_IF_ONCE( EXP, msg ) + * \brief Logs an Info message if on root and iff EXP is true, only once per call site. + * + * \param [in] EXP user-supplied boolean expression. + * \param [in] msg user-supplied message. + * + * \note The SLIC_INFO_ROOT_IF_ONCE macro is always active. + * + * Usage: + * \code + * SLIC_INFO_ROOT_IF_ONCE( (val < 0), "my_val should always be positive" ); + * \endcode + * + */ +#define SLIC_INFO_ROOT_IF_ONCE(EXP, msg) \ + SLIC_DETAIL_LOG_IF_ONCE(SLIC_INFO_IF, (EXP) && (axom::slic::isRoot()), msg) + #ifdef AXOM_DEBUG /*! @@ -630,13 +679,23 @@ * \endcode * */ - #define SLIC_DEBUG(msg) \ - do \ - { \ - std::ostringstream __oss; \ - __oss << msg; \ - axom::slic::logMessage(axom::slic::message::Debug, __oss.str(), __FILE__, __LINE__); \ - } while(axom::slic::detail::false_value) + #define SLIC_DEBUG(msg) SLIC_DEBUG_IF(true, msg) + + /*! + * \def SLIC_DEBUG_ONCE( msg ) + * \brief Logs a Debug message only once per call site. + * + * \param [in] msg user-supplied message + * + * \note The SLIC_DEBUG_ONCE macro is active when AXOM_DEBUG is defined. + * + * Usage: + * \code + * SLIC_DEBUG_ONCE( "debug message goes here" ); + * \endcode + * + */ + #define SLIC_DEBUG_ONCE(msg) SLIC_DETAIL_LOG_IF_ONCE(SLIC_DEBUG_IF, true, msg) /*! * \def SLIC_DEBUG_IF( EXP, msg ) @@ -664,6 +723,23 @@ } \ } while(axom::slic::detail::false_value) + /*! + * \def SLIC_DEBUG_IF_ONCE( EXP, msg ) + * \brief Logs an Debug message iff EXP is true only once per call site. + * + * \param [in] EXP user-supplied boolean expression. + * \param [in] msg user-supplied message. + * + * \note The SLIC_DEBUG_IF_ONCE macro is active when AXOM_DEBUG is defined. + * + * Usage: + * \code + * SLIC_DEBUG_IF_ONCE( (val < 0), "my_val should always be positive" ); + * \endcode + * + */ + #define SLIC_DEBUG_IF_ONCE(EXP, msg) SLIC_DETAIL_LOG_IF_ONCE(SLIC_DEBUG_IF, EXP, msg) + /*! * \def SLIC_DEBUG_ROOT( msg ) * \brief Logs a Debug message if on root @@ -680,6 +756,23 @@ */ #define SLIC_DEBUG_ROOT(msg) SLIC_DEBUG_IF(axom::slic::isRoot(), msg) + /*! + * \def SLIC_DEBUG_ROOT_ONCE( msg ) + * \brief Logs a Debug message if on root only once per call site. + * + * \param [in] msg user-supplied message. + * + * \note The SLIC_DEBUG_ROOT_ONCE macro is active when AXOM_DEBUG is defined. + * + * Usage: + * \code + * SLIC_DEBUG_ROOT_ONCE( "informative text goes here" ); + * \endcode + * + */ + #define SLIC_DEBUG_ROOT_ONCE(msg) \ + SLIC_DETAIL_LOG_IF_ONCE(SLIC_DEBUG_IF, axom::slic::isRoot(), msg) + /*! * \def SLIC_DEBUG_ROOT_IF( EXP, msg ) * \brief Logs a Debug message if on root and iff EXP is true @@ -697,6 +790,24 @@ */ #define SLIC_DEBUG_ROOT_IF(EXP, msg) SLIC_DEBUG_IF((EXP) && (axom::slic::isRoot()), msg) + /*! + * \def SLIC_DEBUG_ROOT_IF_ONCE( EXP, msg ) + * \brief Logs a Debug message if on root and iff EXP is true, only once per call site. + * + * \param [in] EXP user-supplied boolean expression. + * \param [in] msg user-supplied message. + * + * \note The SLIC_DEBUG_ROOT_IF_ONCE macro is active when AXOM_DEBUG is defined. + * + * Usage: + * \code + * SLIC_DEBUG_ROOT_IF_ONCE( (val < 0), "my_val should always be positive" ); + * \endcode + * + */ + #define SLIC_DEBUG_ROOT_IF_ONCE(EXP, msg) \ + SLIC_DETAIL_LOG_IF_ONCE(SLIC_DEBUG_IF, (EXP) && (axom::slic::isRoot()), msg) + /*! * \def SLIC_DEBUG_PRINT_CONTAINER( name, container ) * \brief Logs a Debug message containing the contents of the container, moving @@ -722,16 +833,72 @@ axom::slic::logMessage(axom::slic::message::Debug, __oss.str(), __FILE__, __LINE__); \ } while(axom::slic::detail::false_value) + /*! + * \def SLIC_DEBUG_PRINT_CONTAINER_ONCE( name, container ) + * \brief Logs a Debug message containing the contents of the container, moving + * the contents to the host if needed. Called only once per call site. + * + * \param [in] name The name of the container in the printed message. + * \param [in] container The container (array, vector, view). + * + * \note The SLIC_DEBUG_PRINT_CONTAINER_ONCE macro is active when AXOM_DEBUG is defined. + * + * Usage: + * \code + * axom::ArrayView dataView; + * SLIC_DEBUG_PRINT_CONTAINER_ONCE( "dataView", dataView ); + * \endcode + * + */ + #define SLIC_DEBUG_PRINT_CONTAINER_ONCE(name, container) \ + do \ + { \ + static bool once = true; \ + if(once) \ + { \ + SLIC_DEBUG_PRINT_CONTAINER(name, container); \ + once = false; \ + } \ + } while(axom::slic::detail::false_value) + #else // turn off debug macros #define SLIC_DEBUG(ignore_EXP) ((void)0) + #define SLIC_DEBUG_ONCE(ignore_EXP) ((void)0) #define SLIC_DEBUG_IF(ignore_EXP, ignore_msg) ((void)0) + #define SLIC_DEBUG_IF_ONCE(ignore_EXP, ignore_msg) ((void)0) #define SLIC_DEBUG_ROOT(ignore_EXP) ((void)0) + #define SLIC_DEBUG_ROOT_ONCE(ignore_EXP) ((void)0) #define SLIC_DEBUG_ROOT_IF(ignore_EXP, ignore_msg) ((void)0) + #define SLIC_DEBUG_ROOT_IF_ONCE(ignore_EXP, ignore_msg) ((void)0) #define SLIC_DEBUG_PRINT_CONTAINER(ignore_name, ignore_container) ((void)0) + #define SLIC_DEBUG_PRINT_CONTAINER_ONCE(ignore_name, ignore_container) ((void)0) #endif +/*! + * \brief Helper macro to define SLIC_*_ONCE macros that log only the first + * time they are used. + * + * \param [in] macro the macro to call. + * \param [in] EXP user-supplied boolean expression. + * \param [in] msg user-supplied message. + * + */ +#define SLIC_DETAIL_LOG_IF_ONCE(macro, EXP, msg) \ + do \ + { \ + static bool once = true; \ + if(EXP) \ + { \ + if(once) \ + { \ + macro(true, msg); \ + once = false; \ + } \ + } \ + } while(axom::slic::detail::false_value) + namespace axom { namespace slic diff --git a/src/axom/slic/tests/slic_macros.cpp b/src/axom/slic/tests/slic_macros.cpp index 747390f538..b2d0576369 100644 --- a/src/axom/slic/tests/slic_macros.cpp +++ b/src/axom/slic/tests/slic_macros.cpp @@ -118,6 +118,57 @@ void check_tag(const std::string& msg, const std::string& expected_tag) EXPECT_EQ(tag, expected_tag); } +//------------------------------------------------------------------------------ +int check_count(const std::string& msg, const std::string& expected_level) +{ + EXPECT_FALSE(msg.empty()); + + int count = 0; + for(size_t pos = msg.find(expected_level); pos != std::string::npos; + pos = msg.find(expected_level, pos + expected_level.size())) + { + ++count; + } + return count; +} + +//------------------------------------------------------------------------------ +// Checks level, message, line number, and file location. +// Clears stream when finished. +void check_level_msg_line_file(const std::string& level, const std::string& message, int expected_line) +{ + EXPECT_FALSE(slic::internal::is_stream_empty()); + const std::string str = slic::internal::test_stream.str(); + + check_level(str, level); + check_msg(str, message); + check_line(str, expected_line); + check_file(str); + slic::internal::clear(); +} + +// Convenience test macro that checks slic macro has logged a message +#define EXPECT_SLIC_LOG(macro_call, level, message) \ + do \ + { \ + const int expected_line = __LINE__; \ + macro_call; \ + check_level_msg_line_file(level, message, expected_line); \ + } while(false) + +// Convenience test macro that checks SLIC_*_ONCE macro logs one message +#define EXPECT_SLIC_ONCE(macro_call, level, message) \ + do \ + { \ + const int expected_line = __LINE__; \ + for(int i = 0; i < 2; i++) \ + { \ + macro_call; \ + } \ + EXPECT_EQ(check_count(slic::internal::test_stream.str(), level), 1); \ + check_level_msg_line_file(level, message, expected_line); \ + } while(false) + } // end anonymous namespace //------------------------------------------------------------------------------ @@ -125,29 +176,16 @@ void check_tag(const std::string& msg, const std::string& expected_tag) //------------------------------------------------------------------------------ TEST(slic_macros, test_error_macros) { - int expected_line_number; - EXPECT_TRUE(slic::internal::is_stream_empty()); - SLIC_ERROR("test error message"); - expected_line_number = __LINE__ - 1; - EXPECT_FALSE(slic::internal::is_stream_empty()); - check_level(slic::internal::test_stream.str(), "ERROR"); - check_msg(slic::internal::test_stream.str(), "test error message"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - slic::internal::clear(); + EXPECT_SLIC_LOG(SLIC_ERROR("test error message"), "ERROR", "test error message"); SLIC_ERROR_IF(false, "this message should not be logged!"); EXPECT_TRUE(slic::internal::is_stream_empty()); - SLIC_ERROR_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; - EXPECT_FALSE(slic::internal::is_stream_empty()); - check_level(slic::internal::test_stream.str(), "ERROR"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - slic::internal::clear(); + // Single line - Placement of ")" matters for __LINE__ for slic call and checking + // clang-format off + EXPECT_SLIC_LOG(SLIC_ERROR_IF(true, "this message is logged!"), "ERROR", "this message is logged!"); + // clang-format on // Check selective filtering based on root == false axom::slic::setIsRoot(false); @@ -157,13 +195,10 @@ TEST(slic_macros, test_error_macros) // Check selective filter based on root == true axom::slic::setIsRoot(true); SLIC_ERROR_ROOT_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; - EXPECT_FALSE(slic::internal::is_stream_empty()); - check_level(slic::internal::test_stream.str(), "ERROR"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - slic::internal::clear(); + + // clang-format off + EXPECT_SLIC_LOG(SLIC_ERROR_ROOT_IF(true, "this message is logged!"), "ERROR", "this message is logged!"); + // clang-format on // is root, but conditional is false -> no message axom::slic::setIsRoot(true); @@ -179,163 +214,190 @@ TEST(slic_macros, test_error_macros) //------------------------------------------------------------------------------ TEST(slic_macros, test_warning_macros) { - int expected_line_number; - EXPECT_TRUE(slic::internal::is_stream_empty()); - SLIC_WARNING("test warning message"); - expected_line_number = __LINE__ - 1; - EXPECT_FALSE(slic::internal::is_stream_empty()); - check_level(slic::internal::test_stream.str(), "WARNING"); - check_msg(slic::internal::test_stream.str(), "test warning message"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); + EXPECT_SLIC_LOG(SLIC_WARNING("test warning message"), "WARNING", "test warning message"); + + // Called once per call site + // Single line - Placement of ")" matters for __LINE__ for slic call and checking + // clang-format off + EXPECT_SLIC_ONCE(SLIC_WARNING_ONCE("test warning message once"), "WARNING", "test warning message once"); + // clang-format on + + // Two different call sites, will have two messages + SLIC_WARNING_ONCE("test warning message #1"); + SLIC_WARNING_ONCE("test warning message #2"); + EXPECT_EQ(check_count(slic::internal::test_stream.str(), "WARNING"), 2); slic::internal::clear(); SLIC_WARNING_IF(false, "this message should not be logged!"); EXPECT_TRUE(slic::internal::is_stream_empty()); - SLIC_WARNING_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; - EXPECT_FALSE(slic::internal::is_stream_empty()); - check_level(slic::internal::test_stream.str(), "WARNING"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - slic::internal::clear(); + SLIC_WARNING_IF_ONCE(false, "this message should not be logged!"); + EXPECT_TRUE(slic::internal::is_stream_empty()); + + // clang-format off + EXPECT_SLIC_LOG(SLIC_WARNING_IF(true, "this message is logged!"), "WARNING", "this message is logged!"); + + EXPECT_SLIC_ONCE(SLIC_WARNING_IF_ONCE(true, "this message is logged once!"), "WARNING", "this message is logged once!"); + // clang-format on // Check selective filtering based on root == false axom::slic::setIsRoot(false); SLIC_WARNING_ROOT_IF(false, "this message should not be logged!"); EXPECT_TRUE(slic::internal::is_stream_empty()); - // Check selective filter based on root == true + SLIC_WARNING_ROOT_IF_ONCE(false, "this message should not be logged!"); + EXPECT_TRUE(slic::internal::is_stream_empty()); + axom::slic::setIsRoot(true); - SLIC_WARNING_ROOT_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; - EXPECT_FALSE(slic::internal::is_stream_empty()); - check_level(slic::internal::test_stream.str(), "WARNING"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - slic::internal::clear(); + // clang-format off + EXPECT_SLIC_LOG(SLIC_WARNING_ROOT("this message is logged on root!"), "WARNING", "this message is logged on root!"); + + EXPECT_SLIC_ONCE(SLIC_WARNING_ROOT_ONCE("this message is logged on root once!"), "WARNING", "this message is logged on root once!"); + + // Check selective filter based on root == true + EXPECT_SLIC_LOG(SLIC_WARNING_ROOT_IF(true, "this message is logged!"), "WARNING", "this message is logged!"); + + EXPECT_SLIC_ONCE(SLIC_WARNING_ROOT_IF_ONCE(true, "this message is logged once!"), "WARNING", "this message is logged once!"); + // clang-format on // is root, but conditional is false -> no message axom::slic::setIsRoot(true); SLIC_WARNING_ROOT_IF(false, "this message should not be logged!"); EXPECT_TRUE(slic::internal::is_stream_empty()); + SLIC_WARNING_ROOT_IF_ONCE(false, "this message should not be logged!"); + EXPECT_TRUE(slic::internal::is_stream_empty()); + // is not root, and conditional is true -> no message axom::slic::setIsRoot(false); SLIC_WARNING_ROOT_IF(true, "this message should not be logged!"); EXPECT_TRUE(slic::internal::is_stream_empty()); + + SLIC_WARNING_ROOT_IF_ONCE(true, "this message should not be logged!"); + EXPECT_TRUE(slic::internal::is_stream_empty()); } //------------------------------------------------------------------------------ TEST(slic_macros, test_info_macros) { - int expected_line_number; - EXPECT_TRUE(slic::internal::is_stream_empty()); - SLIC_INFO("test info message"); - expected_line_number = __LINE__ - 1; - EXPECT_FALSE(slic::internal::is_stream_empty()); - check_level(slic::internal::test_stream.str(), "INFO"); - check_msg(slic::internal::test_stream.str(), "test info message"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - slic::internal::clear(); + EXPECT_SLIC_LOG(SLIC_INFO("test info message"), "INFO", "test info message"); + + EXPECT_SLIC_ONCE(SLIC_INFO_ONCE("this info message once"), "INFO", "this info message once"); SLIC_INFO_IF(false, "this message should not be logged!"); EXPECT_TRUE(slic::internal::is_stream_empty()); - SLIC_INFO_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; - EXPECT_FALSE(slic::internal::is_stream_empty()); - check_level(slic::internal::test_stream.str(), "INFO"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - slic::internal::clear(); + SLIC_INFO_IF_ONCE(false, "this message should not be logged!"); + EXPECT_TRUE(slic::internal::is_stream_empty()); + + EXPECT_SLIC_LOG(SLIC_INFO_IF(true, "this message is logged!"), "INFO", "this message is logged!"); + + // Single line - Placement of ")" matters for __LINE__ for slic call and checking + // clang-format off + EXPECT_SLIC_ONCE(SLIC_INFO_IF_ONCE(true, "this message is logged once!"), "INFO", "this message is logged once!"); + // clang-format on - // is root, but conditional is false -> no message axom::slic::setIsRoot(true); + // clang-format off + EXPECT_SLIC_LOG(SLIC_INFO_ROOT("this message is logged on root!"), "INFO", "this message is logged on root!"); + + EXPECT_SLIC_ONCE(SLIC_INFO_ROOT_ONCE("this message is logged on root once!"), "INFO", "this message is logged on root once!"); + // clang-format on + + // is root, but conditional is false -> no message SLIC_INFO_ROOT_IF(false, "this message should not be logged!"); EXPECT_TRUE(slic::internal::is_stream_empty()); + SLIC_INFO_ROOT_IF_ONCE(false, "this message should not be logged!"); + EXPECT_TRUE(slic::internal::is_stream_empty()); + // is not root, and conditional is true -> no message axom::slic::setIsRoot(false); SLIC_INFO_ROOT_IF(true, "this message should not be logged!"); EXPECT_TRUE(slic::internal::is_stream_empty()); + + SLIC_INFO_ROOT_IF_ONCE(true, "this message should not be logged!"); + EXPECT_TRUE(slic::internal::is_stream_empty()); } //------------------------------------------------------------------------------ TEST(slic_macros, test_debug_macros) { - int expected_line_number; - EXPECT_TRUE(slic::internal::is_stream_empty()); - SLIC_DEBUG("test debug message"); - expected_line_number = __LINE__ - 1; #ifdef AXOM_DEBUG - EXPECT_FALSE(slic::internal::is_stream_empty()); - check_level(slic::internal::test_stream.str(), "DEBUG"); - check_msg(slic::internal::test_stream.str(), "test debug message"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - slic::internal::clear(); + EXPECT_SLIC_LOG(SLIC_DEBUG("test debug message"), "DEBUG", "test debug message"); + + EXPECT_SLIC_ONCE(SLIC_DEBUG_ONCE("test debug message once"), "DEBUG", "test debug message once"); + + // Single line - Placement of ")" matters for __LINE__ for slic call and checking + // clang-format off + EXPECT_SLIC_LOG(SLIC_DEBUG_IF(true, "this message is logged!"), "DEBUG", "this message is logged!"); + + EXPECT_SLIC_ONCE(SLIC_DEBUG_IF_ONCE(true, "this message is logged once!"), "DEBUG", "this message is logged once!"); + // clang-format on + + axom::slic::setIsRoot(true); + EXPECT_SLIC_LOG(SLIC_DEBUG_ROOT("this message is logged!"), "DEBUG", "this message is logged!"); + + // clang-format off + EXPECT_SLIC_ONCE(SLIC_DEBUG_ROOT_ONCE("this message is logged once!"), "DEBUG", "this message is logged once!"); + + // Check selective filter based on root == true + EXPECT_SLIC_LOG(SLIC_DEBUG_ROOT_IF(true, "this message is logged!"), "DEBUG", "this message is logged!"); + + EXPECT_SLIC_ONCE(SLIC_DEBUG_ROOT_IF_ONCE(true, "this message is logged once!"), "DEBUG", "this message is logged once!"); + // clang-format on + #else // SLIC_DEBUG macros only log messages when AXOM_DEBUG is defined + + SLIC_DEBUG("test debug message"); + SLIC_DEBUG_ONCE("test debug message"); + + SLIC_DEBUG_IF(true, "this message is logged!"); + SLIC_DEBUG_IF_ONCE(true, "this message is logged!"); + + axom::slic::setIsRoot(true); + SLIC_DEBUG_ROOT("this message is logged!"); + SLIC_DEBUG_ROOT_ONCE("this message is logged!"); + + SLIC_DEBUG_ROOT_IF(true, "this message is logged!"); + SLIC_DEBUG_ROOT_IF_ONCE(true, "this message is logged!"); + EXPECT_TRUE(slic::internal::is_stream_empty()); #endif SLIC_DEBUG_IF(false, "this message should not be logged!"); EXPECT_TRUE(slic::internal::is_stream_empty()); - SLIC_DEBUG_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; -#ifdef AXOM_DEBUG - EXPECT_FALSE(slic::internal::is_stream_empty()); - check_level(slic::internal::test_stream.str(), "DEBUG"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - slic::internal::clear(); -#else - // SLIC_DEBUG macros only log messages when AXOM_DEBUG is defined + SLIC_DEBUG_IF_ONCE(false, "this message should not be logged!"); EXPECT_TRUE(slic::internal::is_stream_empty()); -#endif // Check selective filtering based on root == false axom::slic::setIsRoot(false); SLIC_DEBUG_ROOT_IF(false, "this message should not be logged!"); EXPECT_TRUE(slic::internal::is_stream_empty()); - // Check selective filter based on root == true - axom::slic::setIsRoot(true); - SLIC_DEBUG_ROOT_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; -#ifdef AXOM_DEBUG - EXPECT_FALSE(slic::internal::is_stream_empty()); - check_level(slic::internal::test_stream.str(), "DEBUG"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - slic::internal::clear(); -#else - // SLIC_DEBUG macros only log messages when AXOM_DEBUG is defined + SLIC_DEBUG_ROOT_IF_ONCE(false, "this message should not be logged!"); EXPECT_TRUE(slic::internal::is_stream_empty()); - AXOM_UNUSED_VAR(expected_line_number); -#endif // is root, but conditional is false -> no message axom::slic::setIsRoot(true); SLIC_DEBUG_ROOT_IF(false, "this message should not be logged!"); EXPECT_TRUE(slic::internal::is_stream_empty()); + SLIC_DEBUG_ROOT_IF_ONCE(false, "this message should not be logged!"); + EXPECT_TRUE(slic::internal::is_stream_empty()); + // is not root, and conditional is true -> no message axom::slic::setIsRoot(false); SLIC_DEBUG_ROOT_IF(true, "this message should not be logged!"); EXPECT_TRUE(slic::internal::is_stream_empty()); + + SLIC_DEBUG_ROOT_IF_ONCE(true, "this message should not be logged!"); + EXPECT_TRUE(slic::internal::is_stream_empty()); } //------------------------------------------------------------------------------ @@ -348,12 +410,7 @@ TEST(slic_macros, test_assert_macros) SLIC_ASSERT(val < 0); expected_line_number = __LINE__ - 1; #if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) - EXPECT_FALSE(slic::internal::is_stream_empty()); - check_level(slic::internal::test_stream.str(), "ERROR"); - check_msg(slic::internal::test_stream.str(), "Failed Assert: val < 0"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - slic::internal::clear(); + check_level_msg_line_file("ERROR", "Failed Assert: val < 0", expected_line_number); #else // SLIC_ASSERT macros only log messages when AXOM_DEBUG is defined AXOM_UNUSED_VAR(val); @@ -367,12 +424,9 @@ TEST(slic_macros, test_assert_macros) SLIC_ASSERT_MSG(val < 0, "val should be negative!"); expected_line_number = __LINE__ - 1; #if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) - EXPECT_FALSE(slic::internal::is_stream_empty()); - check_level(slic::internal::test_stream.str(), "ERROR"); - check_msg(slic::internal::test_stream.str(), "Failed Assert: val < 0\nval should be negative!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - slic::internal::clear(); + check_level_msg_line_file("ERROR", + "Failed Assert: val < 0\nval should be negative!", + expected_line_number); #else // SLIC_ASSERT macros only log messages when AXOM_DEBUG is defined AXOM_UNUSED_VAR(val); @@ -391,12 +445,7 @@ TEST(slic_macros, test_check_macros) SLIC_CHECK(val < 0); expected_line_number = __LINE__ - 1; #if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) - EXPECT_FALSE(slic::internal::is_stream_empty()); - check_level(slic::internal::test_stream.str(), "WARNING"); - check_msg(slic::internal::test_stream.str(), "Failed Check: val < 0"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - slic::internal::clear(); + check_level_msg_line_file("WARNING", "Failed Check: val < 0", expected_line_number); #else // SLIC_CHECK macros only log messages when AXOM_DEBUG is defined AXOM_UNUSED_VAR(val); @@ -410,12 +459,9 @@ TEST(slic_macros, test_check_macros) SLIC_CHECK_MSG(val < 0, "val should be negative!"); expected_line_number = __LINE__ - 1; #if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) - EXPECT_FALSE(slic::internal::is_stream_empty()); - check_level(slic::internal::test_stream.str(), "WARNING"); - check_msg(slic::internal::test_stream.str(), "Failed Check: val < 0\nval should be negative!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - slic::internal::clear(); + check_level_msg_line_file("WARNING", + "Failed Check: val < 0\nval should be negative!", + expected_line_number); #else // SLIC_CHECK macros only log messages when AXOM_DEBUG is defined AXOM_UNUSED_VAR(val); @@ -432,21 +478,77 @@ TEST(slic_macros, test_tagged_macros) EXPECT_TRUE(slic::internal::is_stream_empty()); SLIC_INFO_TAGGED("test tagged info message", "myTag"); expected_line_number = __LINE__ - 1; - EXPECT_FALSE(slic::internal::is_stream_empty()); - check_level(slic::internal::test_stream.str(), "INFO"); - check_msg(slic::internal::test_stream.str(), "test tagged info message"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); + check_tag(slic::internal::test_stream.str(), "myTag"); - slic::internal::clear(); + check_level_msg_line_file("INFO", "test tagged info message", expected_line_number); + + for(int i = 0; i < 2; i++) + { + SLIC_INFO_TAGGED_ONCE("test tagged info message once", "myTag"); + } + expected_line_number = __LINE__ - 2; + + EXPECT_EQ(check_count(slic::internal::test_stream.str(), "INFO"), 1); + check_tag(slic::internal::test_stream.str(), "myTag"); + check_level_msg_line_file("INFO", "test tagged info message once", expected_line_number); SLIC_INFO_TAGGED("this message should not be logged (no tag given)!", ""); + SLIC_INFO_TAGGED_ONCE("this message should not be logged (no tag given)!", ""); EXPECT_TRUE(slic::internal::is_stream_empty()); SLIC_INFO_TAGGED("this message should not be logged (tag DNE)!", "tag404"); + SLIC_INFO_TAGGED_ONCE("this message should not be logged (tag DNE)!", "tag404"); EXPECT_TRUE(slic::internal::is_stream_empty()); } +//------------------------------------------------------------------------------ +TEST(slic_macros, test_if_once_macros) +{ + // Check that message is logged when condition is satisfied only once + for(int i = 0; i < 3; i++) + { + SLIC_INFO_IF_ONCE(i > 0, i << "th message is logged!"); + } + int expected_line_number = __LINE__ - 2; + EXPECT_EQ(check_count(slic::internal::test_stream.str(), "INFO"), 1); + check_level_msg_line_file("INFO", "1th message is logged", expected_line_number); + + axom::slic::setIsRoot(true); + for(int i = 0; i < 3; i++) + { + SLIC_INFO_ROOT_IF_ONCE(i > 0, i << "th message is logged!"); + } + expected_line_number = __LINE__ - 2; + EXPECT_EQ(check_count(slic::internal::test_stream.str(), "INFO"), 1); + check_level_msg_line_file("INFO", "1th message is logged", expected_line_number); + + // Two call-sites have a single message each + for(int i = 0; i < 3; i++) + { + SLIC_INFO_IF_ONCE(i == 0, "message 1 logs " << i); + SLIC_INFO_IF_ONCE(i > 0, "message 2 logs " << i); + } + int msg_1_line = __LINE__ - 3; + int msg_2_line = __LINE__ - 3; + + EXPECT_FALSE(slic::internal::is_stream_empty()); + const std::string str = slic::internal::test_stream.str(); + EXPECT_EQ(check_count(str, "INFO"), 2); + + std::string msg_1 = str.substr(0, str.size() / 2); + std::string msg_2 = str.substr(str.size() / 2); + + check_level(msg_1, "INFO"); + check_level(msg_2, "INFO"); + check_msg(msg_1, "message 1 logs 0"); + check_msg(msg_2, "message 2 logs 1"); + check_line(msg_1, msg_1_line); + check_line(msg_2, msg_2_line); + check_file(msg_1); + check_file(msg_2); + slic::internal::clear(); +} + //------------------------------------------------------------------------------ TEST(slic_macros, test_macros_file_output) { diff --git a/src/axom/slic/tests/slic_macros_parallel.cpp b/src/axom/slic/tests/slic_macros_parallel.cpp index a716895940..98ec942fc1 100644 --- a/src/axom/slic/tests/slic_macros_parallel.cpp +++ b/src/axom/slic/tests/slic_macros_parallel.cpp @@ -144,6 +144,7 @@ void check_tag(const std::string& msg, const std::string& expected_tag) } //------------------------------------------------------------------------------ +// For SynchronizedStream where each message has one associated rank void check_rank(const std::string& msg, int expected_rank) { EXPECT_FALSE(msg.empty()); @@ -157,6 +158,7 @@ void check_rank(const std::string& msg, int expected_rank) } //------------------------------------------------------------------------------ +// For LumberjackStream where message from multiple ranks are at the root void check_ranks(const std::string& msg, int expected_ranks) { // Check all ranks from [0, expected_ranks) are in message @@ -167,12 +169,12 @@ void check_ranks(const std::string& msg, int expected_ranks) } //------------------------------------------------------------------------------ -void check_rank_count(const std::string& msg, const std::string& streamType, int expected_rank_count) +void check_rank_count(const std::string& msg, const std::string& stream_type, int expected_rank_count) { EXPECT_FALSE(msg.empty()); // Always 1 for SynchronizedStream - if(streamType == "Synchronized") + if(stream_type == "Synchronized") { expected_rank_count = 1; } @@ -184,6 +186,162 @@ void check_rank_count(const std::string& msg, const std::string& streamType, int EXPECT_EQ(rank_count, expected_rank_count); } +//------------------------------------------------------------------------------ +// Use level to determine number of messages - used for SLIC_*_ONCE macros +int check_msg_count(const std::string& msg, const std::string& expected_level) +{ + EXPECT_FALSE(msg.empty()); + + int count = 0; + for(size_t pos = msg.find(expected_level); pos != std::string::npos; + pos = msg.find(expected_level, pos + expected_level.size())) + { + ++count; + } + return count; +} + +//------------------------------------------------------------------------------ +// Checks message logged on all ranks +void check_all_ranks(const std::string& stream_type, + const std::string& level, + const std::string& message, + int expected_line, + int rank, + int nranks) +{ + if(stream_type == "Synchronized" || (stream_type == "Lumberjack" && rank == 0)) + { + EXPECT_FALSE(slic::internal::are_all_streams_empty()); + const std::string str = slic::internal::test_stream.str(); + + SCOPED_TRACE(std::string("SLIC trace (all ranks): Failed line was ") + + std::to_string(expected_line)); + + check_level(str, level); + check_msg(str, message); + check_line(str, expected_line); + if(stream_type == "Synchronized") + { + check_rank(str, rank); + } + else + { + check_ranks(str, nranks); + } + check_rank_count(str, stream_type, nranks); + + check_file(str); + } +} + +// Convenience test macro that calls given slic macro and checks +// message logged on all ranks +#define EXPECT_SLIC_LOG_ALL_RANKS(macro_call, level, message) \ + do \ + { \ + const int expected_line = __LINE__; \ + macro_call; \ + slic::flushStreams(); \ + check_all_ranks(GetParam(), level, message, expected_line, rank, nranks); \ + slic::internal::clear_streams(); \ + } while(false) + +//------------------------------------------------------------------------------ +// Checks message logged only on root +void check_root(const std::string& stream_type, + const std::string& level, + const std::string& message, + int expected_line, + int rank) +{ + if(rank == 0) + { + EXPECT_FALSE(slic::internal::are_all_streams_empty()); + const std::string str = slic::internal::test_stream.str(); + + SCOPED_TRACE(std::string("SLIC trace (root): Failed line was ") + std::to_string(expected_line)); + + check_level(str, level); + check_msg(str, message); + check_line(str, expected_line); + check_rank(str, rank); + + // Only one rank has logged a message + check_rank_count(str, stream_type, 1); + + check_file(str); + } + else + { + EXPECT_TRUE(slic::internal::are_all_streams_empty()); + } +} + +// Convenience test macro that calls given slic macro and checks +// message logged only on root +#define EXPECT_SLIC_LOG_ROOT(macro_call, level, message) \ + do \ + { \ + axom::slic::setIsRoot(rank == 0); \ + const int expected_line = __LINE__; \ + macro_call; \ + slic::flushStreams(); \ + check_root(GetParam(), level, message, expected_line, rank); \ + slic::internal::clear_streams(); \ + } while(false) + +//------------------------------------------------------------------------------ +// Checks message logged only on even ranks +void check_even(const std::string& stream_type, + const std::string& level, + const std::string& message, + int expected_line, + int rank, + int nranks) +{ + if(((rank % 2) == 0 && stream_type == "Synchronized") || (rank == 0 && stream_type == "Lumberjack")) + { + EXPECT_FALSE(slic::internal::are_all_streams_empty()); + const std::string str = slic::internal::test_stream.str(); + + SCOPED_TRACE(std::string("SLIC trace (even): Failed line was ") + std::to_string(expected_line)); + + check_level(str, level); + check_msg(str, message); + check_line(str, expected_line); + if(stream_type == "Synchronized") + { + check_rank(str, rank); + } + else + { + for(int i = 0; i < nranks; i += 2) + { + check_rank(str, i); + } + } + check_rank_count(str, stream_type, (nranks / 2) + (nranks % 2)); + check_file(str); + } + else + { + EXPECT_TRUE(slic::internal::are_all_streams_empty()); + } +} + +// Convenience test macro that calls given slic macro and checks +// message logged only on even ranks +#define EXPECT_SLIC_LOG_EVEN(macro_call, level, message) \ + do \ + { \ + const int expected_line = __LINE__; \ + macro_call; \ + slic::flushStreams(); \ + check_even(GetParam(), level, message, expected_line, rank, nranks); \ + slic::internal::clear_streams(); \ + } while(false) + //------------------------------------------------------------------------------ bool has_aborted = false; void custom_abort_function() { has_aborted = true; } @@ -256,56 +414,18 @@ class SlicMacrosParallel : public ::testing::TestWithParam //------------------------------------------------------------------------------ TEST_P(SlicMacrosParallel, test_error_macros) { - int expected_line_number; - EXPECT_TRUE(slic::internal::are_all_streams_empty()); - SLIC_ERROR("test error message"); - expected_line_number = __LINE__ - 1; - slic::flushStreams(); - if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) - { - EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "ERROR"); - check_msg(slic::internal::test_stream.str(), "test error message"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - if(GetParam() == "Synchronized") - { - check_rank(slic::internal::test_stream.str(), rank); - } - else - { - check_ranks(slic::internal::test_stream.str(), nranks); - } - check_rank_count(slic::internal::test_stream.str(), GetParam(), nranks); - } - slic::internal::clear_streams(); + + EXPECT_SLIC_LOG_ALL_RANKS(SLIC_ERROR("test error message"), "ERROR", "test error message"); SLIC_ERROR_IF(false, "this message should not be logged!"); slic::flushStreams(); EXPECT_TRUE(slic::internal::are_all_streams_empty()); - SLIC_ERROR_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; - slic::flushStreams(); - if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) - { - EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "ERROR"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - if(GetParam() == "Synchronized") - { - check_rank(slic::internal::test_stream.str(), rank); - } - else - { - check_ranks(slic::internal::test_stream.str(), nranks); - } - check_rank_count(slic::internal::test_stream.str(), GetParam(), nranks); - } - slic::internal::clear_streams(); + // Single line - Placement of ")" matters for __LINE__ for slic call and checking + // clang-format off + EXPECT_SLIC_LOG_ALL_RANKS(SLIC_ERROR_IF(true, "this message is logged!"), "ERROR", "this message is logged!"); + // clang-format on // Check selective filtering based on root == false axom::slic::setIsRoot(false); @@ -315,27 +435,9 @@ TEST_P(SlicMacrosParallel, test_error_macros) // Check selective filter based on root == true axom::slic::setIsRoot(true); - SLIC_ERROR_ROOT_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; - slic::flushStreams(); - if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) - { - EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "ERROR"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - if(GetParam() == "Synchronized") - { - check_rank(slic::internal::test_stream.str(), rank); - } - else - { - check_ranks(slic::internal::test_stream.str(), nranks); - } - check_rank_count(slic::internal::test_stream.str(), GetParam(), nranks); - } - slic::internal::clear_streams(); + // clang-format off + EXPECT_SLIC_LOG_ALL_RANKS(SLIC_ERROR_ROOT_IF(true, "this message is logged!"), "ERROR", "this message is logged!"); + // clang-format on // is root, but conditional is false -> no message axom::slic::setIsRoot(true); @@ -349,57 +451,13 @@ TEST_P(SlicMacrosParallel, test_error_macros) slic::flushStreams(); EXPECT_TRUE(slic::internal::are_all_streams_empty()); - // Check for one rank being root - axom::slic::setIsRoot(rank == 0); - SLIC_ERROR_ROOT_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; - slic::flushStreams(); - if(rank == 0) - { - EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "ERROR"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - check_rank(slic::internal::test_stream.str(), rank); - check_rank_count(slic::internal::test_stream.str(), GetParam(), 1); - } - else - { - EXPECT_TRUE(slic::internal::are_all_streams_empty()); - } - slic::internal::clear_streams(); + // clang-format off + // Check for message only on root + EXPECT_SLIC_LOG_ROOT(SLIC_ERROR_ROOT_IF(true, "this message is logged!"), "ERROR", "this message is logged!"); - // Check for more than one rank being root for SynchronizedStream - axom::slic::setIsRoot((rank % 2) == 0); - SLIC_ERROR_ROOT_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; - slic::flushStreams(); - if(((rank % 2) == 0 && GetParam() == "Synchronized") || (rank == 0 && GetParam() == "Lumberjack")) - { - EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "ERROR"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - if(GetParam() == "Synchronized") - { - check_rank(slic::internal::test_stream.str(), rank); - } - else - { - for(int i = 0; i < nranks; i += 2) - { - check_rank(slic::internal::test_stream.str(), i); - } - } - check_rank_count(slic::internal::test_stream.str(), GetParam(), (nranks / 2) + (nranks % 2)); - } - else - { - EXPECT_TRUE(slic::internal::are_all_streams_empty()); - } - slic::internal::clear_streams(); + // Check for message on every even rank only + EXPECT_SLIC_LOG_EVEN(SLIC_ERROR_IF((rank % 2) == 0, "this message is logged!"), "ERROR", "this message is logged!"); + // clang-format on } //------------------------------------------------------------------------------ @@ -408,146 +466,166 @@ TEST_P(SlicMacrosParallel, test_warning_macros) int expected_line_number; EXPECT_TRUE(slic::internal::are_all_streams_empty()); - SLIC_WARNING("test warning message"); - expected_line_number = __LINE__ - 1; + + EXPECT_SLIC_LOG_ALL_RANKS(SLIC_WARNING("test warning message"), "WARNING", "test warning message"); + + SLIC_WARNING_IF(false, "this message should not be logged!"); + SLIC_WARNING_IF_ONCE(false, "this message should not be logged!"); slic::flushStreams(); - if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) + EXPECT_TRUE(slic::internal::are_all_streams_empty()); + + // Called once per call site; since this test is ran for parameters + // Synchronized and Lumberjack, a separate call site is needed for each parameter, + // otherwise ONCE macro logs nothing when its Lumberjack's turn to run. + for(int i = 0; i < 3; i++) { - EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "WARNING"); - check_msg(slic::internal::test_stream.str(), "test warning message"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); if(GetParam() == "Synchronized") { - check_rank(slic::internal::test_stream.str(), rank); + SLIC_WARNING_ONCE("test warning message " << i); + expected_line_number = __LINE__ - 1; } else { - check_ranks(slic::internal::test_stream.str(), nranks); + SLIC_WARNING_ONCE("test warning message " << i); + expected_line_number = __LINE__ - 1; } - check_rank_count(slic::internal::test_stream.str(), GetParam(), nranks); } - slic::internal::clear_streams(); - - SLIC_WARNING_IF(false, "this message should not be logged!"); - slic::flushStreams(); - EXPECT_TRUE(slic::internal::are_all_streams_empty()); - - SLIC_WARNING_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; slic::flushStreams(); if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) { EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "WARNING"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); + EXPECT_EQ(check_msg_count(slic::internal::test_stream.str(), "WARNING"), 1); + } + check_all_ranks(GetParam(), "WARNING", "test warning message 0", expected_line_number, rank, nranks); + slic::internal::clear_streams(); + + // Single line - Placement of ")" matters for __LINE__ for slic call and checking + // clang-format off + EXPECT_SLIC_LOG_ALL_RANKS(SLIC_WARNING_IF(true, "this message is logged!"), "WARNING", "this message is logged!"); + // clang-format on + + for(int i = 0; i < 3; i++) + { if(GetParam() == "Synchronized") { - check_rank(slic::internal::test_stream.str(), rank); + SLIC_WARNING_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } else { - check_ranks(slic::internal::test_stream.str(), nranks); + SLIC_WARNING_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } - check_rank_count(slic::internal::test_stream.str(), GetParam(), nranks); } + slic::flushStreams(); + if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) + { + EXPECT_FALSE(slic::internal::are_all_streams_empty()); + EXPECT_EQ(check_msg_count(slic::internal::test_stream.str(), "WARNING"), 1); + } + check_all_ranks(GetParam(), "WARNING", "this message is logged 0", expected_line_number, rank, nranks); slic::internal::clear_streams(); // Check selective filtering based on root == false axom::slic::setIsRoot(false); SLIC_WARNING_ROOT_IF(false, "this message should not be logged!"); + SLIC_WARNING_ROOT_IF_ONCE(false, "this message should not be logged!"); slic::flushStreams(); EXPECT_TRUE(slic::internal::are_all_streams_empty()); // Check selective filter based on root == true axom::slic::setIsRoot(true); - SLIC_WARNING_ROOT_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; - slic::flushStreams(); - if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) + // clang-format off + EXPECT_SLIC_LOG_ALL_RANKS(SLIC_WARNING_ROOT_IF(true, "this message is logged!"), "WARNING", "this message is logged!"); + // clang-format on + + for(int i = 0; i < 3; i++) { - EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "WARNING"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); if(GetParam() == "Synchronized") { - check_rank(slic::internal::test_stream.str(), rank); + SLIC_WARNING_ROOT_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } else { - check_ranks(slic::internal::test_stream.str(), nranks); + SLIC_WARNING_ROOT_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } - check_rank_count(slic::internal::test_stream.str(), GetParam(), nranks); } + slic::flushStreams(); + if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) + { + EXPECT_FALSE(slic::internal::are_all_streams_empty()); + EXPECT_EQ(check_msg_count(slic::internal::test_stream.str(), "WARNING"), 1); + } + check_all_ranks(GetParam(), "WARNING", "this message is logged 0", expected_line_number, rank, nranks); slic::internal::clear_streams(); // is root, but conditional is false -> no message axom::slic::setIsRoot(true); SLIC_WARNING_ROOT_IF(false, "this message should not be logged!"); + SLIC_WARNING_ROOT_IF_ONCE(false, "this message should not be logged!"); slic::flushStreams(); EXPECT_TRUE(slic::internal::are_all_streams_empty()); // is not root, and conditional is true -> no message axom::slic::setIsRoot(false); SLIC_WARNING_ROOT_IF(true, "this message should not be logged!"); + SLIC_WARNING_ROOT_IF_ONCE(true, "this message should not be logged!"); slic::flushStreams(); EXPECT_TRUE(slic::internal::are_all_streams_empty()); - // Check for one rank being root + // clang-format off + // Check for message only on root + EXPECT_SLIC_LOG_ROOT(SLIC_WARNING_ROOT_IF(true, "this message is logged!"), "WARNING", "this message is logged!"); + axom::slic::setIsRoot(rank == 0); - SLIC_WARNING_ROOT_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; + for(int i = 0; i < 3; i++) + { + if(GetParam() == "Synchronized") + { + SLIC_WARNING_ROOT_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; + } + else + { + SLIC_WARNING_ROOT_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; + } + } slic::flushStreams(); if(rank == 0) { EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "WARNING"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - check_rank(slic::internal::test_stream.str(), rank); - check_rank_count(slic::internal::test_stream.str(), GetParam(), 1); - } - else - { - EXPECT_TRUE(slic::internal::are_all_streams_empty()); + EXPECT_EQ(check_msg_count(slic::internal::test_stream.str(), "WARNING"), 1); } + check_root(GetParam(), "WARNING", "this message is logged 0", expected_line_number, rank); slic::internal::clear_streams(); - // Check for more than one rank being root for SynchronizedStream - axom::slic::setIsRoot((rank % 2) == 0); - SLIC_WARNING_ROOT_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; - slic::flushStreams(); - if(((rank % 2) == 0 && GetParam() == "Synchronized") || (rank == 0 && GetParam() == "Lumberjack")) + // Check for message on every even rank only + EXPECT_SLIC_LOG_EVEN(SLIC_WARNING_IF((rank % 2) == 0, "this message is logged!"), "WARNING", "this message is logged!"); + // clang-format on + + for(int i = 0; i < 3; i++) { - EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "WARNING"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); if(GetParam() == "Synchronized") { - check_rank(slic::internal::test_stream.str(), rank); + SLIC_WARNING_IF_ONCE((rank % 2) == 0, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } else { - for(int i = 0; i < nranks; i += 2) - { - check_rank(slic::internal::test_stream.str(), i); - } + SLIC_WARNING_IF_ONCE((rank % 2) == 0, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } - check_rank_count(slic::internal::test_stream.str(), GetParam(), (nranks / 2) + (nranks % 2)); } - else + slic::flushStreams(); + if(((rank % 2) == 0 && stream_type == "Synchronized") || (rank == 0 && stream_type == "Lumberjack")) { - EXPECT_TRUE(slic::internal::are_all_streams_empty()); + EXPECT_FALSE(slic::internal::are_all_streams_empty()); + EXPECT_EQ(check_msg_count(slic::internal::test_stream.str(), "WARNING"), 1); } + check_even(GetParam(), "WARNING", "this message is logged 0", expected_line_number, rank, nranks); slic::internal::clear_streams(); } @@ -558,26 +636,31 @@ TEST_P(SlicMacrosParallel, test_info_macros) int expected_tag_number; EXPECT_TRUE(slic::internal::are_all_streams_empty()); - SLIC_INFO("test info message"); - expected_line_number = __LINE__ - 1; - slic::flushStreams(); - if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) + EXPECT_SLIC_LOG_ALL_RANKS(SLIC_INFO("test info message"), "INFO", "test info message"); + + // Called once per call site; since this test is ran for parameters + // Synchronized and Lumberjack, a separate call site is needed for each parameter, + // otherwise ONCE macro logs nothing when its Lumberjack's turn to run. + for(int i = 0; i < 3; i++) { - EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "INFO"); - check_msg(slic::internal::test_stream.str(), "test info message"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); if(GetParam() == "Synchronized") { - check_rank(slic::internal::test_stream.str(), rank); + SLIC_INFO_ONCE("test info message " << i); + expected_line_number = __LINE__ - 1; } else { - check_ranks(slic::internal::test_stream.str(), nranks); + SLIC_INFO_ONCE("test info message " << i); + expected_line_number = __LINE__ - 1; } - check_rank_count(slic::internal::test_stream.str(), GetParam(), nranks); } + slic::flushStreams(); + if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) + { + EXPECT_FALSE(slic::internal::are_all_streams_empty()); + EXPECT_EQ(check_msg_count(slic::internal::test_stream.str(), "INFO"), 1); + } + check_all_ranks(GetParam(), "INFO", "test info message 0", expected_line_number, rank, nranks); slic::internal::clear_streams(); SLIC_INFO_TAGGED("test tagged info message", "myTag"); @@ -647,131 +730,147 @@ TEST_P(SlicMacrosParallel, test_info_macros) slic::internal::clear_streams(); SLIC_INFO_TAGGED("this message should not be logged (no tag given)!", ""); + SLIC_INFO_TAGGED_ONCE("this message should not be logged (no tag given)!", ""); slic::flushStreams(); EXPECT_TRUE(slic::internal::are_all_streams_empty()); SLIC_INFO_TAGGED("this message should not be logged (tag DNE)!", "tag404"); + SLIC_INFO_TAGGED_ONCE("this message should not be logged (tag DNE)!", "tag404"); slic::flushStreams(); EXPECT_TRUE(slic::internal::are_all_streams_empty()); SLIC_INFO_IF(false, "this message should not be logged!"); + SLIC_INFO_IF_ONCE(false, "this message should not be logged!"); slic::flushStreams(); EXPECT_TRUE(slic::internal::are_all_streams_empty()); - SLIC_INFO_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; - slic::flushStreams(); - if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) + // Single line - Placement of ")" matters for __LINE__ for slic call and checking + // clang-format off + EXPECT_SLIC_LOG_ALL_RANKS(SLIC_INFO_IF(true, "this message is logged!"), "INFO", "this message is logged!"); + // clang-format on + + for(int i = 0; i < 3; i++) { - EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "INFO"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); if(GetParam() == "Synchronized") { - check_rank(slic::internal::test_stream.str(), rank); + SLIC_INFO_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } else { - check_ranks(slic::internal::test_stream.str(), nranks); + SLIC_INFO_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } - check_rank_count(slic::internal::test_stream.str(), GetParam(), nranks); } + slic::flushStreams(); + if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) + { + EXPECT_FALSE(slic::internal::are_all_streams_empty()); + EXPECT_EQ(check_msg_count(slic::internal::test_stream.str(), "INFO"), 1); + } + check_all_ranks(GetParam(), "INFO", "this message is logged 0", expected_line_number, rank, nranks); slic::internal::clear_streams(); // Check selective filtering based on root == false axom::slic::setIsRoot(false); SLIC_INFO_ROOT_IF(false, "this message should not be logged!"); + SLIC_INFO_ROOT_IF_ONCE(false, "this message should not be logged!"); slic::flushStreams(); EXPECT_TRUE(slic::internal::are_all_streams_empty()); // Check selective filter based on root == true axom::slic::setIsRoot(true); - SLIC_INFO_ROOT_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; - slic::flushStreams(); - if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) + // clang-format off + EXPECT_SLIC_LOG_ALL_RANKS(SLIC_INFO_ROOT_IF(true, "this message is logged!"), "INFO", "this message is logged!"); + // clang-format on + + for(int i = 0; i < 3; i++) { - EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "INFO"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); if(GetParam() == "Synchronized") { - check_rank(slic::internal::test_stream.str(), rank); + SLIC_INFO_ROOT_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } else { - check_ranks(slic::internal::test_stream.str(), nranks); + SLIC_INFO_ROOT_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } - check_rank_count(slic::internal::test_stream.str(), GetParam(), nranks); } + slic::flushStreams(); + if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) + { + EXPECT_FALSE(slic::internal::are_all_streams_empty()); + EXPECT_EQ(check_msg_count(slic::internal::test_stream.str(), "INFO"), 1); + } + check_all_ranks(GetParam(), "INFO", "this message is logged 0", expected_line_number, rank, nranks); slic::internal::clear_streams(); // is root, but conditional is false -> no message axom::slic::setIsRoot(true); SLIC_INFO_ROOT_IF(false, "this message should not be logged!"); + SLIC_INFO_ROOT_IF_ONCE(false, "this message should not be logged!"); slic::flushStreams(); EXPECT_TRUE(slic::internal::are_all_streams_empty()); // is not root, and conditional is true -> no message axom::slic::setIsRoot(false); SLIC_INFO_ROOT_IF(true, "this message should not be logged!"); + SLIC_INFO_ROOT_IF_ONCE(true, "this message should not be logged!"); slic::flushStreams(); EXPECT_TRUE(slic::internal::are_all_streams_empty()); - // Check for one rank being root + // clang-format off + // Check for message only on root + EXPECT_SLIC_LOG_ROOT(SLIC_INFO_ROOT_IF(true, "this message is logged!"), "INFO", "this message is logged!"); + axom::slic::setIsRoot(rank == 0); - SLIC_INFO_ROOT_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; + for(int i = 0; i < 3; i++) + { + if(GetParam() == "Synchronized") + { + SLIC_INFO_ROOT_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; + } + else + { + SLIC_INFO_ROOT_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; + } + } slic::flushStreams(); if(rank == 0) { EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "INFO"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - check_rank(slic::internal::test_stream.str(), rank); - check_rank_count(slic::internal::test_stream.str(), GetParam(), 1); - } - else - { - EXPECT_TRUE(slic::internal::are_all_streams_empty()); + EXPECT_EQ(check_msg_count(slic::internal::test_stream.str(), "INFO"), 1); } + check_root(GetParam(), "INFO", "this message is logged 0", expected_line_number, rank); slic::internal::clear_streams(); - // Check for more than one rank being root - axom::slic::setIsRoot((rank % 2) == 0); - SLIC_INFO_ROOT_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; - slic::flushStreams(); - if(((rank % 2) == 0 && GetParam() == "Synchronized") || (rank == 0 && GetParam() == "Lumberjack")) + // Check for message on every even rank only + EXPECT_SLIC_LOG_EVEN(SLIC_INFO_IF((rank % 2) == 0, "this message is logged!"), "INFO", "this message is logged!"); + // clang-format on + + for(int i = 0; i < 3; i++) { - EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "INFO"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); if(GetParam() == "Synchronized") { - check_rank(slic::internal::test_stream.str(), rank); + SLIC_INFO_IF_ONCE((rank % 2) == 0, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } else { - for(int i = 0; i < nranks; i += 2) - { - check_rank(slic::internal::test_stream.str(), i); - } + SLIC_INFO_IF_ONCE((rank % 2) == 0, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } - check_rank_count(slic::internal::test_stream.str(), GetParam(), (nranks / 2) + (nranks % 2)); } - else + slic::flushStreams(); + if(((rank % 2) == 0 && stream_type == "Synchronized") || (rank == 0 && stream_type == "Lumberjack")) { - EXPECT_TRUE(slic::internal::are_all_streams_empty()); + EXPECT_FALSE(slic::internal::are_all_streams_empty()); + EXPECT_EQ(check_msg_count(slic::internal::test_stream.str(), "INFO"), 1); } + check_even(GetParam(), "INFO", "this message is logged 0", expected_line_number, rank, nranks); slic::internal::clear_streams(); } @@ -781,173 +880,186 @@ TEST_P(SlicMacrosParallel, test_debug_macros) int expected_line_number; EXPECT_TRUE(slic::internal::are_all_streams_empty()); - SLIC_DEBUG("test debug message"); - expected_line_number = __LINE__ - 1; - slic::flushStreams(); + #ifdef AXOM_DEBUG - if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) + + EXPECT_SLIC_LOG_ALL_RANKS(SLIC_DEBUG("test debug message"), "DEBUG", "test debug message"); + + // Called once per call site; since this test is ran for parameters + // Synchronized and Lumberjack, a separate call site is needed for each parameter, + // otherwise ONCE macro logs nothing when its Lumberjack's turn to run. + for(int i = 0; i < 3; i++) { - EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "DEBUG"); - check_msg(slic::internal::test_stream.str(), "test debug message"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); if(GetParam() == "Synchronized") { - check_rank(slic::internal::test_stream.str(), rank); + SLIC_DEBUG_ONCE("test debug message " << i); + expected_line_number = __LINE__ - 1; } else { - check_ranks(slic::internal::test_stream.str(), nranks); + SLIC_DEBUG_ONCE("test debug message " << i); + expected_line_number = __LINE__ - 1; } - check_rank_count(slic::internal::test_stream.str(), GetParam(), nranks); } + slic::flushStreams(); + if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) + { + EXPECT_FALSE(slic::internal::are_all_streams_empty()); + EXPECT_EQ(check_msg_count(slic::internal::test_stream.str(), "DEBUG"), 1); + } + check_all_ranks(GetParam(), "DEBUG", "test debug message 0", expected_line_number, rank, nranks); slic::internal::clear_streams(); -#else - // SLIC_DEBUG macros only log messages when AXOM_DEBUG is defined - EXPECT_TRUE(slic::internal::are_all_streams_empty()); -#endif - SLIC_DEBUG_IF(false, "this message should not be logged!"); - slic::flushStreams(); - EXPECT_TRUE(slic::internal::are_all_streams_empty()); + // Single line - Placement of ")" matters for __LINE__ for slic call and checking + // clang-format off + EXPECT_SLIC_LOG_ALL_RANKS(SLIC_DEBUG_IF(true, "this message is logged!"), "DEBUG", "this message is logged!"); - SLIC_DEBUG_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; + for(int i = 0; i < 3; i++) + { + if(GetParam() == "Synchronized") + { + SLIC_DEBUG_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; + } + else + { + SLIC_DEBUG_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; + } + } slic::flushStreams(); -#ifdef AXOM_DEBUG if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) { - EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "DEBUG"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); + EXPECT_FALSE(slic::internal::are_all_streams_empty()); + EXPECT_EQ(check_msg_count(slic::internal::test_stream.str(), "DEBUG"), 1); + } + check_all_ranks(GetParam(), "DEBUG", "this message is logged 0", expected_line_number, rank, nranks); + slic::internal::clear_streams(); + + // Check selective filter based on root == true + axom::slic::setIsRoot(true); + EXPECT_SLIC_LOG_ALL_RANKS(SLIC_DEBUG_ROOT_IF(true, "this message is logged!"), "DEBUG", "this message is logged!"); + + for(int i = 0; i < 3; i++) + { if(GetParam() == "Synchronized") { - check_rank(slic::internal::test_stream.str(), rank); + SLIC_DEBUG_ROOT_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } else { - check_ranks(slic::internal::test_stream.str(), nranks); + SLIC_DEBUG_ROOT_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } - check_rank_count(slic::internal::test_stream.str(), GetParam(), nranks); } - slic::internal::clear_streams(); -#else - // SLIC_DEBUG macros only log messages when AXOM_DEBUG is defined - EXPECT_TRUE(slic::internal::are_all_streams_empty()); -#endif - - // Check selective filtering based on root == false - axom::slic::setIsRoot(false); - SLIC_DEBUG_ROOT_IF(false, "this message should not be logged!"); slic::flushStreams(); - EXPECT_TRUE(slic::internal::are_all_streams_empty()); - - // Check selective filter based on root == true - axom::slic::setIsRoot(true); - SLIC_DEBUG_ROOT_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; - slic::flushStreams(); -#ifdef AXOM_DEBUG if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) { EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "DEBUG"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); + EXPECT_EQ(check_msg_count(slic::internal::test_stream.str(), "DEBUG"), 1); + } + check_all_ranks(GetParam(), "DEBUG", "this message is logged 0", expected_line_number, rank, nranks); + slic::internal::clear_streams(); + + // Check for message only on root + EXPECT_SLIC_LOG_ROOT(SLIC_DEBUG_ROOT_IF(true, "this message is logged!"), "DEBUG", "this message is logged!"); + + axom::slic::setIsRoot(rank == 0); + for(int i = 0; i < 3; i++) + { if(GetParam() == "Synchronized") { - check_rank(slic::internal::test_stream.str(), rank); + SLIC_DEBUG_ROOT_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } else { - check_ranks(slic::internal::test_stream.str(), nranks); + SLIC_DEBUG_ROOT_IF_ONCE(true, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } - check_rank_count(slic::internal::test_stream.str(), GetParam(), nranks); } - slic::internal::clear_streams(); -#else - // SLIC_DEBUG macros only log messages when AXOM_DEBUG is defined - EXPECT_TRUE(slic::internal::are_all_streams_empty()); -#endif - - // is root, but conditional is false -> no message - axom::slic::setIsRoot(true); - SLIC_DEBUG_ROOT_IF(false, "this message should not be logged!"); - slic::flushStreams(); - EXPECT_TRUE(slic::internal::are_all_streams_empty()); - - // is not root, and conditional is true -> no message - axom::slic::setIsRoot(false); - SLIC_DEBUG_ROOT_IF(true, "this message should not be logged!"); - slic::flushStreams(); - EXPECT_TRUE(slic::internal::are_all_streams_empty()); - - // Check for one rank being root - axom::slic::setIsRoot(rank == 0); - SLIC_DEBUG_ROOT_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; slic::flushStreams(); -#ifdef AXOM_DEBUG if(rank == 0) { EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "DEBUG"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - check_rank(slic::internal::test_stream.str(), rank); - check_rank_count(slic::internal::test_stream.str(), GetParam(), 1); - } - else - { - EXPECT_TRUE(slic::internal::are_all_streams_empty()); + EXPECT_EQ(check_msg_count(slic::internal::test_stream.str(), "DEBUG"), 1); } + check_root(GetParam(), "DEBUG", "this message is logged 0", expected_line_number, rank); slic::internal::clear_streams(); -#else - // SLIC_DEBUG macros only log messages when AXOM_DEBUG is defined - EXPECT_TRUE(slic::internal::are_all_streams_empty()); -#endif - // Check for more than one rank being root - axom::slic::setIsRoot((rank % 2) == 0); - SLIC_DEBUG_ROOT_IF(true, "this message is logged!"); - expected_line_number = __LINE__ - 1; - slic::flushStreams(); -#ifdef AXOM_DEBUG - if(((rank % 2) == 0 && GetParam() == "Synchronized") || (rank == 0 && GetParam() == "Lumberjack")) + // Check for message on every even rank only + EXPECT_SLIC_LOG_EVEN(SLIC_DEBUG_IF((rank % 2) == 0, "this message is logged!"), "DEBUG", "this message is logged!"); + // clang-format on + + for(int i = 0; i < 3; i++) { - EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "DEBUG"); - check_msg(slic::internal::test_stream.str(), "this message is logged!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); if(GetParam() == "Synchronized") { - check_rank(slic::internal::test_stream.str(), rank); + SLIC_DEBUG_IF_ONCE((rank % 2) == 0, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } else { - for(int i = 0; i < nranks; i += 2) - { - check_rank(slic::internal::test_stream.str(), i); - } + SLIC_DEBUG_IF_ONCE((rank % 2) == 0, "this message is logged " << i); + expected_line_number = __LINE__ - 1; } - check_rank_count(slic::internal::test_stream.str(), GetParam(), (nranks / 2) + (nranks % 2)); } - else + slic::flushStreams(); + if(((rank % 2) == 0 && stream_type == "Synchronized") || (rank == 0 && stream_type == "Lumberjack")) { - EXPECT_TRUE(slic::internal::are_all_streams_empty()); + EXPECT_FALSE(slic::internal::are_all_streams_empty()); + EXPECT_EQ(check_msg_count(slic::internal::test_stream.str(), "DEBUG"), 1); } + check_even(GetParam(), "DEBUG", "this message is logged 0", expected_line_number, rank, nranks); slic::internal::clear_streams(); + #else // SLIC_DEBUG macros only log messages when AXOM_DEBUG is defined - EXPECT_TRUE(slic::internal::are_all_streams_empty()); AXOM_UNUSED_VAR(expected_line_number); + + SLIC_DEBUG("test debug message"); + SLIC_DEBUG_ONCE("test debug message"); + + SLIC_DEBUG_IF(true, "this message is logged!"); + SLIC_DEBUG_IF_ONCE(true, "this message is logged!"); + + axom::slic::setIsRoot(true); + SLIC_DEBUG_ROOT_IF(true, "this message is logged!"); + SLIC_DEBUG_ROOT_IF_ONCE(true, "this message is logged!"); + + SLIC_DEBUG_IF((rank % 2) == 0, "this message is logged!"); + SLIC_DEBUG_IF_ONCE((rank % 2) == 0, "this message is logged!"); + + slic::flushStreams(); + EXPECT_TRUE(slic::internal::are_all_streams_empty()); #endif + + SLIC_DEBUG_IF(false, "this message should not be logged!"); + SLIC_DEBUG_IF_ONCE(false, "this message should not be logged!"); + slic::flushStreams(); + EXPECT_TRUE(slic::internal::are_all_streams_empty()); + + // Check selective filtering based on root == false + axom::slic::setIsRoot(false); + SLIC_DEBUG_ROOT_IF(false, "this message should not be logged!"); + SLIC_DEBUG_ROOT_IF_ONCE(false, "this message should not be logged!"); + slic::flushStreams(); + EXPECT_TRUE(slic::internal::are_all_streams_empty()); + + // is root, but conditional is false -> no message + axom::slic::setIsRoot(true); + SLIC_DEBUG_ROOT_IF(false, "this message should not be logged!"); + SLIC_DEBUG_ROOT_IF_ONCE(false, "this message should not be logged!"); + slic::flushStreams(); + EXPECT_TRUE(slic::internal::are_all_streams_empty()); + + // is not root, and conditional is true -> no message + axom::slic::setIsRoot(false); + SLIC_DEBUG_ROOT_IF(true, "this message should not be logged!"); + SLIC_DEBUG_ROOT_IF_ONCE(true, "this message should not be logged!"); + slic::flushStreams(); + EXPECT_TRUE(slic::internal::are_all_streams_empty()); } TEST_P(SlicMacrosParallel, test_abort_error_macros) @@ -1216,144 +1328,179 @@ TEST_P(SlicMacrosParallel, test_abort_warning_macros) //------------------------------------------------------------------------------ TEST_P(SlicMacrosParallel, test_assert_macros) { - [[maybe_unused]] int expected_line_number; - slic::internal::clear_streams(); EXPECT_TRUE(slic::internal::are_all_streams_empty()); - constexpr int val = 42; - SLIC_ASSERT(val < 0); - expected_line_number = __LINE__ - 1; - slic::flushStreams(); + #if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) - if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) - { - EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "ERROR"); - check_msg(slic::internal::test_stream.str(), "Failed Assert: val < 0"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - if(GetParam() == "Synchronized") - { - check_rank(slic::internal::test_stream.str(), rank); - } - else - { - check_ranks(slic::internal::test_stream.str(), nranks); - } - check_rank_count(slic::internal::test_stream.str(), GetParam(), nranks); - } - slic::internal::clear_streams(); + + EXPECT_SLIC_LOG_ALL_RANKS(SLIC_ASSERT(val < 0), "ERROR", "Failed Assert: val < 0"); + + // Single line - Placement of ")" matters for __LINE__ for slic call and checking + // clang-format off + EXPECT_SLIC_LOG_ALL_RANKS(SLIC_ASSERT_MSG(val < 0, "val should be negative!"), "ERROR", "Failed Assert: val < 0\nval should be negative!"); + // clang-format on + #else // SLIC_ASSERT macros only log messages when AXOM_DEBUG is defined AXOM_UNUSED_VAR(val); - AXOM_UNUSED_VAR(expected_line_number); + + SLIC_ASSERT(val < 0); + + SLIC_ASSERT_MSG(val < 0, "val should be negative!"); + + slic::flushStreams(); EXPECT_TRUE(slic::internal::are_all_streams_empty()); #endif SLIC_ASSERT(val > 0); slic::flushStreams(); EXPECT_TRUE(slic::internal::are_all_streams_empty()); +} + +// ------------------------------------------------------------------------------ +TEST_P(SlicMacrosParallel, test_check_macros) +{ + EXPECT_TRUE(slic::internal::are_all_streams_empty()); + + constexpr int val = 42; - SLIC_ASSERT_MSG(val < 0, "val should be negative!"); - expected_line_number = __LINE__ - 1; - slic::flushStreams(); #if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) - if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) - { - EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "ERROR"); - check_msg(slic::internal::test_stream.str(), "Failed Assert: val < 0\nval should be negative!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); - if(GetParam() == "Synchronized") - { - check_rank(slic::internal::test_stream.str(), rank); - } - else - { - check_ranks(slic::internal::test_stream.str(), nranks); - } - check_rank_count(slic::internal::test_stream.str(), GetParam(), nranks); - } - slic::internal::clear_streams(); + EXPECT_SLIC_LOG_ALL_RANKS(SLIC_CHECK(val < 0), "WARNING", "Failed Check: val < 0"); + + // Single line - Placement of ")" matters for __LINE__ for slic call and checking + // clang-format off + EXPECT_SLIC_LOG_ALL_RANKS(SLIC_CHECK_MSG(val < 0, "val should be negative!"), "WARNING", "Failed Check: val < 0\nval should be negative!"); + // clang-format on + #else - // SLIC_ASSERT macros only log messages when AXOM_DEBUG is defined + // SLIC_CHECK macros only log messages when AXOM_DEBUG is defined AXOM_UNUSED_VAR(val); - AXOM_UNUSED_VAR(expected_line_number); + + SLIC_CHECK(val < 0); + + SLIC_CHECK_MSG(val < 0, "val should be negative!"); + + slic::flushStreams(); EXPECT_TRUE(slic::internal::are_all_streams_empty()); #endif + + SLIC_CHECK(val > 0); + slic::flushStreams(); + EXPECT_TRUE(slic::internal::are_all_streams_empty()); } -// ------------------------------------------------------------------------------ -TEST_P(SlicMacrosParallel, test_check_macros) +//------------------------------------------------------------------------------ +TEST_P(SlicMacrosParallel, test_if_once_macros) { - [[maybe_unused]] int expected_line_number; - - EXPECT_TRUE(slic::internal::are_all_streams_empty()); + int expected_line_number; - constexpr int val = 42; - SLIC_CHECK(val < 0); - expected_line_number = __LINE__ - 1; + // Check that message is logged when condition is satisfied only once + // Called once per call site; since this test is ran for parameters + // Synchronized and Lumberjack, a separate call site is needed for each parameter, + // otherwise ONCE macro logs nothing when its Lumberjack's turn to run. + for(int i = 0; i < 3; i++) + { + if(GetParam() == "Synchronized") + { + SLIC_INFO_IF_ONCE(i > 0, i << "th message is logged!"); + expected_line_number = __LINE__ - 1; + } + else + { + SLIC_INFO_IF_ONCE(i > 0, i << "th message is logged!"); + expected_line_number = __LINE__ - 1; + } + } slic::flushStreams(); -#if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) { EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "WARNING"); - check_msg(slic::internal::test_stream.str(), "Failed Check: val < 0"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); + EXPECT_EQ(check_msg_count(slic::internal::test_stream.str(), "INFO"), 1); + } + check_all_ranks(GetParam(), "INFO", "1th message is logged", expected_line_number, rank, nranks); + slic::internal::clear_streams(); + + axom::slic::setIsRoot(true); + for(int i = 0; i < 3; i++) + { if(GetParam() == "Synchronized") { - check_rank(slic::internal::test_stream.str(), rank); + SLIC_INFO_ROOT_IF_ONCE(i > 0, i << "th message is logged!"); + expected_line_number = __LINE__ - 1; } else { - check_ranks(slic::internal::test_stream.str(), nranks); + SLIC_INFO_ROOT_IF_ONCE(i > 0, i << "th message is logged!"); + expected_line_number = __LINE__ - 1; } - check_rank_count(slic::internal::test_stream.str(), GetParam(), nranks); } + slic::flushStreams(); + if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) + { + EXPECT_FALSE(slic::internal::are_all_streams_empty()); + EXPECT_EQ(check_msg_count(slic::internal::test_stream.str(), "INFO"), 1); + } + check_all_ranks(GetParam(), "INFO", "1th message is logged", expected_line_number, rank, nranks); slic::internal::clear_streams(); -#else - // SLIC_CHECK macros only log messages when AXOM_DEBUG is defined - AXOM_UNUSED_VAR(val); - AXOM_UNUSED_VAR(expected_line_number); - EXPECT_TRUE(slic::internal::are_all_streams_empty()); -#endif - SLIC_CHECK(val > 0); + // Two call-sites have a single message each + int msg_1_line; + int msg_2_line; + for(int i = 0; i < 3; i++) + { + if(GetParam() == "Synchronized") + { + SLIC_INFO_IF_ONCE(i == 0, "message 1 logs " << i); + msg_1_line = __LINE__ - 1; + SLIC_INFO_IF_ONCE(i > 0, "message 2 logs " << i); + msg_2_line = __LINE__ - 1; + } + else + { + SLIC_INFO_IF_ONCE(i == 0, "message 1 logs " << i); + msg_1_line = __LINE__ - 1; + SLIC_INFO_IF_ONCE(i > 0, "message 2 logs " << i); + msg_2_line = __LINE__ - 1; + } + } slic::flushStreams(); - EXPECT_TRUE(slic::internal::are_all_streams_empty()); - SLIC_CHECK_MSG(val < 0, "val should be negative!"); - expected_line_number = __LINE__ - 1; - slic::flushStreams(); -#if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) if(GetParam() == "Synchronized" || (GetParam() == "Lumberjack" && rank == 0)) { EXPECT_FALSE(slic::internal::are_all_streams_empty()); - check_level(slic::internal::test_stream.str(), "WARNING"); - check_msg(slic::internal::test_stream.str(), "Failed Check: val < 0\nval should be negative!"); - check_file(slic::internal::test_stream.str()); - check_line(slic::internal::test_stream.str(), expected_line_number); + const std::string str = slic::internal::test_stream.str(); + EXPECT_EQ(check_msg_count(str, "INFO"), 2); + + check_level(str, "INFO"); + + std::string msg_1 = str.substr(0, str.size() / 2); + std::string msg_2 = str.substr(str.size() / 2); + + check_level(msg_1, "INFO"); + check_level(msg_2, "INFO"); + check_msg(msg_1, "message 1 logs 0"); + check_msg(msg_2, "message 2 logs 1"); + check_line(msg_1, msg_1_line); + check_line(msg_2, msg_2_line); + check_file(msg_1); + check_file(msg_2); if(GetParam() == "Synchronized") { - check_rank(slic::internal::test_stream.str(), rank); + check_rank(msg_1, rank); + check_rank(msg_2, rank); } else { - check_ranks(slic::internal::test_stream.str(), nranks); + check_ranks(msg_1, nranks); + check_ranks(msg_2, nranks); } - check_rank_count(slic::internal::test_stream.str(), GetParam(), nranks); + check_rank_count(msg_1, GetParam(), nranks); + check_rank_count(msg_2, GetParam(), nranks); + check_file(msg_1); + check_file(msg_2); } slic::internal::clear_streams(); -#else - // SLIC_CHECK macros only log messages when AXOM_DEBUG is defined - AXOM_UNUSED_VAR(val); - AXOM_UNUSED_VAR(expected_line_number); - EXPECT_TRUE(slic::internal::are_all_streams_empty()); -#endif } //------------------------------------------------------------------------------