read and write pipes
This commit is contained in:
@@ -22,9 +22,11 @@
|
|||||||
|
|
||||||
# include "piincludes_p.h"
|
# include "piincludes_p.h"
|
||||||
# include "piprocess.h"
|
# include "piprocess.h"
|
||||||
|
# include "piliterals_bytes.h"
|
||||||
# ifndef WINDOWS
|
# ifndef WINDOWS
|
||||||
# include <csignal>
|
# include <csignal>
|
||||||
# include <sys/wait.h>
|
# include <sys/wait.h>
|
||||||
|
# include <fcntl.h>
|
||||||
# endif
|
# endif
|
||||||
# ifdef MAC_OS
|
# ifdef MAC_OS
|
||||||
# include <crt_externs.h>
|
# include <crt_externs.h>
|
||||||
@@ -80,7 +82,9 @@ constexpr int StdFileCount = StdLast + 1;
|
|||||||
|
|
||||||
# ifdef WINDOWS
|
# ifdef WINDOWS
|
||||||
using PipeHandleType = HANDLE;
|
using PipeHandleType = HANDLE;
|
||||||
|
using SizeType = DWORD;
|
||||||
# else
|
# else
|
||||||
|
using SizeType = ssize_t;
|
||||||
using PipeHandleType = int;
|
using PipeHandleType = int;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
@@ -108,6 +112,7 @@ char * const * convertToCharArrays(const PIStringList & sl) {
|
|||||||
|
|
||||||
|
|
||||||
PRIVATE_DEFINITION_START(PIProcess)
|
PRIVATE_DEFINITION_START(PIProcess)
|
||||||
|
|
||||||
# ifdef WINDOWS
|
# ifdef WINDOWS
|
||||||
PROCESS_INFORMATION pi;
|
PROCESS_INFORMATION pi;
|
||||||
# else
|
# else
|
||||||
@@ -116,6 +121,7 @@ PRIVATE_DEFINITION_START(PIProcess)
|
|||||||
PipeHandleType pipes[StdFileCount][PipesDirections];
|
PipeHandleType pipes[StdFileCount][PipesDirections];
|
||||||
bool grab[StdFileCount];
|
bool grab[StdFileCount];
|
||||||
|
|
||||||
|
|
||||||
void forEachPipe(std::function<void(PipeHandleType &)> func) {
|
void forEachPipe(std::function<void(PipeHandleType &)> func) {
|
||||||
for (int i = 0; i < StdFileCount; ++i) {
|
for (int i = 0; i < StdFileCount; ++i) {
|
||||||
for (int j = 0; j < PipesDirections; ++j) {
|
for (int j = 0; j < PipesDirections; ++j) {
|
||||||
@@ -124,23 +130,22 @@ PRIVATE_DEFINITION_START(PIProcess)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void initGrab() {
|
void initGrab() {
|
||||||
for (int i = 0; i < StdFileCount; ++i) {
|
for (int i = 0; i < StdFileCount; ++i) {
|
||||||
grab[i] = false;
|
grab[i] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool createPipe(StdFile pipe_type) {
|
bool createPipe(StdFile pipe_type) {
|
||||||
const int pt = static_cast<int>(pipe_type);
|
const int pt = pipe_type;
|
||||||
# ifdef WINDOWS
|
# ifdef WINDOWS
|
||||||
SECURITY_ATTRIBUTES saAttr;
|
SECURITY_ATTRIBUTES saAttr;
|
||||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||||
saAttr.bInheritHandle = TRUE;
|
saAttr.bInheritHandle = TRUE;
|
||||||
saAttr.lpSecurityDescriptor = NULL;
|
satrueAttr.lpSecurityDescriptor = NULL;
|
||||||
if (!CreatePipe(&(pipes[pt][PipeDirection::Read]), &(pipes[pt][PipeDirection::Write]), &saAttr, 0)) {
|
if (!CreatePipe(&(pipes[pt][PipeRead]), &(pipes[pt][PipeWrite]), &saAttr, 0)) return false;
|
||||||
piCout << "CreatePipe failed: " << GetLastError() << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
# else
|
# else
|
||||||
int ret = pipe(pipes[pt]);
|
int ret = pipe(pipes[pt]);
|
||||||
@@ -148,6 +153,76 @@ PRIVATE_DEFINITION_START(PIProcess)
|
|||||||
# endif
|
# endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool createPipes() {
|
||||||
|
for (int i = 0; i < StdFileCount; ++i) {
|
||||||
|
if (grab[i]) {
|
||||||
|
if (!createPipe(static_cast<StdFile>(i))) {
|
||||||
|
piCout << "CreatePipe failed";
|
||||||
|
# ifdef WINDOWS
|
||||||
|
piCout << GetLastError();
|
||||||
|
# endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void closePipe(StdFile pipe_type, PipeDirection direction) {
|
||||||
|
if (grab[pipe_type]) {
|
||||||
|
# ifdef WINDOWS
|
||||||
|
CloseHandle(pipes[pipe_type][direction]);
|
||||||
|
pipes[pipe_type][direction] = 0;
|
||||||
|
# else
|
||||||
|
::close(pipes[pipe_type][direction]);
|
||||||
|
pipes[pipe_type][direction] = -1;
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PIByteArray readPipe(StdFile pipe_type) {
|
||||||
|
if (!grab[pipe_type]) return {};
|
||||||
|
constexpr size_t read_buffer_size = 4_KiB;
|
||||||
|
PIByteArray read_buffer;
|
||||||
|
read_buffer.resize(read_buffer_size);
|
||||||
|
SizeType bytes_read = 0;
|
||||||
|
size_t offset = 0;
|
||||||
|
while (1) {
|
||||||
|
# ifdef WINDOWS
|
||||||
|
BOOL ok = ReadFile(pipes[pipe_type][PipeRead], read_buffer.data(offset), read_buffer.size() - offset, &bytes_read, NULL);
|
||||||
|
if (!ok) bytes_read = 0;
|
||||||
|
#else
|
||||||
|
bytes_read = ::read(pipes[pipe_type][PipeRead], read_buffer.data(offset), read_buffer.size() - offset);
|
||||||
|
#endif
|
||||||
|
piCout << "readed" << bytes_read;
|
||||||
|
if (bytes_read > 0) {
|
||||||
|
offset += bytes_read;
|
||||||
|
read_buffer.resize(offset + read_buffer_size);
|
||||||
|
} else {
|
||||||
|
read_buffer.resize(offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
piCout << "readPipe" << read_buffer.toHex();
|
||||||
|
return read_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool writePipe(const PIByteArray & data) {
|
||||||
|
SizeType sz = 0;
|
||||||
|
# ifdef WINDOWS
|
||||||
|
BOOL ok = WriteFile(pipes[StdIn][PipeWrite], data.data(), data.size(), &sz, NULL);
|
||||||
|
if (!ok) sz = 0;
|
||||||
|
#else
|
||||||
|
sz = ::write(pipes[StdIn][PipeWrite], data.data(), data.size());
|
||||||
|
# endif
|
||||||
|
return sz == data.size_s();
|
||||||
|
}
|
||||||
|
|
||||||
PRIVATE_DEFINITION_END(PIProcess)
|
PRIVATE_DEFINITION_END(PIProcess)
|
||||||
|
|
||||||
|
|
||||||
@@ -159,7 +234,8 @@ PIProcess::PIProcess(): PIThread() {
|
|||||||
PRIVATE->pid = 0;
|
PRIVATE->pid = 0;
|
||||||
PRIVATE->forEachPipe([](PipeHandleType & pipe) { pipe = -1; });
|
PRIVATE->forEachPipe([](PipeHandleType & pipe) { pipe = -1; });
|
||||||
# endif
|
# endif
|
||||||
is_exec = false;
|
exec_start = false;
|
||||||
|
exec_finished = false;
|
||||||
PRIVATE->initGrab();
|
PRIVATE->initGrab();
|
||||||
env = PIProcess::currentEnvironment();
|
env = PIProcess::currentEnvironment();
|
||||||
}
|
}
|
||||||
@@ -171,39 +247,23 @@ PIProcess::~PIProcess() {
|
|||||||
|
|
||||||
|
|
||||||
void PIProcess::exec_() {
|
void PIProcess::exec_() {
|
||||||
is_exec = false;
|
exec_finished = false;
|
||||||
|
exec_start = false;
|
||||||
startOnce();
|
startOnce();
|
||||||
// cout << "exec wait" << endl;
|
|
||||||
while (!is_exec)
|
|
||||||
piMinSleep();
|
|
||||||
// cout << "exec end" << endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIProcess::startProc(bool detached) {
|
void PIProcess::startProc(bool detached) {
|
||||||
// cout << "run" << endl;
|
|
||||||
const PIString & str = args.front();
|
const PIString & str = args.front();
|
||||||
/// arguments convertion
|
|
||||||
|
|
||||||
/// files for stdin/out/err
|
|
||||||
|
|
||||||
is_exec = true;
|
|
||||||
if (!detached) execStarted(str);
|
if (!detached) execStarted(str);
|
||||||
|
if (!PRIVATE->createPipes()) return;
|
||||||
// PRIVATE->tf_in = PRIVATE->tf_out = PRIVATE->tf_err = 0;
|
|
||||||
// // cout << "exec " << tf_in << ", " << tf_out << ", " << tf_err << endl;
|
|
||||||
// // cout << f_out.path() << endl;
|
|
||||||
// if (g_in) PRIVATE->tf_in = freopen(f_in.path().data(), "r", stdin);
|
|
||||||
// if (g_out) PRIVATE->tf_out = freopen(f_out.path().data(), "w", stdout);
|
|
||||||
// if (g_err) PRIVATE->tf_err = freopen(f_err.path().data(), "w", stderr);
|
|
||||||
|
|
||||||
# ifdef WINDOWS
|
# ifdef WINDOWS
|
||||||
STARTUPINFOA si;
|
STARTUPINFOA si;
|
||||||
piZeroMemory(pi);
|
piZeroMemory(pi);
|
||||||
si.cb = sizeof(STARTUPINFOA);
|
si.cb = sizeof(STARTUPINFOA);
|
||||||
if (PRIVATE->grab[StdFile::In]) si.hStdInput = PRIVATE->pipes[StdFile::In][PipeDirection::Read];
|
if (PRIVATE->grab[StdIn]) si.hStdInput = PRIVATE->pipes[StdIn][PipeRead];
|
||||||
if (PRIVATE->grab[StdFile::Out]) si.hStdOutput = PRIVATE->pipes[StdFile::Out][PipeDirection::Write];
|
if (PRIVATE->grab[StdOut]) si.hStdOutput = PRIVATE->pipes[StdOut][PipeWrite];
|
||||||
if (PRIVATE->grab[StdFile::Err]) si.hStdError = PRIVATE->pipes[StdFile::Err][PipeDirection::Write];
|
if (PRIVATE->grab[StdErr]) si.hStdError = PRIVATE->pipes[StdErr][PipeWrite];
|
||||||
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)
|
||||||
convertWindowsCmd(args), // Command line
|
convertWindowsCmd(args), // Command line
|
||||||
@@ -216,10 +276,12 @@ void PIProcess::startProc(bool detached) {
|
|||||||
&si, // Pointer to STARTUPINFO structure
|
&si, // Pointer to STARTUPINFO structure
|
||||||
&(PRIVATE->pi))) // Pointer to PROCESS_INFORMATION structure
|
&(PRIVATE->pi))) // Pointer to PROCESS_INFORMATION structure
|
||||||
{
|
{
|
||||||
|
exec_start = true;
|
||||||
if (!detached) {
|
if (!detached) {
|
||||||
WaitForSingleObject(PRIVATE->pi.hProcess, INFINITE);
|
WaitForSingleObject(PRIVATE->pi.hProcess, INFINITE);
|
||||||
DWORD code = -1;
|
DWORD code = -1;
|
||||||
if (GetExitCodeProcess(PRIVATE->pi.hProcess, &code) != 0) exit_code = code;
|
if (GetExitCodeProcess(PRIVATE->pi.hProcess, &code) != 0) exit_code = code;
|
||||||
|
exec_finished = true;
|
||||||
}
|
}
|
||||||
CloseHandle(PRIVATE->pi.hThread);
|
CloseHandle(PRIVATE->pi.hThread);
|
||||||
CloseHandle(PRIVATE->pi.hProcess);
|
CloseHandle(PRIVATE->pi.hProcess);
|
||||||
@@ -235,46 +297,54 @@ void PIProcess::startProc(bool detached) {
|
|||||||
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;
|
PRIVATE->closePipe(StdIn, PipeWrite);
|
||||||
if (execve(str.data(), largs, lenv) < 0) {
|
PRIVATE->closePipe(StdOut, PipeRead);
|
||||||
|
PRIVATE->closePipe(StdErr, PipeRead);
|
||||||
|
|
||||||
|
if (PRIVATE->grab[StdIn]) dup2(PRIVATE->pipes[StdIn][PipeRead], STDIN_FILENO);
|
||||||
|
if (PRIVATE->grab[StdOut]) dup2(PRIVATE->pipes[StdOut][PipeWrite], STDOUT_FILENO);
|
||||||
|
if (PRIVATE->grab[StdErr]) dup2(PRIVATE->pipes[StdErr][PipeWrite], STDERR_FILENO);
|
||||||
|
|
||||||
|
PRIVATE->closePipe(StdIn, PipeRead);
|
||||||
|
PRIVATE->closePipe(StdOut, PipeWrite);
|
||||||
|
PRIVATE->closePipe(StdErr, PipeWrite);
|
||||||
|
|
||||||
|
execve(str.data(), largs, lenv);
|
||||||
|
// normaly execve can't return, if it returns - error occured
|
||||||
piCoutObj << "\"execve" << str << args << "\" error :" << errorString();
|
piCoutObj << "\"execve" << str << args << "\" error :" << errorString();
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
PRIVATE->closePipe(StdIn, PipeRead);
|
||||||
|
PRIVATE->closePipe(StdOut, PipeWrite);
|
||||||
|
PRIVATE->closePipe(StdErr, PipeWrite);
|
||||||
|
|
||||||
|
if (PRIVATE->grab[StdOut]) fcntl(PRIVATE->pipes[StdOut][PipeRead], F_SETFL, O_NONBLOCK);
|
||||||
|
if (PRIVATE->grab[StdErr]) fcntl(PRIVATE->pipes[StdErr][PipeRead], F_SETFL, O_NONBLOCK);
|
||||||
|
|
||||||
|
exec_start = true;
|
||||||
piMinSleep();
|
piMinSleep();
|
||||||
// cout << "wait" << endl;
|
|
||||||
if (!detached) {
|
if (!detached) {
|
||||||
waitpid(pid_, &exit_code, 0);
|
waitpid(pid_, &exit_code, 0);
|
||||||
|
exec_finished = true;
|
||||||
pid_ = 0;
|
pid_ = 0;
|
||||||
if (!detached) PRIVATE->pid = pid_;
|
if (!detached) PRIVATE->pid = pid_;
|
||||||
// cout << "wait done" << endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete[] largs;
|
delete[] largs;
|
||||||
delete[] lenv;
|
delete[] lenv;
|
||||||
# endif
|
# endif
|
||||||
if (!detached) execFinished(str, exit_code);
|
if (!detached) execFinished(str, exit_code);
|
||||||
is_exec = false;
|
exec_start = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// PIByteArray PIProcess::readFile(PIFile & f, bool clear)
|
|
||||||
// {
|
|
||||||
// f.open(PIIODevice::ReadOnly);
|
|
||||||
// const auto ret = f.readAll();
|
|
||||||
// if (clear) {
|
|
||||||
// f.clear();
|
|
||||||
// }
|
|
||||||
// return ret;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
void PIProcess::terminate() {
|
void PIProcess::terminate() {
|
||||||
# ifdef WINDOWS
|
# ifdef WINDOWS
|
||||||
if (is_exec) {
|
if (exec_start) {
|
||||||
if (!TerminateProcess(PRIVATE->pi.hProcess, 0)) return;
|
if (!TerminateProcess(PRIVATE->pi.hProcess, 0)) return;
|
||||||
}
|
}
|
||||||
PRIVATE->pi.dwProcessId = 0;
|
PRIVATE->pi.dwProcessId = 0;
|
||||||
# else
|
# else
|
||||||
if (is_exec) kill(PRIVATE->pid, SIGKILL);
|
if (exec_start) kill(PRIVATE->pid, SIGKILL);
|
||||||
PRIVATE->pid = 0;
|
PRIVATE->pid = 0;
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
@@ -297,27 +367,41 @@ int PIProcess::pID() const {
|
|||||||
|
|
||||||
|
|
||||||
PIByteArray PIProcess::readOutput() {
|
PIByteArray PIProcess::readOutput() {
|
||||||
return {}; // readFile(f_out, clear);
|
return PRIVATE->readPipe(StdOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PIByteArray PIProcess::readError() {
|
PIByteArray PIProcess::readError() {
|
||||||
return {}; // readFile(f_err, clear);
|
return PRIVATE->readPipe(StdErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PIProcess::writeInput(const PIByteArray &data)
|
|
||||||
{
|
|
||||||
if (PRIVATE->grab[StdIn]) {
|
|
||||||
|
|
||||||
}
|
bool PIProcess::writeInput(const PIByteArray & data) {
|
||||||
|
if (PRIVATE->grab[StdIn]) return PRIVATE->writePipe(data);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PIProcess::closeInput()
|
|
||||||
{
|
|
||||||
if (PRIVATE->grab[StdIn]) {
|
|
||||||
|
|
||||||
|
void PIProcess::closeInput() {
|
||||||
|
if (PRIVATE->grab[StdIn]) PRIVATE->closePipe(StdIn, PipeWrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIProcess::enableWriteStdIn(bool on)
|
||||||
|
{
|
||||||
|
PRIVATE->grab[StdIn] = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIProcess::enableReadStdOut(bool on)
|
||||||
|
{
|
||||||
|
PRIVATE->grab[StdOut] = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIProcess::enableReadStdErr(bool on)
|
||||||
|
{
|
||||||
|
PRIVATE->grab[StdErr] = on;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,10 @@ public:
|
|||||||
//! \~russian
|
//! \~russian
|
||||||
void closeInput();
|
void closeInput();
|
||||||
|
|
||||||
|
void enableWriteStdIn(bool on = true);
|
||||||
|
void enableReadStdOut(bool on = true);
|
||||||
|
void enableReadStdErr(bool on = true);
|
||||||
|
|
||||||
//! \~english Returns current attached execution environment
|
//! \~english Returns current attached execution environment
|
||||||
//! \~russian
|
//! \~russian
|
||||||
PIStringList environment() { return env; }
|
PIStringList environment() { return env; }
|
||||||
@@ -123,6 +127,9 @@ public:
|
|||||||
EVENT1(execStarted, PIString, program);
|
EVENT1(execStarted, PIString, program);
|
||||||
EVENT2(execFinished, PIString, program, int, exit_code);
|
EVENT2(execFinished, PIString, program, int, exit_code);
|
||||||
|
|
||||||
|
bool isExecFinished() const {return exec_finished;}
|
||||||
|
bool isExecStarted() const {return exec_start;}
|
||||||
|
|
||||||
|
|
||||||
//! \~english Start detached execution "program" without arguments
|
//! \~english Start detached execution "program" without arguments
|
||||||
//! \~russian
|
//! \~russian
|
||||||
@@ -203,7 +210,8 @@ private:
|
|||||||
PIStringList args, env;
|
PIStringList args, env;
|
||||||
PIString wd;
|
PIString wd;
|
||||||
int exit_code;
|
int exit_code;
|
||||||
bool is_exec;
|
std::atomic_bool exec_start;
|
||||||
|
std::atomic_bool exec_finished;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MICRO_PIP
|
#endif // MICRO_PIP
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
#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());
|
|
||||||
}
|
|
||||||
91
tests/system/process_test.cpp
Normal file
91
tests/system/process_test.cpp
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#include "piprocess.h"
|
||||||
|
#include "pitime.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessTest: public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
PIProcess launcher;
|
||||||
|
const PIString command =
|
||||||
|
#ifdef _WIN32
|
||||||
|
"cmd.exe";
|
||||||
|
#else
|
||||||
|
"/bin/sh";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
launcher.enableWriteStdIn(false);
|
||||||
|
launcher.enableReadStdOut();
|
||||||
|
launcher.enableReadStdErr();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
if (launcher.isRunning()) {
|
||||||
|
launcher.waitForFinish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(ProcessTest, Output) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
const PIStringList args = {"/c", "echo Hello from stdout && echo Hello from stderr 1>&2"};
|
||||||
|
#else
|
||||||
|
const PIStringList args = {"-c", "echo Hello from stdout; echo Hello from stderr 1>&2"};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
launcher.exec(command, args);
|
||||||
|
ASSERT_TRUE(launcher.isRunning());
|
||||||
|
|
||||||
|
piMSleep(100);
|
||||||
|
|
||||||
|
ASSERT_TRUE(launcher.isExecFinished());
|
||||||
|
|
||||||
|
const auto out = PIString::fromConsole(launcher.readOutput());
|
||||||
|
const auto err = PIString::fromConsole(launcher.readError());
|
||||||
|
|
||||||
|
EXPECT_TRUE(out.contains("Hello from stdout"));
|
||||||
|
EXPECT_TRUE(err.contains("Hello from stderr"));
|
||||||
|
|
||||||
|
ASSERT_TRUE(launcher.waitForFinish());
|
||||||
|
const 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(ProcessTest, Input) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
const PIStringList args = {"/c", "set /p input= && echo %input%"};
|
||||||
|
#else
|
||||||
|
const PIStringList args = {"-c", "read input; echo $input"};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
launcher.enableWriteStdIn();
|
||||||
|
launcher.exec(command, args);
|
||||||
|
ASSERT_TRUE(launcher.isRunning());
|
||||||
|
|
||||||
|
const PIString test_input = "Test input string\n";
|
||||||
|
EXPECT_TRUE(launcher.writeInput(test_input.toSystem()));
|
||||||
|
launcher.closeInput();
|
||||||
|
|
||||||
|
piMSleep(100);
|
||||||
|
|
||||||
|
const auto out = PIString::fromConsole(launcher.readOutput());
|
||||||
|
EXPECT_TRUE(out.contains("Test input string"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(ProcessTest, DISABLED_NonexistentCommand) {
|
||||||
|
const PIString command = {"nonexistent_command_12345"};
|
||||||
|
|
||||||
|
launcher.exec(command);
|
||||||
|
EXPECT_FALSE(launcher.isRunning());
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user