493771c866b5a6b1c565b16d4d9759d124f01e64
[alexxy/gromacs.git] / src / testutils / refdata-checkers.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2015,2016, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source 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 /*! \internal \file
36  * \brief
37  * Declares internal helper classes for the reference data framework to check
38  * reference data values of different types.
39  *
40  * \author Teemu Murtola <teemu.murtola@gmail.com>
41  * \ingroup module_testutils
42  */
43 #ifndef GMX_TESTUTILS_REFDATA_CHECKERS_H
44 #define GMX_TESTUTILS_REFDATA_CHECKERS_H
45
46 #include <cstdlib>
47
48 #include <limits>
49 #include <string>
50
51 #include <gtest/gtest.h>
52
53 #include "gromacs/utility/basedefinitions.h"
54 #include "gromacs/utility/exceptions.h"
55 #include "gromacs/utility/stringutil.h"
56
57 #include "testutils/refdata-impl.h"
58 #include "testutils/testasserts.h"
59 #include "testutils/testexceptions.h"
60
61 namespace gmx
62 {
63 namespace test
64 {
65
66 class IReferenceDataEntryChecker
67 {
68     public:
69         virtual void fillEntry(ReferenceDataEntry *entry) const = 0;
70         virtual ::testing::AssertionResult
71         checkEntry(const ReferenceDataEntry &entry, const std::string &fullId) const = 0;
72
73     protected:
74         virtual ~IReferenceDataEntryChecker() {}
75 };
76
77 class NullChecker : public IReferenceDataEntryChecker
78 {
79     public:
80         virtual void fillEntry(ReferenceDataEntry *) const {}
81         virtual ::testing::AssertionResult
82         checkEntry(const ReferenceDataEntry &, const std::string &) const
83         {
84             return ::testing::AssertionSuccess();
85         }
86 };
87
88 class ExactStringChecker : public IReferenceDataEntryChecker
89 {
90     public:
91         explicit ExactStringChecker(const std::string &value)
92             : value_(value)
93         {
94         }
95
96         virtual void fillEntry(ReferenceDataEntry *entry) const
97         {
98             entry->setValue(value_);
99         }
100         virtual ::testing::AssertionResult
101         checkEntry(const ReferenceDataEntry &entry, const std::string &fullId) const
102         {
103             if (entry.value() == value_)
104             {
105                 return ::testing::AssertionSuccess();
106             }
107             return ::testing::AssertionFailure()
108                    << "  In item: " << fullId << std::endl
109                    << "   Actual: '" << value_ << "'" << std::endl
110                    << "Reference: '" << entry.value() << "'";
111         }
112
113     private:
114         std::string  value_;
115 };
116
117 class ExactStringBlockChecker : public IReferenceDataEntryChecker
118 {
119     public:
120         explicit ExactStringBlockChecker(const std::string &value)
121             : value_(value)
122         {
123         }
124
125         virtual void fillEntry(ReferenceDataEntry *entry) const
126         {
127             entry->setTextBlockValue(value_);
128         }
129         virtual ::testing::AssertionResult
130         checkEntry(const ReferenceDataEntry &entry, const std::string &fullId) const
131         {
132             if (entry.value() == value_)
133             {
134                 return ::testing::AssertionSuccess();
135             }
136             return ::testing::AssertionFailure()
137                    << "  In item: " << fullId << std::endl
138                    << "   Actual: '" << value_ << "'" << std::endl
139                    << "Reference: '" << entry.value() << "'";
140         }
141
142     private:
143         std::string  value_;
144 };
145
146 //! Helper function to parse a floating-point value.
147 // TODO: Move this into src/gromacs/utility/, and consolidate with similar code
148 // elsewhere.
149 double convertDouble(const std::string &value)
150 {
151     char   *endptr;
152     double  convertedValue = std::strtod(value.c_str(), &endptr);
153     // TODO: Check for overflow
154     if (*endptr != '\0')
155     {
156         GMX_THROW(InvalidInputError("Invalid floating-point value: " + value));
157     }
158     return convertedValue;
159 }
160
161 //! Helper function to parse a floating-point reference data value.
162 double convertDoubleReferenceValue(const std::string &value)
163 {
164     try
165     {
166         return convertDouble(value);
167     }
168     catch (const InvalidInputError &ex)
169     {
170         GMX_THROW_WRAPPER_TESTEXCEPTION(ex);
171     }
172 }
173
174 template <typename FloatType>
175 class FloatingPointChecker : public IReferenceDataEntryChecker
176 {
177     public:
178         FloatingPointChecker(FloatType value, const FloatingPointTolerance &tolerance)
179             : value_(value), tolerance_(tolerance)
180         {
181         }
182
183         virtual void fillEntry(ReferenceDataEntry *entry) const
184         {
185             const int prec = std::numeric_limits<FloatType>::digits10 + 2;
186             entry->setValue(formatString("%.*g", prec, value_));
187         }
188         virtual ::testing::AssertionResult
189         checkEntry(const ReferenceDataEntry &entry, const std::string &fullId) const
190         {
191             FloatType               refValue = static_cast<FloatType>(convertDoubleReferenceValue(entry.value()));
192             FloatingPointDifference diff(refValue, value_);
193             if (tolerance_.isWithin(diff))
194             {
195                 return ::testing::AssertionSuccess();
196             }
197             return ::testing::AssertionFailure()
198                    << "   In item: " << fullId << std::endl
199                    << "    Actual: " << value_ << std::endl
200                    << " Reference: " << refValue << std::endl
201                    << "Difference: " << diff.toString() << std::endl
202                    << " Tolerance: " << tolerance_.toString(diff);
203         }
204
205     private:
206         FloatType               value_;
207         FloatingPointTolerance  tolerance_;
208 };
209
210 template <typename FloatType>
211 class FloatingPointFromStringChecker : public IReferenceDataEntryChecker
212 {
213     public:
214         FloatingPointFromStringChecker(
215             const std::string &value, const FloatingPointTolerance &tolerance)
216             : value_(value), tolerance_(tolerance)
217         {
218         }
219
220         virtual void fillEntry(ReferenceDataEntry *entry) const
221         {
222             entry->setValue(value_);
223         }
224         virtual ::testing::AssertionResult
225         checkEntry(const ReferenceDataEntry &entry, const std::string &fullId) const
226         {
227             FloatType               value    = static_cast<FloatType>(convertDouble(value_));
228             FloatType               refValue = static_cast<FloatType>(convertDoubleReferenceValue(entry.value()));
229             FloatingPointDifference diff(refValue, value);
230             if (tolerance_.isWithin(diff))
231             {
232                 return ::testing::AssertionSuccess();
233             }
234             return ::testing::AssertionFailure()
235                    << "   In item: " << fullId << std::endl
236                    << "    Actual: " << value << std::endl
237                    << " Reference: " << entry.value() << std::endl
238                    << "Difference: " << diff.toString() << std::endl
239                    << " Tolerance: " << tolerance_.toString(diff);
240         }
241
242     private:
243         std::string             value_;
244         FloatingPointTolerance  tolerance_;
245 };
246
247 } // namespace test
248 } // namespace gmx
249
250 #endif