wibble  0.1.28
test-main.h
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 #include <wibble/sys/macros.h>
00003 
00004 #ifdef POSIX
00005 
00006 #include <unistd.h>
00007 #include <sys/wait.h>
00008 #include <cstring>
00009 #include <sys/socket.h>
00010 #include <cstdio>
00011 
00012 #include <wibble/sys/pipe.h>
00013 
00014 struct Main : RunFeedback {
00015 
00016     int suite, test;
00017     wibble::sys::Pipe p_status;
00018     wibble::sys::Pipe p_confirm;
00019     int status_fds[2];
00020     int confirm_fds[2];
00021     pid_t pid;
00022     int argc;
00023     char **argv;
00024     pid_t finished;
00025     int status_code;
00026     int test_ok;
00027 
00028     int suite_ok, suite_failed;
00029     int total_ok, total_failed;
00030 
00031     int announced_suite;
00032     std::string current;
00033     bool want_fork;
00034 
00035     RunAll all;
00036 
00037     Main() : suite(0), test(0) {
00038         suite_ok = suite_failed = 0;
00039         total_ok = total_failed = 0;
00040         test_ok = 0;
00041         announced_suite = -1;
00042     }
00043 
00044     void child() {
00045         close( status_fds[0] );
00046         close( confirm_fds[1] );
00047         p_confirm = wibble::sys::Pipe( confirm_fds[0] );
00048         if ( argc > 1 ) {
00049             RunSuite *s = all.findSuite( argv[1] );
00050             if (!s) {
00051                 std::cerr << "No such suite " << argv[1] << std::endl;
00052                 // todo dump possible suites?
00053                 exit(250);
00054             }
00055             if ( argc > 2 ) {
00056                 if ( !test ) {
00057                     char *end;
00058                     int t = strtol( argv[2], &end, 0 );
00059                     if ( end == argv[2] && t == 0 ) {
00060                         t = s->findTest( argv[2] );
00061                         if ( t < 0 ) {
00062                             std::cerr << "No such test " << argv[2]
00063                                       << " in suite " << argv[1] << std::endl;
00064                             // todo dump possible suites?
00065                             exit(250);
00066                         }
00067                     }
00068                     all.runTest( *s, t );
00069                 }
00070             } else
00071                 all.runSuite( *s, test, 0, 1 );
00072         }
00073         if ( argc == 1 ) {
00074             all.runFrom( suite, test );
00075         }
00076         status( "done" );
00077         exit( 0 );
00078     }
00079 
00080     void testDied()
00081     {
00082         /* std::cerr << "test died: " << test << "/"
00083            << suites[suite].testCount << std::endl; */
00084         if ( WIFEXITED( status_code ) ) {
00085             if ( WEXITSTATUS( status_code ) == 250 )
00086                 exit( 3 );
00087             if ( WEXITSTATUS( status_code ) == 0 )
00088                 return;
00089         }
00090         std::cout << "--> FAILED: "<< current;
00091         if ( WIFEXITED( status_code ) )
00092             std::cout << " (exit status " << WEXITSTATUS( status_code ) << ")";
00093         if ( WIFSIGNALED( status_code ) )
00094             std::cout << " (caught signal " << WTERMSIG( status_code ) << ")";
00095         std::cout << std::endl;
00096         // re-announce the suite
00097         announced_suite --;
00098         ++ test; // continue with next test
00099         test_ok = 0;
00100         suite_failed ++;
00101     }
00102 
00103     void processStatus( std::string line ) {
00104         // std::cerr << line << std::endl;
00105         if ( line == "done" ) { // finished
00106             if ( want_fork ) {
00107                 finished = waitpid( pid, &status_code, 0 );
00108                 assert_eq( pid, finished );
00109                 assert( WIFEXITED( status_code ) );
00110                 assert_eq( WEXITSTATUS( status_code ), 0 );
00111             }
00112             std::cout << "overall " << total_ok << "/"
00113                       << total_ok + total_failed
00114                       << " ok" << std::endl;
00115             exit( total_failed == 0 ? 0 : 1 );
00116         }
00117 
00118         if ( test_ok ) {
00119             /* std::cerr << "test ok: " << test << "/"
00120                << suites[suite].testCount << std::endl; */
00121             std::cout << "." << std::flush;
00122             suite_ok ++;
00123             ++ test;
00124             test_ok = 0;
00125         }
00126 
00127         if ( line[0] == 's' ) {
00128             if ( line[2] == 'd' ) {
00129                 std::cout << " " << suite_ok << "/" << suite_ok + suite_failed
00130                           << " ok" << std::endl;
00131                 ++ suite; test = 0;
00132                 assert( !test_ok );
00133                 total_ok += suite_ok;
00134                 total_failed += suite_failed;
00135                 suite_ok = suite_failed = 0;
00136             }
00137             if ( line[2] == 's' ) {
00138                 if ( announced_suite < suite ) {
00139                     std::cout << std::string( line.begin() + 5, line.end() )
00140                               << ": " << std::flush;
00141                     announced_suite = suite;
00142                 }
00143             }
00144         }
00145         if ( line[0] == 't' ) {
00146             if ( line[2] == 'd' ) {
00147                 confirm();
00148                 test_ok = 1;
00149             }
00150             if ( line[2] == 's' ) {
00151                 confirm();
00152                 current = std::string( line.begin() + 5, line.end() );
00153             }
00154         }
00155     }
00156 
00157     void parent() {
00158         close( status_fds[1] );
00159         close( confirm_fds[0] );
00160         p_status = wibble::sys::Pipe( status_fds[ 0 ]);
00161         std::string line;
00162 
00163         while ( true ) {
00164             if ( p_status.eof() ) {
00165                 finished = waitpid( pid, &status_code, 0 );
00166                 if ( finished < 0 ) {
00167                     perror( "waitpid failed" );
00168                     exit( 5 );
00169                 }
00170                 assert_eq( pid, finished );
00171                 testDied();
00172                 return;
00173             }
00174 
00175             line = p_status.nextLineBlocking();
00176             processStatus( line );
00177         }
00178     }
00179 
00180     void status( std::string line ) {
00181         // std::cerr << "status: " << line << std::endl;
00182         if ( want_fork ) {
00183             line += "\n";
00184             ::write( status_fds[ 1 ], line.c_str(), line.length() );
00185         } else
00186             processStatus( line );
00187     }
00188 
00189     void confirm() {
00190         std::string line( "ack\n" );
00191         if ( want_fork )
00192             ::write( confirm_fds[ 1 ], line.c_str(), line.length() );
00193     }
00194 
00195     void waitForAck() {
00196         if ( want_fork ) {
00197             std::string line = p_confirm.nextLineBlocking();
00198             assert_eq( std::string( "ack" ), line );
00199         }
00200     }
00201 
00202     int main( int _argc, char **_argv )
00203     {
00204         argc = _argc;
00205         argv = _argv;
00206 
00207         all.suiteCount = sizeof(suites)/sizeof(RunSuite);
00208         all.suites = suites;
00209         all.feedback = this;
00210         want_fork = argc <= 2;
00211 
00212         while (true) {
00213             if ( socketpair( PF_UNIX,SOCK_STREAM, 0, status_fds ) )
00214                 return 1;
00215             if ( socketpair( PF_UNIX,SOCK_STREAM, 0, confirm_fds ) )
00216                 return 1;
00217             if ( want_fork ) {
00218                 pid = fork();
00219                 if ( pid < 0 )
00220                     return 2;
00221                 if ( pid == 0 ) { // child
00222                     child();
00223                 } else {
00224                     parent();
00225                 }
00226             } else
00227                 child();
00228         }
00229     }
00230 };
00231 
00232 int main( int argc, char **argv ) {
00233     return Main().main( argc, argv );
00234 }
00235 
00236 #else
00237 #include <iostream>
00238 
00239 int main( int argc, char **argv ) {
00240     std::cerr << "Sorry, test runner not implemented on this non-POSIX platform." << std::endl;
00241     return 0;
00242 }
00243 
00244 #endif