mirror of
https://github.com/pocoproject/poco.git
synced 2025-10-26 18:42:41 +01:00
* enh(ProcessRunner): does not detect launch errors #4482 * enh(File): add absolutePath and existsAnywhere() #4482 * fix windows build and tsan fail * fix tsan * fix windows file tests * comment out some CI env path -related issues * fix tsan and windows build * try to fix ci * ignore ProcessRunner test fail on windows cmake * enh(File): canExecute throws FileNotFoundException if the file to be executed can't be found in the path. * Few C++ modernisation changes. * enh(File): Windows specifics of File::canExecute. Returns false if the file to be executed can't be found using absolutePath. --------- Co-authored-by: Matej Kenda <matejken@gmail.com>
381 lines
9.8 KiB
C++
381 lines
9.8 KiB
C++
//
|
|
// ProcessRunnerTest.cpp
|
|
//
|
|
// Copyright (c) 2023, Applied Informatics Software Engineering GmbH.
|
|
// Aleph ONE Software Engineering d.o.o.,
|
|
// and Contributors.
|
|
//
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
//
|
|
|
|
|
|
#include "ProcessRunnerTest.h"
|
|
#include "CppUnit/TestCaller.h"
|
|
#include "CppUnit/TestSuite.h"
|
|
#include "Poco/PIDFile.h"
|
|
#include "Poco/Format.h"
|
|
#include "Poco/Path.h"
|
|
#include "Poco/File.h"
|
|
#include "Poco/FileStream.h"
|
|
|
|
|
|
using namespace Poco;
|
|
|
|
|
|
ProcessRunnerTest::ProcessRunnerTest(const std::string& name):
|
|
CppUnit::TestCase(name)
|
|
{
|
|
}
|
|
|
|
|
|
ProcessRunnerTest::~ProcessRunnerTest()
|
|
{
|
|
}
|
|
|
|
|
|
void ProcessRunnerTest::testPIDFile()
|
|
{
|
|
std::string pidFile = Path::tempHome() + "test.pid";
|
|
|
|
{
|
|
PIDFile f;
|
|
assertTrue (f.getName().empty());
|
|
assertTrue (f.getPID() == PIDFile::INVALID_PID);
|
|
assertFalse (File(pidFile).exists());
|
|
|
|
f.setName(pidFile);
|
|
assertTrue (f.getName() == pidFile);
|
|
assertTrue (f.getPID() != PIDFile::INVALID_PID);
|
|
assertTrue (File(pidFile).exists());
|
|
}
|
|
assertFalse (File(pidFile).exists());
|
|
|
|
{
|
|
PIDFile f(pidFile);
|
|
std::string pf = pidFile;
|
|
|
|
assertTrue (f.getName() == pf);
|
|
assertTrue (File(pf).exists());
|
|
assertTrue (f.getPID() != PIDFile::INVALID_PID);
|
|
|
|
assertTrue (f.exists());
|
|
}
|
|
assertFalse (File(pidFile).exists());
|
|
|
|
{
|
|
PIDFile f(pidFile);
|
|
assertTrue (f.getName() == pidFile);
|
|
assertTrue (File(pidFile).exists());
|
|
assertTrue (f.getPID() != PIDFile::INVALID_PID);
|
|
|
|
assertTrue (f.exists());
|
|
}
|
|
assertFalse (File(pidFile).exists());
|
|
|
|
{
|
|
PIDFile f(pidFile, false);
|
|
std::string pf = pidFile;
|
|
|
|
assertTrue (f.getName() == pf);
|
|
assertTrue (!File(pf).exists());
|
|
assertTrue (f.getPID() == PIDFile::INVALID_PID);
|
|
|
|
f.create();
|
|
assertTrue (f.exists());
|
|
}
|
|
assertFalse (File(pidFile).exists());
|
|
}
|
|
|
|
|
|
void ProcessRunnerTest::testProcessRunner()
|
|
{
|
|
std::string name("TestApp");
|
|
std::string cmd;
|
|
#if defined(_DEBUG) && (POCO_OS != POCO_OS_ANDROID)
|
|
name += "d";
|
|
#endif
|
|
|
|
#if defined(POCO_OS_FAMILY_UNIX)
|
|
cmd += name;
|
|
#else
|
|
cmd = name;
|
|
#endif
|
|
|
|
// non-auto start, no PID
|
|
{
|
|
std::vector<std::string> args;
|
|
char c = Path::separator();
|
|
std::string pidFile = Poco::format("run%c%s.pid", c, name);
|
|
args.push_back(std::string("--pidfile=").append(pidFile));
|
|
ProcessRunner pr(cmd, args, "", ProcessRunner::NO_OUT, 10, false);
|
|
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
|
|
{
|
|
pr.start();
|
|
fail("It should not be possible to start a started process.");
|
|
}
|
|
catch(const Poco::InvalidAccessException&) {}
|
|
pr.stop();
|
|
sw.restart();
|
|
while (pr.running())
|
|
checkTimeout(sw, "Waiting for process to stop", 1000, __LINE__);
|
|
assertFalse (pr.running());
|
|
pr.start();
|
|
while (!pr.running())
|
|
checkTimeout(sw, "Waiting for process to start", 1000, __LINE__);
|
|
assertTrue (pr.running());
|
|
pr.stop();
|
|
pr.stop(); // second stop() should be silent no-op
|
|
}
|
|
|
|
// non-auto start with PID
|
|
{
|
|
std::vector<std::string> args;
|
|
char c = Path::separator();
|
|
std::string pidFile = Poco::format("run%c%s.pid", c, name);
|
|
args.push_back(std::string("--pidfile=").append(pidFile));
|
|
ProcessRunner pr(cmd, args, "", ProcessRunner::NO_OUT, 10, false);
|
|
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
|
|
{
|
|
pr.start();
|
|
fail("It should not be possible to start a started process.");
|
|
}
|
|
catch(const Poco::InvalidAccessException&) {}
|
|
pr.stop();
|
|
sw.restart();
|
|
while (pr.running())
|
|
checkTimeout(sw, "Waiting for process to stop", 1000, __LINE__);
|
|
assertFalse (pr.running());
|
|
pr.start();
|
|
while (!pr.running())
|
|
checkTimeout(sw, "Waiting for process to start", 1000, __LINE__);
|
|
assertTrue (pr.running());
|
|
pr.stop();
|
|
pr.stop(); // second stop() should be silent no-op
|
|
}
|
|
|
|
// autodetect PID file from the long command line argument
|
|
{
|
|
std::vector<std::string> args;
|
|
char c = Path::separator();
|
|
std::string pidFile = Poco::format("run%c%s.pid", c, name);
|
|
args.push_back(std::string("--pidfile=").append(pidFile));
|
|
{
|
|
ProcessRunner pr(cmd, args);
|
|
assertTrue (pr.cmdLine() == cmdLine(cmd, args));
|
|
assertTrue (pr.pidFile() == PIDFile::getFileName(pidFile));
|
|
assertTrue (File(pidFile).exists());
|
|
assertTrue (PIDFile::contains(pidFile, pr.pid()));
|
|
}
|
|
assertTrue (!File(pidFile).exists());
|
|
}
|
|
|
|
// autodetect PID file from the short command line argument
|
|
{
|
|
std::vector<std::string> args;
|
|
char c = Path::separator();
|
|
std::string pidFile = Poco::format("run%c%s.pid", c, name);
|
|
args.push_back(std::string("-p=").append(pidFile));
|
|
{
|
|
ProcessRunner pr(cmd, args, PIDFile::getFileName(pidFile));
|
|
assertTrue (pr.cmdLine() == cmdLine(cmd, args));
|
|
assertTrue (pr.pidFile() == pidFile);
|
|
assertTrue (File(pidFile).exists());
|
|
assertTrue (PIDFile::contains(pidFile, pr.pid()));
|
|
}
|
|
assertTrue (!File(pidFile).exists());
|
|
}
|
|
|
|
// ProcessRunner should NOT autodetect PID from command line args
|
|
// if argument formats list is empty
|
|
{
|
|
std::vector<std::string> args;
|
|
char c = Path::separator();
|
|
std::string pidFile = Poco::format("run%c%s.pid", c, name);
|
|
args.push_back(std::string("--pidfile=").append(pidFile));
|
|
{
|
|
ProcessRunner pr(cmd, args, "", ProcessRunner::NO_OUT, 10, true, {});
|
|
assertTrue (pr.cmdLine() == cmdLine(cmd, args));
|
|
assertTrue (pr.pidFile().empty()); // ProcessRunner has no PID file
|
|
|
|
PIDFile::getFileName(pidFile);
|
|
Stopwatch sw; sw.start();
|
|
while (!File(pidFile).exists())
|
|
checkTimeout(sw, "Waiting for PID file", 1000, __LINE__);
|
|
|
|
// PID file exists and is valid
|
|
assertTrue (File(pidFile).exists());
|
|
assertTrue (PIDFile::contains(pidFile, pr.pid()));
|
|
}
|
|
assertTrue (!File(pidFile).exists());
|
|
}
|
|
|
|
{
|
|
std::vector<std::string> args;
|
|
char c = Path::separator();
|
|
std::string pidFile = Poco::format("run%c%s.pid", c, name);
|
|
args.push_back(std::string("-p=").append(pidFile));
|
|
{
|
|
ProcessRunner pr(cmd, args, "", ProcessRunner::NO_OUT, 10, true, {});
|
|
assertTrue (pr.cmdLine() == cmdLine(cmd, args));
|
|
assertTrue (pr.pidFile().empty()); // ProcessRunner has no PID file
|
|
|
|
PIDFile::getFileName(pidFile);
|
|
Stopwatch sw; sw.start();
|
|
while (!File(pidFile).exists())
|
|
checkTimeout(sw, "Waiting for PID file", 1000, __LINE__);
|
|
|
|
// PID file exists and is valid
|
|
assertTrue (File(pidFile).exists());
|
|
assertTrue (PIDFile::contains(pidFile, pr.pid()));
|
|
}
|
|
assertTrue (!File(pidFile).exists());
|
|
}
|
|
|
|
// no PID file created at all
|
|
{
|
|
std::vector<std::string> args;
|
|
char c = Path::separator();
|
|
std::string pidFile = Poco::format("run%c%s.pid", c, name);
|
|
{
|
|
ProcessRunner pr(cmd, args);
|
|
assertTrue (pr.cmdLine() == cmdLine(cmd, args));
|
|
assertTrue (pr.pidFile().empty());
|
|
|
|
Thread::sleep(500);
|
|
|
|
assertTrue (!File(pidFile).exists());
|
|
assertTrue (!PIDFile::contains(pidFile, pr.pid()));
|
|
}
|
|
assertTrue (!File(pidFile).exists());
|
|
}
|
|
|
|
// non-existent executable with no PID file created
|
|
{
|
|
std::string cmd = "nonexistent_123-xyz";
|
|
std::vector<std::string> args;
|
|
char c = Path::separator();
|
|
std::string pidFile = Poco::format("run%c%s.pid", c, name);
|
|
{
|
|
std::unique_ptr<ProcessRunner> 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<std::string> 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<ProcessRunner> 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
|
|
{
|
|
std::vector<std::string> args;
|
|
char c = Path::separator();
|
|
std::string pidFile = Poco::format("run%c%s.pid", c, name);
|
|
args.push_back(std::string("--pidfile=").append(pidFile));
|
|
args.push_back(std::string("--launch-thread"));
|
|
ProcessRunner pr(cmd, args, "", ProcessRunner::NO_OUT, 10, false);
|
|
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
|
|
{
|
|
pr.start();
|
|
fail("It should not be possible to start a started process.");
|
|
}
|
|
catch(const Poco::InvalidAccessException&) {}
|
|
pr.stop();
|
|
sw.restart();
|
|
while (pr.running())
|
|
checkTimeout(sw, "Waiting for process to stop", 1000, __LINE__);
|
|
assertFalse (pr.running());
|
|
assertEqual (pr.result(), 0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
std::string ProcessRunnerTest::cmdLine(const std::string& cmd, const ProcessRunner::Args& args)
|
|
{
|
|
std::string cmdL = cmd + ' ';
|
|
auto it = args.begin();
|
|
auto end = args.end();
|
|
for (; it != end;)
|
|
{
|
|
cmdL.append(*it);
|
|
if (++it == end) break;
|
|
cmdL.append(1, ' ');
|
|
}
|
|
return cmdL;
|
|
}
|
|
|
|
|
|
void ProcessRunnerTest::checkTimeout(const Stopwatch& sw, const std::string& msg, int timeoutMS, LineNumber line)
|
|
{
|
|
if (sw.elapsedSeconds()*1000 > timeoutMS)
|
|
{
|
|
throw Poco::TimeoutException(
|
|
Poco::format("ProcessRunner::checkTimeout(): %s, line: %d", msg, line));
|
|
}
|
|
Thread::sleep(10);
|
|
}
|
|
|
|
|
|
void ProcessRunnerTest::setUp()
|
|
{
|
|
}
|
|
|
|
|
|
void ProcessRunnerTest::tearDown()
|
|
{
|
|
}
|
|
|
|
|
|
CppUnit::Test* ProcessRunnerTest::suite()
|
|
{
|
|
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ProcessRunnerTest");
|
|
|
|
CppUnit_addTest(pSuite, ProcessRunnerTest, testPIDFile);
|
|
CppUnit_addTest(pSuite, ProcessRunnerTest, testProcessRunner);
|
|
|
|
return pSuite;
|
|
}
|