tut logo

tut_posix.hpp Source File


tut_posix.hpp
1 #ifndef TUT_FORK_H_GUARD
2 #define TUT_FORK_H_GUARD
3 #include <tut/tut_config.hpp>
4 
5 #if defined(TUT_USE_POSIX)
6 #include <errno.h>
7 #include <unistd.h>
8 #include <signal.h>
9 #include <sys/wait.h>
10 #include <sys/time.h>
11 #include <sys/types.h>
12 
13 #include <cstring>
14 #include <cstdlib>
15 #include <map>
16 #include <iterator>
17 #include <functional>
18 
19 #include "tut_result.hpp"
20 #include "tut_assert.hpp"
21 #include "tut_runner.hpp"
22 
23 namespace tut
24 {
25 
26 template<typename, int>
27 class test_group;
28 
29 template<typename T>
30 class test_object;
31 
32 class test_group_posix
33 {
34 private:
35  template<typename, int>
36  friend class test_group;
37 
38  template<typename T>
39  void send_result_(const T *obj, const test_result &tr)
40  {
41  if(obj->get_pipe_() == -1)
42  {
43  return;
44  }
45 
46  if(tr.result != test_result::ok)
47  {
48  std::ostringstream ss;
49  ss << int(tr.result) << "\n"
50  << tr.group << "\n"
51  << tr.test << "\n"
52  << tr.name << "\n"
53  << tr.exception_typeid << "\n";
54  std::copy( tr.message.begin(), tr.message.end(), std::ostreambuf_iterator<char>(ss.rdbuf()) );
55 
56  int size = ss.str().length();
57  int w = write(obj->get_pipe_(), ss.str().c_str(), size);
58  ensure_errno("write() failed", w == size);
59  }
60  }
61 
62  virtual ~test_group_posix()
63  {
64  }
65 };
66 
67 template<typename T>
68 struct tut_posix
69 {
70  pid_t fork()
71  {
72  test_object<T> *self = dynamic_cast< tut::test_object<T>* >(this);
73  ensure("trying to call 'tut_fork' in ctor of test object", self != NULL);
74 
75  return self->fork_();
76  }
77 
78  pid_t waitpid(pid_t pid, int *status, int flags = 0)
79  {
80  test_object<T> *self = dynamic_cast< tut::test_object<T>* >(this);
81  ensure("trying to call 'tut_waitpid' in ctor of test object", self != NULL);
82 
83  return self->waitpid_(pid, status, flags);
84  }
85 
86  void ensure_child_exit(pid_t pid, int exit_status = 0)
87  {
88  test_object<T> *self = dynamic_cast< tut::test_object<T>* >(this);
89  ensure("trying to call 'ensure_child_exit' in ctor of test object", self != NULL);
90 
91  int status;
92  self->waitpid_(pid, &status);
93 
94  self->ensure_child_exit_(status, exit_status);
95  }
96 
97 
98  void ensure_child_signal(pid_t pid, int signal = SIGTERM)
99  {
100  test_object<T> *self = dynamic_cast< tut::test_object<T>* >(this);
101  ensure("trying to call 'ensure_child_signal' in ctor of test object", self != NULL);
102 
103  int status;
104  self->waitpid_(pid, &status);
105 
106  self->ensure_child_signal_(status, signal);
107  }
108 
109  std::set<pid_t> get_pids() const
110  {
111  using namespace std;
112 
113  const test_object<T> *self = dynamic_cast< const tut::test_object<T>* >(this);
114  ensure("trying to call 'get_pids' in ctor of test object", self != NULL);
115 
116  return self->get_pids_();
117  }
118 
119  virtual ~tut_posix()
120  {
121  }
122 
123 };
124 
125 class test_object_posix
126 {
127 public:
128  typedef std::map<pid_t, int> pid_map;
129 
133  test_object_posix()
134  : pids_(),
135  pipe_(-1)
136  {
137  }
138 
139 
140  virtual ~test_object_posix()
141  {
142  // we have forked
143  if(pipe_ != -1)
144  {
145  // in child, force exit
146  std::exit(0);
147  }
148 
149  if(!pids_.empty())
150  {
151  std::ostringstream ss;
152 
153  // in parent, reap children
154  for(std::map<pid_t, int>::iterator i = pids_.begin(); i != pids_.end(); ++i)
155  {
156  try {
157  kill_child_(i->first);
158  } catch(const rethrown &ex) {
159  ss << std::endl << "child " << ex.tr.pid << " has thrown an exception: " << ex.what();
160  } catch(const failure &ex) {
161  ss << std::endl << ex.what();
162  }
163  }
164 
165  if(!ss.str().empty())
166  {
167  fail(ss.str().c_str());
168  }
169  }
170  }
171 
172 private:
173  template<typename T>
174  friend class tut_posix;
175 
176  friend class test_group_posix;
177 
178  int get_pipe_() const
179  {
180  return pipe_;
181  }
182 
183 
184  pid_t fork_()
185  {
186  // create pipe
187  int fds[2];
188  ensure_errno("pipe() failed", ::pipe(fds) == 0);
189 
190  pid_t pid = ::fork();
191 
192  ensure_errno("fork() failed", pid >= 0);
193 
194  if(pid != 0)
195  {
196  // in parent, register pid
197  ensure("duplicated child", pids_.insert( std::make_pair(pid, fds[0]) ).second);
198 
199  // close writing side
200  close(fds[1]);
201  }
202  else
203  {
204  // in child, shutdown reporter
205  tut::runner.get().clear_callbacks();
206 
207  // close reading side
208  close(fds[0]);
209  pipe_ = fds[1];
210  }
211 
212  return pid;
213  }
214 
215  void kill_child_(pid_t pid)
216  {
217  int status;
218 
219  if(waitpid_(pid, &status, WNOHANG) == pid)
220  {
221  ensure_child_exit_(status, 0);
222  return;
223  }
224 
225  if(::kill(pid, SIGTERM) != 0)
226  {
227  if(errno == ESRCH)
228  {
229  // no such process
230  return;
231  }
232  else
233  {
234  // cannot kill, we are in trouble
235  std::ostringstream ss;
236  char e[1024];
237  ss << "child " << pid << " could not be killed with SIGTERM, " << strerror_r(errno, e, sizeof(e)) << std::endl;
238  fail(ss.str());
239  }
240  }
241 
242  if(waitpid_(pid, &status, WNOHANG) == pid)
243  {
244  // child killed, check signal
245  ensure_child_signal_(status, SIGTERM);
246 
247  ensure_equals("child process exists after SIGTERM", ::kill(pid, 0), -1);
248  return;
249  }
250 
251  // child seems to be still exiting, give it some time
252  sleep(2);
253 
254  if(waitpid_(pid, &status, WNOHANG) != pid)
255  {
256  // child is still running, kill it
257  if(::kill(pid, SIGKILL) != 0)
258  {
259  if(errno == ESRCH)
260  {
261  // no such process
262  return;
263  }
264  else
265  {
266  std::ostringstream ss;
267  char e[1024];
268  ss << "child " << pid << " could not be killed with SIGKILL, " << strerror_r(errno, e, sizeof(e)) << std::endl;
269  fail(ss.str());
270  }
271  }
272 
273  ensure_equals("wait after SIGKILL", waitpid_(pid, &status), pid);
274  ensure_child_signal_(status, SIGKILL);
275 
276  ensure_equals("child process exists after SIGKILL", ::kill(pid, 0), -1);
277 
278  std::ostringstream ss;
279  ss << "child " << pid << " had to be killed with SIGKILL";
280  fail(ss.str());
281  }
282  }
283 
284  test_result receive_result_(std::istream &ss, pid_t pid)
285  {
286  test_result tr;
287 
288  int type;
289  ss >> type;
290  tr.result = test_result::result_type(type);
291  ss.ignore(1024, '\n');
292 
293  std::getline(ss, tr.group);
294  ss >> tr.test;
295  ss.ignore(1024, '\n');
296  std::getline(ss, tr.name);
297  std::getline(ss, tr.exception_typeid);
298  std::copy( std::istreambuf_iterator<char>(ss.rdbuf()),
299  std::istreambuf_iterator<char>(),
300  std::back_inserter(tr.message) );
301 
302  tr.pid = pid;
303 
304  return tr;
305  }
306 
307  struct fdclose
308  {
309  fdclose(int fd): fd_(fd) { }
310  ~fdclose()
311  {
312  close(fd_);
313  }
314  private:
315  int fd_;
316  };
317 
318  pid_t waitpid_(pid_t pid, int *status, int flags = 0)
319  {
320 
321  ensure("trying to wait for unknown pid", pids_.count(pid) > 0);
322 
323  pid_t p = ::waitpid(pid, status, flags);
324  if( (flags & WNOHANG) && (p != pid) )
325  {
326  return p;
327  }
328 
329  // read child result from pipe
330  fd_set fdset;
331  timeval tv;
332  tv.tv_sec = 0;
333  tv.tv_usec = 0;
334 
335  FD_ZERO(&fdset);
336 
337  int pipe = pids_[pid];
338  fdclose guard(pipe);
339 
340  FD_SET(pipe, &fdset);
341 
342  int result = select(pipe+1, &fdset, NULL, NULL, &tv);
343  ensure_errno("sanity check on select() failed", result >= 0);
344 
345  if(result > 0)
346  {
347  ensure("sanity check on FD_ISSET() failed", FD_ISSET(pipe, &fdset) );
348 
349  std::stringstream ss;
350 
351  //TODO: max failure length
352  char buffer[1024];
353  int r = read(pipe, buffer, sizeof(buffer));
354  ensure_errno("sanity check on read() failed", r >= 0);
355 
356  if(r > 0)
357  {
358  ss.write(buffer, r);
359  throw rethrown( receive_result_(ss, pid) );
360  }
361  }
362 
363  return pid;
364  }
365 
366  void ensure_child_exit_(int status, int exit_status)
367  {
368  if(WIFSIGNALED(status))
369  {
370  std::ostringstream ss;
371  ss << "child killed by signal " << WTERMSIG(status)
372  << ": expected exit with code " << exit_status;
373 
374  throw failure(ss.str().c_str());
375  }
376 
377  if(WIFEXITED(status))
378  {
379  if(WEXITSTATUS(status) != exit_status)
380  {
381  std::ostringstream ss;
382  ss << "child exited, expected '"
383  << exit_status
384  << "' actual '"
385  << WEXITSTATUS(status)
386  << '\'';
387 
388  throw failure(ss.str().c_str());
389  }
390  }
391 
392  if(WIFSTOPPED(status))
393  {
394  std::ostringstream ss;
395  ss << "child stopped by signal " << WTERMSIG(status)
396  << ": expected exit with code " << exit_status;
397  throw failure(ss.str().c_str());
398  }
399  }
400 
401  void ensure_child_signal_(int status, int signal)
402  {
403  if(WIFSIGNALED(status))
404  {
405  if(WTERMSIG(status) != signal)
406  {
407  std::ostringstream ss;
408  ss << "child killed by signal, expected '"
409  << signal
410  << "' actual '"
411  << WTERMSIG(status)
412  << '\'';
413  throw failure(ss.str().c_str());
414  }
415  }
416 
417  if(WIFEXITED(status))
418  {
419  std::ostringstream ss;
420  ss << "child exited with code " << WEXITSTATUS(status)
421  << ": expected signal " << signal;
422 
423  throw failure(ss.str().c_str());
424  }
425 
426  if(WIFSTOPPED(status))
427  {
428  std::ostringstream ss;
429  ss << "child stopped by signal " << WTERMSIG(status)
430  << ": expected kill by signal " << signal;
431 
432  throw failure(ss.str().c_str());
433  }
434  }
435 
436  std::set<pid_t> get_pids_() const
437  {
438  using namespace std;
439 
440  set<pid_t> pids;
441  for(pid_map::const_iterator i = pids_.begin(); i != pids_.end(); ++i)
442  {
443  pids.insert( i->first );
444  }
445 
446  return pids;
447  }
448 
449  pid_map pids_;
450  int pipe_;
451 };
452 
453 } // namespace tut
454 
455 #else
456 
457 namespace tut
458 {
459 
461 {
462  virtual ~test_object_posix()
463  {
464  }
465 };
466 
468 {
469  template<typename T>
470  void send_result_(const T*, const test_result &)
471  {
472  }
473 
474  virtual ~test_group_posix()
475  {
476  }
477 };
478 
479 } // namespace tut
480 
481 #endif
482 
483 
484 #endif
485 
Definition: tut_posix.hpp:460
Definition: tut_posix.hpp:467
Definition: tut.hpp:43
void clear_callbacks()
Definition: tut_runner.hpp:182
test finished successfully
Definition: tut_result.hpp:91
result_type
Definition: tut_result.hpp:89
Definition: tut_result.hpp:69

All Rights Reserved. Generated on Wed Dec 18 2013 11:19:52 for TUT by doxygen 1.8.5