Skip to content
Snippets Groups Projects
Unverified Commit 890dbeca authored by Tomáš Pecka's avatar Tomáš Pecka
Browse files

integrationtest: Report stdout and stderr output from child

parent 55805ac4
No related branches found
No related tags found
Loading
......@@ -19,6 +19,9 @@
#define PIPE_RD 0
#define PIPE_WR 1
 
#define FD_STDOUT 1
#define FD_STDERR 2
/* Communication between signal handler and the rest of the program */
int g_Wakeup [ 2 ]; // pipe for wakeup
int g_RecvSignal; // signalled flag
......@@ -43,16 +46,35 @@ int waitSignalTimeout ( int timeout ) {
return g_RecvSignal;
}
 
std::string readFromFD ( int fd ) {
static const size_t BUFSIZE = 64;
std::string res;
int rd;
char buf [ BUFSIZE ];
while ( ( rd = read ( fd, &buf, BUFSIZE - 1 ) ) > 0 ) {
res.append ( buf, rd );
}
return res;
}
void writeToFD ( int fd, const std::string & message, const std::string & errorDesc ) {
if ( write ( fd, message.c_str ( ), message.length ( ) ) != ( ssize_t ) message.length ( ) )
throw std::runtime_error ( "TimeoutAqlTest: write() to pipe failed (" + errorDesc + ")" );
}
void newSigChild ( int ) {
char dummy = 0;
g_RecvSignal = 1;
 
// write into the pipe so select can read something, this effectively means that SIGCHILD was raised
if ( write ( g_Wakeup [ PIPE_WR ], &dummy, 1 ) != 1 )
throw std::runtime_error ( "TimeoutAqlTest: write() failure (wakeup signalling)" );
writeToFD ( g_Wakeup [ PIPE_WR ], " ", "wakeup signalling" );
}
 
