hayai_main.hpp
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             // Clean up the outputters.
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                 // List flag.
00221                 if ((!strcmp(arg, "-l")) || (!strcmp(arg, "--list")))
00222                     ExecutionMode = ::hayai::MainListBenchmarks;
00223                 // Shuffle flag.
00224                 else if ((!strcmp(arg, "-s")) || (!strcmp(arg, "--shuffle")))
00225                     ShuffleBenchmarks = true;
00226                 // Filter flag.
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                 // Output flag.
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                 // Console coloring flag.
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                 // Help.
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             // Execute based on the selected mode.
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             // Hook up the outputs.
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             // Run the benchmarks.
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             // List out the unique benchmark names.
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             // Sort the benchmark names.
00423             std::sort(testNames.begin(), testNames.end());
00424 
00425             // Dump the list.
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


hayai
Author(s): Nick Bruun
autogenerated on Thu Jun 6 2019 18:13:43