HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_TestManager.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: UT_TestManager.h (C++)
7  *
8  * COMMENTS: This class provides a test harness framework.
9  *
10  * USAGE:
11  * 1. Create a main() method for your test application like so:
12  * int main(int argc, char *argv[])
13  * {
14  * return UT_TestManager::get().run(argc, argv);
15  * }
16  * The return code will be the number of test failures.
17  * To obtain the command line usage, use "testapp -h".
18  *
19  * 2. For each test case, use one of the TEST_REGISTER*() macros for
20  * registering a test function. The function should return true if the
21  * test passed.
22  */
23 
24 #ifndef __UT_TESTMANAGER_H__
25 #define __UT_TESTMANAGER_H__
26 
27 #include "UT_API.h"
28 #include "UT_Functor.h"
29 #include "UT_Function.h"
30 #include "UT_Main.h"
31 #include "UT_NonCopyable.h"
32 #include "UT_StopWatch.h"
33 #include "UT_WorkArgs.h"
34 #include "UT_WorkBuffer.h"
35 #include <SYS/SYS_Types.h>
36 
38 {
39 public:
41  virtual ~UT_TestManager() { }
42 
44 
45  /// Access the global test manager
46  static UT_TestManager & get();
47 
48  /// Determine if the test was interactively invoked
49  virtual bool isInteractive() const = 0;
50 
51  /// Determine if the tests are being run for performance
52  virtual bool isPerformance() const = 0;
53 
54  /// Determine the number of threads requested
55  virtual int numThreads() const = 0;
56 
57  /// Called by main() to run the tests. Returns the number of test failures.
58  virtual int run(int argc, char *argv[]) = 0;
59 
60  /// Called by the TEST_REGISTER*() macros to automatically add a new test
61  /// case
62  virtual void addTest(const char *name,
63  UT_Functor<bool> &callback) = 0;
64 
65  /// Allow for logging of intermediate test results. If logging is turned
66  /// on in the test manager, the test name, status and run time will be
67  /// saved.
68  virtual void logResult(const char *name, bool success,
69  fpreal64 run_time) = 0;
70 
71  /// Get the arguments passed into this program.
72  virtual const UT_WorkArgs& getArgs() const = 0;
73 };
74 
75 /// @brief Status maintainer used to run a test.
76 ///
77 /// This class simplifies the book-keeping when running a test. The unit test
78 /// will automatically log information (i.e. performance, memory) if the
79 /// UT_TestManager is so configured.
80 ///
81 /// Example 1: @code
82 /// static bool
83 /// unitTest(int argument)
84 /// {
85 /// UT_TestUnit status("UnitTest: %d", argument);
86 ///
87 /// if (!doTest(...))
88 /// return status.fail("Test failed: %s", some_string);
89 ///
90 /// return status.ok();
91 /// }
92 /// @endcode
93 ///
94 /// Example 2: @code
95 /// static bool
96 /// unitTest(int argument)
97 /// {
98 /// UT_TestUnit unit(UT_TestUnit::OK, "UnitTest: %d", argument);
99 ///
100 /// if (!doTest1(...))
101 /// unit.fail("Test 1 failed: %s", some_string);
102 /// if (!doTest2(...))
103 /// unit.fail("Test 2 failed: %s", some_string);
104 ///
105 /// return unit.status();
106 /// }
107 /// @endcode
109 {
110 public:
111  enum InitEnum { OK, FAIL };
112 
113  UT_TestUnit(const char *format, ...) SYS_PRINTF_CHECK_ATTRIBUTE(2, 3);
114  UT_TestUnit(InitEnum init_enum, const char *format, ...)
116  ~UT_TestUnit();
117 
118  /// Print out the message given by a format. This includes the test name,
119  /// and other information. No newline should be included in the format.
120  ///
121  /// The function @b always returns @b false.
122  bool fail(const char *format=NULL, ...)
123  SYS_PRINTF_CHECK_ATTRIBUTE(2, 3);
124 
125  /// Print out the message given by a format. This includes the test name,
126  /// and other information. No newline should be included in the format.
127  ///
128  /// The function @b always returns @b true.
129  bool ok(const char *format=NULL, ...)
130  SYS_PRINTF_CHECK_ATTRIBUTE(2, 3);
131 
132  /// Restart the timer. If format is non-NULL, then it will print the time
133  /// as well from when the timer last started. Note that the timer is
134  /// automatically started in the constructor.
135  void restartTimer(const char *format=NULL, ...)
136  SYS_PRINTF_CHECK_ATTRIBUTE(2, 3);
137 
138  /// Enable printing of success/failure when this unit test is destroyed.
139  void setAlwaysPrint(bool f) { myAlwaysPrint = f; }
140 
141  /// Query the status of the test
142  bool status() const { return myStatus; }
143  bool getStatus() const { return myStatus; }
144  bool setStatus(bool b) { myStatus = b; return myStatus; }
145 private:
146  UT_WorkBuffer myName; // Test-name
147  UT_Timer myTimer;
148  bool myStatus;
149  bool myPrint;
150  bool myAlwaysPrint;
151 };
152 
153 
154 // Forward-declaration of nanobench classes.
155 namespace ankerl::nanobench
156 {
157  class Bench;
158 }
159 
160 /// @brief Wrapper around the nanobench object for unit performance tests.
161 ///
162 /// This class wraps the nanobench::Bench object for performance testing,
163 /// and streamlines the workflows for deploying it in unit tests.
164 ///
165 /// Example: @code
166 /// static void
167 /// performanceTest(UT_TestUnit &test_unit)
168 /// {
169 /// int result = expensiveComputation();
170 /// if (result != 42)
171 /// test_unit.fail("Wrong computation: got %d, expected 42", result);
172 /// }
173 ///
174 /// static bool
175 /// test()
176 /// {
177 /// UT_TestBench bench;
178 /// bool ok = true;
179 ///
180 /// // Time the call to the function.
181 /// ok &= bench.run("Forty two", performanceTest));
182 ///
183 /// // Time the call to the lambda.
184 /// ok &= bench.run("Loop", [](UT_testUnit &test_unit)
185 /// auto sum = 0;
186 /// for(auto i=0; i<1000; i++)
187 /// sum += i;
188 /// });
189 ///
190 /// // Prints the nanobench timing report, saves it to file for the future,
191 /// // tries to read a corresponding report from the ./correct subdir,
192 /// // and returns true if the performance has not deteriorated
193 /// // (thus passing the performance measurement test).
194 /// ok &= bench.saveAndCompareResults("MyPerformanceTest");
195 ///
196 /// return ok;
197 /// }
198 /// @endcode
199 ///
201 {
202 public:
203  UT_TestBench(const UT_StringRef &title);
204  ~UT_TestBench();
205 
207 
208 
209  /// Runs and times the given function @p f.
210  /// Prints the unit test wall clock time to the output as a summary.
211  ///
212  /// Stores the nanobench timing in the internal structures for later
213  /// comparisons and detection of the performance regressions.
214  ///
215  /// It passes a UT_TestUnit to that function, for setting status.
216  /// I.e., bad results may be fast, but they should not be considered
217  /// for parformance timing, and hence status should be set to error.
218  /// Returns true if that status is a success or false otherwise.
219  ///
220  /// @param test_name The name of the timing test to appear in the reports.
221  /// @param fn The test function to time.
222  bool run( const UT_StringRef &test_name,
223  UT_Function<void(UT_TestUnit &)> fn );
224 
225 
226  /// Saves the nanobench timings from the previously performed runs
227  /// to a file with the given name in the current working directory.
228  /// Compares the timings with a similarly named file in a ./correct subdir,
229  /// if it exists. If it does not exists, it creates one as a base line
230  /// for the future runs.
231  /// comparing to the baseline times, and false otherise.
232  /// Prints the report of the current run times comparison to the
233  /// times recorded as a baseline in the ./correct file.
234  /// Returns true if the current timings are within an acceptable delta
235  ///
236  /// @param file_name_root The base name to use for constructing the
237  /// timing result file name.
238  /// @return True if the current timings are within an acceptable delta;
239  /// False otherwise.
240  bool saveAndCompareResults(const UT_StringRef &file_name_root) const;
241 
242 private:
243  // Wrapping Bench using pimpl idiom.
244  using Bench = ankerl::nanobench::Bench;
245  UT_UniquePtr<Bench> myBench;
246 };
247 
248 
249 /// Use this define to log information into the result table
250 #define TEST_LOGRESULT(NAME, SUCCESS, TIME) \
251  UT_TestManager::get().logResult(NAME, SUCCESS, TIME)
252 
253 /// Use this macro after your test function is declared. It will register
254 /// your method with the test manager. Your method should return true if the
255 /// test succeeded, false otherwise. The name is the label that will be used
256 /// for invoking this specific test.
257 #define TEST_REGISTER_FN(name, method) \
258  class method##Registrar \
259  { \
260  public: \
261  method##Registrar() \
262  { \
263  UT_Functor<bool> callback(&method); \
264  UT_TestManager::get().addTest(name, callback); \
265  } \
266  }; \
267  static method##Registrar the##method##Registrar;
268 
269 /// Use this macro after your test class is declared. It will create a static
270 /// instance of your class that then registers your 'bool classname::method()'
271 /// with the test manager. Your method should return true if the test
272 /// succeeded, false otherwise. The name is the label that will be used for
273 /// invoking this specific test.
274 #define TEST_REGISTER(name, classname, method) \
275  class classname##method##Registrar \
276  { \
277  public: \
278  classname##method##Registrar() \
279  { \
280  UT_Functor<bool> callback(&myTest, \
281  &classname::method); \
282  UT_TestManager::get().addTest(name, callback); \
283  } \
284  private: \
285  classname myTest; \
286  }; \
287  static classname##method##Registrar \
288  the##classname##method##Registrar;
289 
290 #define TEST_UNIT_MAIN() \
291  int theMain(int argc, char *argv[]) \
292  { \
293  /*Ensure we reset the task scheduler to avoid hangning on exit in \
294  * Windows.*/ \
295  UT_Thread::resetNumProcessors(); \
296  return UT_TestManager::get().run(argc, argv); \
297  } \
298  UT_MAIN(theMain);
299 
300 #endif // __UT_TESTMANAGER_H__
Status maintainer used to run a test.
*get result *(waiting if necessary)*A common idiom is to fire a bunch of sub tasks at the and then *wait for them to all complete We provide a helper class
Definition: thread.h:632
#define UT_API
Definition: UT_API.h:14
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:39
double fpreal64
Definition: SYS_Types.h:201
GLfloat f
Definition: glcorearb.h:1926
#define SYS_PRINTF_CHECK_ATTRIBUTE(string_index, first_to_check)
Definition: SYS_Types.h:448
GLint GLint GLsizei GLint GLenum format
Definition: glcorearb.h:108
virtual ~UT_TestManager()
#define UT_NON_COPYABLE(CLASS)
Define deleted copy constructor and assignment operator inside a class.
auto get(const UT_ARTIterator< T > &it) -> decltype(it.key())
Definition: UT_ARTMap.h:1173
GLuint const GLchar * name
Definition: glcorearb.h:786
bool status() const
Query the status of the test.
std::function< T > UT_Function
Definition: UT_Function.h:37
GLboolean GLboolean GLboolean b
Definition: glcorearb.h:1222
Wrapper around the nanobench object for unit performance tests.
bool getStatus() const
bool setStatus(bool b)