X-Git-Url: https://git.stg.codes/stg.git/blobdiff_plain/e3e2d6326db86d7ca22d2cba1193aa64a8e33b2d..08dd72f2d8d3d7766e4fa87f01840c3ed8211091:/tests/tut/tut_posix.hpp diff --git a/tests/tut/tut_posix.hpp b/tests/tut/tut_posix.hpp deleted file mode 100644 index c7cfe57a..00000000 --- a/tests/tut/tut_posix.hpp +++ /dev/null @@ -1,485 +0,0 @@ -#ifndef TUT_FORK_H_GUARD -#define TUT_FORK_H_GUARD -#include - -#if defined(TUT_USE_POSIX) -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "tut_result.hpp" -#include "tut_assert.hpp" -#include "tut_runner.hpp" - -namespace tut -{ - -template -class test_group; - -template -class test_object; - -class test_group_posix -{ -private: - template - friend class test_group; - - template - void send_result_(const T *obj, const test_result &tr) - { - if(obj->get_pipe_() == -1) - { - return; - } - - if(tr.result != test_result::ok) - { - std::ostringstream ss; - ss << int(tr.result) << "\n" - << tr.group << "\n" - << tr.test << "\n" - << tr.name << "\n" - << tr.exception_typeid << "\n"; - std::copy( tr.message.begin(), tr.message.end(), std::ostreambuf_iterator(ss.rdbuf()) ); - - int size = ss.str().length(); - int w = write(obj->get_pipe_(), ss.str().c_str(), size); - ensure_errno("write() failed", w == size); - } - } - - virtual ~test_group_posix() - { - } -}; - -template -struct tut_posix -{ - pid_t fork() - { - test_object *self = dynamic_cast< tut::test_object* >(this); - ensure("trying to call 'tut_fork' in ctor of test object", self != NULL); - - return self->fork_(); - } - - pid_t waitpid(pid_t pid, int *status, int flags = 0) - { - test_object *self = dynamic_cast< tut::test_object* >(this); - ensure("trying to call 'tut_waitpid' in ctor of test object", self != NULL); - - return self->waitpid_(pid, status, flags); - } - - void ensure_child_exit(pid_t pid, int exit_status = 0) - { - test_object *self = dynamic_cast< tut::test_object* >(this); - ensure("trying to call 'ensure_child_exit' in ctor of test object", self != NULL); - - int status; - self->waitpid_(pid, &status); - - self->ensure_child_exit_(status, exit_status); - } - - - void ensure_child_signal(pid_t pid, int signal = SIGTERM) - { - test_object *self = dynamic_cast< tut::test_object* >(this); - ensure("trying to call 'ensure_child_signal' in ctor of test object", self != NULL); - - int status; - self->waitpid_(pid, &status); - - self->ensure_child_signal_(status, signal); - } - - std::set get_pids() const - { - using namespace std; - - const test_object *self = dynamic_cast< const tut::test_object* >(this); - ensure("trying to call 'get_pids' in ctor of test object", self != NULL); - - return self->get_pids_(); - } - - virtual ~tut_posix() - { - } - -}; - -class test_object_posix -{ -public: - typedef std::map pid_map; - - /** - * Default constructor - */ - test_object_posix() - : pids_(), - pipe_(-1) - { - } - - - virtual ~test_object_posix() - { - // we have forked - if(pipe_ != -1) - { - // in child, force exit - std::exit(0); - } - - if(!pids_.empty()) - { - std::ostringstream ss; - - // in parent, reap children - for(std::map::iterator i = pids_.begin(); i != pids_.end(); ++i) - { - try { - kill_child_(i->first); - } catch(const rethrown &ex) { - ss << std::endl << "child " << ex.tr.pid << " has thrown an exception: " << ex.what(); - } catch(const failure &ex) { - ss << std::endl << ex.what(); - } - } - - if(!ss.str().empty()) - { - fail(ss.str().c_str()); - } - } - } - -private: - template - friend class tut_posix; - - friend class test_group_posix; - - int get_pipe_() const - { - return pipe_; - } - - - pid_t fork_() - { - // create pipe - int fds[2]; - ensure_errno("pipe() failed", ::pipe(fds) == 0); - - pid_t pid = ::fork(); - - ensure_errno("fork() failed", pid >= 0); - - if(pid != 0) - { - // in parent, register pid - ensure("duplicated child", pids_.insert( std::make_pair(pid, fds[0]) ).second); - - // close writing side - close(fds[1]); - } - else - { - // in child, shutdown reporter - tut::runner.get().clear_callbacks(); - - // close reading side - close(fds[0]); - pipe_ = fds[1]; - } - - return pid; - } - - void kill_child_(pid_t pid) - { - int status; - - if(waitpid_(pid, &status, WNOHANG) == pid) - { - ensure_child_exit_(status, 0); - return; - } - - if(::kill(pid, SIGTERM) != 0) - { - if(errno == ESRCH) - { - // no such process - return; - } - else - { - // cannot kill, we are in trouble - std::ostringstream ss; - char e[1024]; - ss << "child " << pid << " could not be killed with SIGTERM, " << strerror_r(errno, e, sizeof(e)) << std::endl; - fail(ss.str()); - } - } - - if(waitpid_(pid, &status, WNOHANG) == pid) - { - // child killed, check signal - ensure_child_signal_(status, SIGTERM); - - ensure_equals("child process exists after SIGTERM", ::kill(pid, 0), -1); - return; - } - - // child seems to be still exiting, give it some time - sleep(2); - - if(waitpid_(pid, &status, WNOHANG) != pid) - { - // child is still running, kill it - if(::kill(pid, SIGKILL) != 0) - { - if(errno == ESRCH) - { - // no such process - return; - } - else - { - std::ostringstream ss; - char e[1024]; - ss << "child " << pid << " could not be killed with SIGKILL, " << strerror_r(errno, e, sizeof(e)) << std::endl; - fail(ss.str()); - } - } - - ensure_equals("wait after SIGKILL", waitpid_(pid, &status), pid); - ensure_child_signal_(status, SIGKILL); - - ensure_equals("child process exists after SIGKILL", ::kill(pid, 0), -1); - - std::ostringstream ss; - ss << "child " << pid << " had to be killed with SIGKILL"; - fail(ss.str()); - } - } - - test_result receive_result_(std::istream &ss, pid_t pid) - { - test_result tr; - - int type; - ss >> type; - tr.result = test_result::result_type(type); - ss.ignore(1024, '\n'); - - std::getline(ss, tr.group); - ss >> tr.test; - ss.ignore(1024, '\n'); - std::getline(ss, tr.name); - std::getline(ss, tr.exception_typeid); - std::copy( std::istreambuf_iterator(ss.rdbuf()), - std::istreambuf_iterator(), - std::back_inserter(tr.message) ); - - tr.pid = pid; - - return tr; - } - - struct fdclose - { - fdclose(int fd): fd_(fd) { } - ~fdclose() - { - close(fd_); - } - private: - int fd_; - }; - - pid_t waitpid_(pid_t pid, int *status, int flags = 0) - { - - ensure("trying to wait for unknown pid", pids_.count(pid) > 0); - - pid_t p = ::waitpid(pid, status, flags); - if( (flags & WNOHANG) && (p != pid) ) - { - return p; - } - - // read child result from pipe - fd_set fdset; - timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 0; - - FD_ZERO(&fdset); - - int pipe = pids_[pid]; - fdclose guard(pipe); - - FD_SET(pipe, &fdset); - - int result = select(pipe+1, &fdset, NULL, NULL, &tv); - ensure_errno("sanity check on select() failed", result >= 0); - - if(result > 0) - { - ensure("sanity check on FD_ISSET() failed", FD_ISSET(pipe, &fdset) ); - - std::stringstream ss; - - //TODO: max failure length - char buffer[1024]; - int r = read(pipe, buffer, sizeof(buffer)); - ensure_errno("sanity check on read() failed", r >= 0); - - if(r > 0) - { - ss.write(buffer, r); - throw rethrown( receive_result_(ss, pid) ); - } - } - - return pid; - } - - void ensure_child_exit_(int status, int exit_status) - { - if(WIFSIGNALED(status)) - { - std::ostringstream ss; - ss << "child killed by signal " << WTERMSIG(status) - << ": expected exit with code " << exit_status; - - throw failure(ss.str().c_str()); - } - - if(WIFEXITED(status)) - { - if(WEXITSTATUS(status) != exit_status) - { - std::ostringstream ss; - ss << "child exited, expected '" - << exit_status - << "' actual '" - << WEXITSTATUS(status) - << '\''; - - throw failure(ss.str().c_str()); - } - } - - if(WIFSTOPPED(status)) - { - std::ostringstream ss; - ss << "child stopped by signal " << WTERMSIG(status) - << ": expected exit with code " << exit_status; - throw failure(ss.str().c_str()); - } - } - - void ensure_child_signal_(int status, int signal) - { - if(WIFSIGNALED(status)) - { - if(WTERMSIG(status) != signal) - { - std::ostringstream ss; - ss << "child killed by signal, expected '" - << signal - << "' actual '" - << WTERMSIG(status) - << '\''; - throw failure(ss.str().c_str()); - } - } - - if(WIFEXITED(status)) - { - std::ostringstream ss; - ss << "child exited with code " << WEXITSTATUS(status) - << ": expected signal " << signal; - - throw failure(ss.str().c_str()); - } - - if(WIFSTOPPED(status)) - { - std::ostringstream ss; - ss << "child stopped by signal " << WTERMSIG(status) - << ": expected kill by signal " << signal; - - throw failure(ss.str().c_str()); - } - } - - std::set get_pids_() const - { - using namespace std; - - set pids; - for(pid_map::const_iterator i = pids_.begin(); i != pids_.end(); ++i) - { - pids.insert( i->first ); - } - - return pids; - } - - pid_map pids_; - int pipe_; -}; - -} // namespace tut - -#else - -namespace tut -{ - -struct test_object_posix -{ - virtual ~test_object_posix() - { - } -}; - -struct test_group_posix -{ - template - void send_result_(const T*, const test_result &) - { - } - - virtual ~test_group_posix() - { - } -}; - -} // namespace tut - -#endif - - -#endif -