Treat exceptions better in interactive selections.
[alexxy/gromacs.git] / src / testutils / testasserts.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2013, by the GROMACS development team, led by
5  * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6  * others, as listed in the AUTHORS file in the top-level source
7  * directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \libinternal \file
36  * \brief
37  * Extra assertions for unit tests.
38  *
39  * This file provides assert macros to replace (ASSERT|EXPECT)(_NO)?_THROW
40  * from Google Test.  They behave otherwise the same as the Google Test ones,
41  * but also print details of any unexpected exceptions.  This makes it much
42  * easier to see at one glance what went wrong.
43  *
44  * This file also provides extra floating-point assertions.
45  *
46  * \if internal
47  * \todo
48  * The implementation is somewhat ugly, and accesses some Google Test
49  * internals.  Could be nice to clean it up a bit.
50  * \endif
51  *
52  * \author Teemu Murtola <teemu.murtola@gmail.com>
53  * \inlibraryapi
54  * \ingroup module_testutils
55  */
56 #ifndef GMX_TESTUTILS_TESTASSERTS_H
57 #define GMX_TESTUTILS_TESTASSERTS_H
58
59 #include <gtest/gtest.h>
60
61 #include "gromacs/legacyheaders/maths.h"
62
63 #include "gromacs/utility/exceptions.h"
64
65 namespace gmx
66 {
67 namespace test
68 {
69
70 /*! \cond internal */
71 /*! \internal
72  * \brief
73  * Internal implementation macro for exception assertations.
74  *
75  * \param statement          Statements to execute.
76  * \param expected_exception Exception type that \p statement should throw.
77  * \param fail               Function/macro to call on failure.
78  *
79  * The implementation is copied and adjusted from
80  * include/gtest/internal/gtest-internal.h in Google Test 1.6.0.
81  */
82 #define GMX_TEST_THROW_(statement, expected_exception, fail) \
83     GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
84     if (::testing::AssertionResult gmx_ar = ::testing::AssertionSuccess()) { \
85         bool gmx_caught_expected = false; \
86         try { \
87             GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
88         } \
89         catch (expected_exception const &) { \
90             gmx_caught_expected = true; \
91         } \
92         catch (std::exception const &ex) { \
93             gmx_ar << "Expected: " #statement " throws an exception of type " \
94             << #expected_exception ".\n  Actual: it throws a different type.\n" \
95             << "Exception details:\n" << ::gmx::formatExceptionMessageToString(ex); \
96             goto GTEST_CONCAT_TOKEN_(gmx_label_testthrow_, __LINE__); \
97         } \
98         catch (...) { \
99             gmx_ar << "Expected: " #statement " throws an exception of type " \
100             << #expected_exception ".\n  Actual: it throws a different type."; \
101             goto GTEST_CONCAT_TOKEN_(gmx_label_testthrow_, __LINE__); \
102         } \
103         if (!gmx_caught_expected) { \
104             gmx_ar << "Expected: " #statement " throws an exception of type " \
105             << #expected_exception ".\n  Actual: it throws nothing."; \
106             goto GTEST_CONCAT_TOKEN_(gmx_label_testthrow_, __LINE__); \
107         } \
108     } else \
109         GTEST_CONCAT_TOKEN_(gmx_label_testthrow_, __LINE__) : \
110             fail(gmx_ar.message())
111
112 /*! \internal
113  * \brief
114  * Internal implementation macro for exception assertations.
115  *
116  * \param statement          Statements to execute.
117  * \param fail               Function/macro to call on failure.
118  *
119  * The implementation is copied and adjusted from
120  * include/gtest/internal/gtest-internal.h in Google Test 1.6.0.
121  */
122 #define GMX_TEST_NO_THROW_(statement, fail) \
123     GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
124     if (::testing::AssertionResult gmx_ar = ::testing::AssertionSuccess()) { \
125         try { \
126             GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
127         } \
128         catch (std::exception const &ex) { \
129             gmx_ar << "Expected: " #statement " doesn't throw an exception.\n" \
130             << "  Actual: it throws.\n" \
131             << "Exception details:\n" << ::gmx::formatExceptionMessageToString(ex); \
132             goto GTEST_CONCAT_TOKEN_(gmx_label_testnothrow_, __LINE__); \
133         } \
134         catch (...) { \
135             gmx_ar << "Expected: " #statement " doesn't throw an exception.\n" \
136             << "  Actual: it throws."; \
137             goto GTEST_CONCAT_TOKEN_(gmx_label_testnothrow_, __LINE__); \
138         } \
139     } else \
140         GTEST_CONCAT_TOKEN_(gmx_label_testnothrow_, __LINE__) : \
141             fail(gmx_ar.message())
142 //! \endcond
143
144 /*! \brief
145  * Asserts that a statement throws a given exception.
146  *
147  * See Google Test documentation on EXPECT_THROW.
148  * This macro works the same, but additionally prints details of unexpected
149  * exceptions.
150  */
151 #define EXPECT_THROW_GMX(statement, expected_exception) \
152     GMX_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_)
153 /*! \brief
154  * Asserts that a statement does not throw.
155  *
156  * See Google Test documentation on EXPECT_NO_THROW.
157  * This macro works the same, but additionally prints details of unexpected
158  * exceptions.
159  */
160 #define EXPECT_NO_THROW_GMX(statement) \
161     GMX_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_)
162 /*! \brief
163  * Asserts that a statement throws a given exception.
164  *
165  * See Google Test documentation on ASSERT_THROW.
166  * This macro works the same, but additionally prints details of unexpected
167  * exceptions.
168  */
169 #define ASSERT_THROW_GMX(statement, expected_exception) \
170     GMX_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_)
171 /*! \brief
172  * Asserts that a statement does not throw.
173  *
174  * See Google Test documentation on ASSERT_NO_THROW.
175  * This macro works the same, but additionally prints details of unexpected
176  * exceptions.
177  */
178 #define ASSERT_NO_THROW_GMX(statement) \
179     GMX_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_)
180
181 /*! \cond internal */
182 /*! \internal \brief
183  * Assertion predicate formatter for comparing two floating-point values.
184  */
185 static ::testing::AssertionResult assertWithinRelativeTolerance(
186         const char *expr1, const char *expr2, const char * /*exprTolerance*/,
187         real val1, real val2, real relativeTolerance)
188 {
189     if (gmx_within_tol(val1, val2, relativeTolerance))
190     {
191         return ::testing::AssertionSuccess();
192     }
193     return ::testing::AssertionFailure()
194            << "Value of: " << expr2 << " not within tolerance of " << relativeTolerance << "\n"
195            << "  Actual: " << val2 << "\n"
196            << "Expected: " << expr1 << "\n"
197            << "Which is: " << val1;
198 }
199 //! \endcond
200
201 /*! \brief
202  * Asserts that two floating-point values are within the given relative error.
203  *
204  * This assert works as EXPECT_NEAR from Google Test, except that it uses a
205  * relative instead of a absolute tolerance.
206  * See gmx_within_tol() for definition of the tolerance.
207  */
208 #define EXPECT_NEAR_REL(val1, val2, rel_error) \
209     EXPECT_PRED_FORMAT3(::gmx::test::assertWithinRelativeTolerance, val1, val2, rel_error)
210 /*! \brief
211  * Asserts that two floating-point values are within the given relative error.
212  *
213  * This assert works as ASSERT_NEAR from Google Test, except that it uses a
214  * relative instead of a absolute tolerance.
215  * See gmx_within_tol() for definition of the tolerance.
216  */
217 #define ASSERT_NEAR_REL(val1, val2, rel_error) \
218     ASSERT_PRED_FORMAT3(::gmx::test::assertWithinRelativeTolerance, val1, val2, rel_error)
219
220 } // namespace test
221 } // namespace gmx
222
223 #endif