diff options
Diffstat (limited to 'deps/inja/third_party/include/hayai/hayai_main.hpp')
-rwxr-xr-x | deps/inja/third_party/include/hayai/hayai_main.hpp | 530 |
1 files changed, 530 insertions, 0 deletions
diff --git a/deps/inja/third_party/include/hayai/hayai_main.hpp b/deps/inja/third_party/include/hayai/hayai_main.hpp new file mode 100755 index 0000000..d6d7cd3 --- /dev/null +++ b/deps/inja/third_party/include/hayai/hayai_main.hpp @@ -0,0 +1,530 @@ +#ifndef __HAYAI_MAIN +#define __HAYAI_MAIN +#include <algorithm> +#include <cstdlib> +#include <cstring> +#include <ctime> +#include <errno.h> +#include <fstream> +#include <set> +#include <vector> + +#include "hayai.hpp" + + +#if defined(_WIN32) +# define PATH_SEPARATOR '\\' +#else +# define PATH_SEPARATOR '/' +#endif + + +#define HAYAI_MAIN_FORMAT_FLAG(_desc) \ + ::hayai::Console::TextGreen << _desc << ::hayai::Console::TextDefault +#define HAYAI_MAIN_FORMAT_ARGUMENT(_desc) \ + ::hayai::Console::TextYellow << _desc << ::hayai::Console::TextDefault +#define HAYAI_MAIN_FORMAT_ERROR(_desc) \ + ::hayai::Console::TextRed << "Error:" << \ + ::hayai::Console::TextDefault << " " << _desc +#define HAYAI_MAIN_USAGE_ERROR(_desc) \ + { \ + std::cerr << HAYAI_MAIN_FORMAT_ERROR(_desc) << std::endl \ + << std::endl; \ + ShowUsage(argv[0]); \ + return EXIT_FAILURE; \ + } + + +namespace hayai +{ + /// Execution mode. + enum MainExecutionMode + { + /// Run benchmarks. + MainRunBenchmarks, + + + /// List benchmarks but do not execute them. + MainListBenchmarks + }; + + + /// File outputter. + class FileOutputter + { + public: + /// File outputter. + + /// @param path Output path. Expected to be available during the life + /// time of the outputter. + FileOutputter(const char* path) + : _path(path), + _outputter(NULL) + { + + } + + + virtual ~FileOutputter() + { + if (_outputter) + delete _outputter; + + _stream.close(); + } + + + /// Set up. + + /// Opens the output file for writing and initializes the outputter. + virtual void SetUp() + { + _stream.open(_path, + std::ios_base::out | + std::ios_base::trunc | + std::ios_base::binary); + if (_stream.bad()) + { + std::stringstream error; + error << "failed to open " << _path << " for writing: " + << strerror(errno); + throw std::runtime_error(error.str()); + } + + _outputter = CreateOutputter(_stream); + } + + + /// Outputter. + virtual ::hayai::Outputter& Outputter() + { + if (!_outputter) + throw std::runtime_error("outputter has not been set up"); + + return *_outputter; + } + protected: + /// Create outputter from output stream. + + /// @param stream Output stream for the outputter. + /// @returns the outputter for the given format. + virtual ::hayai::Outputter* CreateOutputter(std::ostream& stream) = 0; + private: + const char* _path; + std::ofstream _stream; + ::hayai::Outputter* _outputter; + }; + + +#define FILE_OUTPUTTER_IMPLEMENTATION(_prefix) \ + class _prefix ## FileOutputter \ + : public FileOutputter \ + { \ + public: \ + _prefix ## FileOutputter(const char* path) \ + : FileOutputter(path) \ + {} \ + protected: \ + virtual ::hayai::Outputter* CreateOutputter(std::ostream& stream) \ + { \ + return new ::hayai::_prefix ## Outputter(stream); \ + } \ + } + + + FILE_OUTPUTTER_IMPLEMENTATION(Json); + FILE_OUTPUTTER_IMPLEMENTATION(Console); + FILE_OUTPUTTER_IMPLEMENTATION(JUnitXml); + +#undef FILE_OUTPUTTER_IMPLEMENTATION + + + /// Default main executable runner for Hayai. + class MainRunner + { + public: + MainRunner() + : ExecutionMode(MainRunBenchmarks), + ShuffleBenchmarks(false), + StdoutOutputter(NULL) + { + + } + + + ~MainRunner() + { + // Clean up the outputters. + for (std::vector<FileOutputter*>::iterator it = + FileOutputters.begin(); + it != FileOutputters.end(); + ++it) + delete *it; + + if (StdoutOutputter) + delete StdoutOutputter; + } + + + /// Execution mode. + MainExecutionMode ExecutionMode; + + + /// Shuffle benchmarks. + bool ShuffleBenchmarks; + + + /// File outputters. + /// + /// Outputter will be freed by the class on destruction. + std::vector<FileOutputter*> FileOutputters; + + + /// Standard output outputter. + /// + /// Will be freed by the class on destruction. + Outputter* StdoutOutputter; + + + /// Parse arguments. + + /// @param argc Argument count including the executable name. + /// @param argv Arguments. + /// @param residualArgs Pointer to vector to hold residual arguments + /// after parsing. If not NULL, the parser will not fail upon + /// encounting an unknown argument but will instead add it to the list + /// of residual arguments and return a success code. Note: the parser + /// will still fail if an invalid value is provided to a known + /// argument. + /// @returns 0 on success, otherwise the exit status code to be + /// returned from the executable. + int ParseArgs(int argc, + char** argv, + std::vector<char*>* residualArgs = NULL) + { + int argI = 1; + while (argI < argc) + { + char* arg = argv[argI++]; + bool argLast = (argI == argc); + std::size_t argLen = strlen(arg); + + if (argLen == 0) + continue; + + // List flag. + if ((!strcmp(arg, "-l")) || (!strcmp(arg, "--list"))) + ExecutionMode = ::hayai::MainListBenchmarks; + // Shuffle flag. + else if ((!strcmp(arg, "-s")) || (!strcmp(arg, "--shuffle"))) + ShuffleBenchmarks = true; + // Filter flag. + else if ((!strcmp(arg, "-f")) || (!strcmp(arg, "--filter"))) + { + if ((argLast) || (*argv[argI] == 0)) + HAYAI_MAIN_USAGE_ERROR(HAYAI_MAIN_FORMAT_FLAG(arg) << + " requires a pattern to be specified"); + char* pattern = argv[argI++]; + + ::hayai::Benchmarker::ApplyPatternFilter(pattern); + } + // Output flag. + else if ((!strcmp(arg, "-o")) || (!strcmp(arg, "--output"))) + { + if (argLast) + HAYAI_MAIN_USAGE_ERROR(HAYAI_MAIN_FORMAT_FLAG(arg) << + " requires a format to be specified"); + char* formatSpecifier = argv[argI++]; + + char* format = formatSpecifier; + char* path = strchr(formatSpecifier, ':'); + if (path) + { + *(path++) = 0; + if (!strlen(path)) + path = NULL; + } + +#define ADD_OUTPUTTER(_prefix) \ + { \ + if (path) \ + FileOutputters.push_back( \ + new ::hayai::_prefix ## FileOutputter(path) \ + ); \ + else \ + { \ + if (StdoutOutputter) \ + delete StdoutOutputter; \ + StdoutOutputter = \ + new ::hayai::_prefix ## Outputter(std::cout); \ + } \ + } + + if (!strcmp(format, "console")) + ADD_OUTPUTTER(Console) + else if (!strcmp(format, "json")) + ADD_OUTPUTTER(Json) + else if (!strcmp(format, "junit")) + ADD_OUTPUTTER(JUnitXml) + else + HAYAI_MAIN_USAGE_ERROR("invalid format: " << format); + +#undef ADD_OUTPUTTER + } + // Console coloring flag. + else if ((!strcmp(arg, "-c")) || (!strcmp(arg, "--color"))) + { + if (argLast) + HAYAI_MAIN_USAGE_ERROR( + HAYAI_MAIN_FORMAT_FLAG(arg) << + " requires an argument " << + "of either " << HAYAI_MAIN_FORMAT_FLAG("yes") << + " or " << HAYAI_MAIN_FORMAT_FLAG("no") + ); + + char* choice = argv[argI++]; + bool enabled; + + if ((!strcmp(choice, "yes")) || + (!strcmp(choice, "true")) || + (!strcmp(choice, "on")) || + (!strcmp(choice, "1"))) + enabled = true; + else if ((!strcmp(choice, "no")) || + (!strcmp(choice, "false")) || + (!strcmp(choice, "off")) || + (!strcmp(choice, "0"))) + enabled = false; + else + HAYAI_MAIN_USAGE_ERROR( + "invalid argument to " << + HAYAI_MAIN_FORMAT_FLAG(arg) << + ": " << choice + ); + + ::hayai::Console::SetFormattingEnabled(enabled); + } + // Help. + else if ((!strcmp(arg, "-?")) || + (!strcmp(arg, "-h")) || + (!strcmp(arg, "--help"))) + { + ShowUsage(argv[0]); + return EXIT_FAILURE; + } + else if (residualArgs) + { + residualArgs->push_back(arg); + } + else + { + HAYAI_MAIN_USAGE_ERROR("unknown option: " << arg); + } + } + + return EXIT_SUCCESS; + } + + + /// Run the selected execution mode. + + /// @returns the exit status code to be returned from the executable. + int Run() + { + // Execute based on the selected mode. + switch (ExecutionMode) + { + case ::hayai::MainRunBenchmarks: + return RunBenchmarks(); + + case ::hayai::MainListBenchmarks: + return ListBenchmarks(); + + default: + std::cerr << HAYAI_MAIN_FORMAT_ERROR( + "invalid execution mode: " << ExecutionMode + ) << std::endl; + return EXIT_FAILURE; + } + } + private: + /// Run benchmarks. + + /// @returns the exit status code to be returned from the executable. + int RunBenchmarks() + { + // Hook up the outputs. + if (StdoutOutputter) + ::hayai::Benchmarker::AddOutputter(*StdoutOutputter); + + for (std::vector< ::hayai::FileOutputter*>::iterator it = + FileOutputters.begin(); + it < FileOutputters.end(); + ++it) + { + ::hayai::FileOutputter& fileOutputter = **it; + + try + { + fileOutputter.SetUp(); + } + catch (std::exception& e) + { + std::cerr << HAYAI_MAIN_FORMAT_ERROR(e.what()) << std::endl; + return EXIT_FAILURE; + } + + ::hayai::Benchmarker::AddOutputter(fileOutputter.Outputter()); + } + + // Run the benchmarks. + if (ShuffleBenchmarks) + { + std::srand(static_cast<unsigned>(std::time(0))); + ::hayai::Benchmarker::ShuffleTests(); + } + + ::hayai::Benchmarker::RunAllTests(); + + return EXIT_SUCCESS; + } + + + /// List benchmarks. + + /// @returns the exit status code to be returned from the executable. + int ListBenchmarks() + { + // List out the unique benchmark names. + std::vector<const ::hayai::TestDescriptor*> tests = + ::hayai::Benchmarker::ListTests(); + std::vector<std::string> testNames; + std::set<std::string> uniqueTestNames; + + for (std::vector<const ::hayai::TestDescriptor*>::iterator it = + tests.begin(); + it < tests.end(); + ++it) + { + if (uniqueTestNames.find((*it)->CanonicalName) != + uniqueTestNames.end()) + continue; + + testNames.push_back((*it)->CanonicalName); + uniqueTestNames.insert((*it)->CanonicalName); + } + + // Sort the benchmark names. + std::sort(testNames.begin(), testNames.end()); + + // Dump the list. + for (std::vector<std::string>::iterator it = testNames.begin(); + it < testNames.end(); + ++it) + std::cout << *it << std::endl; + + return EXIT_SUCCESS; + } + + + /// Show usage. + + /// @param execName Executable name. + void ShowUsage(const char* execName) + { + const char* baseName = strrchr(execName, PATH_SEPARATOR); + + std::cerr << "Usage: " << (baseName ? baseName + 1 : execName) + << " " << HAYAI_MAIN_FORMAT_FLAG("[OPTIONS]") << std::endl + << std::endl + + << " Runs the benchmarks for this project." << std::endl + << std::endl + + << "Benchmark selection options:" << std::endl + << " " << HAYAI_MAIN_FORMAT_FLAG("-l") << ", " + << HAYAI_MAIN_FORMAT_FLAG("--list") + << std::endl + << " List the names of all benchmarks instead of " + << "running them." << std::endl + << " " << HAYAI_MAIN_FORMAT_FLAG("-f") << ", " + << HAYAI_MAIN_FORMAT_FLAG("--filter") + << " <" << HAYAI_MAIN_FORMAT_ARGUMENT("pattern") << ">" + << std::endl + << " Run only the tests whose name matches one of the " + << "positive patterns but" << std::endl + << " none of the negative patterns. '?' matches any " + << "single character; '*'" << std::endl + << " matches any substring; ':' separates two " + << "patterns." + << std::endl + + << "Benchmark execution options:" << std::endl + << " " << HAYAI_MAIN_FORMAT_FLAG("-s") << ", " + << HAYAI_MAIN_FORMAT_FLAG("--shuffle") + << std::endl + << " Randomize benchmark execution order." + << std::endl + << std::endl + + << "Benchmark output options:" << std::endl + << " " << HAYAI_MAIN_FORMAT_FLAG("-o") << ", " + << HAYAI_MAIN_FORMAT_FLAG("--output") + << " <" << HAYAI_MAIN_FORMAT_ARGUMENT("format") << ">[:" + << HAYAI_MAIN_FORMAT_ARGUMENT("<path>") << "]" + << std::endl + << " Output results in a specific format. If no " + << "path is specified, the output" << std::endl + << " will be presented on stdout. Can be specified " + << "multiple times to get output" << std::endl + << " in different formats. The supported formats are:" + << std::endl + << std::endl + << " " << HAYAI_MAIN_FORMAT_ARGUMENT("console") + << std::endl + << " Standard console output." << std::endl + << " " << HAYAI_MAIN_FORMAT_ARGUMENT("json") + << std::endl + << " JSON." << std::endl + << " " << HAYAI_MAIN_FORMAT_ARGUMENT("junit") + << std::endl + << " JUnit-compatible XML (very restrictive.)" + << std::endl + << std::endl + << " If multiple output formats are provided without " + << "a path, only the last" << std::endl + << " provided format will be output to stdout." + << std::endl + << " " << HAYAI_MAIN_FORMAT_FLAG("--c") << ", " + << HAYAI_MAIN_FORMAT_FLAG("--color") << " (" + << ::hayai::Console::TextGreen << "yes" + << ::hayai::Console::TextDefault << "|" + << ::hayai::Console::TextGreen << "no" + << ::hayai::Console::TextDefault << ")" << std::endl + << " Enable colored output when available. Default " + << ::hayai::Console::TextGreen << "yes" + << ::hayai::Console::TextDefault << "." << std::endl + << std::endl + + << "Miscellaneous options:" << std::endl + << " " << HAYAI_MAIN_FORMAT_FLAG("-?") << ", " + << HAYAI_MAIN_FORMAT_FLAG("-h") << ", " + << HAYAI_MAIN_FORMAT_FLAG("--help") << std::endl + << " Show this help information." << std::endl + << std::endl + + << "hayai version: " << HAYAI_VERSION << std::endl + << "Clock implementation: " + << ::hayai::Clock::Description() + << std::endl; + } + }; +} + + +#undef HAYAI_MAIN_FORMAT_FLAG +#undef HAYAI_MAIN_FORMAT_ARGUMENT +#undef HAYAI_MAIN_FORMAT_ERROR +#undef HAYAI_MAIN_USAGE_ERROR + +#endif |