Ocean
ValidationPrecision.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  */
7 
8 #ifndef META_OCEAN_TEST_VALIDATION_PRECISION_H
9 #define META_OCEAN_TEST_VALIDATION_PRECISION_H
10 
11 #include "ocean/test/Test.h"
12 #include "ocean/test/Validation.h"
13 
15 
16 namespace Ocean
17 {
18 
19 namespace Test
20 {
21 
22 /**
23  * This class implements a helper class to validate the accuracy of tests.
24  * @see Validation.
25  * @ingroup test
26  */
28 {
29  public:
30 
31  /**
32  * Creates a new scoped iteration.
33  * By default the iteration is accurate enough.<br>
34  * Call setInaccurate() for an iteration which is not precise enough.
35  */
37  {
38  public:
39 
40  /**
41  * Creates a new scoped iteration associated with a validation object.
42  */
44 
45  /**
46  * Destructs this scoped object and forwards the information to the owning validation.
47  */
48  inline ~ScopedIteration();
49 
50  /**
51  * Sets this iteration to be not precise enough.
52  */
53  inline void setInaccurate();
54 
55  /**
56  * Sets this iteration to be not precise enough.
57  * This function will also write a message to the error log.
58  * @param expected The expected value
59  * @param actual The actual value
60  * @param file The source file in which the function call happens, e.g., __FILE__, must be valid
61  * @param line The line in the source file in which the function call happens, e.g., __LINE__, must be valid
62  * @tparam T The data type of the expected and actual value
63  */
64  template <typename T>
65  inline void setInaccurate(const T& expected, const T& actual, const char* file, const int line);
66 
67  protected:
68 
69  /**
70  * Disabled default constructor.
71  */
72  ScopedIteration() = delete;
73 
74  /**
75  * Disabled copy constructor.
76  */
77  ScopedIteration(const ScopedIteration&) = delete;
78 
79  /**
80  * Disabled move constructor.
81  */
82  ScopedIteration(const ScopedIteration&&) = delete;
83 
84  protected:
85 
86  /// True, if the iteration is accurate; False, if the iteration was not accurate enough.
87  bool accurate_ = true;
88 
89  /// The owner of this scoped object.
91  };
92 
93  public:
94 
95  /**
96  * Creates a new precision-based validation object with specified threshold.
97  * @param threshold The necessary percent of accurate iterations necessary for a successful verification, with range (0, 1]
98  * @param minimumIterations The minimum number of iterations necessary for a successful verification, with range [1, infinity)
99  */
100  explicit inline ValidationPrecision(const double threshold, const unsigned int minimumIterations = 1u);
101 
102  /**
103  * Creates a new validation object associated with a random generator, by default the verified has succeeded.
104  * @param threshold The necessary percent of accurate iterations necessary for a successful verification, with range (0, 1]
105  * @param randomGenerator The random generator which will be used during verification
106  */
107  inline ValidationPrecision(const double threshold, RandomGenerator& randomGenerator, const unsigned int minimumIterations = 1u);
108 
109  /**
110  * Destructs this validation object.
111  */
112  inline ~ValidationPrecision();
113 
114  /**
115  * Explicitly adds a new iteration which is either accurate or not.
116  * @param accurate True, if the iteration was precise enough; False, if the iteration was not precise enough
117  * @see ScopedIteration.
118  */
119  inline void addIteration(const bool accurate);
120 
121  /**
122  * Explicitly adds new iterations for which the amount of accurate iterations is known.
123  * @param accurateIterations The number of accurate iterations, with range [0, iterations]
124  * @param iterations The number of new iterations, with range [1, infinity)
125  * @see ScopedIteration.
126  */
127  inline void addIterations(const size_t accurateIterations, const size_t iterations);
128 
129  /**
130  * Explicitly sets the validation to be failed.
131  * Setting this validation to be failed will result in a failed validation even if all iteration were precise enough.
132  * @see succeeded().
133  */
134  inline void setFailed();
135 
136  /**
137  * Explicitly sets the validation to be failed.
138  * Setting this validation to be failed will result in a failed validation even if all iteration were precise enough.
139  * @param file The source file in which the function call happens, e.g., __FILE__, must be valid
140  * @param line The line in the source file in which the function call happens, e.g., __LINE__, must be valid
141  * @see succeeded().
142  */
143  inline void setFailed(const char* file, const int line);
144 
145  /**
146  * Returns the number of iterations in which the precision has been determined.
147  * @return The number of iterations, with range [0, infinity)
148  */
149  inline uint64_t iterations() const;
150 
151  /**
152  * Returns the necessary iterations to allow determining success or failure based on the specified success threshold.
153  * @return The number of necessary iterations, with range [1, infinity)
154  */
155  inline uint64_t necessaryIterations() const;
156 
157  /**
158  * Returns whether the number of iterations is not yet sufficient to determine a success or failure.
159  * @return True, if more iterations are required
160  */
161  inline bool needMoreIterations() const;
162 
163  /**
164  * Returns if this validation has succeeded.
165  * @return True, if so; False, if the validation has failed
166  */
167  [[nodiscard]] inline bool succeeded() const;
168 
169  /**
170  * Returns the accuracy of all iterations.
171  * Beware: Ensure that at least one iteration has been added.
172  * @return The validation's accuracy, in percent, with range [0, 1]
173  */
174  [[nodiscard]] inline double accuracy() const;
175 
176  /**
177  * Returns the defined threshold.
178  * @return The validation object's precision threshold, with range [0, 1]
179  */
180  inline double threshold() const;
181 
182  /**
183  * Returns whether this validation object has been set to failed explicitly.
184  * This function is intended for internal use only, use succeeded() instead.
185  * @return True, if so
186  * @see succeeded().
187  */
188  [[nodiscard]] inline bool hasSetFailed() const;
189 
190  /**
191  * Returns a string containing the random generator's initial seed, if any
192  * @return The string with initial seed test, empty if no random generator is associated with this validation object
193  */
194  inline std::string randomGeneratorOutput() const;
195 
196  protected:
197 
198  /**
199  * Disabled copy constructor.
200  */
202 
203  /**
204  * Sets the succeeded state to false.
205  */
206  inline void setSucceededFalse();
207 
208  protected:
209 
210  /// The necessary percent of accurate iterations necessary for a successful verification, with range (0, 1]
211  double threshold_ = 1.0;
212 
213  /// True, if the validation has succeeded; False, if the validation has failed.
214  bool succeeded_ = true;
215 
216  /// The number of iterations needed to determine success or failure.
217  uint64_t necessaryIterations_ = 0ull;
218 
219  /// The overall number of iterations which have been added.
220  uint64_t iterations_ = 0ull;
221 
222  /// The number of iterations which were precise enough, with range [0, iterations_]
223  uint64_t accurateIterations_ = 0ull;
224 
225  /// Optional random generator object which will be used during validation.
227 
228 #ifdef OCEAN_DEBUG
229  /// True, if the success state of this validation has been checked.
230  mutable bool succeededChecked_ = false;
231 #endif // OCEAN_DEBUG
232 };
233 
234 #ifndef OCEAN_SET_INACCURATE
235  #define OCEAN_SET_INACCURATE(scopedIteration, expected, actual) scopedIteration.setInaccurate(expected, actual, __FILE__, __LINE__);
236 #endif
237 
239  validationPrecision_(ValidationPrecision)
240 {
241  // nothing to do here
242 }
243 
245 {
246  validationPrecision_.addIteration(accurate_);
247 }
248 
250 {
251  accurate_ = false;
252 }
253 
254 template <typename T>
255 inline void ValidationPrecision::ScopedIteration::setInaccurate(const T& expected, const T& actual, const char* file, const int line)
256 {
257  accurate_ = false;
258 
259  ocean_assert(file != nullptr);
260  if (file != nullptr)
261  {
262  Log::error() << "ScopedIteration::setInaccurate() in '" << file << "', in line " << line << ": expected " << expected << ", got " << actual << validationPrecision_.randomGeneratorOutput();
263  }
264 }
265 
266 inline ValidationPrecision::ValidationPrecision(const double threshold, const unsigned int minimumIterations)
267 {
268  ocean_assert(threshold > 0.0 && threshold <= 1.0);
269 
270  if (threshold > 0.0 && threshold <= 1.0)
271  {
273 
274  const double failureRate = 1.0 - threshold_;
275  ocean_assert(failureRate > 0.0);
276 
277  const double idealIterations = 1.0 / failureRate;
278 
279  ocean_assert(idealIterations <= 1000000000u);
280  necessaryIterations_ = std::max(minimumIterations, (unsigned int)(std::ceil(idealIterations) * 2.0));
281  }
282  else
283  {
285  }
286 }
287 
288 inline ValidationPrecision::ValidationPrecision(const double threshold, RandomGenerator& randomGenerator, const unsigned int minimumIterations) :
289  ValidationPrecision(threshold, minimumIterations)
290 {
291  randomGenerator_ = &randomGenerator;
292 }
293 
295 {
296 #ifdef OCEAN_DEBUG
297  ocean_assert(succeededChecked_ && "The verifier has not been check for success");
298 #endif
299 }
300 
301 inline void ValidationPrecision::addIteration(const bool accurate)
302 {
303  ++iterations_;
304 
305  if (accurate)
306  {
308  }
309 }
310 
311 inline void ValidationPrecision::addIterations(const size_t accurateIterations, const size_t iterations)
312 {
313  ocean_assert(accurateIterations <= iterations);
314  ocean_assert(iterations >= 1);
315 
316  if (accurateIterations > iterations)
317  {
319  }
320 
321  accurateIterations_ += accurateIterations;
323 }
324 
326 {
328 }
329 
330 inline void ValidationPrecision::setFailed(const char* file, const int line)
331 {
333 
334  ocean_assert(file != nullptr);
335  if (file != nullptr)
336  {
337 #ifdef OCEAN_USE_GTEST
338  std::cerr << "\nValidationPrecision::setFailed() in '" << file << "', in line " << line << randomGeneratorOutput() << "\n" << std::endl;
339 #else
340  Log::error() << "ValidationPrecision::setFailed() in '" << file << "', in line " << line << randomGeneratorOutput();
341 #endif
342  }
343 }
344 
345 inline uint64_t ValidationPrecision::iterations() const
346 {
347  return iterations_;
348 }
349 
351 {
352  ocean_assert(necessaryIterations_ >= 1ull);
353 
354  return necessaryIterations_;
355 }
356 
358 {
359  ocean_assert(necessaryIterations_ >= 1ull);
360 
362 }
363 
365 {
366 #ifdef OCEAN_DEBUG
367  succeededChecked_ = true;
368 #endif
369 
370  ocean_assert(threshold_ > 0.0 && threshold_ <= 1.0);
371 
372  if (threshold_ <= 0.0 || threshold_ > 1.0)
373  {
374  return false;
375  }
376 
377  const double percent = accuracy();
378 
379  if (percent < threshold_)
380  {
381 #ifdef OCEAN_USE_GTEST
382 
383  std::ostream& stream = std::cerr << "\nFAILED with only " << String::toAString(accuracy() * 100.0, 1u) << "%, threshold is " << String::toAString(threshold() * 100.0, 1u) << "%" << randomGeneratorOutput() << "\n";
384 
385  if (needMoreIterations())
386  {
387  stream << "Not enough iterations for the specified success threshold (executed " << iterations_ << " of " << necessaryIterations_ << " necessary iterations)\n";
388  }
389 
390  stream << std::endl;
391 #endif
392  return false;
393  }
394 
395  return succeeded_;
396 }
397 
398 [[nodiscard]] inline double ValidationPrecision::accuracy() const
399 {
400  ocean_assert(iterations_ != 0ull);
401 
402  if (iterations_ == 0u)
403  {
404  return -1.0;
405  }
406 
407  ocean_assert(accurateIterations_ <= iterations_);
408 
409  const double percent = double(accurateIterations_) / double(iterations_);
410  ocean_assert(percent >= 0.0 && percent <= 1.0);
411 
412  return percent;
413 }
414 
415 inline double ValidationPrecision::threshold() const
416 {
417  return threshold_;
418 }
419 
420 [[nodiscard]] inline bool ValidationPrecision::hasSetFailed() const
421 {
422  return succeeded_ == false;
423 }
424 
426 {
427  if (randomGenerator_ != nullptr)
428  {
429  return ", with random generator initial seed '" + String::toAString(randomGenerator_->initialSeed()) + "'";
430  }
431 
432  return std::string();
433 }
434 
436 {
437  succeeded_ = false;
438 }
439 
440 inline std::ostream& operator<<(std::ostream& stream, const ValidationPrecision& validation)
441 {
442  if (validation.hasSetFailed())
443  {
444  stream << "FAILED!" << validation.randomGeneratorOutput();
445  }
446  else
447  {
448  if (validation.accuracy() < validation.threshold())
449  {
450  stream << "FAILED with only " << String::toAString(validation.accuracy() * 100.0, 1u) << "%, threshold is " << String::toAString(validation.threshold() * 100.0, 1u) << "%" << validation.randomGeneratorOutput();
451  }
452  else
453  {
454  stream << String::toAString(validation.accuracy() * 100.0, 1u) << "% succeeded.";
455  }
456  }
457 
458  return stream;
459 }
460 
461 template <bool tActive>
462 MessageObject<tActive>& operator<<(MessageObject<tActive>& messageObject, const ValidationPrecision& validation)
463 {
464  if (validation.hasSetFailed())
465  {
466  messageObject << "FAILED!" << validation.randomGeneratorOutput();
467  }
468  else
469  {
470  if (validation.accuracy() < validation.threshold())
471  {
472  messageObject << "FAILED with only " << String::toAString(validation.accuracy() * 100.0, 1u) << "%, threshold is " << String::toAString(validation.threshold() * 100.0, 1u) << "%" << validation.randomGeneratorOutput();
473  }
474  else
475  {
476  messageObject << String::toAString(validation.accuracy() * 100.0, 1u) << "% succeeded.";
477  }
478  }
479 
480  return messageObject;
481 }
482 
483 template <bool tActive>
484 MessageObject<tActive>& operator<<(MessageObject<tActive>&& messageObject, const ValidationPrecision& validation)
485 {
486  if (validation.hasSetFailed())
487  {
488  messageObject << "FAILED!" << validation.randomGeneratorOutput();
489  }
490  else
491  {
492  if (validation.accuracy() < validation.threshold())
493  {
494  messageObject << "FAILED with only " << String::toAString(validation.accuracy() * 100.0, 1u) << "%, threshold is " << String::toAString(validation.threshold() * 100.0, 1u) << "%" << validation.randomGeneratorOutput();
495  }
496  else
497  {
498  messageObject << String::toAString(validation.accuracy() * 100.0, 1u) << "% succeeded.";
499  }
500  }
501 
502  return messageObject;
503 }
504 
505 }
506 
507 }
508 
509 #endif // META_OCEAN_TEST_VALIDATION_PRECISION_H
static MessageObject error()
Returns the message for error messages.
Definition: Messenger.h:1074
Messenger object, one object for each message.
Definition: Messenger.h:427
This class implements a generator for random numbers.
Definition: RandomGenerator.h:42
unsigned int initialSeed() const
Returns the initial seed value which was used to initialize this random generator.
Definition: RandomGenerator.h:178
static std::string toAString(const char value)
Converts a value to a string with 8bit character.
Creates a new scoped iteration.
Definition: ValidationPrecision.h:37
ScopedIteration(const ScopedIteration &&)=delete
Disabled move constructor.
void setInaccurate()
Sets this iteration to be not precise enough.
Definition: ValidationPrecision.h:249
ValidationPrecision & validationPrecision_
The owner of this scoped object.
Definition: ValidationPrecision.h:90
ScopedIteration(const ScopedIteration &)=delete
Disabled copy constructor.
bool accurate_
True, if the iteration is accurate; False, if the iteration was not accurate enough.
Definition: ValidationPrecision.h:87
~ScopedIteration()
Destructs this scoped object and forwards the information to the owning validation.
Definition: ValidationPrecision.h:244
ScopedIteration()=delete
Disabled default constructor.
This class implements a helper class to validate the accuracy of tests.
Definition: ValidationPrecision.h:28
double threshold() const
Returns the defined threshold.
Definition: ValidationPrecision.h:415
void addIterations(const size_t accurateIterations, const size_t iterations)
Explicitly adds new iterations for which the amount of accurate iterations is known.
Definition: ValidationPrecision.h:311
uint64_t iterations_
The overall number of iterations which have been added.
Definition: ValidationPrecision.h:220
ValidationPrecision(const double threshold, const unsigned int minimumIterations=1u)
Creates a new precision-based validation object with specified threshold.
Definition: ValidationPrecision.h:266
RandomGenerator * randomGenerator_
Optional random generator object which will be used during validation.
Definition: ValidationPrecision.h:226
uint64_t accurateIterations_
The number of iterations which were precise enough, with range [0, iterations_].
Definition: ValidationPrecision.h:223
std::string randomGeneratorOutput() const
Returns a string containing the random generator's initial seed, if any.
Definition: ValidationPrecision.h:425
bool needMoreIterations() const
Returns whether the number of iterations is not yet sufficient to determine a success or failure.
Definition: ValidationPrecision.h:357
bool succeeded_
True, if the validation has succeeded; False, if the validation has failed.
Definition: ValidationPrecision.h:214
double accuracy() const
Returns the accuracy of all iterations.
Definition: ValidationPrecision.h:398
void setSucceededFalse()
Sets the succeeded state to false.
Definition: ValidationPrecision.h:435
void addIteration(const bool accurate)
Explicitly adds a new iteration which is either accurate or not.
Definition: ValidationPrecision.h:301
~ValidationPrecision()
Destructs this validation object.
Definition: ValidationPrecision.h:294
ValidationPrecision(const ValidationPrecision &)=delete
Disabled copy constructor.
uint64_t necessaryIterations() const
Returns the necessary iterations to allow determining success or failure based on the specified succe...
Definition: ValidationPrecision.h:350
bool succeeded() const
Returns if this validation has succeeded.
Definition: ValidationPrecision.h:364
double threshold_
The necessary percent of accurate iterations necessary for a successful verification,...
Definition: ValidationPrecision.h:211
uint64_t iterations() const
Returns the number of iterations in which the precision has been determined.
Definition: ValidationPrecision.h:345
bool succeededChecked_
True, if the success state of this validation has been checked.
Definition: ValidationPrecision.h:230
uint64_t necessaryIterations_
The number of iterations needed to determine success or failure.
Definition: ValidationPrecision.h:217
bool hasSetFailed() const
Returns whether this validation object has been set to failed explicitly.
Definition: ValidationPrecision.h:420
void setFailed()
Explicitly sets the validation to be failed.
Definition: ValidationPrecision.h:325
std::ostream & operator<<(std::ostream &stream, const Validation &validation)
Definition: Validation.h:824
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15