diff options
author | axel <axel@liljencrantz.se> | 2005-09-20 23:26:39 +1000 |
---|---|---|
committer | axel <axel@liljencrantz.se> | 2005-09-20 23:26:39 +1000 |
commit | 149594f974350bb364a76c73b91b1d5ffddaa1fa (patch) | |
tree | 95650e9982d5fabe4bd805d94c5d700cbbc1ca7f /fish_tests.c |
Initial revision
darcs-hash:20050920132639-ac50b-fa3b476891e1f5f67207cf4cc7bf623834cc5edc.gz
Diffstat (limited to 'fish_tests.c')
-rw-r--r-- | fish_tests.c | 647 |
1 files changed, 647 insertions, 0 deletions
diff --git a/fish_tests.c b/fish_tests.c new file mode 100644 index 00000000..73756693 --- /dev/null +++ b/fish_tests.c @@ -0,0 +1,647 @@ +/** \file main.c + Various bug and feature tests. Compiled and run by make test. +*/ + +#include "config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <wchar.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <unistd.h> +#include <termios.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdarg.h> + +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif + +#include <signal.h> + +#include <locale.h> +#include <dirent.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" + +#define LAPS 50 + +static int err_count=0; + +static void say( wchar_t *blah, ... ) +{ + va_list va; + va_start( va, blah ); + vwprintf( blah, va ); + va_end( va ); + wprintf( L"\n" ); +} + +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" ); +} + +static void ok() +{ + wprintf( L"OK\n" ); +} + + +static int pq_compare( void *e1, void *e2 ) +{ + return e1-e2; +} + +static int 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<elements; i++ ) + { + int foo = rand() % 100; +// printf( "Adding %d\n", foo ); + pq_put( &q, (void *)foo ); + count[foo]++; + } + + prev = 100; + + for( i=0; i<elements; i++ ) + { + int pos = (int)pq_get( &q ); + count[ pos ]--; + if( pos > 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" ); + } + } +} + + +static int stack_test( int elements ) +{ + int i; + + int res=1; + + array_list_t s; + + al_init( &s ); + + for( i=0; i<elements; i++ ) + { + int foo; + + al_push( &s, (void*)i); + al_push( &s, (void*)i); + + if( (foo=(int)al_pop( &s )) != i ) + { + err( L"Unexpected data" ); + res = 0; + break; + } + } + + for( i=0; i<elements; i++ ) + { + int foo; + + if( (foo=(int)al_pop( &s )) != (elements-i-1) ) + { + err( L"Unexpected data" ); + res = 0; + break; + } + + + } + + + al_destroy( &s ); + + return res; +} + + +static int hash_func( const void *data ) +{ +/* srand( (int)data ); + return rand(); +*/ + int foo = (int)data; + return 127*((foo^0xefc7e214)) ^(foo<<11); +} + +static int compare_func( const void *key1, const void *key2 ) +{ + return key1==key2; +} + + +static int hash_test( int elements ) +{ + int i; + int res=1; + + hash_table_t h; + + hash_init( &h, hash_func, compare_func ); + + for( i=1; i< elements+1; i++ ) + { + hash_put( &h, (void*)i, (void*)100-i ); + } + + for( i=1; i< elements+1; i++ ) + { + if( (int)hash_get( &h, (void*)i ) != (100-i) ) + { + err( L"Key %d gave data %d, expected data %d", + i, + (int)hash_get( &h, (void*)i ), + 100-i ); + res = 0; + + break; + } + } + + if( hash_get_count( &h ) != elements ) + { + err( L"Table holds %d elements, should hold %d elements", + hash_get_count( &h ), + elements ); + res = 0; + + } + + + for( i=1; i<elements+1; i+=2 ) + { + hash_remove( &h, (void*)i, 0, 0 ); + + } + + if( hash_get_count( &h ) != ((elements)/2) ) + { + err( L"Table contains %d elements, should contain %d elements", + hash_get_count( &h ), + elements/2 ); + res = 0; + } + + for( i=1; i<elements+1; i++ ) + { + if( hash_contains( &h, (void*)i) != (i+1)%2 ) + { + if( i%2 ) + err( L"Key %d remains, should be deleted", + i ); + else + err( L"Key %d does not exist", + i ); + res = 0; + break; + } + } + + hash_destroy( &h ); + + return res; + +} + +static int al_test( int sz) +{ + int i; + array_list_t l; + + + + al_init( &l ); + + al_set( &l, 1, (void *)7 ); + al_set( &l, sz, (void *)7 ); + + if( al_get_count( &l ) != maxf( sz+1, 2 ) ) + err( L"Wrong number of elements in array list" ); + + for( i=0; i<al_get_count( &l ); i++ ) + { + int val = (int)((long) al_get( &l, i )); + if( (i == 1) || (i==sz)) + { + if( val != 7 ) + err( L"Canary changed to %d at index %d", val, i ); + } + else + { + if( val != 0 ) + err( L"False canary %d found at index %d", val, i ); + } + } +} + +static void sb_test() +{ + string_buffer_t b; + int res; + + sb_init( &b ); + + if( res=sb_printf( &b, L"%ls%s", L"Testing ", "string_buffer_t " ) == -1 ) + { + err( L"Error %d while testing stringbuffers", res ); + } + + if( (res=sb_printf( &b, L"%ls", L"functionality" ))==-1) + { + err( L"Error %d while testing stringbuffers", res ); + } + say( (wchar_t *)b.buff ); +} + + +static void test_util() +{ + int i; + + say( L"Testing utility library" ); + + for( i=0; i<18; i++ ) + { + long t1, t2; + pq_test( 1<<i ); + stack_test( 1<<i ); + t1 = get_time(); + hash_test( 1<<i ); + t2 = get_time(); + if( i > 8 ) + say( L"Hashtable uses %f microseconds per element at size %d", + ((double)(t2-t1))/(1<<i), + 1<<i ); + al_test( 1<<i ); + } + + sb_test(); + + +/* + int i; + for( i=2; i<10000000; i*=2 ) + { + + printf( "%d", i ); + + t1 = get_time(); + + if(!hash_test(i)) + exit(0); + + t2 = get_time(); + + printf( " %d\n", (t2-t1)/i ); + + + } +*/ +} + + + + +static void test_tok() +{ + tokenizer t; + + say( L"Testing tokenizer" ); + + + say( L"Testing invalid input" ); + tok_init( &t, 0, 0 ); + + if( tok_last_type( &t ) != TOK_ERROR ) + { + err(L"Invalid input to tokenizer was undetected" ); + } + + say( L"Testing use of broken tokenizer" ); + if( !tok_has_next( &t ) ) + { + err( L"tok_has_next() should return 1 once on broken tokenizer" ); + } + + tok_next( &t ); + if( tok_last_type( &t ) != TOK_ERROR ) + { + err(L"Invalid input to tokenizer was undetected" ); + } + + /* + This should crash if there is a bug. No reliable way to detect otherwise. + */ + say( L"Test destruction of broken tokenizer" ); + tok_destroy( &t ); + + { + + wchar_t *str = L"string <redirection 2>&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 )) ); + } + } + } + + + +} + +static void test_parser() +{ + say( L"Testing parser" ); + + + say( L"Testing null input to parser" ); + if( !parser_test( 0, 0 ) ) + { + err( L"Null input to parser_test undetected" ); + } + + say( L"Testing block nesting" ); + if( !parser_test( L"if; end", 0 ) ) + { + err( L"Incomplete if statement undetected" ); + } + if( !parser_test( L"if test; echo", 0 ) ) + { + err( L"Missing end undetected" ); + } + if( !parser_test( L"if test; end; end", 0 ) ) + { + err( L"Unbalanced end undetected" ); + } + + say( L"Testing detection of invalid use of builtin commands" ); + if( !parser_test( L"case foo", 0 ) ) + { + err( L"'case' command outside of block context undetected" ); + } + if( !parser_test( L"switch ggg; if true; case foo;end;end", 0 ) ) + { + err( L"'case' command outside of switch block context undetected" ); + } + if( !parser_test( L"else", 0 ) ) + { + err( L"'else' command outside of conditional block context undetected" ); + } + if( !parser_test( L"break", 0 ) ) + { + err( L"'break' command outside of loop block context undetected" ); + } + if( !parser_test( L"exec ls|less", 0 ) || !parser_test( L"echo|return", 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 expantion 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( 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, (void (*)(const void *))&free ); + return res; + +} + + +static void test_expand() +{ + say( L"Testing parameter expantion" ); + + 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 expantion is broken" ); + } + + if( !expand_test( L"a*", EXPAND_SKIP_WILDCARDS, L"a*", 0 ) ) + { + err( L"Cannot skip wildcard expantion" ); + } + +} + + +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, (void (*)(const void *))&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<LAPS; i++ ) + { + str[0]='a'+(rand()%26); + str[1]='a'+(rand()%26); + + reader_set_buffer( str, 0 ); + + complete( str, &out ); + + matches += al_get_count( &out ); + + al_foreach( &out, (void (*)(const void *))&free ); + al_truncate( &out, 0 ); + } + t2=get_time(); + + t = (double)(t2-t1)/(1000000*LAPS); + + say( L"Two letter command completion took %f seconds per completion, %f microseconds/match", t, (double)(t2-t1)/matches ); + + al_destroy( &out ); + + reader_pop(); + +} + + +int main( int argc, char **argv ) +{ + + say( L"Testing low-level functionality"); + say( L"Lines beginning with 'fish:' are not errors, they are warning messages\ngenerated by the fish parser library when given broken input, and can be\nignored. All errors begin with 'Error:'." ); + + parser_init(); + function_init(); + builtin_init(); + env_init(); + complete_init(); + reader_init(); + + test_util(); + test_tok(); + test_parser(); + test_expand(); + + say( L"Encountered %d errors in low-level tests", err_count ); + + /* + Skip performance tests for now, since they seem to hang when running from inside make (?) + */ +// say( L"Testing performance" ); +// perf_complete(); + + reader_destroy(); + + parser_destroy(); + function_destroy(); + builtin_destroy(); + env_destroy(); + complete_destroy(); + wutil_destroy(); + +} |