LCOV - code coverage report
Current view: top level - include/utils - MooseUnitUtils.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 23 38 60.5 %
Date: 2026-05-29 20:35:17 Functions: 241 241 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #pragma once
      11             : 
      12             : #include "gtest/gtest.h"
      13             : 
      14             : #include <filesystem>
      15             : #include <string>
      16             : 
      17             : #include "libmesh/utility.h"
      18             : 
      19             : #include "MooseUtils.h"
      20             : #include "Moose.h"
      21             : 
      22             : namespace Moose::UnitUtils
      23             : {
      24             : /**
      25             :  * A helper for asserting that calling something throws an exception.
      26             :  *
      27             :  * @param action A function that calls the thing that should throw
      28             :  * @param contains Optional argument to check that the assertion message contains this sub string
      29             :  * @param set_throw_on_error Set to true to set moose to throw on error
      30             :  */
      31             : template <class ExceptionType = std::exception, class Action = bool>
      32             : void
      33         224 : assertThrows(const Action & action,
      34             :              const std::optional<std::string> & contains = {},
      35             :              const bool set_throw_on_error = false)
      36             : {
      37             :   static_assert(std::is_base_of_v<std::exception, ExceptionType>, "Not an exception");
      38             : 
      39         224 :   std::unique_ptr<Moose::ScopedThrowOnError> scoped_throw_on_error;
      40         224 :   if (set_throw_on_error)
      41           2 :     scoped_throw_on_error = std::make_unique<Moose::ScopedThrowOnError>();
      42             : 
      43             :   try
      44             :   {
      45         224 :     action();
      46           0 :     FAIL() << "Expected " << MooseUtils::prettyCppType<ExceptionType>() << " not thrown";
      47             :   }
      48         448 :   catch (std::exception const & e)
      49             :   {
      50             :     if constexpr (!std::is_same_v<std::exception, ExceptionType>)
      51          38 :       if (!dynamic_cast<const ExceptionType *>(&e))
      52           0 :         FAIL() << "Threw " << demangle(typeid(e).name()) << " instead of "
      53           0 :                << MooseUtils::prettyCppType<ExceptionType>() << " with message '" << e.what()
      54           0 :                << "'";
      55             : 
      56         224 :     if (contains)
      57             :     {
      58         672 :       ASSERT_TRUE(std::string(e.what()).find(*contains) != std::string::npos)
      59           0 :           << "Exception \"" << e.what() << "\" does not contain \"" << *contains << "\"";
      60             :     }
      61             :   }
      62         224 : }
      63             : 
      64             : /**
      65             :  * Helper for the [EXPECT,ASSERT]_[MOOSEERROR,THROW]_[MSG,MSG_CONTAINS] macros.
      66             :  */
      67             : template <typename ExceptionType, bool exact, bool assert, bool set_throw_on_error, typename Func>
      68             : void
      69         492 : throwsWithMessage(Func && fn, const std::string_view message, const char * file, int line)
      70             : {
      71             :   static_assert(std::is_base_of_v<std::exception, ExceptionType>, "Not an exception");
      72             : 
      73         492 :   std::ostringstream error;
      74             : 
      75             :   try
      76             :   {
      77         492 :     std::unique_ptr<Moose::ScopedThrowOnError> scoped_throw_on_error;
      78             :     if constexpr (set_throw_on_error)
      79         130 :       scoped_throw_on_error = std::make_unique<Moose::ScopedThrowOnError>();
      80             : 
      81         492 :     fn();
      82             : 
      83           0 :     error << "Expected exception of type " << libMesh::demangle(typeid(ExceptionType).name())
      84           0 :           << " but no exception was thrown.";
      85         492 :   }
      86         984 :   catch (const ExceptionType & ex)
      87             :   {
      88             :     // Exact match
      89             :     if constexpr (exact)
      90             :     {
      91         888 :       if (std::string(ex.what()) == message)
      92         444 :         return;
      93             :     }
      94             :     // Partial match
      95             :     else
      96             :     {
      97          96 :       if (std::string(ex.what()).find(message) != std::string::npos)
      98          48 :         return;
      99             :     }
     100             : 
     101           0 :     error << "Expected" << (exact ? "" : " partial") << " exception message: \"" << message
     102           0 :           << "\"\n  Actual exception message: \"" << ex.what() << "\"";
     103             :   }
     104           0 :   catch (const std::exception & ex)
     105             :   {
     106           0 :     error << "Expected exception of type " << libMesh::demangle(typeid(ExceptionType).name())
     107           0 :           << " but exception of type " << libMesh::demangle(typeid(ex).name()) << " was thrown.";
     108             :   }
     109             : 
     110           0 :   ::testing::internal::AssertHelper(
     111           0 :       ::testing::TestPartResult::kNonFatalFailure, file, line, error.str().c_str()) =
     112           0 :       ::testing::Message();
     113         492 : }
     114             : 
     115             : /**
     116             :  * Create a temporary file and delete it upon destruction.
     117             :  */
     118             : class TempFile
     119             : {
     120             : public:
     121             :   TempFile();
     122             :   ~TempFile();
     123             : 
     124             :   /**
     125             :    * @return The path to the temporary file.
     126             :    */
     127          46 :   const std::filesystem::path & path() const { return _path; }
     128             : 
     129             : private:
     130             :   static std::filesystem::path generatePath();
     131             : 
     132             :   const std::filesystem::path _path;
     133             : };
     134             : 
     135             : } // namespace Moose::UnitUtils
     136             : 
     137             : /// Expect that an action throws with an exact message
     138             : #define EXPECT_THROW_MSG(stmt, exc_type, expected_msg)                                             \
     139             :   ::Moose::UnitUtils::throwsWithMessage<exc_type, true, false, false>(                             \
     140             :       [&]() { stmt; }, expected_msg, __FILE__, __LINE__)
     141             : 
     142             : /// Assert that an action throws with an exact message
     143             : #define ASSERT_THROW_MSG(stmt, exc_type, expected_msg)                                             \
     144             :   ::Moose::UnitUtils::throwsWithMessage<exc_type, true, true, false>(                              \
     145             :       [&]() { stmt; }, expected_msg, __FILE__, __LINE__)
     146             : 
     147             : /// Expect that an action throws with a partial message
     148             : #define EXPECT_THROW_MSG_CONTAINS(stmt, exc_type, expected_substr)                                 \
     149             :   ::Moose::UnitUtils::throwsWithMessage<exc_type, false, false, false>(                            \
     150             :       [&]() { stmt; }, expected_substr, __FILE__, __LINE__)
     151             : 
     152             : /// Assert that an action throws with a partial message
     153             : #define ASSERT_THROW_MSG_CONTAINS(stmt, exc_type, expected_substr)                                 \
     154             :   ::Moose::UnitUtils::throwsWithMessage<exc_type, false, true, false>(                             \
     155             :       [&]() { stmt; }, expected_substr, __FILE__, __LINE__)
     156             : 
     157             : /// Expect that a mooseError is thrown with an exact message
     158             : #define EXPECT_MOOSEERROR_MSG(stmt, expected_msg)                                                  \
     159             :   ::Moose::UnitUtils::throwsWithMessage<MooseRuntimeError, true, false, true>(                     \
     160             :       [&]() { stmt; }, expected_msg, __FILE__, __LINE__)
     161             : 
     162             : /// Assert that an mooseError is thrown with an exact message
     163             : #define ASSERT_MOOSEERROR_MSG(stmt, expected_msg)                                                  \
     164             :   ::Moose::UnitUtils::throwsWithMessage<MooseRuntimeError, true, true, true>(                      \
     165             :       [&]() { stmt; }, expected_msg, __FILE__, __LINE__)
     166             : 
     167             : /// Expect that an mooseError is thrown with a partial message
     168             : #define EXPECT_MOOSEERROR_MSG_CONTAINS(stmt, expected_substr)                                      \
     169             :   ::Moose::UnitUtils::throwsWithMessage<MooseRuntimeError, false, false, true>(                    \
     170             :       [&]() { stmt; }, expected_substr, __FILE__, __LINE__)
     171             : 
     172             : /// Assert that an mooseError is thrown with a partial message
     173             : #define ASSERT_MOOSEERROR_MSG_CONTAINS(stmt, expected_substr)                                      \
     174             :   ::Moose::UnitUtils::throwsWithMessage<MooseRuntimeError, false, true, true>(                     \
     175             :       [&]() { stmt; }, expected_substr, __FILE__, __LINE__)

Generated by: LCOV version 1.14