ca933a5a82d03b1344af645cd747b8be3bf6b35c
[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/strconvert.h"
56 #include "gromacs/utility/stringutil.h"
57
58 #include "testutils/refdata-impl.h"
59 #include "testutils/testasserts.h"
60 #include "testutils/testexceptions.h"
61
62 namespace gmx
63 {
64 namespace test
65 {
66
67 class IReferenceDataEntryChecker
68 {
69     public:
70         virtual void fillEntry(ReferenceDataEntry *entry) const = 0;
71         virtual ::testing::AssertionResult
72         checkEntry(const ReferenceDataEntry &entry, const std::string &fullId) const = 0;
73
74     protected:
75         virtual ~IReferenceDataEntryChecker() {}
76 };
77
78 class NullChecker : public IReferenceDataEntryChecker
79 {
80     public:
81         virtual void fillEntry(ReferenceDataEntry *) const {}
82         virtual ::testing::AssertionResult
83         checkEntry(const ReferenceDataEntry &, const std::string &) const
84         {
85             return ::testing::AssertionSuccess();
86         }
87 };
88
89 class ExactStringChecker : public IReferenceDataEntryChecker
90 {
91     public:
92         explicit ExactStringChecker(const std::string &value)
93             : value_(value)
94         {
95         }
96
97         virtual void fillEntry(ReferenceDataEntry *entry) const
98         {
99             entry->setValue(value_);
100         }
101         virtual ::testing::AssertionResult
102         checkEntry(const ReferenceDataEntry &entry, const std::string &fullId) const
103         {
104             if (entry.value() == value_)
105             {
106                 return ::testing::AssertionSuccess();
107             }
108             return ::testing::AssertionFailure()
109                    << "  In item: " << fullId << std::endl
110                    << "   Actual: '" << value_ << "'" << std::endl
111                    << "Reference: '" << entry.value() << "'";
112         }
113
114     private:
115         std::string  value_;
116 };
117
118 class ExactStringBlockChecker : public IReferenceDataEntryChecker
119 {
120     public:
121         explicit ExactStringBlockChecker(const std::string &value)
122             : value_(value)
123         {
124         }
125
126         virtual void fillEntry(ReferenceDataEntry *entry) const
127         {
128             entry->setTextBlockValue(value_);
129         }
130         virtual ::testing::AssertionResult
131         checkEntry(const ReferenceDataEntry &entry, const std::string &fullId) const
132         {
133             if (entry.value() == value_)
134             {
135                 return ::testing::AssertionSuccess();
136             }
137             return ::testing::AssertionFailure()
138                    << "  In item: " << fullId << std::endl
139                    << "   Actual: '" << value_ << "'" << std::endl
140                    << "Reference: '" << entry.value() << "'";
141         }
142
143     private:
144         std::string  value_;
145 };
146
147
148 //! Helper function to parse a floating-point reference data value.
149 double convertDoubleReferenceValue(const std::string &value)
150 {
151     try
152     {
153         return fromString<double>(value);
154     }
155     catch (const InvalidInputError &ex)
156     {
157         GMX_THROW_WRAPPER_TESTEXCEPTION(ex);
158     }
159 }
160
161 template <typename FloatType>
162 class FloatingPointChecker : public IReferenceDataEntryChecker
163 {
164     public:
165         FloatingPointChecker(FloatType value, const FloatingPointTolerance &tolerance)
166             : value_(value), tolerance_(tolerance)
167         {
168         }
169
170         virtual void fillEntry(ReferenceDataEntry *entry) const
171         {
172             const int prec = std::numeric_limits<FloatType>::digits10 + 2;
173             entry->setValue(formatString("%.*g", prec, value_));
174         }
175         virtual ::testing::AssertionResult
176         checkEntry(const ReferenceDataEntry &entry, const std::string &fullId) const
177         {
178             FloatType               refValue = static_cast<FloatType>(convertDoubleReferenceValue(entry.value()));
179             FloatingPointDifference diff(refValue, value_);
180             if (tolerance_.isWithin(diff))
181             {
182                 return ::testing::AssertionSuccess();
183             }
184             return ::testing::AssertionFailure()
185                    << "   In item: " << fullId << std::endl
186                    << "    Actual: " << value_ << std::endl
187                    << " Reference: " << refValue << std::endl
188                    << "Difference: " << diff.toString() << std::endl
189                    << " Tolerance: " << tolerance_.toString(diff);
190         }
191
192     private:
193         FloatType               value_;
194         FloatingPointTolerance  tolerance_;
195 };
196
197 template <typename FloatType>
198 class FloatingPointFromStringChecker : public IReferenceDataEntryChecker
199 {
200     public:
201         FloatingPointFromStringChecker(
202             const std::string &value, const FloatingPointTolerance &tolerance)
203             : value_(value), tolerance_(tolerance)
204         {
205         }
206
207         virtual void fillEntry(ReferenceDataEntry *entry) const
208         {
209             entry->setValue(value_);
210         }
211         virtual ::testing::AssertionResult
212         checkEntry(const ReferenceDataEntry &entry, const std::string &fullId) const
213         {
214             FloatType               value    = fromString<FloatType>(value_);
215             FloatType               refValue = static_cast<FloatType>(convertDoubleReferenceValue(entry.value()));
216             FloatingPointDifference diff(refValue, value);
217             if (tolerance_.isWithin(diff))
218             {
219                 return ::testing::AssertionSuccess();
220             }
221             return ::testing::AssertionFailure()
222                    << "   In item: " << fullId << std::endl
223                    << "    Actual: " << value << std::endl
224                    << " Reference: " << entry.value() << std::endl
225                    << "Difference: " << diff.toString() << std::endl
226                    << " Tolerance: " << tolerance_.toString(diff);
227         }
228
229     private:
230         std::string             value_;
231         FloatingPointTolerance  tolerance_;
232 };
233
234 template <typename ValueType>
235 class ValueExtractor : public IReferenceDataEntryChecker
236 {
237     public:
238         explicit ValueExtractor(ValueType *value)
239             : value_(value)
240         {
241         }
242
243         virtual void fillEntry(ReferenceDataEntry *) const
244         {
245             GMX_THROW(TestException("Extracting value from non-existent reference data entry"));
246         }
247         virtual ::testing::AssertionResult
248         checkEntry(const ReferenceDataEntry &entry, const std::string &) const
249         {
250             extractValue(entry.value());
251             return ::testing::AssertionSuccess();
252         }
253
254         void extractValue(const std::string &value) const
255         {
256             *value_ = fromString<ValueType>(value);
257         }
258
259     private:
260         ValueType *value_;
261 };
262
263 template <> inline void
264 ValueExtractor<std::string>::extractValue(const std::string &value) const
265 {
266     *value_ = value;
267 }
268
269 } // namespace test
270 } // namespace gmx
271
272 #endif