HART  0.2.0
High level Audio Regression and Testing
Loading...
Searching...
No Matches
hart_condition.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cmath> // isnan()
4#include <ostream>
5#include <sstream>
6#include <string>
7#include <type_traits>
8#include <utility>
9
11#include "hart_utils.hpp" // addCents()
12
13namespace hart
14{
15
16/// @brief A class representing some condition
17/// @details This condition id intended to be used by matcher function or a free-standing assertion macro.
18/// To instantiate, use macros like `HART_EQUAL()` or `HART_FLOAT_IN_RANGE()` - see @ref Conditions.
19/// @ingroup Conditions
21{
22public:
23 /// @private
24 Condition (bool result):
25 Condition (
26 result,
27 "Condition (<bool>)", // tokenRepresentation
28 "Condition (" + toString (result) + ')', // stringRepresentation
29 "", // file
30 -1, // line
31 false // hasDetailedMetadata
32 )
33 {
34 }
35
36 /// @private
37 Condition():
38 Condition (
39 false, // result
40 "Condition()", // tokenRepresentation
41 "Condition()", // stringRepresentation
42 "", // file
43 -1, // line
44 false // hasDetailedMetadata
45 )
46 {
47 }
48
49 /// @private
50 bool getResult() const
51 {
52 return m_result;
53 }
54
55 /// @private
56 const char* getFile() const
57 {
58 return m_file;
59 }
60
61 /// @private
62 int getLine() const
63 {
64 return m_line;
65 }
66
67 /// @brief Returns whether it has rich metadata that's worth displaying on failure.
68 /// @details
69 /// Conditions created via proper macros have some useful diagnostic metadata
70 /// like token representation, or file name and line number where it was defined.
71 /// Default-constructed Conditions and Conditions created from basic `bool` value
72 /// do not have it.
73 /// @private
74 bool hasDetailedMetadata() const
75 {
76 return m_hasDetailedMetadata;
77 }
78
79 /// @private
80 void representWithTokens (std::ostream& stream) const
81 {
82 stream << m_tokenRepresentation;
83 }
84
85 /// @private
86 void representWithStringRepresentations (std::ostream& stream) const
87 {
88 stream << m_stringRepresentation;
89 }
90
91 /// @TODO: Enforce explicit booleans here?
92 /// @private
93 template <typename ValueType>
94 static Condition truth (ValueType&& value, const char* valueTokens, const char* file, int line)
95 {
96 std::ostringstream tokenRepresentationStream;
97 std::ostringstream stringRepresentationStream;
98 tokenRepresentationStream << "HART_TRUE (" << valueTokens << ")";
99 stringRepresentationStream << "HART_TRUE (" << hart::toString (value) << ")";
100 return Condition (static_cast<bool> (value), tokenRepresentationStream.str(), stringRepresentationStream.str(), file, line);
101 }
102
103 /// @TODO: Enforce explicit booleans here?
104 /// @private
105 template <typename ValueType>
106 static Condition falsehood (ValueType&& value, const char* valueTokens, const char* file, int line)
107 {
108 std::ostringstream tokenRepresentationStream;
109 std::ostringstream stringRepresentationStream;
110 tokenRepresentationStream << "HART_FALSE (" << valueTokens << ")";
111 stringRepresentationStream << "HART_FALSE (" << hart::toString (value) << ")";
112 return Condition (! static_cast<bool> (value), tokenRepresentationStream.str(), stringRepresentationStream.str(), file, line);
113 }
114
115 /// @TODO: Do a warning if used with floats?
116 /// @private
117 template <typename LHSType, typename RHSType>
118 static Condition equals (LHSType&& lhs, RHSType&& rhs, const char* lhsTokens, const char* rhsTokens, const char* file, int line)
119 {
120 std::ostringstream tokenRepresentationStream;
121 std::ostringstream stringRepresentationStream;
122 tokenRepresentationStream << "HART_EQUAL (" << lhsTokens << ", " << rhsTokens << ")";
123 stringRepresentationStream << "HART_EQUAL (" << hart::toString (lhs) << ", " << hart::toString (rhs) << ")";
124 return Condition (lhs == rhs, tokenRepresentationStream.str(), stringRepresentationStream.str(), file, line);
125 }
126
127 /// @TODO: Do a warning if used with floats?
128 /// @private
129 template <typename LHSType, typename RHSType>
130 static Condition notEquals (LHSType&& lhs, RHSType&& rhs, const char* lhsTokens, const char* rhsTokens, const char* file, int line)
131 {
132 std::ostringstream tokenRepresentationStream;
133 std::ostringstream stringRepresentationStream;
134 tokenRepresentationStream << "HART_NOT_EQUAL (" << lhsTokens << ", " << rhsTokens << ")";
135 stringRepresentationStream << "HART_NOT_EQUAL (" << hart::toString (lhs) << ", " << hart::toString (rhs) << ")";
136 return Condition (lhs != rhs, tokenRepresentationStream.str(), stringRepresentationStream.str(), file, line);
137 }
138
139 /// @private
140 template <typename FloatType>
141 static typename std::enable_if<std::is_floating_point<FloatType>::value, Condition>::type
142 floatEqual (
143 FloatType lhs,
144 FloatType rhs,
145 FloatType tolerance,
146 const char* lhsTokens,
147 const char* rhsTokens,
148 const char* toleranceTokens,
149 const char* file,
150 int line
151 )
152 {
153 std::ostringstream tokenRepresentationStream;
154 std::ostringstream stringRepresentationStream;
155 tokenRepresentationStream << "HART_FLOAT_EQUAL (" << lhsTokens << ", " << rhsTokens << ", " << toleranceTokens << ")";
156 stringRepresentationStream << "HART_FLOAT_EQUAL (" << hart::toString (lhs) << ", " << hart::toString (rhs) << ", " << hart::toString (tolerance) << ")";
157 return Condition (
158 lhs - tolerance <= rhs && rhs <= lhs + tolerance,
159 tokenRepresentationStream.str(),
160 stringRepresentationStream.str(),
161 file,
162 line
163 );
164 }
165
166 /// @private
167 template <typename FloatType>
168 static typename std::enable_if<std::is_floating_point<FloatType>::value, Condition>::type
169 floatNotEqual (
170 FloatType lhs,
171 FloatType rhs,
172 FloatType tolerance,
173 const char* lhsTokens,
174 const char* rhsTokens,
175 const char* toleranceTokens,
176 const char* file,
177 int line
178 )
179 {
180 std::ostringstream tokenRepresentationStream;
181 std::ostringstream stringRepresentationStream;
182 tokenRepresentationStream << "HART_FLOAT_NOT_EQUAL (" << lhsTokens << ", " << rhsTokens << ", " << toleranceTokens << ")";
183 stringRepresentationStream << "HART_FLOAT_NOT_EQUAL (" << hart::toString (lhs) << ", " << hart::toString (rhs) << ", " << hart::toString (tolerance) << ")";
184 return Condition (
185 lhs - tolerance > rhs || rhs > lhs + tolerance,
186 tokenRepresentationStream.str(),
187 stringRepresentationStream.str(),
188 file,
189 line
190 );
191 }
192
193 ///@private
194 static Condition
195 frequenciesEqual (
196 double observedFrequencyHz,
197 double expectedFrequencyHz,
198 double toleranceCents,
199 const char* observedFrequencyTokens,
200 const char* expectedFrequencyTokens,
201 const char* toleranceTokens,
202 const char* file,
203 int line
204 )
205 {
206 std::ostringstream tokenRepresentationStream;
207 std::ostringstream stringRepresentationStream;
208 tokenRepresentationStream << "HART_FREQUENCIES_EQUAL (" << observedFrequencyTokens << ", " << expectedFrequencyTokens << ", " << toleranceTokens << ')';
209 stringRepresentationStream << "HART_FREQUENCIES_EQUAL (" << hart::toString (observedFrequencyHz) << ", " << hart::toString (expectedFrequencyHz) << ", " << hart::toString (toleranceCents) << ')';
210 return Condition (
211 (observedFrequencyHz >= addCents (expectedFrequencyHz, -toleranceCents)) && (observedFrequencyHz <= addCents (expectedFrequencyHz, toleranceCents)),
212 tokenRepresentationStream.str(),
213 stringRepresentationStream.str(),
214 file,
215 line
216 );
217 }
218
219 ///@private
220 static Condition
221 frequenciesNotEqual (
222 double observedFrequencyHz,
223 double expectedFrequencyHz,
224 double toleranceCents,
225 const char* observedFrequencyTokens,
226 const char* expectedFrequencyTokens,
227 const char* toleranceTokens,
228 const char* file,
229 int line
230 )
231 {
232 std::ostringstream tokenRepresentationStream;
233 std::ostringstream stringRepresentationStream;
234 tokenRepresentationStream << "HART_FREQUENCIES_NOT_EQUAL (" << observedFrequencyTokens << ", " << expectedFrequencyTokens << ", " << toleranceTokens << ')';
235 stringRepresentationStream << "HART_FREQUENCIES_NOT_EQUAL (" << hart::toString (observedFrequencyHz) << ", " << hart::toString (expectedFrequencyHz) << ", " << hart::toString (toleranceCents) << ')';
236 return Condition (
237 ! ((observedFrequencyHz >= addCents (expectedFrequencyHz, -toleranceCents)) && (observedFrequencyHz <= addCents (expectedFrequencyHz, toleranceCents))),
238 tokenRepresentationStream.str(),
239 stringRepresentationStream.str(),
240 file,
241 line
242 );
243 }
244
245 /// @private
246 template <typename LHSType, typename RHSType>
247 static Condition greaterThan (LHSType&& lhs, RHSType&& rhs, const char* lhsTokens, const char* rhsTokens, const char* file, int line)
248 {
249 std::ostringstream tokenRepresentationStream;
250 std::ostringstream stringRepresentationStream;
251 tokenRepresentationStream << "HART_GREATER_THAN (" << lhsTokens << ", " << rhsTokens << ")";
252 stringRepresentationStream << "HART_GREATER_THAN (" << hart::toString (lhs) << ", " << hart::toString (rhs) << ")";
253 return Condition (lhs > rhs, tokenRepresentationStream.str(), stringRepresentationStream.str(), file, line);
254 }
255
256 /// @private
257 template <typename LHSType, typename RHSType>
258 static Condition greaterOrEqual (LHSType&& lhs, RHSType&& rhs, const char* lhsTokens, const char* rhsTokens, const char* file, int line)
259 {
260 std::ostringstream tokenRepresentationStream;
261 std::ostringstream stringRepresentationStream;
262 tokenRepresentationStream << "HART_GREATER_OR_EQUAL (" << lhsTokens << ", " << rhsTokens << ")";
263 stringRepresentationStream << "HART_GREATER_OR_EQUAL (" << hart::toString (lhs) << ", " << hart::toString (rhs) << ")";
264 return Condition (lhs >= rhs, tokenRepresentationStream.str(), stringRepresentationStream.str(), file, line);
265 }
266
267 /// @private
268 template <typename LHSType, typename RHSType>
269 static Condition lessThan (LHSType&& lhs, RHSType&& rhs, const char* lhsTokens, const char* rhsTokens, const char* file, int line)
270 {
271 std::ostringstream tokenRepresentationStream;
272 std::ostringstream stringRepresentationStream;
273 tokenRepresentationStream << "HART_LESS_THAN (" << lhsTokens << ", " << rhsTokens << ")";
274 stringRepresentationStream << "HART_LESS_THAN (" << hart::toString (lhs) << ", " << hart::toString (rhs) << ")";
275 return Condition (lhs < rhs, tokenRepresentationStream.str(), stringRepresentationStream.str(), file, line);
276 }
277
278 /// @private
279 template <typename LHSType, typename RHSType>
280 static Condition lessOrEqual (LHSType&& lhs, RHSType&& rhs, const char* lhsTokens, const char* rhsTokens, const char* file, int line)
281 {
282 std::ostringstream tokenRepresentationStream;
283 std::ostringstream stringRepresentationStream;
284 tokenRepresentationStream << "HART_LESS_OR_EQUAL (" << lhsTokens << ", " << rhsTokens << ")";
285 stringRepresentationStream << "HART_LESS_OR_EQUAL (" << hart::toString (lhs) << ", " << hart::toString (rhs) << ")";
286 return Condition (lhs <= rhs, tokenRepresentationStream.str(), stringRepresentationStream.str(), file, line);
287 }
288
289 /// @private
290 template <typename ValueType, typename BoundType>
291 static Condition inRange (
292 ValueType&& value,
293 BoundType&& minValue,
294 BoundType&& maxValue,
295 const char* valueTokens,
296 const char* minValueTokens,
297 const char* maxValueTokens,
298 const char* file,
299 int line
300 )
301 {
302 std::ostringstream tokenRepresentationStream;
303 std::ostringstream stringRepresentationStream;
304 tokenRepresentationStream << "HART_IN_RANGE (" << valueTokens << ", " << minValueTokens << ", " << maxValueTokens << ")";
305 stringRepresentationStream << "HART_IN_RANGE (" << hart::toString (value) << ", " << hart::toString (minValue) << ", " << hart::toString (maxValue) << ")";
306 return Condition (minValue <= value && value <= maxValue, tokenRepresentationStream.str(), stringRepresentationStream.str(), file, line);
307 }
308
309 /// @private
310 template <typename FloatType>
311 static typename std::enable_if<std::is_floating_point<FloatType>::value, Condition>::type
312 floatInRange (
313 FloatType value,
314 FloatType minValue,
315 FloatType maxValue,
316 FloatType tolerance,
317 const char* valueTokens,
318 const char* minValueTokens,
319 const char* maxValueTokens,
320 const char* toleranceTokens,
321 const char* file,
322 int line
323 )
324 {
325 std::ostringstream tokenRepresentationStream;
326 std::ostringstream stringRepresentationStream;
327 tokenRepresentationStream << "HART_FLOAT_IN_RANGE (" << valueTokens << ", " << minValueTokens << ", " << maxValueTokens << ", " << toleranceTokens << ")";
328 stringRepresentationStream << "HART_FLOAT_IN_RANGE (" << hart::toString (value) << ", " << hart::toString (minValue) << ", " << hart::toString (maxValue) << ", " << hart::toString (tolerance) << ")";
329 return Condition (
330 minValue - tolerance <= value && value <= maxValue + tolerance,
331 tokenRepresentationStream.str(),
332 stringRepresentationStream.str(),
333 file,
334 line
335 );
336 }
337
338 /// @private
339 template <typename FloatType>
340 static typename std::enable_if<std::is_floating_point<FloatType>::value, Condition>::type
341 isNaN (
342 FloatType value,
343 const char* valueTokens,
344 const char* file,
345 int line
346 )
347 {
348 std::ostringstream tokenRepresentationStream;
349 std::ostringstream stringRepresentationStream;
350 tokenRepresentationStream << "HART_IS_NAN (" << valueTokens << ')';
351 stringRepresentationStream << "HART_IS_NAN (" << hart::toString (value) << ')';
352 return Condition (
353 std::isnan (value),
354 tokenRepresentationStream.str(),
355 stringRepresentationStream.str(),
356 file,
357 line
358 );
359 }
360
361 /// @private
362 template <typename FloatType>
363 static typename std::enable_if<std::is_floating_point<FloatType>::value, Condition>::type
364 notNaN (
365 FloatType value,
366 const char* valueTokens,
367 const char* file,
368 int line
369 )
370 {
371 std::ostringstream tokenRepresentationStream;
372 std::ostringstream stringRepresentationStream;
373 tokenRepresentationStream << "HART_NOT_NAN (" << valueTokens << ')';
374 stringRepresentationStream << "HART_NOT_NAN (" << hart::toString (value) << ')';
375 return Condition (
376 ! std::isnan (value),
377 tokenRepresentationStream.str(),
378 stringRepresentationStream.str(),
379 file,
380 line
381 );
382 }
383
384private:
385 Condition (bool result, std::string tokenRepresentation, std::string stringRepresentation, const char* file, int line, bool hasDetailedMetadata = true) :
386 m_result (result),
387 m_hasDetailedMetadata (hasDetailedMetadata),
388 m_tokenRepresentation (std::move (tokenRepresentation)),
389 m_stringRepresentation (std::move (stringRepresentation)),
390 m_file (file),
391 m_line (line)
392 {
393 }
394
395 bool m_result;
396 bool m_hasDetailedMetadata;
397 std::string m_tokenRepresentation;
398 std::string m_stringRepresentation;
399 const char* m_file;
400 int m_line;
401};
402
403} // namespace hart
A class representing some condition.
double addCents(double baseFrequencyHz, double cents)
Anns an offset in cents to a frequency in Hz.