add process tests

This commit is contained in:
2025-08-13 14:09:18 +03:00
parent d9719a7a50
commit da30ae558f
8 changed files with 204 additions and 108 deletions

View File

@@ -604,7 +604,7 @@ if(NOT PIP_FREERTOS)
add_subdirectory("utils/translator")
add_subdirectory("utils/value_tree_translator")
if(PIP_UTILS AND (NOT CROSSTOOLS))
add_subdirectory("utils/system_test")
add_subdirectory("utils/system_calib")
add_subdirectory("utils/udp_file_transfer")
if(sodium_FOUND)
add_subdirectory("utils/system_daemon")

View File

@@ -62,28 +62,49 @@
namespace {
enum class PipeDirection {
Read,
Write,
Last = Write
enum PipeDirection {
PipeRead,
PipeWrite,
PipeLast = PipeWrite
};
enum class StdFile {
In,
Out,
Err,
Last = Err
enum StdFile {
StdIn,
StdOut,
StdErr,
StdLast = StdErr
};
constexpr int PipesDirections = static_cast<int>(PipeDirection::Last) + 1;
constexpr int StdFileCount = static_cast<int>(StdFile::Last) + 1;
constexpr int PipesDirections = PipeLast + 1;
constexpr int StdFileCount = StdLast + 1;
# ifdef WINDOWS
using PipeHandleType = HANDLE;
# else
using PipeHandleType = int;
# endif
# ifdef WINDOWS
const char * convertWindowsCmd(PIStringList sl) {
for (int i = 0; i < sl.size_s(); ++i) {
sl[i].push_front('"');
sl[i].push_back('"');
}
PIString s = sl.join(' ');
return s.data();
}
# else
char * const * convertToCharArrays(const PIStringList & sl) {
char ** cc = new char *[sl.size() + 1];
for (int i = 0; i < sl.size_s(); ++i) {
cc[i] = const_cast<char *>(sl[i].data());
}
cc[sl.size()] = 0;
return cc;
}
# endif
} // namespace
PRIVATE_DEFINITION_START(PIProcess)
@@ -136,9 +157,9 @@ PIProcess::PIProcess(): PIThread() {
PRIVATE->pi.dwProcessId = 0;
# else
PRIVATE->pid = 0;
PRIVATE->forEachPipe([](PipeHandleType & pipe) { pipe = -1; });
# endif
is_exec = false;
PRIVATE->forEachPipe([](PipeHandleType & pipe) { pipe = 0;});
PRIVATE->initGrab();
env = PIProcess::currentEnvironment();
}
@@ -161,49 +182,14 @@ void PIProcess::exec_() {
void PIProcess::startProc(bool detached) {
// cout << "run" << endl;
PIString str;
const PIString & str = args.front();
/// arguments convertion
int as = 0;
const char * argscc[args.size() + 1];
int argsl[args.size()];
for (int i = 0; i < args.size_s(); ++i) {
argscc[i] = args[i].data();
argsl[i] = strlen(argscc[i]);
as += argsl[i] + 3;
}
argscc[args.size()] = 0;
# ifdef WINDOWS
char * a = new char[as];
memset(a, ' ', as - 1);
as = 0;
for (int i = 0; i < args.size_s(); ++i) {
str = args[i];
a[as] = '"';
memcpy(&(a[as + 1]), argscc[i], argsl[i]);
a[as + argsl[i] + 1] = '"';
as += argsl[i] + 3;
}
a[as - 1] = 0;
// piCout << a;
# endif
# ifndef WINDOWS
/// environment convertion
const char * envcc[env.size() + 1];
envcc[env.size_s()] = 0;
for (int i = 0; i < env.size_s(); ++i) {
envcc[i] = env[i].data();
}
# endif
/// files for stdin/out/err
str = args.front();
is_exec = true;
if (!detached) execStarted(str);
# ifndef WINDOWS
int pid_ = fork();
if (!detached) PRIVATE->pid = pid_;
if (pid_ == 0) {
# endif
// PRIVATE->tf_in = PRIVATE->tf_out = PRIVATE->tf_err = 0;
// // cout << "exec " << tf_in << ", " << tf_out << ", " << tf_err << endl;
// // cout << f_out.path() << endl;
@@ -220,12 +206,12 @@ void PIProcess::startProc(bool detached) {
if (PRIVATE->grab[StdFile::Err]) si.hStdError = PRIVATE->pipes[StdFile::Err][PipeDirection::Write];
si.dwFlags |= STARTF_USESTDHANDLES;
if (CreateProcessA(0, // No module name (use command line)
a, // Command line
convertWindowsCmd(args), // Command line
0, // Process handle not inheritable
0, // Thread handle not inheritable
false, // Set handle inheritance to FALSE
detached ? DETACHED_PROCESS /*CREATE_NEW_CONSOLE*/ : 0, // Creation flags
0, // envcc, // Use environment
0, // Use environment
wd.isEmpty() ? 0 : wd.data(), // Use working directory
&si, // Pointer to STARTUPINFO structure
&(PRIVATE->pi))) // Pointer to PROCESS_INFORMATION structure
@@ -240,13 +226,17 @@ void PIProcess::startProc(bool detached) {
} else {
piCoutObj << "\"CreateProcess\" error: %1"_tr("PIProcess").arg(errorString());
}
# endif
# ifndef WINDOWS
# else
auto largs = convertToCharArrays(args);
auto lenv = convertToCharArrays(env);
int pid_ = fork();
if (!detached) PRIVATE->pid = pid_;
if (pid_ == 0) {
if (!wd.isEmpty()) {
if (!chdir(wd.data())) piCoutObj << "Error while set working directory";
}
// cout << "exec " << tf_in << ", " << tf_out << ", " << tf_err << endl;
if (execve(str.data(), (char * const *)argscc, (char * const *)envcc) < 0) {
if (execve(str.data(), largs, lenv) < 0) {
piCoutObj << "\"execve" << str << args << "\" error :" << errorString();
}
} else {
@@ -259,12 +249,11 @@ void PIProcess::startProc(bool detached) {
// cout << "wait done" << endl;
}
}
delete[] largs;
delete[] lenv;
# endif
if (!detached) execFinished(str, exit_code);
is_exec = false;
# ifdef WINDOWS
delete[] a;
# endif
}
// PIByteArray PIProcess::readFile(PIFile & f, bool clear)
@@ -280,8 +269,7 @@ void PIProcess::startProc(bool detached) {
void PIProcess::terminate() {
# ifdef WINDOWS
if (is_exec)
{
if (is_exec) {
if (!TerminateProcess(PRIVATE->pi.hProcess, 0)) return;
}
PRIVATE->pi.dwProcessId = 0;
@@ -308,15 +296,30 @@ int PIProcess::pID() const {
}
PIByteArray PIProcess::readOutput(bool clear) {
PIByteArray PIProcess::readOutput() {
return {}; // readFile(f_out, clear);
}
PIByteArray PIProcess::readError(bool clear) {
PIByteArray PIProcess::readError() {
return {}; // readFile(f_err, clear);
}
bool PIProcess::writeInput(const PIByteArray &data)
{
if (PRIVATE->grab[StdIn]) {
}
return false;
}
void PIProcess::closeInput()
{
if (PRIVATE->grab[StdIn]) {
}
}
int PIProcess::currentPID() {
# ifdef WINDOWS

View File

@@ -68,11 +68,19 @@ public:
//! \~english Returns all attached execution output stream
//! \~russian
PIByteArray readOutput(bool clear = false);
PIByteArray readOutput();
//! \~english Returns all attached execution error stream
//! \~russian
PIByteArray readError(bool clear = false);
PIByteArray readError();
//! \~english Write data to attached execution input stream
//! \~russian
bool writeInput(const PIByteArray & data);
//! \~english Close attached execution input stream and send EOF
//! \~russian
void closeInput();
//! \~english Returns current attached execution environment
//! \~russian

View File

@@ -36,3 +36,4 @@ pip_test(core)
pip_test(piobject)
pip_test(client_server pip_client_server)
pip_test(io)
pip_test(system)

84
tests/system/process.cpp Normal file
View File

@@ -0,0 +1,84 @@
#include "piprocess.h"
#include "pitime.h"
#include "gtest/gtest.h"
class ProcessLauncherTest: public ::testing::Test {
protected:
PIProcess launcher;
PIString command =
#ifdef _WIN32
"cmd.exe";
#else
"/bin/sh";
#endif
void SetUp() override {}
void TearDown() override {
if (launcher.isRunning()) {
launcher.waitForFinish();
}
}
};
TEST_F(ProcessLauncherTest, Output) {
#ifdef _WIN32
PIStringList args = {"/c", "echo Hello from stdout && echo Hello from stderr 1>&2"};
#else
PIStringList args = {"-c", "echo Hello from stdout; echo Hello from stderr 1>&2"};
#endif
launcher.exec(command, args);
ASSERT_TRUE(launcher.isRunning());
piMSleep(100);
auto out = PIString::fromConsole(launcher.readOutput());
auto err = PIString::fromConsole(launcher.readError());
EXPECT_TRUE(out.contains("Hello from stdout"));
EXPECT_TRUE(err.contains("Hello from stderr"));
ASSERT_TRUE(launcher.waitForFinish());
int exit_code = launcher.exitCode();
EXPECT_FALSE(launcher.isRunning());
#ifdef _WIN32
EXPECT_EQ(exit_code, 0);
#else
EXPECT_TRUE(WIFEXITED(exit_code));
EXPECT_EQ(WEXITSTATUS(exit_code), 0);
#endif
}
TEST_F(ProcessLauncherTest, Input) {
#ifdef _WIN32
PIStringList args = {"/c", "set /p input= && echo %input%"};
#else
PIStringList args = {"-c", "read input; echo $input"};
#endif
launcher.exec(command, args);
ASSERT_TRUE(launcher.isRunning());
PIString test_input = "Test input string\n";
EXPECT_TRUE(launcher.writeInput(test_input.toSystem()));
launcher.closeInput();
piMSleep(100);
auto out = PIString::fromConsole(launcher.readOutput());
EXPECT_TRUE(out.contains("Test input string"));
}
TEST_F(ProcessLauncherTest, DISABLED_NonexistentCommand) {
PIString command = {"nonexistent_command_12345"};
launcher.exec(command);
EXPECT_FALSE(launcher.isRunning());
}

View File

@@ -0,0 +1,7 @@
list(APPEND PIP_UTILS_LIST "pip_system_calib")
set(PIP_UTILS_LIST ${PIP_UTILS_LIST} PARENT_SCOPE)
add_executable(pip_system_calib "main.cpp")
target_link_libraries(pip_system_calib pip)
if (DEFINED LIB)
install(TARGETS pip_system_calib DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
endif ()

View File

@@ -1,7 +0,0 @@
list(APPEND PIP_UTILS_LIST "pip_system_test")
set(PIP_UTILS_LIST ${PIP_UTILS_LIST} PARENT_SCOPE)
add_executable(pip_system_test "main.cpp")
target_link_libraries(pip_system_test pip)
if (DEFINED LIB)
install(TARGETS pip_system_test DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
endif ()