16#include <initializer_list>
21#ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
30#ifdef CPP_UTILITIES_BOOST_PROCESS
31#include <boost/asio/buffers_iterator.hpp>
32#include <boost/asio/io_context.hpp>
33#include <boost/asio/streambuf.hpp>
34#include <boost/process/async.hpp>
35#include <boost/process/child.hpp>
36#include <boost/process/env.hpp>
37#include <boost/process/environment.hpp>
38#include <boost/process/group.hpp>
39#include <boost/process/io.hpp>
40#include <boost/process/search_path.hpp>
43#ifdef PLATFORM_WINDOWS
60 return stat(path.data(), &
res) == 0;
101static bool makeDir(
const string &path)
139 : m_listArg(
"list",
'l',
"lists available test units")
140 , m_runArg(
"run",
'r',
"runs the tests")
141 , m_testFilesPathArg(
"test-files-path",
'p',
"specifies the path of the directory with test files", {
"path" })
142 , m_applicationPathArg(
"app-path",
'a',
"specifies the path of the application to be tested", {
"path" })
143 , m_workingDirArg(
"working-dir",
'w',
"specifies the directory to store working copies of test files", {
"path" })
144 , m_unitsArg(
"units",
'u',
"specifies the units to test; omit to test all units", {
"unit1",
"unit2",
"unit3" })
148 throw runtime_error(
"only one TestApplication instance allowed at a time");
157 m_runArg.setImplicit(
true);
158 m_runArg.setSubArguments({ &m_testFilesPathArg, &m_applicationPathArg, &m_workingDirArg, &m_unitsArg });
159 m_parser.setMainArguments({ &m_runArg, &m_listArg, &m_parser.noColorArg(), &m_parser.helpArg() });
171 if (m_parser.helpArg().isPresent()) {
178 if (m_testFilesPathArg.isPresent()) {
179 for (
const char *
const testFilesPath : m_testFilesPathArg.values()) {
183 m_testFilesPaths.emplace_back(
"./");
198 m_testFilesPaths.emplace_back(
"./testfiles/");
204 if (m_workingDirArg.isPresent()) {
205 if (*m_workingDirArg.values().front()) {
206 (m_workingDir = m_workingDirArg.values().front()) +=
'/';
216 m_workingDir = m_testFilesPaths.front() +
"workingdir/";
218 m_workingDir =
"./testfiles/workingdir/";
221 cerr <<
"Directory used to store working copies:\n" << m_workingDir <<
'\n';
236 s_instance =
nullptr;
259 throw std::runtime_error(
"The test file \"" %
relativeTestFilePath %
"\" can not be located. Was looking under:\n"
277 throw std::runtime_error(
"The test directory \"" %
relativeTestDirPath %
"\" can not be located. Was looking under:\n"
313 cerr << Phrases::Error <<
"Unable to create working copy for \"" <<
relativeTestFilePath <<
"\": can't create working directory \""
314 << m_workingDir <<
"\"." << Phrases::EndFlush;
320 if (!parts.empty()) {
325 for (
auto i = parts.cbegin(), end = parts.end() - 1;
i != end; ++
i) {
337 <<
currentLevel <<
"\" (inside working directory)." << Phrases::EndFlush;
365 <<
"\": an IO error occurred when opening original file \"" <<
origFilePath <<
"\"." << Phrases::EndFlush;
379 <<
"\": an IO error occurred when opening target file \"" <<
workingCopyPath <<
"\"." << Phrases::EndFlush;
392 cerr <<
"an IO error occurred when reading original file \"" <<
origFilePath <<
"\"";
400 cerr <<
" an IO error occurred when writing to target file \"" <<
workingCopyPath <<
"\".";
407#ifdef CPP_UTILITIES_HAS_EXEC_APP
409#if defined(CPP_UTILITIES_BOOST_PROCESS)
412 const auto begin = boost::asio::buffers_begin(
buf.data());
413 return std::string(
begin,
begin +
static_cast<std::ptrdiff_t
>(
buf.size()));
427 cout <<
'-' <<
' ' << appPath;
429 for (
const char *
const *
i = args + 1; *
i; ++
i) {
436#if defined(CPP_UTILITIES_BOOST_PROCESS)
438 auto ctx = boost::asio::io_context();
439 auto group = boost::process::group();
441#if defined(PLATFORM_WINDOWS)
442 std::vector<std::wstring>();
444 std::vector<std::string>();
447 for (
const char *
const *
arg = args + 1; *
arg; ++
arg) {
448#if defined(PLATFORM_WINDOWS)
449 auto ec = std::error_code();
452 throw std::runtime_error(
argsToString(
"unable to convert arg \"", *
arg,
"\" to wide string"));
460 auto env = boost::process::environment(boost::this_process::environment());
467 ctx.run_for(std::chrono::milliseconds(
timeout));
475 return child.exit_code();
477#elif defined(PLATFORM_UNIX)
493 throw runtime_error(
"Unable to create fork");
511 throw runtime_error(
"Poll time-out");
514 throw runtime_error(
"Poll failed");
559 cerr << Phrases::Error <<
"Unable create process group: " << std::strerror(
errno) << Phrases::EndFlush;
570 execvp(appPath,
const_cast<char *
const *
>(args));
572 execv(appPath,
const_cast<char *
const *
>(args));
574 cerr << Phrases::Error <<
"Unable to execute \"" << appPath <<
"\": " << std::strerror(
errno) << Phrases::EndFlush;
579 throw std::runtime_error(
"lauching test applications is not supported on this platform");
611 throw runtime_error(
"Unable to execute application to be tested: no application path specified");
617 auto path = string();
673string TestApplication::readTestfilePathFromEnv()
687std::vector<std::string> TestApplication::readTestfilePathFromSrcRef()
691 auto res = std::vector<std::string>();
693#if defined(CPP_UTILITIES_USE_STANDARD_FILESYSTEM) && defined(PLATFORM_UNIX)
695 binaryPath = std::filesystem::read_symlink(
"/proc/self/exe").parent_path();
697 }
catch (
const std::filesystem::filesystem_error &
e) {
698 cerr << Phrases::Warning <<
"Unable to detect binary path for finding \"srcdirref\": " <<
e.what() << Phrases::EndFlush;
706 cerr << Phrases::Warning <<
"The file \"srcdirref\" is empty." << Phrases::EndFlush;
717 cerr << Phrases::Warning
718 <<
"The source directory referenced by the file \"srcdirref\" does not contain a \"testfiles\" directory or does not exist."
724 }
catch (
const std::ios_base::failure &
e) {
725 cerr << Phrases::Warning <<
"The file \"" <<
srcdirrefPath <<
"\" can not be opened: " <<
e.what() << Phrases::EndFlush;
const char * executable() const
Returns the name of the current executable.
static constexpr std::size_t varValueCount
Denotes a variable number of values.
const char * firstValue() const
Returns the first parameter value of the first occurrence of the argument.
The ParseError class is thrown by an ArgumentParser when a parsing error occurs.
The TestApplication class simplifies writing test applications that require opening test files.
std::string workingCopyPath(const std::string &relativeTestFilePath, WorkingCopyMode mode=WorkingCopyMode::CreateCopy) const
Returns the full path to a working copy of the test file with the specified relativeTestFilePath.
std::string testFilePath(const std::string &relativeTestFilePath) const
Returns the full path of the test file with the specified relativeTestFilePath.
static const char * appPath()
Returns the application path or an empty string if no application path has been set.
TestApplication()
Constructs a TestApplication instance without further arguments.
std::string workingCopyPathAs(const std::string &relativeTestFilePath, const std::string &relativeWorkingCopyPath, WorkingCopyMode mode=WorkingCopyMode::CreateCopy) const
Returns the full path to a working copy of the test file with the specified relativeTestFilePath.
std::string testDirPath(const std::string &relativeTestDirPath) const
Returns the full path of the test directory with the specified relativeTestDirPath.
~TestApplication()
Destroys the TestApplication.
Encapsulates functions for formatted terminal output using ANSI escape codes.
Contains all utilities provides by the c++utilities library.
CPP_UTILITIES_EXPORT std::string readFile(const std::string &path, std::string::size_type maxSize=std::string::npos)
Reads all contents of the specified file in a single call.
WorkingCopyMode
The WorkingCopyMode enum specifies additional options to influence behavior of TestApplication::worki...
ReturnType joinStrings(const Container &strings, Detail::StringParamForContainer< Container > delimiter=Detail::StringParamForContainer< Container >(), bool omitEmpty=false, Detail::StringParamForContainer< Container > leftClosure=Detail::StringParamForContainer< Container >(), Detail::StringParamForContainer< Container > rightClosure=Detail::StringParamForContainer< Container >())
Joins the given strings using the specified delimiter.
IntegralType stringToNumber(const StringType &string, BaseType base=10)
Converts the given string to an unsigned/signed number assuming string uses the specified base.
std::fstream NativeFileStream
StringType argsToString(Args &&...args)