mirror of
https://github.com/pocoproject/poco.git
synced 2025-10-27 19:10:20 +01:00
Add ProcessRunner and PIDFile (#4225)
* feat(Foundation): PIDFile and ProcessRunner #4064 * feat(Thread): optional signal blocking on POSIX #2978 * fix(ProcessRunner):remove logger, code enhancement #4225 * feat(Foundation): add PIDFile and ProcessRunner Tests #4064 * fix(Foundation): failing ProcessRunner Test #4064 * fix(PIDFile): remove append argument #4064 * remove Windows TODO from ProcessRunner #4064 * feat(ProcessRunnerTest): add line to checkTimeout #4064 * fix(ProcessRunner): add done flag to run() #4064 * fix(ProcessRunnerTest): add missing pidFile argument #4064 * chore(ProcessRunner): remove comments #4064 * fix(ProcessRunner): add runCount flag #4064 * fix(test): SharedLibrary and Class tests paths * fix(ProcessRunner): thread sanitizer reported data races #4064 * fix(build): pass env var to testrunner #4064 * chore(PIDFile): remove ; in comments #4064 * feat(ProcessRunner): add Win argument format #4064 * fix(Tests): add ProcessRunnerTest to vcxproj #4064 * fix(Tests): change path to TestApp #4064 * feat(Tests): windows processrunner tests #4064 * fix(Tests): duplicate ProcessRunnerTest in TestSuite vcxproj #4064 * fix(CodeQL): sw declaration hides variable #4064 * fix test binaries path for cmake * fix(Build): missing include/PIDFile.h buildWin #4064 * fix(Build): add PocoFoundation depend in buildWin #4064 * feat(ProcessRunner): test process launching multiple threads #2976 --------- Co-authored-by: Pavle <pavle@debian-gnu-linux-11.localdomain> Co-authored-by: Alex Fabijanic <alex@pocoproject.org>
This commit is contained in:
348
Foundation/testsuite/src/ProcessRunnerTest.cpp
Normal file
348
Foundation/testsuite/src/ProcessRunnerTest.cpp
Normal file
@@ -0,0 +1,348 @@
|
||||
//
|
||||
// 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;
|
||||
#elif defined(_WIN32_WCE)
|
||||
cmd = "\\";
|
||||
cmd += name;
|
||||
cmd += ".EXE";
|
||||
#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());
|
||||
}
|
||||
#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, int 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;
|
||||
}
|
||||
Reference in New Issue
Block a user