Go to the documentation of this file.00001 #ifndef __HAYAI_MAIN
00002 #define __HAYAI_MAIN
00003 #include <algorithm>
00004 #include <cstdlib>
00005 #include <cstring>
00006 #include <ctime>
00007 #include <errno.h>
00008 #include <fstream>
00009 #include <set>
00010 #include <vector>
00011
00012 #include "hayai/hayai.hpp"
00013
00014
00015 #if defined(_WIN32)
00016 # define PATH_SEPARATOR '\\'
00017 #else
00018 # define PATH_SEPARATOR '/'
00019 #endif
00020
00021
00022 #define HAYAI_MAIN_FORMAT_FLAG(_desc) \
00023 ::hayai::Console::TextGreen << _desc << ::hayai::Console::TextDefault
00024 #define HAYAI_MAIN_FORMAT_ARGUMENT(_desc) \
00025 ::hayai::Console::TextYellow << _desc << ::hayai::Console::TextDefault
00026 #define HAYAI_MAIN_FORMAT_ERROR(_desc) \
00027 ::hayai::Console::TextRed << "Error:" << \
00028 ::hayai::Console::TextDefault << " " << _desc
00029 #define HAYAI_MAIN_USAGE_ERROR(_desc) \
00030 { \
00031 std::cerr << HAYAI_MAIN_FORMAT_ERROR(_desc) << std::endl \
00032 << std::endl; \
00033 ShowUsage(argv[0]); \
00034 return EXIT_FAILURE; \
00035 }
00036
00037
00038 namespace hayai
00039 {
00041 enum MainExecutionMode
00042 {
00044 MainRunBenchmarks,
00045
00046
00048 MainListBenchmarks
00049 };
00050
00051
00053 class FileOutputter
00054 {
00055 public:
00057
00060 FileOutputter(const char* path)
00061 : _path(path),
00062 _outputter(NULL)
00063 {
00064
00065 }
00066
00067
00068 virtual ~FileOutputter()
00069 {
00070 if (_outputter)
00071 {
00072 delete _outputter;
00073 }
00074
00075 _stream.close();
00076 }
00077
00078
00080
00082 virtual void SetUp()
00083 {
00084 _stream.open(_path,
00085 std::ios_base::out |
00086 std::ios_base::trunc |
00087 std::ios_base::binary);
00088
00089 if (_stream.bad())
00090 {
00091 std::stringstream error;
00092 error << "failed to open " << _path << " for writing: "
00093 << strerror(errno);
00094 throw std::runtime_error(error.str());
00095 }
00096
00097 _outputter = CreateOutputter(_stream);
00098 }
00099
00100
00102 virtual ::hayai::Outputter& Outputter()
00103 {
00104 if (!_outputter)
00105 {
00106 throw std::runtime_error("outputter has not been set up");
00107 }
00108
00109 return *_outputter;
00110 }
00111 protected:
00113
00116 virtual ::hayai::Outputter* CreateOutputter(std::ostream& stream) = 0;
00117 private:
00118 const char* _path;
00119 std::ofstream _stream;
00120 ::hayai::Outputter* _outputter;
00121 };
00122
00123
00124 #define FILE_OUTPUTTER_IMPLEMENTATION(_prefix) \
00125 class _prefix ## FileOutputter \
00126 : public FileOutputter \
00127 { \
00128 public: \
00129 _prefix ## FileOutputter(const char* path) \
00130 : FileOutputter(path) \
00131 {} \
00132 protected: \
00133 virtual ::hayai::Outputter* CreateOutputter(std::ostream& stream) \
00134 { \
00135 return new ::hayai::_prefix ## Outputter(stream); \
00136 } \
00137 }
00138
00139
00140 FILE_OUTPUTTER_IMPLEMENTATION(Json);
00141 FILE_OUTPUTTER_IMPLEMENTATION(Console);
00142 FILE_OUTPUTTER_IMPLEMENTATION(JUnitXml);
00143
00144 #undef FILE_OUTPUTTER_IMPLEMENTATION
00145
00146
00148 class MainRunner
00149 {
00150 public:
00151 MainRunner()
00152 : ExecutionMode(MainRunBenchmarks),
00153 ShuffleBenchmarks(false),
00154 StdoutOutputter(NULL)
00155 {
00156
00157 }
00158
00159
00160 ~MainRunner()
00161 {
00162
00163 for (std::vector<FileOutputter*>::iterator it =
00164 FileOutputters.begin();
00165 it != FileOutputters.end();
00166 ++it)
00167 delete *it;
00168
00169 if (StdoutOutputter)
00170 delete StdoutOutputter;
00171 }
00172
00173
00175 MainExecutionMode ExecutionMode;
00176
00177
00179 bool ShuffleBenchmarks;
00180
00181
00185 std::vector<FileOutputter*> FileOutputters;
00186
00187
00191 Outputter* StdoutOutputter;
00192
00193
00195
00206 int ParseArgs(int argc,
00207 char** argv,
00208 std::vector<char*>* residualArgs = NULL)
00209 {
00210 int argI = 1;
00211 while (argI < argc)
00212 {
00213 char* arg = argv[argI++];
00214 bool argLast = (argI == argc);
00215 std::size_t argLen = strlen(arg);
00216
00217 if (argLen == 0)
00218 continue;
00219
00220
00221 if ((!strcmp(arg, "-l")) || (!strcmp(arg, "--list")))
00222 ExecutionMode = ::hayai::MainListBenchmarks;
00223
00224 else if ((!strcmp(arg, "-s")) || (!strcmp(arg, "--shuffle")))
00225 ShuffleBenchmarks = true;
00226
00227 else if ((!strcmp(arg, "-f")) || (!strcmp(arg, "--filter")))
00228 {
00229 if ((argLast) || (*argv[argI] == 0))
00230 HAYAI_MAIN_USAGE_ERROR(HAYAI_MAIN_FORMAT_FLAG(arg) <<
00231 " requires a pattern to be specified");
00232 char* pattern = argv[argI++];
00233
00234 ::hayai::Benchmarker::ApplyPatternFilter(pattern);
00235 }
00236
00237 else if ((!strcmp(arg, "-o")) || (!strcmp(arg, "--output")))
00238 {
00239 if (argLast)
00240 HAYAI_MAIN_USAGE_ERROR(HAYAI_MAIN_FORMAT_FLAG(arg) <<
00241 " requires a format to be specified");
00242 char* formatSpecifier = argv[argI++];
00243
00244 char* format = formatSpecifier;
00245 char* path = strchr(formatSpecifier, ':');
00246 if (path)
00247 {
00248 *(path++) = 0;
00249 if (!strlen(path))
00250 path = NULL;
00251 }
00252
00253 #define ADD_OUTPUTTER(_prefix) \
00254 { \
00255 if (path) \
00256 FileOutputters.push_back( \
00257 new ::hayai::_prefix ## FileOutputter(path) \
00258 ); \
00259 else \
00260 { \
00261 if (StdoutOutputter) \
00262 delete StdoutOutputter; \
00263 StdoutOutputter = \
00264 new ::hayai::_prefix ## Outputter(std::cout); \
00265 } \
00266 }
00267
00268 if (!strcmp(format, "console"))
00269 ADD_OUTPUTTER(Console)
00270 else if (!strcmp(format, "json"))
00271 ADD_OUTPUTTER(Json)
00272 else if (!strcmp(format, "junit"))
00273 ADD_OUTPUTTER(JUnitXml)
00274 else
00275 HAYAI_MAIN_USAGE_ERROR("invalid format: " << format);
00276
00277 #undef ADD_OUTPUTTER
00278 }
00279
00280 else if ((!strcmp(arg, "-c")) || (!strcmp(arg, "--color")))
00281 {
00282 if (argLast)
00283 HAYAI_MAIN_USAGE_ERROR(
00284 HAYAI_MAIN_FORMAT_FLAG(arg) <<
00285 " requires an argument " <<
00286 "of either " << HAYAI_MAIN_FORMAT_FLAG("yes") <<
00287 " or " << HAYAI_MAIN_FORMAT_FLAG("no")
00288 );
00289
00290 char* choice = argv[argI++];
00291 bool enabled;
00292
00293 if ((!strcmp(choice, "yes")) ||
00294 (!strcmp(choice, "true")) ||
00295 (!strcmp(choice, "on")) ||
00296 (!strcmp(choice, "1")))
00297 enabled = true;
00298 else if ((!strcmp(choice, "no")) ||
00299 (!strcmp(choice, "false")) ||
00300 (!strcmp(choice, "off")) ||
00301 (!strcmp(choice, "0")))
00302 enabled = false;
00303 else
00304 HAYAI_MAIN_USAGE_ERROR(
00305 "invalid argument to " <<
00306 HAYAI_MAIN_FORMAT_FLAG(arg) <<
00307 ": " << choice
00308 );
00309
00310 ::hayai::Console::SetFormattingEnabled(enabled);
00311 }
00312
00313 else if ((!strcmp(arg, "-?")) ||
00314 (!strcmp(arg, "-h")) ||
00315 (!strcmp(arg, "--help")))
00316 {
00317 ShowUsage(argv[0]);
00318 return EXIT_FAILURE;
00319 }
00320 else if (residualArgs)
00321 {
00322 residualArgs->push_back(arg);
00323 }
00324 else
00325 {
00326 HAYAI_MAIN_USAGE_ERROR("unknown option: " << arg);
00327 }
00328 }
00329
00330 return EXIT_SUCCESS;
00331 }
00332
00333
00335
00337 int Run()
00338 {
00339
00340 switch (ExecutionMode)
00341 {
00342 case ::hayai::MainRunBenchmarks:
00343 return RunBenchmarks();
00344
00345 case ::hayai::MainListBenchmarks:
00346 return ListBenchmarks();
00347
00348 default:
00349 std::cerr << HAYAI_MAIN_FORMAT_ERROR(
00350 "invalid execution mode: " << ExecutionMode
00351 ) << std::endl;
00352 return EXIT_FAILURE;
00353 }
00354 }
00355 private:
00357
00359 int RunBenchmarks()
00360 {
00361
00362 if (StdoutOutputter)
00363 ::hayai::Benchmarker::AddOutputter(*StdoutOutputter);
00364
00365 for (std::vector< ::hayai::FileOutputter*>::iterator it =
00366 FileOutputters.begin();
00367 it < FileOutputters.end();
00368 ++it)
00369 {
00370 ::hayai::FileOutputter& fileOutputter = **it;
00371
00372 try
00373 {
00374 fileOutputter.SetUp();
00375 }
00376 catch (std::exception& e)
00377 {
00378 std::cerr << HAYAI_MAIN_FORMAT_ERROR(e.what()) << std::endl;
00379 return EXIT_FAILURE;
00380 }
00381
00382 ::hayai::Benchmarker::AddOutputter(fileOutputter.Outputter());
00383 }
00384
00385
00386 if (ShuffleBenchmarks)
00387 {
00388 std::srand(static_cast<unsigned>(std::time(0)));
00389 ::hayai::Benchmarker::ShuffleTests();
00390 }
00391
00392 ::hayai::Benchmarker::RunAllTests();
00393
00394 return EXIT_SUCCESS;
00395 }
00396
00397
00399
00401 int ListBenchmarks()
00402 {
00403
00404 std::vector<const ::hayai::TestDescriptor*> tests =
00405 ::hayai::Benchmarker::ListTests();
00406 std::vector<std::string> testNames;
00407 std::set<std::string> uniqueTestNames;
00408
00409 for (std::vector<const ::hayai::TestDescriptor*>::iterator it =
00410 tests.begin();
00411 it < tests.end();
00412 ++it)
00413 {
00414 if (uniqueTestNames.find((*it)->CanonicalName) !=
00415 uniqueTestNames.end())
00416 continue;
00417
00418 testNames.push_back((*it)->CanonicalName);
00419 uniqueTestNames.insert((*it)->CanonicalName);
00420 }
00421
00422
00423 std::sort(testNames.begin(), testNames.end());
00424
00425
00426 for (std::vector<std::string>::iterator it = testNames.begin();
00427 it < testNames.end();
00428 ++it)
00429 std::cout << *it << std::endl;
00430
00431 return EXIT_SUCCESS;
00432 }
00433
00434
00436
00438 void ShowUsage(const char* execName)
00439 {
00440 const char* baseName = strrchr(execName, PATH_SEPARATOR);
00441
00442 std::cerr << "Usage: " << (baseName ? baseName + 1 : execName)
00443 << " " << HAYAI_MAIN_FORMAT_FLAG("[OPTIONS]") << std::endl
00444 << std::endl
00445
00446 << " Runs the benchmarks for this project." << std::endl
00447 << std::endl
00448
00449 << "Benchmark selection options:" << std::endl
00450 << " " << HAYAI_MAIN_FORMAT_FLAG("-l") << ", "
00451 << HAYAI_MAIN_FORMAT_FLAG("--list")
00452 << std::endl
00453 << " List the names of all benchmarks instead of "
00454 << "running them." << std::endl
00455 << " " << HAYAI_MAIN_FORMAT_FLAG("-f") << ", "
00456 << HAYAI_MAIN_FORMAT_FLAG("--filter")
00457 << " <" << HAYAI_MAIN_FORMAT_ARGUMENT("pattern") << ">"
00458 << std::endl
00459 << " Run only the tests whose name matches one of the "
00460 << "positive patterns but" << std::endl
00461 << " none of the negative patterns. '?' matches any "
00462 << "single character; '*'" << std::endl
00463 << " matches any substring; ':' separates two "
00464 << "patterns."
00465 << std::endl
00466
00467 << "Benchmark execution options:" << std::endl
00468 << " " << HAYAI_MAIN_FORMAT_FLAG("-s") << ", "
00469 << HAYAI_MAIN_FORMAT_FLAG("--shuffle")
00470 << std::endl
00471 << " Randomize benchmark execution order."
00472 << std::endl
00473 << std::endl
00474
00475 << "Benchmark output options:" << std::endl
00476 << " " << HAYAI_MAIN_FORMAT_FLAG("-o") << ", "
00477 << HAYAI_MAIN_FORMAT_FLAG("--output")
00478 << " <" << HAYAI_MAIN_FORMAT_ARGUMENT("format") << ">[:"
00479 << HAYAI_MAIN_FORMAT_ARGUMENT("<path>") << "]"
00480 << std::endl
00481 << " Output results in a specific format. If no "
00482 << "path is specified, the output" << std::endl
00483 << " will be presented on stdout. Can be specified "
00484 << "multiple times to get output" << std::endl
00485 << " in different formats. The supported formats are:"
00486 << std::endl
00487 << std::endl
00488 << " " << HAYAI_MAIN_FORMAT_ARGUMENT("console")
00489 << std::endl
00490 << " Standard console output." << std::endl
00491 << " " << HAYAI_MAIN_FORMAT_ARGUMENT("json")
00492 << std::endl
00493 << " JSON." << std::endl
00494 << " " << HAYAI_MAIN_FORMAT_ARGUMENT("junit")
00495 << std::endl
00496 << " JUnit-compatible XML (very restrictive.)"
00497 << std::endl
00498 << std::endl
00499 << " If multiple output formats are provided without "
00500 << "a path, only the last" << std::endl
00501 << " provided format will be output to stdout."
00502 << std::endl
00503 << " " << HAYAI_MAIN_FORMAT_FLAG("--c") << ", "
00504 << HAYAI_MAIN_FORMAT_FLAG("--color") << " ("
00505 << ::hayai::Console::TextGreen << "yes"
00506 << ::hayai::Console::TextDefault << "|"
00507 << ::hayai::Console::TextGreen << "no"
00508 << ::hayai::Console::TextDefault << ")" << std::endl
00509 << " Enable colored output when available. Default "
00510 << ::hayai::Console::TextGreen << "yes"
00511 << ::hayai::Console::TextDefault << "." << std::endl
00512 << std::endl
00513
00514 << "Miscellaneous options:" << std::endl
00515 << " " << HAYAI_MAIN_FORMAT_FLAG("-?") << ", "
00516 << HAYAI_MAIN_FORMAT_FLAG("-h") << ", "
00517 << HAYAI_MAIN_FORMAT_FLAG("--help") << std::endl
00518 << " Show this help information." << std::endl
00519 << std::endl
00520
00521 << "hayai version: " << HAYAI_VERSION << std::endl
00522 << "Clock implementation: "
00523 << ::hayai::Clock::Description()
00524 << std::endl;
00525 }
00526 };
00527 }
00528
00529
00530 #undef HAYAI_MAIN_FORMAT_FLAG
00531 #undef HAYAI_MAIN_FORMAT_ARGUMENT
00532 #undef HAYAI_MAIN_FORMAT_ERROR
00533 #undef HAYAI_MAIN_USAGE_ERROR
00534
00535 #endif