#ifndef __HAYAI_TESTDESCRIPTOR #define __HAYAI_TESTDESCRIPTOR #include #include #include #include #include "hayai_test.hpp" #include "hayai_test_factory.hpp" namespace hayai { /// Parameter declaration. /// Describes parameter type and name. class TestParameterDescriptor { public: TestParameterDescriptor(std::string declaration, std::string value) : Declaration(declaration), Value(value) { } /// Declaration. std::string Declaration; /// Value. std::string Value; }; /// Test parameters descriptor. class TestParametersDescriptor { private: /// Quoting state. enum QuotingState { /// Unquoted. Unquoted, /// Single quoted. SingleQuoted, /// Double quoted. DoubleQuoted }; /// Trimmed string. /// @param start Start character. /// @param end Character one position beyond end. inline static std::string TrimmedString(const char* start, const char* end) { while (start < end) { if ((*start == ' ') || (*start == '\r') || (*start == '\n') || (*start == '\t')) ++start; else break; } while (end > start) { const char c = *(end - 1); if ((c != ' ') && (c != '\r') && (c != '\n') && (c != '\t')) break; --end; } return std::string(start, std::string::size_type(end - start)); } /// Parse comma separated parentherized value. /// @param separated Separated values as "(..[, ..])". /// @returns the individual values with white space trimmed. static std::vector ParseCommaSeparated(const char* separated) { std::vector result; if (*separated) ++separated; while ((*separated) && (*separated != ')')) { std::size_t escapeCounter = 0; const char* start = separated; QuotingState state = Unquoted; bool escaped = false; while (*separated) { const char c = *separated++; if (state == Unquoted) { if ((c == '"') || (c == '\'')) { state = (c == '"' ? DoubleQuoted : SingleQuoted); escaped = false; } else if ((c == '<') || (c == '(') || (c == '[') || (c == '{')) ++escapeCounter; else if ((escapeCounter) && ((c == '>') || (c == ')') || (c == ']') || (c == '}'))) --escapeCounter; else if ((!escapeCounter) && ((c == ',') || (c == ')'))) { result.push_back(TrimmedString(start, separated - 1)); break; } } else { if (escaped) escaped = false; else if (c == '\\') escaped = true; else if (c == (state == DoubleQuoted ? '"' : '\'')) state = Unquoted; } } } return result; } /// Parse parameter declaration. /// @param raw Raw declaration. TestParameterDescriptor ParseDescriptor(const std::string& raw) { const char* position = raw.c_str(); // Split the declaration into its declaration and its default // type. const char* equalPosition = NULL; std::size_t escapeCounter = 0; QuotingState state = Unquoted; bool escaped = false; while (*position) { const char c = *position++; if (state == Unquoted) { if ((c == '"') || (c == '\'')) { state = (c == '"' ? DoubleQuoted : SingleQuoted); escaped = false; } else if ((c == '<') || (c == '(') || (c == '[') || (c == '{')) ++escapeCounter; else if ((escapeCounter) && ((c == '>') || (c == ')') || (c == ']') || (c == '}'))) --escapeCounter; else if ((!escapeCounter) && (c == '=')) { equalPosition = position; break; } } else { if (escaped) escaped = false; else if (c == '\\') escaped = true; else if (c == (state == DoubleQuoted ? '"' : '\'')) state = Unquoted; } } // Construct the parameter descriptor. if (equalPosition) { const char* start = raw.c_str(); const char* end = start + raw.length(); return TestParameterDescriptor( std::string(TrimmedString(start, equalPosition - 1)), std::string(TrimmedString(equalPosition, end)) ); } else return TestParameterDescriptor(raw, std::string()); } public: TestParametersDescriptor() { } TestParametersDescriptor(const char* rawDeclarations, const char* rawValues) { // Parse the declarations. std::vector declarations = ParseCommaSeparated(rawDeclarations); for (std::vector::const_iterator it = declarations.begin(); it != declarations.end(); ++it) _parameters.push_back(ParseDescriptor(*it)); // Parse the values. std::vector values = ParseCommaSeparated(rawValues); std::size_t straightValues = (_parameters.size() > values.size() ? values.size() : _parameters.size()), variadicValues = 0; if (values.size() > _parameters.size()) { if (straightValues > 0) --straightValues; variadicValues = values.size() - _parameters.size() + 1; } for (std::size_t i = 0; i < straightValues; ++i) _parameters[i].Value = values[i]; if (variadicValues) { std::stringstream variadic; for (std::size_t i = 0; i < variadicValues; ++i) { if (i) variadic << ", "; variadic << values[straightValues + i]; } _parameters[_parameters.size() - 1].Value = variadic.str(); } } inline const std::vector& Parameters() const { return _parameters; } private: std::vector _parameters; }; /// Test descriptor. class TestDescriptor { public: /// Initialize a new test descriptor. /// @param fixtureName Name of the fixture. /// @param testName Name of the test. /// @param runs Number of runs for the test. /// @param iterations Number of iterations per run. /// @param testFactory Test factory implementation for the test. /// @param parameters Parametrized test parameters. TestDescriptor(const char* fixtureName, const char* testName, std::size_t runs, std::size_t iterations, TestFactory* testFactory, TestParametersDescriptor parameters, bool isDisabled = false) : FixtureName(fixtureName), TestName(testName), CanonicalName(std::string(fixtureName) + "." + testName), Runs(runs), Iterations(iterations), Factory(testFactory), Parameters(parameters), IsDisabled(isDisabled) { } /// Dispose of a test descriptor. ~TestDescriptor() { delete this->Factory; } /// Fixture name. std::string FixtureName; /// Test name. std::string TestName; /// Canonical name. /// As: .. std::string CanonicalName; /// Test runs. std::size_t Runs; /// Iterations per test run. std::size_t Iterations; /// Test factory. TestFactory* Factory; /// Parameters for parametrized tests TestParametersDescriptor Parameters; /// Disabled. bool IsDisabled; }; } #endif