diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd575a1a3..b4006c23e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -555,7 +555,9 @@ jobs: class CppUnit::TestCaller.testBigPing, class CppUnit::TestCaller.testMTU, class CppUnit::TestCaller.testProxy, - class CppUnit::TestCaller.testProxy + class CppUnit::TestCaller.testProxy, + class CppUnit::TestCaller.testExists, + class CppUnit::TestCaller.testProcessRunner steps: - uses: actions/checkout@v4 - run: cmake -S. -Bcmake-build -DENABLE_NETSSL_WIN=ON -DENABLE_NETSSL=OFF -DENABLE_CRYPTO=OFF -DENABLE_JWT=OFF -DENABLE_DATA=ON -DENABLE_DATA_ODBC=ON -DENABLE_DATA_MYSQL=OFF -DENABLE_DATA_POSTGRESQL=OFF -DENABLE_TESTS=ON diff --git a/Foundation/include/Poco/Any.h b/Foundation/include/Poco/Any.h index 4415984f1..00673863d 100644 --- a/Foundation/include/Poco/Any.h +++ b/Foundation/include/Poco/Any.h @@ -388,6 +388,9 @@ private: template friend ValueType* AnyCast(Any*); + template + friend const ValueType* AnyCast(const Any*); + template friend ValueType* UnsafeAnyCast(Any*); @@ -426,7 +429,9 @@ const ValueType* AnyCast(const Any* operand) /// const MyType* pTmp = AnyCast(pAny). /// Returns nullptr if the types don't match. { - return AnyCast(const_cast(operand)); + return operand && operand->type() == typeid(ValueType) + ? &static_cast*>(operand->content())->_held + : nullptr; } diff --git a/Foundation/include/Poco/Exception.h b/Foundation/include/Poco/Exception.h index d09d4ff99..61e05ceeb 100644 --- a/Foundation/include/Poco/Exception.h +++ b/Foundation/include/Poco/Exception.h @@ -246,6 +246,7 @@ POCO_DECLARE_EXCEPTION(Foundation_API, CreateFileException, FileException) POCO_DECLARE_EXCEPTION(Foundation_API, OpenFileException, FileException) POCO_DECLARE_EXCEPTION(Foundation_API, WriteFileException, FileException) POCO_DECLARE_EXCEPTION(Foundation_API, ReadFileException, FileException) +POCO_DECLARE_EXCEPTION(Foundation_API, ExecuteFileException, FileException) POCO_DECLARE_EXCEPTION(Foundation_API, FileNotReadyException, FileException) POCO_DECLARE_EXCEPTION(Foundation_API, DirectoryNotEmptyException, FileException) POCO_DECLARE_EXCEPTION(Foundation_API, UnknownURISchemeException, RuntimeException) diff --git a/Foundation/include/Poco/File.h b/Foundation/include/Poco/File.h index c13159da2..56363161e 100644 --- a/Foundation/include/Poco/File.h +++ b/Foundation/include/Poco/File.h @@ -54,7 +54,7 @@ class Foundation_API File: private FileImpl /// use the forward slash ("/") as directory separator. { public: - typedef FileSizeImpl FileSize; + using FileSize = FileSizeImpl; enum LinkType /// Type of link for linkTo(). @@ -84,7 +84,7 @@ public: File(const File& file); /// Copy constructor. - virtual ~File(); + ~File() override; /// Destroys the file. File& operator = (const File& file); @@ -105,9 +105,24 @@ public: const std::string& path() const; /// Returns the path. + std::string absolutePath() const; + /// Returns absolute path. + /// Attempts to find the existing file + /// using curent work directory and the PATH + /// environment variable. + /// If the file doesn't exist, returns empty string. + bool exists() const; /// Returns true iff the file exists. + bool existsAnywhere() const; + /// If the file path is relative, searches + /// for the file in the current working directory + /// and the environment paths. + /// If the file path is absolute, the + /// functionality is identical to the + /// exists() call. + bool canRead() const; /// Returns true iff the file is readable. diff --git a/Foundation/include/Poco/File_UNIX.h b/Foundation/include/Poco/File_UNIX.h index 46ee32028..8c3c35207 100644 --- a/Foundation/include/Poco/File_UNIX.h +++ b/Foundation/include/Poco/File_UNIX.h @@ -19,6 +19,7 @@ #include "Poco/Foundation.h" +#include "Poco/Timestamp.h" namespace Poco { @@ -32,7 +33,7 @@ protected: OPT_FAIL_ON_OVERWRITE_IMPL = 0x01 }; - typedef UInt64 FileSizeImpl; + using FileSizeImpl = UInt64; FileImpl(); FileImpl(const std::string& path); @@ -40,10 +41,11 @@ protected: void swapImpl(FileImpl& file); void setPathImpl(const std::string& path); const std::string& getPathImpl() const; + std::string getExecutablePathImpl() const; bool existsImpl() const; bool canReadImpl() const; bool canWriteImpl() const; - bool canExecuteImpl() const; + bool canExecuteImpl(const std::string& absolutePath) const; bool isFileImpl() const; bool isDirectoryImpl() const; bool isLinkImpl() const; diff --git a/Foundation/include/Poco/File_VX.h b/Foundation/include/Poco/File_VX.h index 8b2f90973..ac99930e2 100644 --- a/Foundation/include/Poco/File_VX.h +++ b/Foundation/include/Poco/File_VX.h @@ -19,7 +19,7 @@ #include "Poco/Foundation.h" - +#include "Poco/Timestamp.h" namespace Poco { @@ -32,7 +32,7 @@ protected: OPT_FAIL_ON_OVERWRITE_IMPL = 0x01 }; - typedef UInt64 FileSizeImpl; + using FileSizeImpl = UInt64; FileImpl(); FileImpl(const std::string& path); @@ -40,10 +40,11 @@ protected: void swapImpl(FileImpl& file); void setPathImpl(const std::string& path); const std::string& getPathImpl() const; + std::string getExecutablePathImpl() const; bool existsImpl() const; bool canReadImpl() const; bool canWriteImpl() const; - bool canExecuteImpl() const; + bool canExecuteImpl(const std::string& absolutePath) const; bool isFileImpl() const; bool isDirectoryImpl() const; bool isLinkImpl() const; diff --git a/Foundation/include/Poco/File_WIN32U.h b/Foundation/include/Poco/File_WIN32U.h index c97cb2efa..a88d8334c 100644 --- a/Foundation/include/Poco/File_WIN32U.h +++ b/Foundation/include/Poco/File_WIN32U.h @@ -33,7 +33,7 @@ protected: OPT_FAIL_ON_OVERWRITE_IMPL = 0x01 }; - typedef UInt64 FileSizeImpl; + using FileSizeImpl = UInt64; FileImpl(); FileImpl(const std::string& path); @@ -41,10 +41,11 @@ protected: void swapImpl(FileImpl& file); void setPathImpl(const std::string& path); const std::string& getPathImpl() const; + std::string getExecutablePathImpl() const; bool existsImpl() const; bool canReadImpl() const; bool canWriteImpl() const; - bool canExecuteImpl() const; + bool canExecuteImpl(const std::string& absolutePath) const; bool isFileImpl() const; bool isDirectoryImpl() const; bool isLinkImpl() const; diff --git a/Foundation/include/Poco/Path.h b/Foundation/include/Poco/Path.h index cf6b76abd..6534e6915 100644 --- a/Foundation/include/Poco/Path.h +++ b/Foundation/include/Poco/Path.h @@ -48,7 +48,7 @@ public: PATH_GUESS /// Guess the style by examining the path }; - typedef std::vector StringVec; + using StringVec = std::vector; Path(); /// Creates an empty relative path. diff --git a/Foundation/include/Poco/ProcessRunner.h b/Foundation/include/Poco/ProcessRunner.h index ed2fa9fdf..3826bd5e3 100644 --- a/Foundation/include/Poco/ProcessRunner.h +++ b/Foundation/include/Poco/ProcessRunner.h @@ -121,6 +121,8 @@ public: int runCount() const; /// Returns the number of times the process has been executed. + const std::string& error() const; + /// Returns the error message. private: static const Poco::ProcessHandle::PID INVALID_PID = -1; @@ -141,10 +143,23 @@ private: /// Process initialization completion is indicated by new pid in /// the pid file. If pid file is not specified, there is no waiting. - void checkTimeout(const Poco::Stopwatch& sw, const std::string& msg); + void checkError(); /// If timeout is exceeded, throws TimeoutException with `msg` /// message. + void checkTimeout(const std::string& msg); + /// If timeout is exceeded, throws TimeoutException with `msg` + /// message. + + void checkStatus(const std::string& msg, bool tOut = true); + /// If there were andy errors during process start/stop, + /// throws RuntimeException with the error message; + /// otherwise, if tOut is true and timeout is exceeded, throws + /// TimeoutException with `msg` message. + + void setError(const std::string& msg); + /// Sets the error message. + Poco::Thread _t; std::string _cmd; Args _args; @@ -156,6 +171,9 @@ private: std::atomic _started; std::atomic _rc; std::atomic _runCount; + Stopwatch _sw; + std::string _error; + mutable Poco::FastMutex _mutex; }; @@ -193,6 +211,20 @@ inline int ProcessRunner::runCount() const } +inline void ProcessRunner::setError(const std::string& msg) +{ + _error = Poco::format("ProcessRunner(%s): %s", cmdLine(), msg); +} + + +inline const std::string& ProcessRunner::error() const +{ + Poco::FastMutex::ScopedLock l(_mutex); + + return _error; +} + + } // namespace Poco diff --git a/Foundation/src/Exception.cpp b/Foundation/src/Exception.cpp index a1460406e..6a382f863 100644 --- a/Foundation/src/Exception.cpp +++ b/Foundation/src/Exception.cpp @@ -169,6 +169,7 @@ POCO_IMPLEMENT_EXCEPTION(CreateFileException, FileException, "Cannot create file POCO_IMPLEMENT_EXCEPTION(OpenFileException, FileException, "Cannot open file") POCO_IMPLEMENT_EXCEPTION(WriteFileException, FileException, "Cannot write file") POCO_IMPLEMENT_EXCEPTION(ReadFileException, FileException, "Cannot read file") +POCO_IMPLEMENT_EXCEPTION(ExecuteFileException, FileException, "Cannot execute file") POCO_IMPLEMENT_EXCEPTION(FileNotReadyException, FileException, "File not ready") POCO_IMPLEMENT_EXCEPTION(DirectoryNotEmptyException, FileException, "Directory not empty") POCO_IMPLEMENT_EXCEPTION(UnknownURISchemeException, RuntimeException, "Unknown URI scheme") diff --git a/Foundation/src/File.cpp b/Foundation/src/File.cpp index 9fe69ee2d..0625b45ad 100644 --- a/Foundation/src/File.cpp +++ b/Foundation/src/File.cpp @@ -15,6 +15,8 @@ #include "Poco/File.h" #include "Poco/Path.h" #include "Poco/DirectoryIterator.h" +#include "Poco/Environment.h" +#include "Poco/StringTokenizer.h" #if defined(POCO_OS_FAMILY_WINDOWS) @@ -95,12 +97,77 @@ void File::swap(File& file) noexcept } +std::string File::absolutePath() const +{ + std::string ret; + + if (Path(path()).isAbsolute()) + // TODO: Should this return empty string if file does not exists to be consistent + // with the function documentation? + ret = getPathImpl(); + else + { + Path curPath(Path::current()); + curPath.append(path()); + if (File(curPath).exists()) + ret = curPath.toString(); + else + { + const std::string envPath = Environment::get("PATH", ""); + const std::string pathSeparator(1, Path::pathSeparator()); + if (!envPath.empty()) + { + const StringTokenizer st(envPath, pathSeparator, + StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); + + for (const auto& p: st) + { + try + { + std::string fileName(p); + if (p.size() && p.back() != Path::separator()) + fileName.append(1, Path::separator()); + fileName.append(path()); + if (File(fileName).exists()) + { + ret = fileName; + break; + } + } + catch (const Poco::PathSyntaxException&) + { + // shield against bad PATH environment entries + } + } + } + } + } + + return ret; +} + + bool File::exists() const { + if (path().empty()) return false; return existsImpl(); } +bool File::existsAnywhere() const +{ + if (path().empty()) return false; + + if (Path(path()).isAbsolute()) + return existsImpl(); + + if (File(absolutePath()).exists()) + return true; + + return false; +} + + bool File::canRead() const { return canReadImpl(); @@ -115,7 +182,14 @@ bool File::canWrite() const bool File::canExecute() const { - return canExecuteImpl(); + // Resolve (platform-specific) executable path and absolute path from relative. + const auto execPath { getExecutablePathImpl() }; + const auto absPath { File(execPath).absolutePath() }; + if (absPath.empty() || !File(absPath).exists()) + { + return false; + } + return canExecuteImpl(absPath); } diff --git a/Foundation/src/File_UNIX.cpp b/Foundation/src/File_UNIX.cpp index b06ff133f..9261a9a36 100644 --- a/Foundation/src/File_UNIX.cpp +++ b/Foundation/src/File_UNIX.cpp @@ -16,6 +16,7 @@ #include "Poco/Buffer.h" #include "Poco/Exception.h" #include "Poco/Error.h" +#include "Poco/Path.h" #include #include #include @@ -80,6 +81,12 @@ void FileImpl::setPathImpl(const std::string& path) } +std::string FileImpl::getExecutablePathImpl() const +{ + return _path; +} + + bool FileImpl::existsImpl() const { poco_assert (!_path.empty()); @@ -127,12 +134,12 @@ bool FileImpl::canWriteImpl() const } -bool FileImpl::canExecuteImpl() const +bool FileImpl::canExecuteImpl(const std::string& absolutePath) const { - poco_assert (!_path.empty()); + poco_assert (!absolutePath.empty()); struct stat st; - if (stat(_path.c_str(), &st) == 0) + if (stat(absolutePath.c_str(), &st) == 0) { if (st.st_uid == geteuid() || geteuid() == 0) return (st.st_mode & S_IXUSR) != 0; diff --git a/Foundation/src/File_VX.cpp b/Foundation/src/File_VX.cpp index ad6b2e63b..ce2399377 100644 --- a/Foundation/src/File_VX.cpp +++ b/Foundation/src/File_VX.cpp @@ -62,6 +62,12 @@ void FileImpl::setPathImpl(const std::string& path) } +std::string FileImpl::getExecutablePathImpl() const +{ + return _path; +} + + bool FileImpl::existsImpl() const { poco_assert (!_path.empty()); @@ -87,7 +93,7 @@ bool FileImpl::canWriteImpl() const } -bool FileImpl::canExecuteImpl() const +bool FileImpl::canExecuteImpl(const std::string& absolutePath) const { return false; } diff --git a/Foundation/src/File_WIN32U.cpp b/Foundation/src/File_WIN32U.cpp index ae9a80c18..f4cab98d6 100644 --- a/Foundation/src/File_WIN32U.cpp +++ b/Foundation/src/File_WIN32U.cpp @@ -14,6 +14,7 @@ #include "Poco/File_WIN32U.h" #include "Poco/Exception.h" +#include "Poco/Path.h" #include "Poco/String.h" #include "Poco/UnicodeConverter.h" #include "Poco/UnWindows.h" @@ -88,6 +89,19 @@ void FileImpl::setPathImpl(const std::string& path) convertPath(_path, _upath); } +std::string FileImpl::getExecutablePathImpl() const +{ + // Windows specific: An executable can be invoked without + // the extension .exe, but the file has it nevertheless. + // This function appends extension "exe" if the file path does not have it. + Path p(_path); + if (!p.getExtension().empty()) + { + return _path; + } + return p.setExtension("exe"s).toString(); +} + bool FileImpl::existsImpl() const { @@ -140,9 +154,9 @@ bool FileImpl::canWriteImpl() const } -bool FileImpl::canExecuteImpl() const +bool FileImpl::canExecuteImpl(const std::string& absolutePath) const { - Path p(_path); + Path p(absolutePath); return icompare(p.getExtension(), "exe") == 0; } diff --git a/Foundation/src/ProcessRunner.cpp b/Foundation/src/ProcessRunner.cpp index a4f868dda..4693da733 100644 --- a/Foundation/src/ProcessRunner.cpp +++ b/Foundation/src/ProcessRunner.cpp @@ -20,6 +20,7 @@ #include "Poco/File.h" #include "Poco/Path.h" #include "Poco/String.h" +#include "Poco/Error.h" #include @@ -104,15 +105,52 @@ std::string ProcessRunner::cmdLine() const void ProcessRunner::run() { + int errHandle = 0; + int errPID = 0; + int errRC = 0; + + { + Poco::FastMutex::ScopedLock l(_mutex); + _error.clear(); + } + + _pid = INVALID_PID; + _pPH = nullptr; + ProcessHandle* pPH = nullptr; try { _pPH = pPH = new ProcessHandle(Process::launch(_cmd, _args, _options)); + errHandle = Error::last(); + _pid = pPH->id(); + errPID = Error::last(); + _rc = pPH->wait(); + errRC = Error::last(); + + if (errHandle || errPID || errRC || _rc != 0) + { + Poco::FastMutex::ScopedLock l(_mutex); + + Poco::format(_error, "ProcessRunner::run() error; " + "handle=%d (%d:%s); pid=%d (%d:%s); return=%d (%d:%s)", + (pPH ? pPH->id() : 0), errHandle, Error::getMessage(errHandle), + _pid.load(), errPID, Error::getMessage(errPID), + _rc.load(), errRC, Error::getMessage(errRC)); + } + } + catch (Poco::Exception& ex) + { + setError(ex.displayText()); + } + catch (std::exception& ex) + { + setError(ex.what()); } catch (...) { + setError("Unknown exception"s); } _pid = INVALID_PID; @@ -127,7 +165,7 @@ void ProcessRunner::stop() if (_started) { PID pid; - Stopwatch sw; sw.start(); + _sw.restart(); if (_pPH.exchange(nullptr) && ((pid = _pid.exchange(INVALID_PID))) != INVALID_PID) { while (Process::isRunning(pid)) @@ -135,9 +173,9 @@ void ProcessRunner::stop() if (pid > 0) { Process::requestTermination(pid); - checkTimeout(sw, "Waiting for process termination"); + checkStatus("Waiting for process termination"); } - else throw Poco::IllegalStateException("Invalid PID, can;t terminate process"); + else throw Poco::IllegalStateException("Invalid PID, can't terminate process"); } _t.join(); } @@ -150,9 +188,8 @@ void ProcessRunner::stop() _pidFile.clear(); std::string msg; Poco::format(msg, "Waiting for PID file (pidFile: '%s')", _pidFile); - sw.restart(); - while (pidFile.exists()) - checkTimeout(sw, msg); + _sw.restart(); + while (pidFile.exists()) checkStatus(msg); } } } @@ -160,9 +197,18 @@ void ProcessRunner::stop() } -void ProcessRunner::checkTimeout(const Stopwatch& sw, const std::string& msg) +void ProcessRunner::checkError() { - if (sw.elapsedSeconds() > _timeout) + Poco::FastMutex::ScopedLock l(_mutex); + + if (!_error.empty()) + throw Poco::RuntimeException(_error); +} + + +void ProcessRunner::checkTimeout(const std::string& msg) +{ + if (_sw.elapsedSeconds() > _timeout) { throw Poco::TimeoutException( Poco::format("ProcessRunner::checkTimeout(): %s", msg)); @@ -171,31 +217,50 @@ void ProcessRunner::checkTimeout(const Stopwatch& sw, const std::string& msg) } +void ProcessRunner::checkStatus(const std::string& msg, bool tOut) +{ + checkError(); + if (tOut) checkTimeout(msg); +} + + void ProcessRunner::start() { if (!_started.exchange(true)) { + File exe(_cmd); + if (!exe.existsAnywhere()) + { + throw Poco::FileNotFoundException( + Poco::format("ProcessRunner::start(%s): command not found", _cmd)); + } + else if (!File(exe.absolutePath()).canExecute()) + { + throw Poco::ExecuteFileException( + Poco::format("ProcessRunner::start(%s): cannot execute", _cmd)); + } + int prevRunCnt = runCount(); _t.start(*this); std::string msg; Poco::format(msg, "Waiting for process to start (pidFile: '%s')", _pidFile); - Stopwatch sw; sw.start(); + _sw.restart(); // wait for the process to be either running or completed by monitoring run counts. - while (!running() && prevRunCnt >= runCount()) checkTimeout(sw, msg); + while (!running() && prevRunCnt >= runCount()) checkStatus(msg); // we could wait for the process handle != INVALID_PID, // but if pidFile name was given, we should wait for // the process to write it if (!_pidFile.empty()) { - sw.restart(); + _sw.restart(); // wait until process is fully initialized File pidFile(_pidFile); while (!pidFile.exists()) - checkTimeout(sw, "waiting for PID file"); + checkStatus(Poco::format("waiting for PID file '%s' creation.", _pidFile)); // verify that the file content is actually the process PID FileInputStream fis(_pidFile); @@ -205,7 +270,7 @@ void ProcessRunner::start() while (fPID != pid()) { fis.clear(); fis.seekg(0); fis >> fPID; - checkTimeout(sw, Poco::format("waiting for new PID (%s)", _pidFile)); + checkStatus(Poco::format("waiting for new PID (%s)", _pidFile)); } } } diff --git a/Foundation/testsuite/src/AnyTest.cpp b/Foundation/testsuite/src/AnyTest.cpp index e13e4841b..f1e3e00c6 100644 --- a/Foundation/testsuite/src/AnyTest.cpp +++ b/Foundation/testsuite/src/AnyTest.cpp @@ -134,7 +134,7 @@ void AnyTest::testAnyCastToReference() int& ra = AnyCast(a); int const& ra_c = AnyCast(a); - // NOTE: The following two AnyCasts will trigger the + // NOTE: The volatile AnyCasts will trigger the // undefined behavior sanitizer. int volatile& ra_v = AnyCast(a); int const volatile& ra_cv = AnyCast(a); diff --git a/Foundation/testsuite/src/FileTest.cpp b/Foundation/testsuite/src/FileTest.cpp index c1c9673fa..f26d964fd 100644 --- a/Foundation/testsuite/src/FileTest.cpp +++ b/Foundation/testsuite/src/FileTest.cpp @@ -18,7 +18,9 @@ #include "Poco/Thread.h" #include #include - +#if defined(POCO_OS_FAMILY_WINDOWS) +#include +#endif using Poco::File; using Poco::TemporaryFile; @@ -192,6 +194,61 @@ void FileTest::testCreateFile() } +void FileTest::testExists() +{ + assertFalse (File("").exists()); + { + File f("testfile.dat"); + f.createFile(); + assertTrue (f.exists()); + assertTrue (f.existsAnywhere()); + assertFalse (f.canExecute()); + } + + { + File f("/testfile.dat"); + assertFalse (f.exists()); + assertFalse (f.existsAnywhere()); + assertFalse (f.canExecute()); + } + + { +#if defined(POCO_OS_FAMILY_UNIX) + File f("echo"); + File f2("/dev/null"); +#elif defined(POCO_OS_FAMILY_WINDOWS) + std::string buffer(MAX_PATH, 0); + UINT r = GetSystemDirectoryA(buffer.data(), static_cast(buffer.size())); + if (r) + { + Path p(buffer); + p.makeDirectory().makeAbsolute().makeParent(); + buffer = p.toString(); + buffer.append("win.ini"); + } + else + { + buffer = R"(c:\windows\win.ini)"; + } + File f("cmd.exe"); + File f2(buffer); + + File f3("cmd"); + assertTrue (f3.canExecute()); + File f4("cmd-nonexistent"); + assertFalse (f4.canExecute()); +#endif + assertFalse (f.exists()); + assertTrue (f.existsAnywhere()); + assertTrue (f.canExecute()); + + assertTrue (f2.exists()); + assertTrue (f2.existsAnywhere()); + assertFalse (f2.canExecute()); + } +} + + void FileTest::testFileAttributes2() { TemporaryFile f; @@ -660,6 +717,7 @@ CppUnit::Test* FileTest::suite() CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("FileTest"); CppUnit_addTest(pSuite, FileTest, testCreateFile); + CppUnit_addTest(pSuite, FileTest, testExists); CppUnit_addTest(pSuite, FileTest, testFileAttributes1); CppUnit_addTest(pSuite, FileTest, testFileAttributes2); CppUnit_addTest(pSuite, FileTest, testFileAttributes3); diff --git a/Foundation/testsuite/src/FileTest.h b/Foundation/testsuite/src/FileTest.h index 65acb507c..d964c97df 100644 --- a/Foundation/testsuite/src/FileTest.h +++ b/Foundation/testsuite/src/FileTest.h @@ -26,6 +26,7 @@ public: void testFileAttributes1(); void testCreateFile(); + void testExists(); void testFileAttributes2(); void testFileAttributes3(); void testCompare(); diff --git a/Foundation/testsuite/src/PathTest.cpp b/Foundation/testsuite/src/PathTest.cpp index 86145e9b3..15cca8b6f 100644 --- a/Foundation/testsuite/src/PathTest.cpp +++ b/Foundation/testsuite/src/PathTest.cpp @@ -349,6 +349,9 @@ void PathTest::testParseUnix4() assertTrue (!p.isDirectory()); assertTrue (p.isFile()); assertTrue (p.toString(Path::PATH_UNIX) == "a/b/c/d"); + + p.makeDirectory(); + assertTrue (p.toString(Path::PATH_UNIX) == "a/b/c/d/"); } diff --git a/Foundation/testsuite/src/ProcessRunnerTest.cpp b/Foundation/testsuite/src/ProcessRunnerTest.cpp index 474dede00..b0209cc0f 100644 --- a/Foundation/testsuite/src/ProcessRunnerTest.cpp +++ b/Foundation/testsuite/src/ProcessRunnerTest.cpp @@ -111,11 +111,11 @@ void ProcessRunnerTest::testProcessRunner() assertTrue (pr.cmdLine() == cmdLine(cmd, args)); assertFalse (pr.running()); pr.start(); - + Stopwatch sw; sw.start(); while (!pr.running()) checkTimeout(sw, "Waiting for process to start", 1000, __LINE__); - + assertTrue (pr.running()); try { @@ -264,6 +264,42 @@ void ProcessRunnerTest::testProcessRunner() } assertTrue (!File(pidFile).exists()); } + + // non-existent executable with no PID file created + { + std::string cmd = "nonexistent_123-xyz"; + std::vector args; + char c = Path::separator(); + std::string pidFile = Poco::format("run%c%s.pid", c, name); + { + std::unique_ptr pr; + try + { + pr.reset(new ProcessRunner(cmd, args)); + failmsg("ProcessRunner should throw an exception."); + } catch(const Poco::FileException& e) {} + } + assertTrue (!File(pidFile).exists()); + } + + // non-existent executable with PID file created + { + std::string cmd = "nonexistent_123-xyz"; + std::vector args; + char c = Path::separator(); + std::string pidFile = Poco::format("run%c%s.pid", c, name); + args.push_back(std::string("-p=").append(pidFile)); + { + std::unique_ptr pr; + try + { + pr.reset(new ProcessRunner(cmd, args)); + failmsg("ProcessRunner should throw an exception."); + } catch(const Poco::FileException& e) {} + } + assertTrue (!File(pidFile).exists()); + } + #if defined(POCO_OS_FAMILY_UNIX) // start process launching multiple threads {