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__)
|