#include <stdlib.h> // exit
#include <sys/wait.h> // waitpid, pid_t, WNOHANG
-
// Project
#include <anna/xml/Compiler.hpp>
#include <anna/core/util/Millisecond.hpp>
namespace {
void handle_sigchld(int sig) {
- while (waitpid((pid_t)(-1), 0, WNOHANG|WNOWAIT) > 0) {}
+ while (waitpid((pid_t)(-1 /* any child (the only) */), 0, WNOHANG|WNOWAIT) > 0) {}
}
void cmdRunOnThread (TestStepCmd *step, const std::string &cmd) {
// Thread running:
step->setThreadRunning(true);
- // Result code:
- int rc = 1;
+ int status = -2;
struct sigaction sa;
sa.sa_handler = &handle_sigchld;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sa, 0) != -1) {
- rc = system(cmd.c_str());
+ status = system(cmd.c_str());
+ /* POPEN version:
+ char readbuf[256];
+ FILE *fp = popen(cmd.c_str(), "r");
+ if (fp) {
+ while(fgets(readbuf, sizeof(readbuf), fp))
+ step->appendOutput("\n");
+ step->appendOutput(readbuf);
+ status = pclose(fp);
+ }
+ else {
+ status = -1;
+ }
+ */
}
else {
perror(0);
}
+ // This can be implemented portably and somewhat more concisely with the signal function if you prefer:
+ // if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {
+ // perror(0);
+ // exit(1);
+ // }
- if (rc < 0) {
+ if (status < 0) {
char buf[256];
char const * str = strerror_r(errno, buf, 256);
- step->setErrorMsg(anna::functions::asString("errno = %d(%s)", errno, str));
- //std::terminate;
- }
- else {
- rc >>= 8; // divide by 256
+ step->setErrorMsg(anna::functions::asString("errno = %d (%s)", errno, str));
}
- step->setResultCode(rc);
+ step->setResultCode(WEXITSTATUS(status)); // rc = status >>= 8; // divide by 256
step->complete();
- // TODO: timeout the system call
+ // TODO: terminate thread when deprecated (RT signal ?)
// TODO: mutex the step while setting data here !!
}
}
result->createAttribute("Script", (a_script != "") ? a_script:"<no script>");
result->createAttribute("Parameters", (a_parameters != "") ? a_parameters:"<no parameters>");
- result->createAttribute("CommandInProgress", a_threadRunning ? "yes":"no");
if (a_errorMsg != "") result->createAttribute("ErrorMessage", a_errorMsg);
if (!a_threadRunning && a_resultCode != -2) {
result->createAttribute("ResultCode", a_resultCode);
}
bool TestStepCmd::do_execute() throw() {
- if (!a_threadRunning) {
+ if (!a_threadRunning /* || a_threadDeprecated DO NOT WANT TO OVERLAP ... */) {
// Special tags to replace:
std::string cmd = getScript();
cmd += " ";
a_thread.detach();
}
- return false; // don't go next (wait complete)
+ return false; // don't go next (wait complete): If system function on thread stucks, then the reset test case will stuck here forever.
+ // We must implement a interrupt procedure for the thread on reset call... TODO !
}
void TestStepCmd::do_complete() throw() {
if (a_threadDeprecated) {
a_threadDeprecated = false;
do_reset();
+ setErrorMsg(anna::functions::asString("Step %d deprecated due to previous reset for Test Case %llu", getNumber(), a_testCase->getId()));
+ a_testCase->setState(TestCase::State::Failed);
return; // ignore TODO: interrupt the thread to avoid execution of the script
}
bool a_threadDeprecated;
int a_resultCode;
std::string a_errorMsg;
- //std::string a_output;
+ //std::string a_output; // for POPEN
public:
TestStepCmd(TestCase *testCase) : TestStep(testCase), a_threadRunning(false), a_threadDeprecated(false), a_resultCode(-2)/*, a_output("")*/, a_errorMsg("") { a_type = Type::Cmd; }
int getResultCode() const throw() { return a_resultCode; }
void setErrorMsg(const std::string &em) throw() { a_errorMsg = em; }
const std::string &getErrorMsg() const throw() { return a_errorMsg; }
- //void setOutput(const std::string &output) throw() { a_output = output; }
+ //void appendOutput(const std::string &output) throw() { a_output += output; }
//const std::string &getOutput() const throw() { return a_output; }
void setScript(const std::string &script) throw() { a_script = script; }