1 #ifndef TUT_FORK_H_GUARD
2 #define TUT_FORK_H_GUARD
4 #if defined(TUT_USE_POSIX)
10 #include <sys/types.h>
18 #include "tut_result.hpp"
19 #include "tut_assert.hpp"
20 #include "tut_runner.hpp"
25 template<typename, int>
31 class test_group_posix
34 template<typename, int>
35 friend class test_group;
38 void send_result_(const T *obj, const test_result &tr)
40 if(obj->get_pipe_() == -1)
45 if(tr.result != test_result::ok)
48 ss << int(tr.result) << "\n"
52 << tr.exception_typeid << "\n";
53 std::copy( tr.message.begin(), tr.message.end(), std::ostreambuf_iterator<char>(ss.rdbuf()) );
55 int size = ss.str().length();
56 int w = write(obj->get_pipe_(), ss.str().c_str(), size);
57 ensure_errno("write() failed", w == size);
67 test_object<T> *self = dynamic_cast< tut::test_object<T>* >(this);
68 ensure("trying to call 'tut_fork' in ctor of test object", self != NULL);
73 pid_t waitpid(pid_t pid, int *status, int flags = 0)
75 test_object<T> *self = dynamic_cast< tut::test_object<T>* >(this);
76 ensure("trying to call 'tut_waitpid' in ctor of test object", self != NULL);
78 return self->waitpid_(pid, status, flags);
81 void ensure_child_exit(pid_t pid, int exit_status = 0)
83 test_object<T> *self = dynamic_cast< tut::test_object<T>* >(this);
84 ensure("trying to call 'ensure_child_exit' in ctor of test object", self != NULL);
87 self->waitpid_(pid, &status);
89 self->ensure_child_exit_(status, exit_status);
93 void ensure_child_signal(pid_t pid, int signal = SIGTERM)
95 test_object<T> *self = dynamic_cast< tut::test_object<T>* >(this);
96 ensure("trying to call 'ensure_child_signal' in ctor of test object", self != NULL);
99 self->waitpid_(pid, &status);
101 self->ensure_child_signal_(status, signal);
104 std::set<pid_t> get_pids() const
108 const test_object<T> *self = dynamic_cast< const tut::test_object<T>* >(this);
109 ensure("trying to call 'get_pids' in ctor of test object", self != NULL);
111 return self->get_pids_();
120 class test_object_posix
123 typedef std::map<pid_t, int> pid_map;
126 * Default constructor
134 virtual ~test_object_posix()
139 // in child, force exit
145 std::stringstream ss;
147 // in parent, reap children
148 for(std::map<pid_t, int>::iterator i = pids_.begin(); i != pids_.end(); ++i)
151 kill_child_(i->first);
152 } catch(const rethrown &ex) {
153 ss << std::endl << "child " << ex.tr.pid << " has thrown an exception: " << ex.what();
154 } catch(const failure &ex) {
155 ss << std::endl << ex.what();
159 if(!ss.str().empty())
161 fail(ss.str().c_str());
168 friend class tut_posix;
170 friend class test_group_posix;
172 int get_pipe_() const
182 ensure_errno("pipe() failed", ::pipe(fds) == 0);
184 pid_t pid = ::fork();
186 ensure_errno("fork() failed", pid >= 0);
190 // in parent, register pid
191 ensure("duplicated child", pids_.insert( std::make_pair(pid, fds[0]) ).second);
193 // close writing side
198 // in child, shutdown reporter
199 tut::runner.get().clear_callbacks();
201 // close reading side
209 void kill_child_(pid_t pid)
213 if(waitpid_(pid, &status, WNOHANG) == pid)
215 ensure_child_exit_(status, 0);
219 if(::kill(pid, SIGTERM) != 0)
228 // cannot kill, we are in trouble
229 std::stringstream ss;
231 ss << "child " << pid << " could not be killed with SIGTERM, " << strerror_r(errno, e, sizeof(e)) << std::endl;
236 if(waitpid_(pid, &status, WNOHANG) == pid)
238 // child killed, check signal
239 ensure_child_signal_(status, SIGTERM);
241 ensure_equals("child process exists after SIGTERM", ::kill(pid, 0), -1);
245 // child seems to be still exiting, give it some time
248 if(waitpid_(pid, &status, WNOHANG) != pid)
250 // child is still running, kill it
251 if(::kill(pid, SIGKILL) != 0)
260 std::stringstream ss;
262 ss << "child " << pid << " could not be killed with SIGKILL, " << strerror_r(errno, e, sizeof(e)) << std::endl;
267 ensure_equals("wait after SIGKILL", waitpid_(pid, &status), pid);
268 ensure_child_signal_(status, SIGKILL);
270 ensure_equals("child process exists after SIGKILL", ::kill(pid, 0), -1);
272 std::stringstream ss;
273 ss << "child " << pid << " had to be killed with SIGKILL";
278 test_result receive_result_(std::istream &ss, pid_t pid)
284 tr.result = test_result::result_type(type);
285 ss.ignore(1024, '\n');
287 std::getline(ss, tr.group);
289 ss.ignore(1024, '\n');
290 std::getline(ss, tr.name);
291 std::getline(ss, tr.exception_typeid);
292 std::copy( std::istreambuf_iterator<char>(ss.rdbuf()),
293 std::istreambuf_iterator<char>(),
294 std::back_inserter(tr.message) );
303 fdclose(int fd): fd_(fd) { }
312 pid_t waitpid_(pid_t pid, int *status, int flags = 0)
315 ensure("trying to wait for unknown pid", pids_.count(pid) > 0);
317 pid_t p = ::waitpid(pid, status, flags);
318 if( (flags & WNOHANG) && (p != pid) )
323 // read child result from pipe
331 int pipe = pids_[pid];
334 FD_SET(pipe, &fdset);
336 int result = select(pipe+1, &fdset, NULL, NULL, &tv);
337 ensure_errno("sanity check on select() failed", result >= 0);
341 ensure("sanity check on FD_ISSET() failed", FD_ISSET(pipe, &fdset) );
343 std::stringstream ss;
345 //TODO: max failure length
347 int r = read(pipe, buffer, sizeof(buffer));
348 ensure_errno("sanity check on read() failed", r >= 0);
353 throw rethrown( receive_result_(ss, pid) );
360 void ensure_child_exit_(int status, int exit_status)
362 if(WIFSIGNALED(status))
364 std::stringstream ss;
365 ss << "child killed by signal " << WTERMSIG(status)
366 << ": expected exit with code " << exit_status;
368 throw failure(ss.str().c_str());
371 if(WIFEXITED(status))
373 if(WEXITSTATUS(status) != exit_status)
375 std::stringstream ss;
376 ss << "child exited, expected '"
379 << WEXITSTATUS(status)
382 throw failure(ss.str().c_str());
386 if(WIFSTOPPED(status))
388 std::stringstream ss;
389 ss << "child stopped by signal " << WTERMSIG(status)
390 << ": expected exit with code " << exit_status;
391 throw failure(ss.str().c_str());
395 void ensure_child_signal_(int status, int signal)
397 if(WIFSIGNALED(status))
399 if(WTERMSIG(status) != signal)
401 std::stringstream ss;
402 ss << "child killed by signal, expected '"
407 throw failure(ss.str().c_str());
411 if(WIFEXITED(status))
413 std::stringstream ss;
414 ss << "child exited with code " << WEXITSTATUS(status)
415 << ": expected signal " << signal;
417 throw failure(ss.str().c_str());
420 if(WIFSTOPPED(status))
422 std::stringstream ss;
423 ss << "child stopped by signal " << WTERMSIG(status)
424 << ": expected kill by signal " << signal;
426 throw failure(ss.str().c_str());
430 std::set<pid_t> get_pids_() const
435 for(pid_map::const_iterator i = pids_.begin(); i != pids_.end(); ++i)
437 pids.insert( i->first );
454 struct test_object_posix
458 struct test_group_posix
461 void send_result_(const T*, const test_result &)