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