int aqlTest ( int fd, const ext::vector < std::string > & queries, unsigned seed ) {
int aqlTest ( int fd_aql, const ext::vector < std::string > & queries, unsigned seed ) {
try {
cli::Environment environment;
 
......@@ -60,41 +82,27 @@ int aqlTest ( int fd, const ext::vector < std::string > & queries, unsigned seed
common::Streams::out = ss;
common::Streams::err = ss;
 
// seed cli
cli::CharSequence sequence { cli::StringLineInterface ( "set seed " + ext::to_string ( seed ) ) };
cli::Parser ( cli::Lexer ( std::move ( sequence ) ) ).parse ( ) -> run ( environment );
 
// run queries
for ( const std::string & q : queries ) {
cli::CharSequence querySequence { cli::StringLineInterface ( q ) };
cli::Parser ( cli::Lexer ( std::move ( querySequence ) ) ).parse ( ) -> run ( environment );
 
if ( write ( fd, ss.str ( ).c_str ( ), ss.str ( ).length ( ) ) != ( ssize_t ) ss.str ( ).length ( ) )
throw std::runtime_error ( "TimeoutAqlTest: child output write() failure (child to parent communication)" );
writeToFD ( fd_aql, ss.str ( ), "writing aql output" );
}
 
return cli::ResultInterpret::cli ( environment.getResult ( ) ); /* 0 = OK */
} catch ( const std::exception & ) {
std::ostringstream oss;
alib::ExceptionHandler::handle ( oss );
if ( write ( fd, oss.str ( ).c_str ( ), oss.str ( ).length ( ) ) != ( ssize_t ) oss.str ( ).length ( ) )
throw std::runtime_error ( "TimeoutAqlTest: write() failure (child to parent communication)" );
writeToFD ( fd_aql, oss.str ( ), "writing aql output" );
return -1;
}
}
 
std::string readChildOutput ( int fd ) {
static const size_t BUFSIZE = 64;
std::string res;
int rd;
char buf [ BUFSIZE ];
while ( ( rd = read ( fd, &buf, BUFSIZE - 1 ) ) > 0 ) {
res.append ( buf, rd );
}
return res;
}
void _TimeoutAqlTest ( const std::chrono::microseconds & timeout, const ext::vector < std::string > & queries ) {
/* Register SIGCHLD handler */
struct sigaction act;
......@@ -102,75 +110,104 @@ void _TimeoutAqlTest ( const std::chrono::microseconds & timeout, const ext::vec
act . sa_handler = newSigChild;
sigaction ( SIGCHLD, &act, nullptr );
 
/* parent-child communication ( for exceptions ) */
int pipefd [ 2 ];
if ( pipe ( pipefd ) != 0 )
throw std::runtime_error ( "TimeoutAqlTest: Failed to initialize pipe (child to parent communication)" );
int pipeAqlOutput [ 2 ]; /* parent-child communication ( aql output ) */
int pipeStdout [ 2 ]; /* parent-child communication ( child stdout ) */
int pipeStderr [ 2 ]; /* parent-child communication ( child stderr ) */
if ( pipe ( pipeAqlOutput ) != 0 )
throw std::runtime_error ( "TimeoutAqlTest: Failed to initialize pipe (aql output)" );
if ( pipe ( pipeStdout ) != 0 )
throw std::runtime_error ( "TimeoutAqlTest: Failed to initialize pipe (child stdout)" );
if ( pipe ( pipeStderr ) != 0 )
throw std::runtime_error ( "TimeoutAqlTest: Failed to initialize pipe (child stderr)" );
 
/* SIGCHLD was not yet raised, initialize communication pipe */
g_RecvSignal = 0;
if ( pipe ( g_Wakeup ) )
throw std::runtime_error ( "TimeoutAqlTest: Failed to initialize pipe (wakeup signalling)" );
 
/* random seed for aql */
unsigned seed = ext::random_devices::random ( );
 
/* do the forking */
pid_t x = fork ();
if ( x < 0 ) {
FAIL ( "Fork error" );
} else if ( x == 0 ) {
/* child, run the test here */
pid_t pid = fork ();
REQUIRE ( pid >= 0 );
 
if ( pid == 0 ) { /* child, run the test here */
act . sa_handler = SIG_DFL;
sigaction ( SIGCHLD, &act, nullptr );
 
/* close unused ends of pipes in child */
close ( g_Wakeup [ PIPE_RD ] );
close ( g_Wakeup [ PIPE_WR ] );
close ( pipefd [ PIPE_RD ] );
close ( pipeAqlOutput [ PIPE_RD ] );
close ( pipeStdout [ PIPE_RD ] );
close ( pipeStderr [ PIPE_RD ] );
 
/* just in case ... */
close ( 1 );
close ( 2 );
/* redirect stderr and stdout to pipe */
dup2 ( pipeStdout [ PIPE_WR ], FD_STDOUT );
dup2 ( pipeStderr [ PIPE_WR ], FD_STDERR );
 
exit ( aqlTest ( pipefd [ PIPE_WR ], queries, seed ) );
/* run test */
exit ( aqlTest ( pipeAqlOutput [ PIPE_WR ], queries, seed ) );
}
 
close ( pipefd [ PIPE_WR ] );
/* close unused ends of pipes in parent */
close ( pipeAqlOutput [ PIPE_WR ] );
close ( pipeStdout [ PIPE_WR ] );
close ( pipeStderr [ PIPE_WR ] );
 
/* lets wait the specified time of microseconds, maybe the child will terminate on its own */
if ( ! waitSignalTimeout ( timeout.count ( ) ) ) {
/* ... and in case it did not ... */
kill ( x, SIGTERM );
kill ( pid, SIGTERM );
while ( ! waitSignalTimeout ( 250000 ) ) /* 1/4 second */
kill ( x, SIGKILL );
kill ( pid, SIGKILL );
}
 
/* child termination confirmed */
int status;
waitpid ( x, &status, 0 );
waitpid ( pid, &status, 0 );
close ( g_Wakeup [ PIPE_RD ] );
close ( g_Wakeup [ PIPE_WR ] );
 
std::string childOutput = readChildOutput ( pipefd [ PIPE_RD ] );
/* read child outputs */
std::string childOutput [ 3 ] = {
readFromFD ( pipeAqlOutput [ PIPE_RD ] ),
readFromFD ( pipeStdout [ PIPE_RD ] ),
readFromFD ( pipeStderr [ PIPE_RD ] )
};
 
close ( pipefd [ PIPE_RD ] );
close ( pipefd [ PIPE_WR ] );
/* communication is done */
close ( pipeAqlOutput [ PIPE_RD ] );
close ( pipeStdout [ PIPE_RD ] );
close ( pipeStderr [ PIPE_RD ] );
 
/* determine test status */
if ( WIFEXITED ( status ) ) {
INFO ( "AqlTest failure. Trying to execute: " << "\n" << formatQueries ( queries ) );
INFO ( "Seed was: " << seed );
INFO ( "Child output was: >" << childOutput << "<" );
INFO ( "Child aqlout was: >" << childOutput [ 0 ] << "<" );
INFO ( "Child stdout was: >" << childOutput [ 1 ] << "<" );
INFO ( "Child stderr was: >" << childOutput [ 2 ] << "<" );
REQUIRE ( WEXITSTATUS ( status ) == 0 );
} else if ( WIFSIGNALED ( status ) ) {
if ( WTERMSIG ( status ) == SIGTERM || WTERMSIG ( status ) == SIGKILL ) { /* killed by timeout control */
WARN ( "AqlTest timeout (" << timeout.count ( ) << " us) reached. Trying to execute:\n" << formatQueries ( queries ) <<
"Seed was: " << seed << "\n" <<
"Child output was: >" << childOutput << "<" );
"Child aqlout was: >" << childOutput [ 0 ] << "<\n" <<
"Child stdout was: >" << childOutput [ 1 ] << "<\n" <<
"Child stderr was: >" << childOutput [ 2 ] << "<\n" );
CHECK_NOFAIL ( "timeout warning" == nullptr );
} else {
INFO ( "AqlTest failure. Trying to execute: " << formatQueries ( queries ) );
INFO ( "Seed was: " << seed );
INFO ( "Child output was: >" << childOutput << "<" );
INFO ( "Child aqlout was: >" << childOutput [ 0 ] << "<" );
INFO ( "Child stdout was: >" << childOutput [ 1 ] << "<" );
INFO ( "Child stderr was: >" << childOutput [ 2 ] << "<" );
INFO ( "Child process signaled, signal " << WTERMSIG ( status ) << " (" << strsignal ( WTERMSIG ( status ) ) << ")" );
FAIL ( );
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment