github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/image/jxr/jxrlib/test/test.cc (about) 1 // Copyright 2013 <chaishushan{AT}gmail.com>. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 #include "test.h" 6 7 #include <stdarg.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <math.h> 12 #include <time.h> 13 14 #include <string> 15 #include <algorithm> 16 17 static std::vector<std::string> args; 18 static std::string flag_list_regexp = ""; 19 static std::string flag_test_regexp = ".*"; 20 static std::string flag_test_bench_regexp = ""; 21 static std::string flag_test_bench_benchtime_second = "1"; 22 23 static struct { int N; double benchtime, timer_start, timer_duration; bool timer_on; } bench; 24 static struct { void (*fn)(void); const char *name, *type; } tests[10000]; 25 static int ntests = 0; 26 27 static bool strHasPrefix(const std::string& str, const std::string& prefix) { 28 return str.size() >= prefix.size() 29 && str.compare(0, prefix.size(), prefix) == 0; 30 } 31 static bool strHasSuffix(const std::string& str, const std::string& suffix) { 32 return str.size() >= suffix.size() 33 && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; 34 } 35 36 static const char* getBaseName(const char* fname) { 37 int len = strlen(fname); 38 const char* s = fname + len; 39 while(s > fname) { 40 if(s[-1] == '/' || s[-1] == '\\') return s; 41 s--; 42 } 43 return s; 44 } 45 46 static int matchhere(const char* regexp, const char* text); 47 static int match(const char *regexp, const char *text) { 48 if (regexp[0] == '^') { 49 return matchhere(regexp+1, text); 50 } 51 do { 52 if (matchhere(regexp, text)) return 1; 53 } while (*text++ != '\0'); 54 return 0; 55 } 56 static int matchstar(int c, const char *regexp, const char *text) { 57 do { 58 if (matchhere(regexp, text)) return 1; 59 } while (*text != '\0' && (*text++ == c || c == '.')); 60 return 0; 61 } 62 static int matchhere(const char *regexp, const char *text) { 63 if (regexp[0] == '\0') return 1; 64 if (regexp[1] == '*') return matchstar(regexp[0], regexp+2, text); 65 if (regexp[0] == '$' && regexp[1] == '\0') return *text == '\0'; 66 if (*text!='\0' && (regexp[0]=='.' || regexp[0]==*text)) return matchhere(regexp+1, text+1); 67 return 0; 68 } 69 70 const std::vector<std::string>& TestArgs() { 71 return args; 72 } 73 74 void RegisterTest(void (*fn)(void), const char* name, const char* type) { 75 if(ntests >= sizeof(tests)/sizeof(tests[0])) { 76 printf("%s %s, line %d: RegisterTest failed\n", name, getBaseName(__FILE__), __LINE__); 77 exit(-1); 78 } 79 tests[ntests].fn = fn; 80 tests[ntests].name = name; 81 tests[ntests].type = type; 82 ntests++; 83 } 84 85 void TestAssertTrue(bool condition, const char* fname, int lineno, const char* fmt, ...) { 86 if(!condition) { 87 fname = getBaseName(fname); 88 if(fmt != NULL && fmt[0] != '\0') { 89 va_list ap; 90 va_start(ap, fmt); 91 printf("fail, %s, line %d: ASSERT_TRUE(false), ", fname, lineno); 92 vprintf(fmt, ap); 93 printf("\n"); 94 va_end(ap); 95 } else { 96 printf("fail, %s, line %d: ASSERT_TRUE(false)\n", fname, lineno); 97 } 98 exit(-1); 99 } 100 } 101 102 void TestAssertEQ(int a, int b, const char* fname, int lineno, const char* fmt, ...) { 103 if(a != b) { 104 fname = getBaseName(fname); 105 if(fmt != NULL && fmt[0] != '\0') { 106 va_list ap; 107 va_start(ap, fmt); 108 printf("fail, %s, line %d: ASSERT_EQ(%d, %d), ", fname, lineno, a, b); 109 vprintf(fmt, ap); 110 printf("\n"); 111 va_end(ap); 112 } else { 113 printf("fail, %s, line %d: ASSERT_EQ(%d, %d)\n", fname, lineno, a, b); 114 } 115 exit(-1); 116 } 117 } 118 void TestAssertStrEQ(const char* a, const char* b, const char* fname, int lineno, const char* fmt, ...) { 119 if(strcmp(a, b) != 0) { 120 fname = getBaseName(fname); 121 if(fmt != NULL && fmt[0] != '\0') { 122 va_list ap; 123 va_start(ap, fmt); 124 printf("fail, %s, line %d: ASSERT_STREQ(\"%s\", \"%s\"), ", fname, lineno, a, b); 125 vprintf(fmt, ap); 126 printf("\n"); 127 va_end(ap); 128 } else { 129 printf("fail, %s, line %d: ASSERT_STREQ(\"%s\", \"%s\")\n", fname, lineno, a, b); 130 } 131 exit(-1); 132 } 133 } 134 void TestAssertNear(float a, float b, float abs_error, const char* fname, int lineno, const char* fmt, ...) { 135 if(abs(a-b) > abs(abs_error)) { 136 fname = getBaseName(fname); 137 if(fmt != NULL && fmt[0] != '\0') { 138 va_list ap; 139 va_start(ap, fmt); 140 printf("fail, %s, line %d: ASSERT_NEAR(%f, %f, %f), ", fname, lineno, a, b, abs_error); 141 vprintf(fmt, ap); 142 printf("\n"); 143 va_end(ap); 144 } else { 145 printf("fail, %s, line %d: ASSERT_NEAR(%f, %f, %f)\n", fname, lineno, a, b, abs_error); 146 } 147 exit(-1); 148 } 149 } 150 151 // roundDown10 rounds a number down to the nearest power of 10. 152 static int roundDown10(int n) { 153 int tens = 0; 154 // tens = floor(log_10(n)) 155 while(n >= 10) { 156 n = n / 10; 157 tens++; 158 } 159 // result = 10^tens 160 int result = 1; 161 for(int i = 0; i < tens; ++i) { 162 result *= 10; 163 } 164 return result; 165 } 166 167 // roundUp rounds x up to a number of the form [1eX, 2eX, 5eX]. 168 static int roundUp(int n) { 169 int base = roundDown10(n); 170 if(n <= base) return base; 171 if(n <= base*2) return base*2; 172 if(n <= base*5) return base*5; 173 return base*10; 174 } 175 176 static double timeNowSec() { 177 return 1.0 * clock() / CLOCKS_PER_SEC; 178 } 179 180 static void benchRunN(int id, int n) { 181 bench.N = n; 182 BenchResetTimer(); 183 BenchStartTimer(); 184 tests[id].fn(); 185 BenchStopTimer(); 186 } 187 188 static void benchRun(int id) { 189 int n = 1; 190 benchRunN(id, n); 191 while(bench.timer_duration < bench.benchtime && n < 1e9) { 192 int last = n; 193 // Run more iterations than we think we'll need for a second (1.5x). 194 // Don't grow too fast in case we had timing errors previously. 195 // Be sure to run at least one more than last time. 196 n = std::max(std::min(n+n/2, 100*last), last+1); 197 // Round up to something easy to read. 198 n = roundUp(n); 199 benchRunN(id, n); 200 } 201 202 double nsop = 1e9*bench.timer_duration/bench.N; 203 if(nsop < 10) { 204 printf("[bench] %s %d %.2f ns/op\n", tests[id].name, bench.N, float(nsop)); 205 } else if(nsop < 100) { 206 printf("[bench] %s %d %.1f ns/op\n", tests[id].name, bench.N, float(nsop)); 207 } else { 208 printf("[bench] %s %d %d ns/op\n", tests[id].name, bench.N, int(nsop)); 209 } 210 } 211 212 int BenchN() { 213 return bench.N; 214 } 215 void BenchResetTimer() { 216 bench.timer_start = timeNowSec(); 217 bench.timer_duration = 0.0; 218 } 219 void BenchStartTimer() { 220 if(!bench.timer_on) { 221 bench.timer_start = timeNowSec(); 222 bench.timer_on = true; 223 } 224 } 225 void BenchStopTimer() { 226 if(bench.timer_on) { 227 bench.timer_duration += timeNowSec() - bench.timer_start; 228 bench.timer_on = false; 229 } 230 } 231 232 static void usage(int argc, char* argv[]) { 233 printf("C++ Mini UnitTest and Benchmark Library.\n"); 234 printf("https://github.com/chai2010/cc-mini-test\n"); 235 printf("\n"); 236 237 printf("Usage: %s\n", getBaseName(argv[0])); 238 printf(" [-list=.*]\n"); 239 printf(" [-test=.*]\n"); 240 printf(" [-test.bench=]\n"); 241 printf(" [-test.benchtime=1second]\n"); 242 printf(" [-help]\n"); 243 printf(" [-h]\n"); 244 printf("\n"); 245 246 printf("Report bugs to <chaishushan{AT}gmail.com>.\n"); 247 } 248 249 int main(int argc, char* argv[]) { 250 args.assign(argv, argv + argc); 251 for(int i = 1; i < argc; ++i) { 252 if(argv[i] == std::string("-help") || argv[i] == std::string("-h")) { 253 usage(argc, argv); 254 return 0; 255 } 256 257 if(argv[i] == std::string("-list")) { 258 flag_list_regexp = ".*"; 259 break; 260 } 261 if(argv[i] == std::string("-test")) { 262 flag_test_regexp = ".*"; 263 continue; 264 } 265 if(argv[i] == std::string("-test.bench")) { 266 flag_test_bench_regexp = ".*"; 267 continue; 268 } 269 270 if(strHasPrefix(argv[i], "-list=")) { 271 flag_list_regexp = argv[i]+sizeof("-list=")-1; 272 continue; 273 } 274 if(strHasPrefix(argv[i], "-test=")) { 275 flag_test_regexp = argv[i]+sizeof("-test=")-1; 276 continue; 277 } 278 if(strHasPrefix(argv[i], "-test.bench=")) { 279 flag_test_bench_regexp = argv[i]+sizeof("-test.bench=")-1; 280 continue; 281 } 282 283 if(strHasPrefix(argv[i], "-test.benchtime=")) { 284 flag_test_bench_benchtime_second = argv[i]+sizeof("-test.benchtime=")-1; 285 bench.benchtime = atof(flag_test_bench_benchtime_second.c_str()); 286 if(bench.benchtime <= 0.1) bench.benchtime = 1.0; 287 continue; 288 } 289 290 // ingore user defined flag 291 } 292 293 if(!flag_list_regexp.empty()) { 294 int total = 0; 295 for(int id = 0; id < ntests; ++id) { 296 if(std::string(tests[id].type) == "init") { 297 if(match(flag_list_regexp.c_str(), tests[id].name) != 0) { 298 printf("[init] %s\n", tests[id].name); 299 total++; 300 } 301 } 302 } 303 for(int id = 0; id < ntests; ++id) { 304 if(std::string(tests[id].type) == "exit") { 305 if(match(flag_list_regexp.c_str(), tests[id].name) != 0) { 306 printf("[exit] %s\n", tests[id].name); 307 total++; 308 } 309 } 310 } 311 for(int id = 0; id < ntests; ++id) { 312 if(std::string(tests[id].type) == "test") { 313 if(match(flag_list_regexp.c_str(), tests[id].name) != 0) { 314 printf("[test] %s\n", tests[id].name); 315 total++; 316 } 317 } 318 } 319 for(int id = 0; id < ntests; ++id) { 320 if(std::string(tests[id].type) == "bench") { 321 if(match(flag_list_regexp.c_str(), tests[id].name) != 0) { 322 printf("[bench] %s\n", tests[id].name); 323 total++; 324 } 325 } 326 } 327 printf("total %d\n", total); 328 return 0; 329 } 330 331 // run init func 332 for(int id = 0; id < ntests; ++id) { 333 if(std::string(tests[id].type) == "init") { 334 printf("[init] %s ", tests[id].name); 335 tests[id].fn(); 336 printf("\n"); 337 } 338 } 339 340 // run test func 341 if(!flag_test_regexp.empty()) { 342 for(int id = 0; id < ntests; ++id) { 343 if(std::string(tests[id].type) == "test") { 344 if(match(flag_test_regexp.c_str(), tests[id].name) != 0) { 345 printf("[test] %s ", tests[id].name); 346 tests[id].fn(); 347 printf("ok\n"); 348 } 349 } 350 } 351 } 352 353 // run bench func 354 if(!flag_test_bench_regexp.empty()) { 355 if(bench.benchtime <= 0.1) bench.benchtime = 1.0; 356 for(int id = 0; id < ntests; ++id) { 357 if(std::string(tests[id].type) == "bench") { 358 if(match(flag_test_bench_regexp.c_str(), tests[id].name) != 0) { 359 benchRun(id); 360 } 361 } 362 } 363 } 364 365 // run exit func 366 for(int id = 0; id < ntests; ++id) { 367 if(std::string(tests[id].type) == "exit") { 368 printf("[exit] %s ", tests[id].name); 369 tests[id].fn(); 370 printf("\n"); 371 } 372 } 373 374 printf("PASS\n"); 375 return 0; 376 }