wibble  0.1.28
test-main.h
Go to the documentation of this file.
1 // -*- C++ -*-
2 #include <wibble/sys/macros.h>
3 
4 #ifdef POSIX
5 
6 #include <unistd.h>
7 #include <sys/wait.h>
8 #include <cstring>
9 #include <sys/socket.h>
10 #include <cstdio>
11 
12 #include <wibble/sys/pipe.h>
13 
14 struct Main : RunFeedback {
15 
16  int suite, test;
17  wibble::sys::Pipe p_status;
18  wibble::sys::Pipe p_confirm;
19  int status_fds[2];
20  int confirm_fds[2];
21  pid_t pid;
22  int argc;
23  char **argv;
24  pid_t finished;
25  int status_code;
26  int test_ok;
27 
28  int suite_ok, suite_failed;
29  int total_ok, total_failed;
30 
31  int announced_suite;
32  std::string current;
33  bool want_fork;
34 
35  RunAll all;
36 
37  Main() : suite(0), test(0) {
38  suite_ok = suite_failed = 0;
39  total_ok = total_failed = 0;
40  test_ok = 0;
41  announced_suite = -1;
42  }
43 
44  void child() {
45  close( status_fds[0] );
46  close( confirm_fds[1] );
47  p_confirm = wibble::sys::Pipe( confirm_fds[0] );
48  if ( argc > 1 ) {
49  RunSuite *s = all.findSuite( argv[1] );
50  if (!s) {
51  std::cerr << "No such suite " << argv[1] << std::endl;
52  // todo dump possible suites?
53  exit(250);
54  }
55  if ( argc > 2 ) {
56  if ( !test ) {
57  char *end;
58  int t = strtol( argv[2], &end, 0 );
59  if ( end == argv[2] && t == 0 ) {
60  t = s->findTest( argv[2] );
61  if ( t < 0 ) {
62  std::cerr << "No such test " << argv[2]
63  << " in suite " << argv[1] << std::endl;
64  // todo dump possible suites?
65  exit(250);
66  }
67  }
68  all.runTest( *s, t );
69  }
70  } else
71  all.runSuite( *s, test, 0, 1 );
72  }
73  if ( argc == 1 ) {
74  all.runFrom( suite, test );
75  }
76  status( "done" );
77  exit( 0 );
78  }
79 
80  void testDied()
81  {
82  /* std::cerr << "test died: " << test << "/"
83  << suites[suite].testCount << std::endl; */
84  if ( WIFEXITED( status_code ) ) {
85  if ( WEXITSTATUS( status_code ) == 250 )
86  exit( 3 );
87  if ( WEXITSTATUS( status_code ) == 0 )
88  return;
89  }
90  std::cout << "--> FAILED: "<< current;
91  if ( WIFEXITED( status_code ) )
92  std::cout << " (exit status " << WEXITSTATUS( status_code ) << ")";
93  if ( WIFSIGNALED( status_code ) )
94  std::cout << " (caught signal " << WTERMSIG( status_code ) << ")";
95  std::cout << std::endl;
96  // re-announce the suite
97  announced_suite --;
98  ++ test; // continue with next test
99  test_ok = 0;
100  suite_failed ++;
101  }
102 
103  void processStatus( std::string line ) {
104  // std::cerr << line << std::endl;
105  if ( line == "done" ) { // finished
106  if ( want_fork ) {
107  finished = waitpid( pid, &status_code, 0 );
108  assert_eq( pid, finished );
109  assert( WIFEXITED( status_code ) );
110  assert_eq( WEXITSTATUS( status_code ), 0 );
111  }
112  std::cout << "overall " << total_ok << "/"
113  << total_ok + total_failed
114  << " ok" << std::endl;
115  exit( total_failed == 0 ? 0 : 1 );
116  }
117 
118  if ( test_ok ) {
119  /* std::cerr << "test ok: " << test << "/"
120  << suites[suite].testCount << std::endl; */
121  std::cout << "." << std::flush;
122  suite_ok ++;
123  ++ test;
124  test_ok = 0;
125  }
126 
127  if ( line[0] == 's' ) {
128  if ( line[2] == 'd' ) {
129  std::cout << " " << suite_ok << "/" << suite_ok + suite_failed
130  << " ok" << std::endl;
131  ++ suite; test = 0;
132  assert( !test_ok );
133  total_ok += suite_ok;
134  total_failed += suite_failed;
135  suite_ok = suite_failed = 0;
136  }
137  if ( line[2] == 's' ) {
138  if ( announced_suite < suite ) {
139  std::cout << std::string( line.begin() + 5, line.end() )
140  << ": " << std::flush;
141  announced_suite = suite;
142  }
143  }
144  }
145  if ( line[0] == 't' ) {
146  if ( line[2] == 'd' ) {
147  confirm();
148  test_ok = 1;
149  }
150  if ( line[2] == 's' ) {
151  confirm();
152  current = std::string( line.begin() + 5, line.end() );
153  }
154  }
155  }
156 
157  void parent() {
158  close( status_fds[1] );
159  close( confirm_fds[0] );
160  p_status = wibble::sys::Pipe( status_fds[ 0 ]);
161  std::string line;
162 
163  while ( true ) {
164  if ( p_status.eof() ) {
165  finished = waitpid( pid, &status_code, 0 );
166  if ( finished < 0 ) {
167  perror( "waitpid failed" );
168  exit( 5 );
169  }
170  assert_eq( pid, finished );
171  testDied();
172  return;
173  }
174 
175  line = p_status.nextLineBlocking();
176  processStatus( line );
177  }
178  }
179 
180  void status( std::string line ) {
181  // std::cerr << "status: " << line << std::endl;
182  if ( want_fork ) {
183  line += "\n";
184  ::write( status_fds[ 1 ], line.c_str(), line.length() );
185  } else
186  processStatus( line );
187  }
188 
189  void confirm() {
190  std::string line( "ack\n" );
191  if ( want_fork )
192  ::write( confirm_fds[ 1 ], line.c_str(), line.length() );
193  }
194 
195  void waitForAck() {
196  if ( want_fork ) {
197  std::string line = p_confirm.nextLineBlocking();
198  assert_eq( std::string( "ack" ), line );
199  }
200  }
201 
202  int main( int _argc, char **_argv )
203  {
204  argc = _argc;
205  argv = _argv;
206 
207  all.suiteCount = sizeof(suites)/sizeof(RunSuite);
208  all.suites = suites;
209  all.feedback = this;
210  want_fork = argc <= 2;
211 
212  while (true) {
213  if ( socketpair( PF_UNIX,SOCK_STREAM, 0, status_fds ) )
214  return 1;
215  if ( socketpair( PF_UNIX,SOCK_STREAM, 0, confirm_fds ) )
216  return 1;
217  if ( want_fork ) {
218  pid = fork();
219  if ( pid < 0 )
220  return 2;
221  if ( pid == 0 ) { // child
222  child();
223  } else {
224  parent();
225  }
226  } else
227  child();
228  }
229  }
230 };
231 
232 int main( int argc, char **argv ) {
233  return Main().main( argc, argv );
234 }
235 
236 #else
237 #include <iostream>
238 
239 int main( int argc, char **argv ) {
240  std::cerr << "Sorry, test runner not implemented on this non-POSIX platform." << std::endl;
241  return 0;
242 }
243 
244 #endif