From d2dbc65f00abe5ccf44af3545a1ec1ddd5ebdad7 Mon Sep 17 00:00:00 2001 From: Petr Rockai Date: Thu, 26 Jun 2014 01:02:13 +0200 Subject: [PATCH] test: Implement journalling and --continue. --- test/lib/journal.h | 65 +++++++++++++++++++++++++++++++++++++++++++++ test/lib/runner.cpp | 36 ++++++++++++++++++------- 2 files changed, 92 insertions(+), 9 deletions(-) diff --git a/test/lib/journal.h b/test/lib/journal.h index ff662f3e9..6d0e2434d 100644 --- a/test/lib/journal.h +++ b/test/lib/journal.h @@ -1,7 +1,10 @@ // -*- C++ -*- #include +#include #include +#include +#include #ifndef RUNNER_JOURNAL_H #define RUNNER_JOURNAL_H @@ -9,6 +12,7 @@ struct Journal { enum R { STARTED, + RETRIED, UNKNOWN, FAILED, INTERRUPTED, @@ -32,9 +36,59 @@ struct Journal { } } + friend std::istream &operator>>( std::istream &i, R &r ) { + std::string x; + i >> x; + + r = UNKNOWN; + if ( x == "started" ) r = STARTED; + if ( x == "retried" ) r = RETRIED; + if ( x == "failed" ) r = FAILED; + if ( x == "interrupted" ) r = INTERRUPTED; + if ( x == "passed" ) r = PASSED; + if ( x == "skipped" ) r = SKIPPED; + if ( x == "timeout" ) r = TIMEOUT; + if ( x == "warnings" ) r = WARNED; + return i; + } + + template< typename S, typename T > + friend std::istream &operator>>( std::istream &i, std::pair< S, T > &r ) { + return i >> r.first >> r.second; + } + typedef std::map< std::string, R > Status; Status status; + std::string location_tmp, location; + + void sync() { + std::ofstream of( location_tmp.c_str() ); + for ( Status::iterator i = status.begin(); i != status.end(); ++i ) + of << i->first << " " << i->second << std::endl; + of.close(); + rename( location_tmp.c_str(), location.c_str() ); + } + + void started( std::string n ) { + if ( status.count( n ) && status[ n ] == STARTED ) + status[ n ] = RETRIED; + else + status[ n ] = STARTED; + sync(); + } + + void done( std::string n, R r ) { + status[ n ] = r; + sync(); + } + + bool done( std::string n ) { + if ( !status.count( n ) ) + return false; + return status[ n ] != STARTED && status[ n ] != INTERRUPTED; + } + int count( R r ) { int c = 0; for ( Status::iterator i = status.begin(); i != status.end(); ++i ) @@ -53,6 +107,17 @@ struct Journal { if ( i->second != PASSED ) std::cout << i->second << ": " << i->first << std::endl; } + + void read() { + std::ifstream ifs( location.c_str() ); + typedef std::istream_iterator< std::pair< std::string, R > > It; + std::copy( It( ifs ), It(), std::inserter( status, status.begin() ) ); + } + + Journal( std::string dir ) + : location( dir + "/journal" ), + location_tmp( dir + "/journal.tmp" ) + {} }; #endif diff --git a/test/lib/runner.cpp b/test/lib/runner.cpp index 4180de3cc..0875b02a6 100644 --- a/test/lib/runner.cpp +++ b/test/lib/runner.cpp @@ -55,11 +55,12 @@ pid_t kill_pid = 0; bool fatal_signal = false; +bool interrupt = false; struct Options { - bool verbose, quiet, interactive; + bool verbose, quiet, interactive, cont; std::string testdir, outdir; - Options() : verbose( false ), quiet( false ), interactive( false ) {} + Options() : verbose( false ), quiet( false ), interactive( false ), cont( false ) {} }; struct TestProcess @@ -115,6 +116,8 @@ struct TestCase { time_t start, end; Options options; + Journal *journal; + void pipe() { int fds[2]; @@ -175,7 +178,7 @@ struct TestCase { } std::string tag( std::string n ) { - int pad = (8 - n.length()); + int pad = (12 - n.length()); return "### " + std::string( pad, ' ' ) + n + ": "; } @@ -217,7 +220,9 @@ struct TestCase { r = Journal::SKIPPED; else r = Journal::FAILED; - } else + } else if ( interrupt && WIFSIGNALED( status ) && WTERMSIG( status ) == SIGINT ) + r = Journal::INTERRUPTED; + else r = Journal::FAILED; ::close( io.fd ); @@ -228,6 +233,7 @@ struct TestCase { close(fd_debuglog); } */ + journal->done( name, r ); progress( Last ) << tag( r ) << name << std::endl; } @@ -241,6 +247,7 @@ struct TestCase { io.close(); child.exec(); } else { + journal->started( name ); progress( First ) << tag( "running" ) << name << std::flush; if ( options.verbose || options.interactive ) progress() << std::endl; @@ -249,8 +256,8 @@ struct TestCase { } } - TestCase( Options opt, std::string path, std::string name ) - : timeout( false ), silent_ctr( 0 ), child( path ), name( name ), options( opt ) + TestCase( Journal &j, Options opt, std::string path, std::string name ) + : timeout( false ), silent_ctr( 0 ), child( path ), name( name ), options( opt ), journal( &j ) { if ( opt.verbose ) io.sinks.push_back( new FdSink( 1 ) ); @@ -276,10 +283,12 @@ struct Main { continue; if ( i->substr( 0, 4 ) == "lib/" ) continue; - cases.push_back( TestCase( options, options.testdir + *i, *i ) ); + cases.push_back( TestCase( journal, options, options.testdir + *i, *i ) ); cases.back().options = options; } + if ( options.cont ) + journal.read(); } void run() { @@ -288,6 +297,10 @@ struct Main { std::cerr << "running " << cases.size() << " tests" << std::endl; for ( Cases::iterator i = cases.begin(); i != cases.end(); ++i ) { + + if ( options.cont && journal.done( i->name ) ) + continue; + i->run(); if ( time(0) - start > 3 * 3600 ) { @@ -304,14 +317,16 @@ struct Main { exit( 1 ); } - Main( Options o ) : die( false ), options( o ) {} + Main( Options o ) : die( false ), options( o ), journal( o.outdir ) {} }; static void handler( int sig ) { - signal( sig, SIG_DFL ); + signal( sig, SIG_DFL ); /* die right away next time */ if ( kill_pid > 0 ) kill( -kill_pid, sig ); fatal_signal = true; + if ( sig == SIGINT ) + interrupt = true; } void setup_handlers() { @@ -378,6 +393,9 @@ int main(int argc, char **argv) Args args( argc, argv ); Options opt; + if ( args.has( "--continue" ) ) + opt.cont = true; + if ( args.has( "--quiet" ) || getenv( "QUIET" ) ) { opt.verbose = false; opt.quiet = true;