2006-05-09 19:35:46 +04:00
/*
2007-01-19 23:30:05 +03:00
* testutils . c : basic test utils
2006-05-09 19:35:46 +04:00
*
2009-02-02 23:35:14 +03:00
* Copyright ( C ) 2005 - 2009 Red Hat , Inc .
2006-05-09 19:35:46 +04:00
*
* See COPYING . LIB for the License of this software
*
* Karel Zak < kzak @ redhat . com >
*/
2008-01-29 21:15:54 +03:00
# include <config.h>
2007-12-07 13:08:06 +03:00
2006-05-09 19:35:46 +04:00
# include <stdio.h>
# include <stdlib.h>
# include <sys/time.h>
2006-08-24 19:05:19 +04:00
# include <sys/types.h>
# include <sys/stat.h>
2008-06-26 13:37:51 +04:00
# ifndef WIN32
2006-08-25 01:46:28 +04:00
# include <sys/wait.h>
2008-06-26 13:37:51 +04:00
# endif
2009-05-29 18:29:22 +04:00
# ifdef HAVE_REGEX_H
2009-05-21 18:22:51 +04:00
# include <regex.h>
2009-05-29 18:29:22 +04:00
# endif
2006-08-24 19:05:19 +04:00
# include <unistd.h>
2008-04-18 19:05:29 +04:00
# include <string.h>
2006-08-25 01:46:28 +04:00
# include <fcntl.h>
# include <limits.h>
2006-05-09 19:35:46 +04:00
# include "testutils.h"
2008-04-30 16:30:55 +04:00
# include "internal.h"
2008-05-29 19:21:45 +04:00
# include "memory.h"
# include "util.h"
2009-01-29 00:53:48 +03:00
# include "threads.h"
# include "virterror_internal.h"
2008-05-29 19:21:45 +04:00
# if TEST_OOM_TRACE
# include <execinfo.h>
# endif
2006-05-09 19:35:46 +04:00
2007-06-15 19:24:20 +04:00
# ifdef HAVE_PATHS_H
# include <paths.h>
# endif
2006-05-09 19:35:46 +04:00
# define GETTIMEOFDAY(T) gettimeofday(T, NULL)
2007-01-19 23:30:05 +03:00
# define DIFF_MSEC(T, U) \
( ( ( ( int ) ( ( T ) - > tv_sec - ( U ) - > tv_sec ) ) * 1000000.0 + \
( ( int ) ( ( T ) - > tv_usec - ( U ) - > tv_usec ) ) ) / 1000.0 )
2006-05-09 19:35:46 +04:00
2008-05-29 19:21:45 +04:00
static unsigned int testOOM = 0 ;
static unsigned int testDebug = 0 ;
static unsigned int testCounter = 0 ;
2006-05-09 19:35:46 +04:00
double
virtTestCountAverage ( double * items , int nitems )
{
2007-01-19 23:30:05 +03:00
long double sum = 0 ;
int i ;
2006-05-09 19:35:46 +04:00
2007-01-19 23:30:05 +03:00
for ( i = 1 ; i < nitems ; i + + )
sum + = items [ i ] ;
2006-05-09 19:35:46 +04:00
2007-01-19 23:30:05 +03:00
return ( double ) ( sum / nitems ) ;
2006-05-09 19:35:46 +04:00
}
2007-01-19 23:30:05 +03:00
/*
2006-05-22 18:38:33 +04:00
* Runs test and count average time ( if the nloops is grater than 1 )
2007-01-19 23:30:05 +03:00
*
* returns : - 1 = error , 0 = success
2006-05-09 19:35:46 +04:00
*/
int
2007-07-19 01:08:22 +04:00
virtTestRun ( const char * title , int nloops , int ( * body ) ( const void * data ) , const void * data )
2006-05-09 19:35:46 +04:00
{
2007-01-19 23:30:05 +03:00
int i , ret = 0 ;
double * ts = NULL ;
2008-04-18 19:05:29 +04:00
2008-05-29 19:21:45 +04:00
testCounter + + ;
2008-04-18 19:05:29 +04:00
2008-05-29 19:21:45 +04:00
if ( testOOM < 2 ) {
fprintf ( stderr , " %2d) %-65s ... " , testCounter , title ) ;
fflush ( stderr ) ;
}
2007-01-19 23:30:05 +03:00
if ( nloops > 1 & & ( ts = calloc ( nloops ,
sizeof ( double ) ) ) = = NULL )
return - 1 ;
for ( i = 0 ; i < nloops ; i + + ) {
struct timeval before , after ;
if ( ts )
GETTIMEOFDAY ( & before ) ;
if ( ( ret = body ( data ) ) ! = 0 )
break ;
if ( ts ) {
GETTIMEOFDAY ( & after ) ;
ts [ i ] = DIFF_MSEC ( & after , & before ) ;
}
}
2008-05-29 19:21:45 +04:00
if ( testOOM < 2 ) {
if ( ret = = 0 & & ts )
fprintf ( stderr , " OK [%.5f ms] \n " ,
virtTestCountAverage ( ts , nloops ) ) ;
else if ( ret = = 0 )
fprintf ( stderr , " OK \n " ) ;
else
fprintf ( stderr , " FAILED \n " ) ;
}
2007-01-19 23:30:05 +03:00
2008-01-29 20:41:07 +03:00
free ( ts ) ;
2007-01-19 23:30:05 +03:00
return ret ;
2006-05-09 19:35:46 +04:00
}
2006-08-24 19:05:19 +04:00
2009-02-02 23:35:14 +03:00
/* Read FILE into buffer BUF of length BUFLEN.
Upon any failure , or if FILE appears to contain more than BUFLEN bytes ,
diagnose it and return - 1 , but don ' t bother trying to preserve errno .
Otherwise , return the number of bytes read ( and copied into BUF ) . */
int virtTestLoadFile ( const char * file ,
2007-01-19 23:30:05 +03:00
char * * buf ,
int buflen ) {
2009-02-02 23:35:14 +03:00
FILE * fp = fopen ( file , " r " ) ;
2006-08-24 19:05:19 +04:00
struct stat st ;
2007-01-19 23:30:05 +03:00
2009-02-02 23:35:14 +03:00
if ( ! fp ) {
fprintf ( stderr , " %s: failed to open: %s \n " , file , strerror ( errno ) ) ;
2006-08-24 19:05:19 +04:00
return - 1 ;
2009-02-02 23:35:14 +03:00
}
2006-08-24 19:05:19 +04:00
if ( fstat ( fileno ( fp ) , & st ) < 0 ) {
2009-02-02 23:35:14 +03:00
fprintf ( stderr , " %s: failed to fstat: %s \n " , file , strerror ( errno ) ) ;
2006-08-24 19:05:19 +04:00
fclose ( fp ) ;
return - 1 ;
}
if ( st . st_size > ( buflen - 1 ) ) {
2009-02-02 23:35:14 +03:00
fprintf ( stderr , " %s: larger than buffer (> %d) \n " , file , buflen - 1 ) ;
2006-08-24 19:05:19 +04:00
fclose ( fp ) ;
return - 1 ;
}
2007-01-19 23:30:05 +03:00
if ( st . st_size ) {
if ( fread ( * buf , st . st_size , 1 , fp ) ! = 1 ) {
2009-02-02 23:35:14 +03:00
fprintf ( stderr , " %s: read failed: %s \n " , file , strerror ( errno ) ) ;
2007-01-19 23:30:05 +03:00
fclose ( fp ) ;
return - 1 ;
}
2006-08-24 19:05:19 +04:00
}
( * buf ) [ st . st_size ] = ' \0 ' ;
fclose ( fp ) ;
return st . st_size ;
}
2008-06-26 13:37:51 +04:00
# ifndef WIN32
2006-08-25 01:46:28 +04:00
static
void virtTestCaptureProgramExecChild ( const char * const argv [ ] ,
2007-01-19 23:30:05 +03:00
int pipefd ) {
int i ;
int open_max ;
int stdinfd = - 1 ;
const char * const env [ ] = {
" LANG=C " ,
2008-11-21 15:16:08 +03:00
# if WITH_DRIVER_MODULES
" LIBVIRT_DRIVER_DIR= " TEST_DRIVER_DIR ,
# endif
2007-01-19 23:30:05 +03:00
NULL
} ;
2008-12-17 21:04:55 +03:00
if ( ( stdinfd = open ( " /dev/null " , O_RDONLY ) ) < 0 )
2007-01-19 23:30:05 +03:00
goto cleanup ;
open_max = sysconf ( _SC_OPEN_MAX ) ;
for ( i = 0 ; i < open_max ; i + + ) {
if ( i ! = stdinfd & &
i ! = pipefd )
close ( i ) ;
}
if ( dup2 ( stdinfd , STDIN_FILENO ) ! = STDIN_FILENO )
goto cleanup ;
if ( dup2 ( pipefd , STDOUT_FILENO ) ! = STDOUT_FILENO )
goto cleanup ;
2009-07-06 18:03:31 +04:00
if ( dup2 ( pipefd , STDERR_FILENO ) ! = STDERR_FILENO )
2007-01-19 23:30:05 +03:00
goto cleanup ;
/* SUS is crazy here, hence the cast */
execve ( argv [ 0 ] , ( char * const * ) argv , ( char * const * ) env ) ;
2006-08-25 01:46:28 +04:00
cleanup :
2007-01-19 23:30:05 +03:00
if ( stdinfd ! = - 1 )
close ( stdinfd ) ;
2006-08-25 01:46:28 +04:00
}
int virtTestCaptureProgramOutput ( const char * const argv [ ] ,
2007-01-19 23:30:05 +03:00
char * * buf ,
int buflen ) {
int pipefd [ 2 ] ;
if ( pipe ( pipefd ) < 0 )
return - 1 ;
int pid = fork ( ) ;
switch ( pid ) {
2006-08-25 01:46:28 +04:00
case 0 :
close ( pipefd [ 0 ] ) ;
2007-01-19 23:30:05 +03:00
virtTestCaptureProgramExecChild ( argv , pipefd [ 1 ] ) ;
close ( pipefd [ 1 ] ) ;
_exit ( 1 ) ;
2006-08-25 01:46:28 +04:00
case - 1 :
return - 1 ;
2007-01-19 23:30:05 +03:00
2006-08-25 01:46:28 +04:00
default :
2007-01-19 23:30:05 +03:00
{
int got = 0 ;
int ret = - 1 ;
int want = buflen - 1 ;
2006-08-25 01:46:28 +04:00
2007-01-19 23:30:05 +03:00
close ( pipefd [ 1 ] ) ;
2006-08-25 01:46:28 +04:00
2007-01-19 23:30:05 +03:00
while ( want ) {
if ( ( ret = read ( pipefd [ 0 ] , ( * buf ) + got , want ) ) < = 0 )
break ;
got + = ret ;
want - = ret ;
}
close ( pipefd [ 0 ] ) ;
2006-08-25 01:46:28 +04:00
2007-01-19 23:30:05 +03:00
if ( ! ret )
( * buf ) [ got ] = ' \0 ' ;
2006-08-25 01:46:28 +04:00
2007-01-19 23:30:05 +03:00
waitpid ( pid , NULL , 0 ) ;
2006-08-25 01:46:28 +04:00
2007-01-19 23:30:05 +03:00
return ret ;
}
}
2006-08-25 01:46:28 +04:00
}
2008-06-26 13:37:51 +04:00
# endif /* !WIN32 */
2008-04-18 19:05:29 +04:00
/**
* @ param stream : output stream write to differences to
* @ param expect : expected output text
* @ param actual : actual output text
*
* Display expected and actual output text , trimmed to
* first and last characters at which differences occur
*/
int virtTestDifference ( FILE * stream ,
const char * expect ,
const char * actual )
{
const char * expectStart = expect ;
const char * expectEnd = expect + ( strlen ( expect ) - 1 ) ;
const char * actualStart = actual ;
const char * actualEnd = actual + ( strlen ( actual ) - 1 ) ;
2008-05-29 19:21:45 +04:00
if ( ! testDebug )
return 0 ;
if ( testDebug < 2 ) {
2008-04-30 16:30:55 +04:00
/* Skip to first character where they differ */
while ( * expectStart & & * actualStart & &
* actualStart = = * expectStart ) {
actualStart + + ;
expectStart + + ;
}
2008-04-18 19:05:29 +04:00
2008-04-30 16:30:55 +04:00
/* Work backwards to last character where they differ */
while ( actualEnd > actualStart & &
expectEnd > expectStart & &
* actualEnd = = * expectEnd ) {
actualEnd - - ;
expectEnd - - ;
}
2008-04-18 19:05:29 +04:00
}
/* Show the trimmed differences */
fprintf ( stream , " \n Expect [ " ) ;
if ( ( expectEnd - expectStart + 1 ) & &
fwrite ( expectStart , ( expectEnd - expectStart + 1 ) , 1 , stream ) ! = 1 )
return - 1 ;
fprintf ( stream , " ] \n " ) ;
fprintf ( stream , " Actual [ " ) ;
if ( ( actualEnd - actualStart + 1 ) & &
fwrite ( actualStart , ( actualEnd - actualStart + 1 ) , 1 , stream ) ! = 1 )
return - 1 ;
fprintf ( stream , " ] \n " ) ;
/* Pad to line up with test name ... in virTestRun */
fprintf ( stream , " ... " ) ;
return 0 ;
}
2008-05-29 19:21:45 +04:00
2008-05-29 23:41:40 +04:00
# if TEST_OOM
2008-05-29 19:21:45 +04:00
static void
virtTestErrorFuncQuiet ( void * data ATTRIBUTE_UNUSED ,
virErrorPtr err ATTRIBUTE_UNUSED )
{ }
2008-05-29 23:41:40 +04:00
# endif
2008-05-29 19:21:45 +04:00
2008-05-29 23:41:40 +04:00
# if TEST_OOM_TRACE
2008-05-29 19:21:45 +04:00
static void
2008-05-29 22:47:00 +04:00
virtTestErrorHook ( int n , void * data ATTRIBUTE_UNUSED )
2008-05-29 19:21:45 +04:00
{
void * trace [ 30 ] ;
int ntrace = ARRAY_CARDINALITY ( trace ) ;
int i ;
char * * symbols = NULL ;
ntrace = backtrace ( trace , ntrace ) ;
symbols = backtrace_symbols ( trace , ntrace ) ;
if ( symbols ) {
2008-05-29 22:47:00 +04:00
fprintf ( stderr , " Failing allocation %d at: \n " , n ) ;
2008-05-29 19:21:45 +04:00
for ( i = 0 ; i < ntrace ; i + + ) {
if ( symbols [ i ] )
fprintf ( stderr , " TRACE: %s \n " , symbols [ i ] ) ;
}
free ( symbols ) ;
}
}
2008-05-29 23:41:40 +04:00
# endif
2008-05-29 19:21:45 +04:00
int virtTestMain ( int argc ,
char * * argv ,
int ( * func ) ( int , char * * ) )
{
2008-07-09 14:27:17 +04:00
char * debugStr ;
2008-05-29 19:21:45 +04:00
int ret ;
2009-01-29 00:53:48 +03:00
# if TEST_OOM
2008-05-29 19:21:45 +04:00
int approxAlloc = 0 ;
int n ;
2008-07-09 14:27:17 +04:00
char * oomStr = NULL ;
2008-05-29 19:21:45 +04:00
int oomCount ;
2008-07-07 14:10:29 +04:00
int mp = 0 ;
pid_t * workers ;
int worker = 0 ;
2008-07-09 14:27:17 +04:00
# endif
2009-01-29 00:53:48 +03:00
if ( virThreadInitialize ( ) < 0 | |
virErrorInitialize ( ) < 0 )
return 1 ;
2008-05-29 19:21:45 +04:00
if ( ( debugStr = getenv ( " VIR_TEST_DEBUG " ) ) ! = NULL ) {
2008-06-12 17:48:29 +04:00
if ( virStrToLong_ui ( debugStr , NULL , 10 , & testDebug ) < 0 )
2008-05-29 19:21:45 +04:00
testDebug = 0 ;
}
2008-07-09 14:27:17 +04:00
# if TEST_OOM
2008-05-29 19:21:45 +04:00
if ( ( oomStr = getenv ( " VIR_TEST_OOM " ) ) ! = NULL ) {
if ( virStrToLong_i ( oomStr , NULL , 10 , & oomCount ) < 0 )
oomCount = 0 ;
if ( oomCount < 0 )
oomCount = 0 ;
if ( oomCount )
testOOM = 1 ;
}
2008-07-07 14:10:29 +04:00
if ( getenv ( " VIR_TEST_MP " ) ! = NULL ) {
mp = sysconf ( _SC_NPROCESSORS_ONLN ) ;
fprintf ( stderr , " Using %d worker processes \n " , mp ) ;
2009-01-29 00:53:48 +03:00
if ( VIR_ALLOC_N ( workers , mp ) < 0 ) {
ret = EXIT_FAILURE ;
goto cleanup ;
}
2008-07-07 14:10:29 +04:00
}
2009-09-02 18:09:04 +04:00
/* Run once to prime any static allocations & ensure it passes */
2008-05-29 19:21:45 +04:00
ret = ( func ) ( argc , argv ) ;
if ( ret ! = EXIT_SUCCESS )
2009-01-29 00:53:48 +03:00
goto cleanup ;
2008-05-29 19:21:45 +04:00
2008-05-29 23:41:40 +04:00
# if TEST_OOM_TRACE
2008-05-29 19:21:45 +04:00
if ( testDebug )
virAllocTestHook ( virtTestErrorHook , NULL ) ;
2008-05-29 23:41:40 +04:00
# endif
2008-05-29 19:21:45 +04:00
if ( testOOM ) {
/* Makes next test runs quiet... */
testOOM + + ;
virSetErrorFunc ( NULL , virtTestErrorFuncQuiet ) ;
2009-09-02 18:09:04 +04:00
virAllocTestInit ( ) ;
/* Run again to count allocs, and ensure it passes :-) */
ret = ( func ) ( argc , argv ) ;
if ( ret ! = EXIT_SUCCESS )
goto cleanup ;
2008-05-29 19:21:45 +04:00
approxAlloc = virAllocTestCount ( ) ;
testCounter + + ;
if ( testDebug )
fprintf ( stderr , " %d) OOM... \n " , testCounter ) ;
else
fprintf ( stderr , " %d) OOM of %d allocs " , testCounter , approxAlloc ) ;
2008-07-07 14:10:29 +04:00
if ( mp ) {
int i ;
for ( i = 0 ; i < mp ; i + + ) {
workers [ i ] = fork ( ) ;
if ( workers [ i ] = = 0 ) {
worker = i + 1 ;
break ;
}
}
}
2008-05-29 19:21:45 +04:00
/* Run once for each alloc, failing a different one
and validating that the test case failed */
2008-07-07 14:10:29 +04:00
for ( n = 0 ; n < approxAlloc & & ( ! mp | | worker ) ; n + + ) {
2009-04-01 14:31:01 +04:00
if ( mp & &
( n % mp ) ! = ( worker - 1 ) )
2008-07-07 14:10:29 +04:00
continue ;
2008-05-29 19:21:45 +04:00
if ( ! testDebug ) {
2008-07-07 14:10:29 +04:00
if ( mp )
fprintf ( stderr , " %d " , worker ) ;
else
fprintf ( stderr , " . " ) ;
2008-05-29 19:21:45 +04:00
fflush ( stderr ) ;
}
virAllocTestOOM ( n + 1 , oomCount ) ;
if ( ( ( func ) ( argc , argv ) ) ! = EXIT_FAILURE ) {
ret = EXIT_FAILURE ;
break ;
}
}
2008-07-07 14:10:29 +04:00
if ( mp ) {
if ( worker ) {
_exit ( ret ) ;
} else {
int i , status ;
for ( i = 0 ; i < mp ; i + + ) {
waitpid ( workers [ i ] , & status , 0 ) ;
if ( WEXITSTATUS ( status ) ! = EXIT_SUCCESS )
ret = EXIT_FAILURE ;
}
VIR_FREE ( workers ) ;
}
}
2008-05-29 19:21:45 +04:00
if ( testDebug )
fprintf ( stderr , " ... OOM of %d allocs " , approxAlloc ) ;
if ( ret = = EXIT_SUCCESS )
fprintf ( stderr , " OK \n " ) ;
else
fprintf ( stderr , " FAILED \n " ) ;
}
2009-01-29 00:53:48 +03:00
cleanup :
2008-05-29 19:21:45 +04:00
# else
2009-01-29 00:53:48 +03:00
ret = ( func ) ( argc , argv ) ;
2008-05-29 19:21:45 +04:00
# endif
2009-01-29 00:53:48 +03:00
virResetLastError ( ) ;
return ret ;
2008-05-29 19:21:45 +04:00
}
2009-05-21 18:22:51 +04:00
2009-05-29 18:29:22 +04:00
# ifdef HAVE_REGEX_H
2009-05-21 18:22:51 +04:00
int virtTestClearLineRegex ( const char * pattern ,
char * str )
{
regex_t reg ;
char * lineStart = str ;
char * lineEnd = strchr ( str , ' \n ' ) ;
if ( regcomp ( & reg , pattern , REG_EXTENDED | REG_NOSUB ) ! = 0 )
return - 1 ;
while ( lineStart ) {
int ret ;
if ( lineEnd )
* lineEnd = ' \0 ' ;
ret = regexec ( & reg , lineStart , 0 , NULL , 0 ) ;
//fprintf(stderr, "Match %d '%s' '%s'\n", ret, lineStart, pattern);
if ( ret = = 0 ) {
if ( lineEnd ) {
memmove ( lineStart , lineEnd + 1 , strlen ( lineEnd + 1 ) + 1 ) ;
/* Don't update lineStart - just iterate again on this
location */
lineEnd = strchr ( lineStart , ' \n ' ) ;
} else {
* lineStart = ' \0 ' ;
lineStart = NULL ;
}
} else {
if ( lineEnd ) {
* lineEnd = ' \n ' ;
lineStart = lineEnd + 1 ;
lineEnd = strchr ( lineStart , ' \n ' ) ;
} else {
lineStart = NULL ;
}
}
}
regfree ( & reg ) ;
return 0 ;
}
2009-05-29 18:29:22 +04:00
# else
int virtTestClearLineRegex ( const char * pattern ATTRIBUTE_UNUSED ,
char * str ATTRIBUTE_UNUSED )
{
return 0 ;
}
# endif