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