test-main.h
Go to the documentation of this file.
// -*- C++ -*-
#include <wibble/sys/macros.h>
#include <unistd.h>
#ifdef POSIX
#include <sys/wait.h>
#include <sys/socket.h>
#endif
#include <cstring>
#include <cstdio>
#include <wibble/sys/pipe.h>
struct Main : RunFeedback {
int suite, test;
wibble::sys::Pipe p_status;
wibble::sys::Pipe p_confirm;
int status_fds[2];
int confirm_fds[2];
pid_t pid;
int argc;
char **argv;
pid_t finished;
int status_code;
int test_ok;
int suite_ok, suite_failed;
int total_ok, total_failed;
int announced_suite;
std::string current;
bool want_fork;
RunAll all;
Main() : suite(0), test(0) {
suite_ok = suite_failed = 0;
total_ok = total_failed = 0;
test_ok = 0;
announced_suite = -1;
}
void child() {
#ifdef POSIX
close( status_fds[0] );
close( confirm_fds[1] );
#endif
p_confirm = wibble::sys::Pipe( confirm_fds[0] );
if ( argc > 1 ) {
RunSuite *s = all.findSuite( argv[1] );
if (!s) {
std::cerr << "No such suite " << argv[1] << std::endl;
// todo dump possible suites?
exit(250);
}
if ( argc > 2 ) {
if ( !test ) {
char *end;
int t = strtol( argv[2], &end, 0 );
if ( end == argv[2] && t == 0 ) {
t = s->findTest( argv[2] );
if ( t < 0 ) {
std::cerr << "No such test " << argv[2]
<< " in suite " << argv[1] << std::endl;
// todo dump possible suites?
exit(250);
}
}
all.runTest( *s, t );
}
} else
all.runSuite( *s, test, 0, 1 );
}
if ( argc == 1 ) {
all.runFrom( suite, test );
}
status( "done" );
exit( 0 );
}
#ifdef POSIX
void testDied()
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
/* std::cerr << "test died: " << test << "/"
<< suites[suite].testCount << std::endl; */
if ( WIFEXITED( status_code ) ) {
if ( WEXITSTATUS( status_code ) == 250 )
exit( 3 );
if ( WEXITSTATUS( status_code ) == 0 )
return;
}
std::cout << "--> FAILED: "<< current;
if ( WIFEXITED( status_code ) )
std::cout << " (exit status " << WEXITSTATUS( status_code ) << ")";
if ( WIFSIGNALED( status_code ) )
std::cout << " (caught signal " << WTERMSIG( status_code ) << ")";
std::cout << std::endl;
// re-announce the suite
announced_suite --;
++ test; // continue with next test
test_ok = 0;
suite_failed ++;
#pragma GCC diagnostic pop
}
#endif
void processStatus( std::string line ) {
// std::cerr << line << std::endl;
if ( line == "done" ) { // finished
#ifdef POSIX
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
if ( want_fork ) {
finished = waitpid( pid, &status_code, 0 );
assert_eq( pid, finished );
assert( WIFEXITED( status_code ) );
assert_eq( WEXITSTATUS( status_code ), 0 );
}
#pragma GCC diagnostic pop
#endif
std::cout << "overall " << total_ok << "/"
<< total_ok + total_failed
<< " ok" << std::endl;
exit( total_failed == 0 ? 0 : 1 );
}
if ( test_ok ) {
/* std::cerr << "test ok: " << test << "/"
<< suites[suite].testCount << std::endl; */
std::cout << "." << std::flush;
suite_ok ++;
++ test;
test_ok = 0;
}
if ( line[0] == 's' ) {
if ( line[2] == 'd' ) {
std::cout << " " << suite_ok << "/" << suite_ok + suite_failed
<< " ok" << std::endl;
++ suite; test = 0;
assert( !test_ok );
total_ok += suite_ok;
total_failed += suite_failed;
suite_ok = suite_failed = 0;
}
if ( line[2] == 's' ) {
if ( announced_suite < suite ) {
std::cout << std::string( line.begin() + 5, line.end() )
<< ": " << std::flush;
announced_suite = suite;
}
}
}
if ( line[0] == 't' ) {
if ( line[2] == 'd' ) {
confirm();
test_ok = 1;
}
if ( line[2] == 's' ) {
confirm();
current = std::string( line.begin() + 5, line.end() );
}
}
}
#ifdef POSIX
void parent() {
close( status_fds[1] );
close( confirm_fds[0] );
p_status = wibble::sys::Pipe( status_fds[ 0 ]);
std::string line;
while ( true ) {
if ( p_status.eof() ) {
finished = waitpid( pid, &status_code, 0 );
if ( finished < 0 ) {
perror( "waitpid failed" );
exit( 5 );
}
assert_eq( pid, finished );
testDied();
return;
}
line = p_status.nextLineBlocking();
processStatus( line );
}
}
#endif
void status( std::string line ) {
// std::cerr << "status: " << line << std::endl;
#ifdef POSIX
if ( want_fork ) {
line += "\n";
::write( status_fds[ 1 ], line.c_str(), line.length() );
} else
#endif
processStatus( line );
}
void confirm() {
std::string line( "ack\n" );
if ( want_fork )
::write( confirm_fds[ 1 ], line.c_str(), line.length() );
}
void waitForAck() {
if ( want_fork ) {
std::string line = p_confirm.nextLineBlocking();
assert_eq( std::string( "ack" ), line );
}
}
int main( int _argc, char **_argv )
{
argc = _argc;
argv = _argv;
all.suiteCount = sizeof(suites)/sizeof(RunSuite);
all.suites = suites;
all.feedback = this;
#ifdef POSIX
want_fork = argc <= 2;
#else
want_fork = false;
#endif
while (true) {
#ifdef POSIX
if ( socketpair( PF_UNIX,SOCK_STREAM, 0, status_fds ) )
return 1;
if ( socketpair( PF_UNIX,SOCK_STREAM, 0, confirm_fds ) )
return 1;
if ( want_fork ) {
pid = fork();
if ( pid < 0 )
return 2;
if ( pid == 0 ) { // child
child();
} else {
parent();
}
} else
#endif
child();
}
return 0;
}
};
int main( int argc, char **argv ) {
return Main().main( argc, argv );
}
Main::confirm_fds
int confirm_fds[2]
Definition: test-main.h:22
pipe.h
RunAll::suiteCount
int suiteCount
Definition: test-runner.h:38
Main::status
void status(std::string line)
Definition: test-main.h:196
RunAll::findSuite
RunSuite * findSuite(std::string name)
Definition: test-runner.h:41
Main::processStatus
void processStatus(std::string line)
Definition: test-main.h:112
Main::waitForAck
void waitForAck()
Definition: test-main.h:213
Main::p_status
wibble::sys::Pipe p_status
Definition: test-main.h:19
Main::child
void child()
Definition: test-main.h:46
Main::p_confirm
wibble::sys::Pipe p_confirm
Definition: test-main.h:20
Main::suite_ok
int suite_ok
Definition: test-main.h:30
Main::all
RunAll all
Definition: test-main.h:37
RunAll::runTest
void runTest(RunSuite &s, int test)
Definition: test-runner.h:65
Main::want_fork
bool want_fork
Definition: test-main.h:35
Main::current
std::string current
Definition: test-main.h:34
macros.h
RunFeedback
Definition: test-runner.h:31
Main::suite
int suite
Definition: test-main.h:18
assert_eq
#define assert_eq(x, y)
Definition: test.h:33
assert
#define assert(x)
Definition: test.h:30
Main::Main
Main()
Definition: test-main.h:39
wibble::sys::Pipe
Definition: pipe.h:27
Main::argv
char ** argv
Definition: test-main.h:25
Main::main
int main(int _argc, char **_argv)
Definition: test-main.h:220
RunSuite::findTest
int findTest(std::string name)
Definition: test-runner.h:22
Main::status_code
int status_code
Definition: test-main.h:27
RunSuite
Definition: test-runner.h:17
Main::test_ok
int test_ok
Definition: test-main.h:28
wibble::sys::Pipe::nextLineBlocking
std::string nextLineBlocking()
Definition: pipe.h:204
RunAll
Definition: test-runner.h:36
Main::confirm
void confirm()
Definition: test-main.h:207
Main::total_ok
int total_ok
Definition: test-main.h:31
Main::total_failed
int total_failed
Definition: test-main.h:31
Main::test
int test
Definition: test-main.h:18
Main::suite_failed
int suite_failed
Definition: test-main.h:30
Main::argc
int argc
Definition: test-main.h:24
Main::pid
pid_t pid
Definition: test-main.h:23
Main::finished
pid_t finished
Definition: test-main.h:26
Main::announced_suite
int announced_suite
Definition: test-main.h:33
RunAll::runSuite
void runSuite(RunSuite &s, int fromTest, int suite, int suiteCount)
Definition: test-runner.h:48
Main
Definition: test-main.h:16
main
int main(int argc, char **argv)
Definition: test-main.h:257
RunAll::suites
RunSuite * suites
Definition: test-runner.h:37
Main::status_fds
int status_fds[2]
Definition: test-main.h:21
wibble::sys::Pipe::eof
bool eof()
Definition: pipe.h:130
wibble::list::end
ListIterator< List > end(List)
Definition: list.h:425
RunAll::runFrom
void runFrom(int suite, int test)
Definition: test-runner.h:76
RunAll::feedback
RunFeedback * feedback
Definition: test-runner.h:39