/** \file fish_tests.c Various bug and feature tests. Compiled and run by make test. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_GETOPT_H #include #endif #include #include #include #include "fallback.h" #include "util.h" #include "common.h" #include "proc.h" #include "reader.h" #include "builtin.h" #include "function.h" #include "complete.h" #include "wutil.h" #include "env.h" #include "expand.h" #include "parser.h" #include "tokenizer.h" #include "output.h" #include "exec.h" #include "event.h" #include "halloc_util.h" /** Number of laps to run performance testing loop */ #define LAPS 50 /** The result of one of the test passes */ #define NUM_ANS L"-7 99999999 1234567 deadbeef DEADBEEFDEADBEEF" /** Number of encountered errors */ static int err_count=0; /** Print formatted output */ static void say( wchar_t *blah, ... ) { va_list va; va_start( va, blah ); vwprintf( blah, va ); va_end( va ); wprintf( L"\n" ); } /** Print formatted error string */ static void err( wchar_t *blah, ... ) { va_list va; va_start( va, blah ); err_count++; wprintf( L"Error: " ); vwprintf( blah, va ); va_end( va ); wprintf( L"\n" ); } /** Print ok message */ static void ok() { wprintf( L"OK\n" ); } /** Compare two pointers */ static int pq_compare( void *e1, void *e2 ) { return e1-e2; } /** Test priority queue functionality */ static void pq_test( int elements ) { int i; int prev; int *count = calloc( sizeof(int), 100 ); priority_queue_t q; pq_init( &q, pq_compare ); for( i=0; i prev ) err( L"Wrong order of elements in priority_queue_t" ); prev = pos; } for( i=0; i<100; i++ ) { if( count[i] != 0 ) { err( L"Wrong number of elements in priority_queue_t" ); } } } /** Test stack functionality */ static int stack_test( int elements ) { long i; int res=1; array_list_t s; al_init( &s ); for( i=0; i 8 ) say( L"Hashtable uses %f microseconds per element at size %d", ((double)(t2-t1))/(1<&1 'nested \"quoted\" '(string containing subshells ){and,brackets}$as[$well (as variable arrays)]"; const int types[] = { TOK_STRING, TOK_REDIRECT_IN, TOK_STRING, TOK_REDIRECT_FD, TOK_STRING, TOK_STRING, TOK_END } ; int i; say( L"Test correct tokenization" ); for( i=0, tok_init( &t, str, 0 ); i<(sizeof(types)/sizeof(int)); i++,tok_next( &t ) ) { if( types[i] != tok_last_type( &t ) ) { err( L"Tokenization error:"); wprintf( L"Token number %d of string \n'%ls'\n, expected token type %ls, got token '%ls' of type %ls\n", i+1, str, tok_get_desc(types[i]), tok_last(&t), tok_get_desc(tok_last_type( &t )) ); } } } } /** Test the parser */ static void test_parser() { say( L"Testing parser" ); say( L"Testing null input to parser" ); if( !parser_test( 0, 0, 0 ) ) { err( L"Null input to parser_test undetected" ); } say( L"Testing block nesting" ); if( !parser_test( L"if; end", 0, 0 ) ) { err( L"Incomplete if statement undetected" ); } if( !parser_test( L"if test; echo", 0, 0 ) ) { err( L"Missing end undetected" ); } if( !parser_test( L"if test; end; end", 0, 0 ) ) { err( L"Unbalanced end undetected" ); } say( L"Testing detection of invalid use of builtin commands" ); if( !parser_test( L"case foo", 0, 0 ) ) { err( L"'case' command outside of block context undetected" ); } if( !parser_test( L"switch ggg; if true; case foo;end;end", 0, 0 ) ) { err( L"'case' command outside of switch block context undetected" ); } if( !parser_test( L"else", 0, 0 ) ) { err( L"'else' command outside of conditional block context undetected" ); } if( !parser_test( L"break", 0, 0 ) ) { err( L"'break' command outside of loop block context undetected" ); } if( !parser_test( L"exec ls|less", 0, 0 ) || !parser_test( L"echo|return", 0, 0 )) { err( L"Invalid pipe command undetected" ); } say( L"Testing basic evaluation" ); if( !eval( 0, 0, TOP ) ) { err( L"Null input when evaluating undetected" ); } if( !eval( L"ls", 0, WHILE ) ) { err( L"Invalid block mode when evaluating undetected" ); } } /** Perform parameter expansion and test if the output equals the zero-terminated parameter list supplied. \param in the string to expand \param flags the flags to send to expand_string */ static int expand_test( const wchar_t *in, int flags, ... ) { array_list_t out; va_list va; int i=0; int res=1; wchar_t *arg; al_init( &out ); expand_string( 0, wcsdup(in), &out, flags); va_start( va, flags ); while( (arg=va_arg(va, wchar_t *) )!= 0 ) { if( al_get_count( &out ) == i ) { res=0; break; } if( wcscmp( al_get( &out, i ),arg) != 0 ) { res=0; break; } i++; } va_end( va ); al_foreach( &out, &free ); return res; } /** Test globbing and other parameter expansion */ static void test_expand() { say( L"Testing parameter expansion" ); if( !expand_test( L"foo", 0, L"foo", 0 )) { err( L"Strings do not expand to themselves" ); } if( !expand_test( L"a{b,c,d}e", 0, L"abe", L"ace", L"ade", 0 ) ) { err( L"Bracket expansion is broken" ); } if( !expand_test( L"a*", EXPAND_SKIP_WILDCARDS, L"a*", 0 ) ) { err( L"Cannot skip wildcard expansion" ); } } /** Test speed of completion calculations */ void perf_complete() { wchar_t c; array_list_t out; long long t1, t2; int matches=0; double t; wchar_t str[3]= { 0, 0, 0 } ; int i; say( L"Testing completion performance" ); al_init( &out ); reader_push(L""); say( L"Here we go" ); t1 = get_time(); for( c=L'a'; c<=L'z'; c++ ) { str[0]=c; reader_set_buffer( str, 0 ); complete( str, &out ); matches += al_get_count( &out ); al_foreach( &out, &free ); al_truncate( &out, 0 ); } t2=get_time(); t = (double)(t2-t1)/(1000000*26); say( L"One letter command completion took %f seconds per completion, %f microseconds/match", t, (double)(t2-t1)/matches ); matches=0; t1 = get_time(); for( i=0; i