1 #include "unittest/testframework.h"
2 #include "unittest/exceptions.h"
3 #include <thrust/memory.h>
4 
5 // #include backends' testframework.h, if they exist and are required for the build
6 #if THRUST_DEVICE_SYSTEM == THRUST_DEVICE_SYSTEM_CUDA
7 #include "backend/cuda/testframework.h"
8 #endif
9 
10 #include <iostream>
11 #include <iomanip>
12 #include <cstdlib>
13 #include <algorithm>
14 #include <numeric>
15 #include <string>
16 #include <limits>
17 #include <ctime>
18 #include <limits>
19 
20 
21 const size_t standard_test_sizes[] =
22 {
23   0, 1, 2, 3, 4, 5, 8, 10, 13, 16, 17, 19, 27, 30, 31, 32,
24   33, 35, 42, 53, 58, 63, 64, 65, 72, 97, 100, 127, 128, 129, 142, 183, 192, 201, 240, 255, 256,
25   257, 302, 511, 512, 513, 687, 900, 1023, 1024, 1025, 1565, 1786, 1973, 2047, 2048, 2049, 3050, 4095, 4096,
26   4097, 5030, 7791, 10000, 10027, 12345, 16384, 17354, 26255, 32768, 43718, 65533, 65536,
27   65539, 123456, 131072, 731588, 1048575, 1048576,
28   3398570, 9760840, (1 << 24) - 1, (1 << 24),
29   (1 << 24) + 1, (1 << 25) - 1, (1 << 25), (1 << 25) + 1, (1 << 26) - 1, 1 << 26,
30   (1 << 26) + 1, (1 << 27) - 1, (1 << 27)
31 };
32 
33 
34 const size_t tiny_threshold    = 1 <<  5;  //   32
35 const size_t small_threshold   = 1 <<  8;  //  256
36 const size_t medium_threshold  = 1 << 12;  //   4K
37 const size_t default_threshold = 1 << 16;  //  64K
38 const size_t large_threshold   = 1 << 20;  //   1M
39 const size_t huge_threshold    = 1 << 24;  //  16M
40 const size_t epic_threshold    = 1 << 26;  //  64M
41 const size_t max_threshold     = (std::numeric_limits<size_t>::max)();
42 
43 
44 std::vector<size_t> test_sizes;
45 
46 
get_test_sizes(void)47 std::vector<size_t> get_test_sizes(void)
48 {
49   return test_sizes;
50 }
51 
52 
set_test_sizes(const std::string & val)53 void set_test_sizes(const std::string& val)
54 {
55   size_t threshold = 0;
56 
57   if(val == "tiny")
58     threshold = tiny_threshold;
59   else if(val == "small")
60     threshold = small_threshold;
61   else if(val == "medium")
62     threshold = medium_threshold;
63   else if(val == "default")
64     threshold = default_threshold;
65   else if(val == "large")
66     threshold = large_threshold;
67   else if(val == "huge")
68     threshold = huge_threshold;
69   else if(val == "epic")
70     threshold = epic_threshold;
71   else if(val == "max")
72     threshold = max_threshold;
73   else
74   {
75     std::cerr << "invalid test size \"" << val << "\"" << std::endl;
76     exit(1);
77   }
78 
79   for(size_t i = 0; i < sizeof(standard_test_sizes) / sizeof(*standard_test_sizes); i++)
80   {
81     if(standard_test_sizes[i] <= threshold)
82       test_sizes.push_back(standard_test_sizes[i]);
83   }
84 }
85 
86 
register_test(UnitTest * test)87 void UnitTestDriver::register_test(UnitTest * test)
88 {
89   if(UnitTestDriver::s_driver().test_map.count(test->name) )
90   {
91     std::cout << "[WARNING] Test name \"" << test->name << " already encountered " << std::endl;
92   }
93 
94   UnitTestDriver::s_driver().test_map[test->name] = test;
95 }
96 
97 
UnitTest(const char * _name)98 UnitTest::UnitTest(const char * _name) : name(_name)
99 {
100   UnitTestDriver::s_driver().register_test(this);
101 }
102 
103 
process_args(int argc,char ** argv,ArgumentSet & args,ArgumentMap & kwargs)104 void process_args(int argc, char ** argv,
105                   ArgumentSet& args,
106                   ArgumentMap& kwargs)
107 
108 {
109   for(int i = 1; i < argc; i++)
110   {
111     std::string arg(argv[i]);
112 
113     // look for --key or --key=value arguments
114     if(arg.substr(0,2) == "--")
115     {
116       std::string::size_type n = arg.find('=',2);
117 
118       if(n == std::string::npos)
119       {
120         kwargs[arg.substr(2)] = std::string();              // (key,"")
121       }
122       else
123       {
124         kwargs[arg.substr(2, n - 2)] = arg.substr(n + 1);   // (key,value)
125       }
126     }
127     else
128     {
129       args.insert(arg);
130     }
131   }
132 }
133 
134 
usage(int,char ** argv)135 void usage(int /*argc*/, char** argv)
136 {
137   std::string indent = "  ";
138 
139   std::cout << "Example Usage:\n";
140   std::cout << indent << argv[0] << "\n";
141   std::cout << indent << argv[0] << " TestName1 [TestName2 ...] \n";
142   std::cout << indent << argv[0] << " PartialTestName1* [PartialTestName2* ...] \n";
143   std::cout << indent << argv[0] << " --device=1\n";
144   std::cout << indent << argv[0] << " --sizes={tiny,small,medium,default,large,huge,epic,max}\n";
145   std::cout << indent << argv[0] << " --verbose or --concise\n";
146   std::cout << indent << argv[0] << " --list\n";
147   std::cout << indent << argv[0] << " --help\n";
148   std::cout << "\n";
149   std::cout << "Options:\n";
150   std::cout << indent << "The sizes option determines which input sizes are tested.\n";
151   std::cout << indent << indent << "--sizes=tiny    tests sizes up to " << tiny_threshold    << "\n";
152   std::cout << indent << indent << "--sizes=small   tests sizes up to " << small_threshold   << "\n";
153   std::cout << indent << indent << "--sizes=medium  tests sizes up to " << medium_threshold  << "\n";
154   std::cout << indent << indent << "--sizes=default tests sizes up to " << default_threshold << "\n";
155   std::cout << indent << indent << "--sizes=large   tests sizes up to " << large_threshold   << " (0.25 GB memory)\n";
156   std::cout << indent << indent << "--sizes=huge    tests sizes up to " << huge_threshold    << " (1.50 GB memory)\n";
157   std::cout << indent << indent << "--sizes=epic    tests sizes up to " << epic_threshold    << " (3.00 GB memory)\n";
158   std::cout << indent << indent << "--sizes=max     tests all available sizes\n";
159 }
160 
161 
162 struct TestResult
163 {
164   TestStatus  status;
165   std::string name;
166   std::string message;
167 
168   // XXX use a c++11 timer result when available
169   std::clock_t elapsed;
170 
TestResultTestResult171   TestResult(const TestStatus status, std::clock_t elapsed, const UnitTest& u, const std::string& message = "")
172       : status(status), name(u.name), message(message), elapsed(elapsed)
173   {}
174 
operator <TestResult175   bool operator<(const TestResult& tr) const
176   {
177     if(status < tr.status)
178     {
179       return true;
180     }
181     else if(tr.status < status)
182     {
183       return false;
184     }
185     else
186     {
187       return name < tr.name;
188     }
189   }
190 };
191 
192 
record_result(const TestResult & test_result,std::vector<TestResult> & test_results)193 void record_result(const TestResult& test_result, std::vector< TestResult >& test_results)
194 {
195   test_results.push_back(test_result);
196 }
197 
198 
report_results(std::vector<TestResult> & test_results,double elapsed_minutes)199 void report_results(std::vector< TestResult >& test_results, double elapsed_minutes)
200 {
201   std::cout << std::endl;
202 
203   std::string hline = "================================================================";
204 
205   std::sort(test_results.begin(), test_results.end());
206 
207   size_t num_passes = 0;
208   size_t num_failures = 0;
209   size_t num_known_failures = 0;
210   size_t num_errors = 0;
211 
212   for(size_t i = 0; i < test_results.size(); i++)
213   {
214     const TestResult& tr = test_results[i];
215 
216     if(tr.status == Pass)
217     {
218       num_passes++;
219     }
220     else
221     {
222       std::cout << hline << std::endl;
223 
224       switch(tr.status)
225       {
226         case Failure:
227           std::cout << "FAILURE";       num_failures++;       break;
228         case KnownFailure:
229           std::cout << "KNOWN FAILURE"; num_known_failures++; break;
230         case Error:
231           std::cout << "ERROR";         num_errors++;         break;
232         default:
233           break;
234       }
235 
236       std::cout << ": " << tr.name << std::endl << tr.message << std::endl;
237     }
238   }
239 
240   std::cout << hline << std::endl;
241 
242   std::cout << "Totals: ";
243   std::cout << num_failures << " failures, ";
244   std::cout << num_known_failures << " known failures, ";
245   std::cout << num_errors << " errors, and ";
246   std::cout << num_passes << " passes." << std::endl;
247   std::cout << "Time:  " << elapsed_minutes << " minutes" << std::endl;
248 }
249 
250 
list_tests(void)251 void UnitTestDriver::list_tests(void)
252 {
253   for(TestMap::iterator iter = test_map.begin(); iter != test_map.end(); iter++)
254   {
255     std::cout << iter->second->name << std::endl;
256   }
257 }
258 
259 
post_test_sanity_check(const UnitTest &,bool)260 bool UnitTestDriver::post_test_sanity_check(const UnitTest &/*test*/, bool /*concise*/)
261 {
262   return true;
263 }
264 
265 
run_tests(std::vector<UnitTest * > & tests_to_run,const ArgumentMap & kwargs)266 bool UnitTestDriver::run_tests(std::vector<UnitTest *>& tests_to_run, const ArgumentMap& kwargs)
267 {
268   std::time_t start_time = std::time(0);
269 
270   THRUST_DISABLE_MSVC_FORCING_VALUE_TO_BOOL_WARNING_BEGIN
271   bool verbose = kwargs.count("verbose");
272   bool concise = kwargs.count("concise");
273   THRUST_DISABLE_MSVC_FORCING_VALUE_TO_BOOL_WARNING_END
274 
275   std::vector< TestResult > test_results;
276 
277   if(verbose && concise)
278   {
279     std::cout << "--verbose and --concise cannot be used together" << std::endl;
280     exit(EXIT_FAILURE);
281   }
282 
283   if(!concise)
284   {
285     std::cout << "Running " << tests_to_run.size() << " unit tests." << std::endl;
286   }
287 
288   for(size_t i = 0; i < tests_to_run.size(); i++)
289   {
290      UnitTest& test = *tests_to_run[i];
291 
292      if(verbose)
293      {
294        std::cout << "Running " << test.name << "..." << std::flush;
295      }
296 
297      try
298      {
299        // time the test
300        std::clock_t start = std::clock();
301 
302        // run the test
303        test.run();
304 
305        // test passed
306        record_result(TestResult(Pass, std::clock() - start, test), test_results);
307      }
308      catch(unittest::UnitTestFailure& f)
309      {
310        record_result(TestResult(Failure, (std::numeric_limits<std::clock_t>::max)(), test, f.message), test_results);
311      }
312      catch(unittest::UnitTestKnownFailure& f)
313      {
314        record_result(TestResult(KnownFailure, (std::numeric_limits<std::clock_t>::max)(), test, f.message), test_results);
315      }
316      catch(std::bad_alloc& e)
317      {
318        record_result(TestResult(Error, (std::numeric_limits<std::clock_t>::max)(), test, e.what()), test_results);
319      }
320      catch(unittest::UnitTestError& e)
321      {
322        record_result(TestResult(Error, (std::numeric_limits<std::clock_t>::max)(), test, e.message), test_results);
323      }
324 
325      // immediate report
326      if(!concise)
327      {
328        if(verbose)
329        {
330          switch(test_results.back().status)
331          {
332            case Pass:
333              std::cout << "\r[PASS] ";
334              std::cout << std::setw(10) << 1000.f * float(test_results.back().elapsed) / CLOCKS_PER_SEC << " ms";
335              break;
336            case Failure:
337              std::cout << "\r[FAILURE]           "; break;
338            case KnownFailure:
339              std::cout << "\r[KNOWN FAILURE]     "; break;
340            case Error:
341              std::cout << "\r[ERROR]             "; break;
342            default:
343              break;
344          }
345 
346          std::cout << " " << test.name << std::endl;
347        }
348        else
349        {
350          switch(test_results.back().status)
351          {
352            case Pass:
353              std::cout << "."; break;
354            case Failure:
355              std::cout << "F"; break;
356            case KnownFailure:
357              std::cout << "K"; break;
358            case Error:
359              std::cout << "E"; break;
360            default:
361              break;
362          }
363        }
364      }
365 
366      if(!post_test_sanity_check(test, concise))
367      {
368        return false;
369      }
370 
371      std::cout.flush();
372   }
373 
374   double elapsed_minutes = double(std::time(0) - start_time) / 60;
375 
376   // summary report
377   if(!concise)
378   {
379     report_results(test_results, elapsed_minutes);
380   }
381 
382 
383   // if any failures or errors return false
384   for(size_t i = 0; i < test_results.size(); i++)
385   {
386     if(test_results[i].status != Pass && test_results[i].status != KnownFailure)
387     {
388       return false;
389     }
390   }
391 
392   // all tests pass or are known failures
393   return true;
394 }
395 
396 
run_tests(const ArgumentSet & args,const ArgumentMap & kwargs)397 bool UnitTestDriver::run_tests(const ArgumentSet& args, const ArgumentMap& kwargs)
398 {
399   if(args.empty())
400   {
401     // run all tests
402     std::vector<UnitTest *> tests_to_run;
403 
404     for(TestMap::iterator iter = test_map.begin(); iter != test_map.end(); iter++)
405     {
406       tests_to_run.push_back(iter->second);
407     }
408 
409     return run_tests(tests_to_run, kwargs);
410   }
411   else
412   {
413     // all non-keyword arguments are assumed to be test names or partial test names
414 
415     typedef TestMap::iterator               TestMapIterator;
416 
417     // vector to accumulate tests
418     std::vector<UnitTest *> tests_to_run;
419 
420     for(ArgumentSet::const_iterator iter = args.begin(); iter != args.end(); iter++)
421     {
422       const std::string& arg = *iter;
423 
424       size_t len = arg.size();
425       size_t matches = 0;
426 
427       if(arg[len-1] == '*')
428       {
429         // wildcard search
430         std::string search = arg.substr(0,len-1);
431 
432         TestMapIterator lb = test_map.lower_bound(search);
433         while(lb != test_map.end())
434         {
435           if(search != lb->first.substr(0,len-1))
436           {
437             break;
438           }
439 
440           tests_to_run.push_back(lb->second);
441           lb++;
442           matches++;
443         }
444       }
445       else
446       {
447         // non-wildcard search
448         TestMapIterator lb = test_map.find(arg);
449 
450         if(lb != test_map.end())
451         {
452           tests_to_run.push_back(lb->second);
453           matches++;
454         }
455       }
456 
457       if(matches == 0)
458       {
459         std::cout << "[ERROR] found no test names matching the pattern: " << arg << std::endl;
460         return false;
461       }
462     }
463 
464     return run_tests(tests_to_run, kwargs);
465   }
466 }
467 
468 
469 // driver_instance maps a DeviceSystem to a singleton UnitTestDriver
470 template<typename DeviceSystem>
driver_instance(DeviceSystem)471 UnitTestDriver &driver_instance(DeviceSystem)
472 {
473   static UnitTestDriver s_instance;
474   return s_instance;
475 }
476 
477 
478 // if we need a special kind of UnitTestDriver, overload
479 // driver_instance in that function
s_driver()480 UnitTestDriver &UnitTestDriver::s_driver()
481 {
482   return driver_instance(thrust::device_system_tag());
483 }
484 
485 
main(int argc,char ** argv)486 int main(int argc, char **argv)
487 {
488   ArgumentSet args;
489   ArgumentMap kwargs;
490 
491   process_args(argc, argv, args, kwargs);
492 
493   if(kwargs.count("help"))
494   {
495     usage(argc, argv);
496     return 0;
497   }
498 
499   if(kwargs.count("list"))
500   {
501     UnitTestDriver::s_driver().list_tests();
502     return 0;
503   }
504 
505   if(kwargs.count("sizes"))
506   {
507     set_test_sizes(kwargs["sizes"]);
508   }
509   else
510   {
511     set_test_sizes("default");
512   }
513 
514   bool passed = UnitTestDriver::s_driver().run_tests(args, kwargs);
515 
516   if(kwargs.count("concise"))
517   {
518     std::cout << ((passed) ? "PASSED" : "FAILED") << std::endl;
519   }
520 
521   return (passed) ? EXIT_SUCCESS : EXIT_FAILURE;
522 }
523 
524