add process tests
This commit is contained in:
@@ -604,7 +604,7 @@ if(NOT PIP_FREERTOS)
|
|||||||
add_subdirectory("utils/translator")
|
add_subdirectory("utils/translator")
|
||||||
add_subdirectory("utils/value_tree_translator")
|
add_subdirectory("utils/value_tree_translator")
|
||||||
if(PIP_UTILS AND (NOT CROSSTOOLS))
|
if(PIP_UTILS AND (NOT CROSSTOOLS))
|
||||||
add_subdirectory("utils/system_test")
|
add_subdirectory("utils/system_calib")
|
||||||
add_subdirectory("utils/udp_file_transfer")
|
add_subdirectory("utils/udp_file_transfer")
|
||||||
if(sodium_FOUND)
|
if(sodium_FOUND)
|
||||||
add_subdirectory("utils/system_daemon")
|
add_subdirectory("utils/system_daemon")
|
||||||
|
|||||||
@@ -62,28 +62,49 @@
|
|||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
enum class PipeDirection {
|
enum PipeDirection {
|
||||||
Read,
|
PipeRead,
|
||||||
Write,
|
PipeWrite,
|
||||||
Last = Write
|
PipeLast = PipeWrite
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class StdFile {
|
enum StdFile {
|
||||||
In,
|
StdIn,
|
||||||
Out,
|
StdOut,
|
||||||
Err,
|
StdErr,
|
||||||
Last = Err
|
StdLast = StdErr
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr int PipesDirections = static_cast<int>(PipeDirection::Last) + 1;
|
constexpr int PipesDirections = PipeLast + 1;
|
||||||
constexpr int StdFileCount = static_cast<int>(StdFile::Last) + 1;
|
constexpr int StdFileCount = StdLast + 1;
|
||||||
|
|
||||||
# ifdef WINDOWS
|
# ifdef WINDOWS
|
||||||
using PipeHandleType = HANDLE;
|
using PipeHandleType = HANDLE;
|
||||||
# else
|
# else
|
||||||
using PipeHandleType = int;
|
using PipeHandleType = int;
|
||||||
# endif
|
# 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)
|
PRIVATE_DEFINITION_START(PIProcess)
|
||||||
@@ -136,9 +157,9 @@ PIProcess::PIProcess(): PIThread() {
|
|||||||
PRIVATE->pi.dwProcessId = 0;
|
PRIVATE->pi.dwProcessId = 0;
|
||||||
# else
|
# else
|
||||||
PRIVATE->pid = 0;
|
PRIVATE->pid = 0;
|
||||||
|
PRIVATE->forEachPipe([](PipeHandleType & pipe) { pipe = -1; });
|
||||||
# endif
|
# endif
|
||||||
is_exec = false;
|
is_exec = false;
|
||||||
PRIVATE->forEachPipe([](PipeHandleType & pipe) { pipe = 0;});
|
|
||||||
PRIVATE->initGrab();
|
PRIVATE->initGrab();
|
||||||
env = PIProcess::currentEnvironment();
|
env = PIProcess::currentEnvironment();
|
||||||
}
|
}
|
||||||
@@ -161,49 +182,14 @@ void PIProcess::exec_() {
|
|||||||
|
|
||||||
void PIProcess::startProc(bool detached) {
|
void PIProcess::startProc(bool detached) {
|
||||||
// cout << "run" << endl;
|
// cout << "run" << endl;
|
||||||
PIString str;
|
const PIString & str = args.front();
|
||||||
/// arguments convertion
|
/// 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
|
/// files for stdin/out/err
|
||||||
|
|
||||||
str = args.front();
|
|
||||||
is_exec = true;
|
is_exec = true;
|
||||||
if (!detached) execStarted(str);
|
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;
|
// PRIVATE->tf_in = PRIVATE->tf_out = PRIVATE->tf_err = 0;
|
||||||
// // cout << "exec " << tf_in << ", " << tf_out << ", " << tf_err << endl;
|
// // cout << "exec " << tf_in << ", " << tf_out << ", " << tf_err << endl;
|
||||||
// // cout << f_out.path() << 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];
|
if (PRIVATE->grab[StdFile::Err]) si.hStdError = PRIVATE->pipes[StdFile::Err][PipeDirection::Write];
|
||||||
si.dwFlags |= STARTF_USESTDHANDLES;
|
si.dwFlags |= STARTF_USESTDHANDLES;
|
||||||
if (CreateProcessA(0, // No module name (use command line)
|
if (CreateProcessA(0, // No module name (use command line)
|
||||||
a, // Command line
|
convertWindowsCmd(args), // Command line
|
||||||
0, // Process handle not inheritable
|
0, // Process handle not inheritable
|
||||||
0, // Thread handle not inheritable
|
0, // Thread handle not inheritable
|
||||||
false, // Set handle inheritance to FALSE
|
false, // Set handle inheritance to FALSE
|
||||||
detached ? DETACHED_PROCESS /*CREATE_NEW_CONSOLE*/ : 0, // Creation flags
|
detached ? DETACHED_PROCESS /*CREATE_NEW_CONSOLE*/ : 0, // Creation flags
|
||||||
0, // envcc, // Use environment
|
0, // Use environment
|
||||||
wd.isEmpty() ? 0 : wd.data(), // Use working directory
|
wd.isEmpty() ? 0 : wd.data(), // Use working directory
|
||||||
&si, // Pointer to STARTUPINFO structure
|
&si, // Pointer to STARTUPINFO structure
|
||||||
&(PRIVATE->pi))) // Pointer to PROCESS_INFORMATION structure
|
&(PRIVATE->pi))) // Pointer to PROCESS_INFORMATION structure
|
||||||
@@ -240,13 +226,17 @@ void PIProcess::startProc(bool detached) {
|
|||||||
} else {
|
} else {
|
||||||
piCoutObj << "\"CreateProcess\" error: %1"_tr("PIProcess").arg(errorString());
|
piCoutObj << "\"CreateProcess\" error: %1"_tr("PIProcess").arg(errorString());
|
||||||
}
|
}
|
||||||
# endif
|
# else
|
||||||
# ifndef WINDOWS
|
auto largs = convertToCharArrays(args);
|
||||||
|
auto lenv = convertToCharArrays(env);
|
||||||
|
int pid_ = fork();
|
||||||
|
if (!detached) PRIVATE->pid = pid_;
|
||||||
|
if (pid_ == 0) {
|
||||||
if (!wd.isEmpty()) {
|
if (!wd.isEmpty()) {
|
||||||
if (!chdir(wd.data())) piCoutObj << "Error while set working directory";
|
if (!chdir(wd.data())) piCoutObj << "Error while set working directory";
|
||||||
}
|
}
|
||||||
// cout << "exec " << tf_in << ", " << tf_out << ", " << tf_err << endl;
|
// 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();
|
piCoutObj << "\"execve" << str << args << "\" error :" << errorString();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -259,12 +249,11 @@ void PIProcess::startProc(bool detached) {
|
|||||||
// cout << "wait done" << endl;
|
// cout << "wait done" << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
delete[] largs;
|
||||||
|
delete[] lenv;
|
||||||
# endif
|
# endif
|
||||||
if (!detached) execFinished(str, exit_code);
|
if (!detached) execFinished(str, exit_code);
|
||||||
is_exec = false;
|
is_exec = false;
|
||||||
# ifdef WINDOWS
|
|
||||||
delete[] a;
|
|
||||||
# endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PIByteArray PIProcess::readFile(PIFile & f, bool clear)
|
// PIByteArray PIProcess::readFile(PIFile & f, bool clear)
|
||||||
@@ -280,8 +269,7 @@ void PIProcess::startProc(bool detached) {
|
|||||||
|
|
||||||
void PIProcess::terminate() {
|
void PIProcess::terminate() {
|
||||||
# ifdef WINDOWS
|
# ifdef WINDOWS
|
||||||
if (is_exec)
|
if (is_exec) {
|
||||||
{
|
|
||||||
if (!TerminateProcess(PRIVATE->pi.hProcess, 0)) return;
|
if (!TerminateProcess(PRIVATE->pi.hProcess, 0)) return;
|
||||||
}
|
}
|
||||||
PRIVATE->pi.dwProcessId = 0;
|
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);
|
return {}; // readFile(f_out, clear);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PIByteArray PIProcess::readError(bool clear) {
|
PIByteArray PIProcess::readError() {
|
||||||
return {}; // readFile(f_err, clear);
|
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() {
|
int PIProcess::currentPID() {
|
||||||
# ifdef WINDOWS
|
# ifdef WINDOWS
|
||||||
|
|||||||
@@ -68,11 +68,19 @@ public:
|
|||||||
|
|
||||||
//! \~english Returns all attached execution output stream
|
//! \~english Returns all attached execution output stream
|
||||||
//! \~russian
|
//! \~russian
|
||||||
PIByteArray readOutput(bool clear = false);
|
PIByteArray readOutput();
|
||||||
|
|
||||||
//! \~english Returns all attached execution error stream
|
//! \~english Returns all attached execution error stream
|
||||||
//! \~russian
|
//! \~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
|
//! \~english Returns current attached execution environment
|
||||||
//! \~russian
|
//! \~russian
|
||||||
|
|||||||
@@ -36,3 +36,4 @@ pip_test(core)
|
|||||||
pip_test(piobject)
|
pip_test(piobject)
|
||||||
pip_test(client_server pip_client_server)
|
pip_test(client_server pip_client_server)
|
||||||
pip_test(io)
|
pip_test(io)
|
||||||
|
pip_test(system)
|
||||||
|
|||||||
84
tests/system/process.cpp
Normal file
84
tests/system/process.cpp
Normal 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());
|
||||||
|
}
|
||||||
7
utils/system_calib/CMakeLists.txt
Executable file
7
utils/system_calib/CMakeLists.txt
Executable 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 ()
|
||||||
@@ -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 ()
|
|
||||||
Reference in New Issue
Block a user