Ejemplo n.º 1
0
/// 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());
    }
}
Ejemplo n.º 2
0
/// Executes an external binary and replaces the current process.
///
/// This differs from process::exec() in that this function reports errors
/// caused by the exec(2) system call to let the caller decide how to handle
/// them.
///
/// This function must not use any of the logging features so that the output
/// of the subprocess is not "polluted" by our own messages.
///
/// This function must also not affect the global state of the current process
/// as otherwise we would not be able to use vfork().  Only state stored in the
/// stack can be touched.
///
/// \param program The binary to execute.
/// \param args The arguments to pass to the binary, without the program name.
///
/// \throw system_error If the exec(2) call fails.
void
process::exec_unsafe(const fs::path& program, const args_vector& args)
{
    PRE(args.size() < MAX_ARGS);
    int original_errno = 0;
    try {
        const char* argv[MAX_ARGS + 1];

        argv[0] = program.c_str();
        for (args_vector::size_type i = 0; i < args.size(); i++)
            argv[1 + i] = args[i].c_str();
        argv[1 + args.size()] = NULL;

        const int ret = ::execv(program.c_str(),
                                (char* const*)(unsigned long)(const void*)argv);
        original_errno = errno;
        INV(ret == -1);
        std::cerr << "Failed to execute " << program << ": "
                  << std::strerror(original_errno) << "\n";
    } catch (const std::runtime_error& error) {
        std::cerr << "Failed to execute " << program << ": "
                  << error.what() << "\n";
        std::abort();
    } catch (...) {
        std::cerr << "Failed to execute " << program << "; got unexpected "
            "exception during exec\n";
        std::abort();
    }

    // We must do this here to prevent our exception from being caught by the
    // generic handlers above.
    INV(original_errno != 0);
    throw system_error("Failed to execute " + program.str(), original_errno);
}
Ejemplo n.º 3
0
Archivo: layout.cpp Proyecto: jmmv/kyua
/// 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;
}
Ejemplo n.º 4
0
/// Creates a temporary file.
///
/// The temporary file is created using mkstemp(3) using the provided template.
/// This should be most likely used in conjunction with fs::auto_file.
///
/// \param path_template The template for the temporary path, which is a
///     basename that is created within the TMPDIR.  Must contain the XXXXXX
///     pattern, which is atomically replaced by a random unique string.
///
/// \return The generated path for the temporary directory.
///
/// \throw fs::system_error If the call to mkstemp(3) fails.
fs::path
fs::mkstemp(const std::string& path_template)
{
    PRE(path_template.find("XXXXXX") != std::string::npos);

    const fs::path tmpdir(utils::getenv_with_default("TMPDIR", "/tmp"));
    const fs::path full_template = tmpdir / path_template;

    utils::auto_array< char > buf(new char[full_template.str().length() + 1]);
    std::strcpy(buf.get(), full_template.c_str());
    if (::mkstemp(buf.get()) == -1) {
        const int original_errno = errno;
        throw fs::system_error(F("Cannot create temporary file using template "
                                 "%s") % full_template, original_errno);
    }
    return fs::path(buf.get());
}
Ejemplo n.º 5
0
/// Backs up a database for schema migration purposes.
///
/// \todo We should probably use the SQLite backup API instead of doing a raw
/// file copy.  We issue our backup call with the database already open, but
/// because it is quiescent, it's OK to do so.
///
/// \param source Location of the database to be backed up.
/// \param old_version Version of the database's CURRENT schema, used to
///     determine the name of the backup file.
///
/// \throw error If there is a problem during the backup.
void
store::detail::backup_database(const fs::path& source, const int old_version)
{
    const fs::path target(F("%s.v%s.backup") % source.str() % old_version);

    LI(F("Backing up database %s to %s") % source % target);

    std::ifstream input(source.c_str());
    if (!input)
        throw error(F("Cannot open database file %s") % source);

    std::ofstream output(target.c_str());
    if (!output)
        throw error(F("Cannot create database backup file %s") % target);

    char buffer[1024];
    while (input.good()) {
        input.read(buffer, sizeof(buffer));
        if (input.good() || input.eof())
            output.write(buffer, input.gcount());
    }
    if (!input.good() && !input.eof())
        throw error(F("Error while reading input file %s") % source);
}
Ejemplo n.º 6
0
/// Recursively removes a directory.
///
/// This operation simulates a "rm -r".  No effort is made to forcibly delete
/// files and no attention is paid to mount points.
///
/// \param directory The directory to remove.
///
/// \throw fs::error If there is a problem removing any directory or file.
void
fs::rm_r(const fs::path& directory)
{
    DIR* dirp = ::opendir(directory.c_str());
    if (dirp == NULL) {
        const int original_errno = errno;
        throw fs::system_error(F("Failed to open directory %s") %
                               directory.str(), original_errno);
    }
    try {
        ::dirent* dp;
        while ((dp = ::readdir(dirp)) != NULL) {
            const std::string name = dp->d_name;
            if (name == "." || name == "..")
                continue;

            const fs::path entry = directory / dp->d_name;

            const struct ::stat sb = safe_stat(entry);
            if (S_ISDIR(sb.st_mode)) {
                LD(F("Descending into %s") % entry);
                fs::rm_r(entry);
            } else {
                LD(F("Removing file %s") % entry);
                fs::unlink(entry);
            }
        }
    } catch (...) {
        ::closedir(dirp);
        throw;
    }
    ::closedir(dirp);

    LD(F("Removing empty directory %s") % directory);
    fs::rmdir(directory);
}