/// Parses a user-provided test filter. /// /// \param str The user-provided string representing a filter for tests. Must /// be of the form <test_program%gt;[:<test_case%gt;]. /// /// \return The parsed filter. /// /// \throw std::runtime_error If the provided filter is invalid. engine::test_filter engine::test_filter::parse(const std::string& str) { if (str.empty()) throw std::runtime_error("Test filter cannot be empty"); const std::string::size_type pos = str.find(':'); if (pos == 0) throw std::runtime_error(F("Program name component in '%s' is empty") % str); if (pos == str.length() - 1) throw std::runtime_error(F("Test case component in '%s' is empty") % str); try { const fs::path test_program_(str.substr(0, pos)); if (test_program_.is_absolute()) throw std::runtime_error(F("Program name '%s' must be relative " "to the test suite, not absolute") % test_program_.str()); if (pos == std::string::npos) { LD(F("Parsed user filter '%s': test program '%s', no test case") % str % test_program_.str()); return test_filter(test_program_, ""); } else { const std::string test_case_(str.substr(pos + 1)); LD(F("Parsed user filter '%s': test program '%s', test case '%s'") % str % test_program_.str() % test_case_); return test_filter(test_program_, test_case_); } } catch (const fs::error& e) { throw std::runtime_error(F("Invalid path in filter '%s': %s") % str % e.what()); } }
/// Concatenates this path with another path. /// /// \param rest The path to concatenate to this one. Cannot be absolute. /// /// \return A new path containing the concatenation of this path and the other /// path. /// /// \throw utils::fs::join_error If the join operation is invalid because the /// two paths are incompatible. fs::path fs::path::operator/(const fs::path& rest) const { if (rest.is_absolute()) throw fs::join_error(_repr, rest._repr, "Cannot concatenate a path to an absolute path"); return fs::path(_repr + '/' + rest._repr); }
/// Constructor. /// /// \param interface_name_ Name of the test program interface. /// \param binary_ The name of the test program binary relative to root_. /// \param root_ The root of the test suite containing the test program. /// \param test_suite_name_ The name of the test suite this program /// belongs to. /// \param md_ Metadata of the test program. impl(const std::string& interface_name_, const fs::path& binary_, const fs::path& root_, const std::string& test_suite_name_, const metadata& md_) : interface_name(interface_name_), binary(binary_), root(root_), test_suite_name(test_suite_name_), md(md_) { PRE_MSG(!binary.is_absolute(), F("The program '%s' must be relative to the root of the test " "suite '%s'") % binary % root); }
/// Returns the test suite name for the current directory. /// /// \return The identifier of the current test suite. std::string layout::test_suite_for_path(const fs::path& path) { std::string test_suite; if (path.is_absolute()) test_suite = path.str(); else test_suite = path.to_absolute().str(); PRE(!test_suite.empty() && test_suite[0] == '/'); std::replace(test_suite.begin(), test_suite.end(), '/', '_'); test_suite.erase(0, 1); return test_suite; }
/// Locates a file in the PATH. /// /// \param name The file to locate. /// /// \return The path to the located file or none if it was not found. The /// returned path is always absolute. optional< fs::path > fs::find_in_path(const char* name) { const optional< std::string > current_path = utils::getenv("PATH"); if (!current_path || current_path.get().empty()) return none; std::istringstream path_input(current_path.get() + ":"); std::string path_component; while (std::getline(path_input, path_component, ':').good()) { const fs::path candidate = path_component.empty() ? fs::path(name) : (fs::path(path_component) / name); if (exists(candidate)) { if (candidate.is_absolute()) return utils::make_optional(candidate); else return utils::make_optional(candidate.to_absolute()); } } return none; }
/// Constructor. /// /// \param interface_name_ Name of the test program interface. /// \param binary_ The name of the test program binary relative to root_. /// \param root_ The root of the test suite containing the test program. /// \param test_suite_name_ The name of the test suite this program /// belongs to. /// \param md_ Metadata of the test program. /// \param test_cases_ The collection of test cases in the test program. impl(const std::string& interface_name_, const fs::path& binary_, const fs::path& root_, const std::string& test_suite_name_, const model::metadata& md_, const model::test_cases_map& test_cases_) : interface_name(interface_name_), binary(binary_), root(root_), test_suite_name(test_suite_name_), md(md_), test_cases(test_cases_) { PRE_MSG(!binary.is_absolute(), F("The program '%s' must be relative to the root of the test " "suite '%s'") % binary % root); for (model::test_cases_map::const_iterator iter = test_cases.begin(); iter != test_cases.end(); ++iter) { PRE_MSG((*iter).first == (*iter).second.name(), F("The test case '%s' has been registered with the " "non-matching name '%s'") % (*iter).first % (*iter).second.name()); } }
/// Gets the absolute path to the test program. /// /// \return The absolute path to the test program binary. const fs::path model::test_program::absolute_path(void) const { const fs::path full_path = _pimpl->root / _pimpl->binary; return full_path.is_absolute() ? full_path : full_path.to_absolute(); }