2005-10-24 23:26:25 +08:00
/** \file fish_tests.c
2005-09-20 21:26:39 +08:00
Various bug and feature tests . Compiled and run by make test .
*/
# include "config.h"
2006-02-28 21:17:16 +08:00
2005-09-20 21:26:39 +08:00
# 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>
2012-03-06 00:27:31 +08:00
# include <sys/wait.h>
2005-09-20 21:26:39 +08:00
# include <fcntl.h>
2012-01-28 03:43:45 +08:00
# include <stdarg.h>
# include <assert.h>
2012-03-07 16:54:01 +08:00
# include <iostream>
# include <string>
# include <sstream>
2012-01-28 03:43:45 +08:00
# include <algorithm>
2012-03-07 16:54:01 +08:00
# include <iterator>
2005-09-20 21:26:39 +08:00
# ifdef HAVE_GETOPT_H
# include <getopt.h>
# endif
# include <signal.h>
# include <locale.h>
# include <dirent.h>
2007-09-24 04:59:18 +08:00
# include <time.h>
2005-09-20 21:26:39 +08:00
2006-02-28 21:17:16 +08:00
# include "fallback.h"
2005-09-20 21:26:39 +08:00
# include "util.h"
2006-02-28 21:17:16 +08:00
2005-09-20 21:26:39 +08:00
# include "common.h"
# include "proc.h"
# include "reader.h"
# include "builtin.h"
# include "function.h"
2012-01-28 03:43:45 +08:00
# include "autoload.h"
2005-09-20 21:26:39 +08:00
# include "complete.h"
# include "wutil.h"
# include "env.h"
# include "expand.h"
# include "parser.h"
# include "tokenizer.h"
2005-10-27 20:20:03 +08:00
# include "output.h"
# include "exec.h"
# include "event.h"
2007-05-11 03:11:28 +08:00
# include "path.h"
2012-02-06 08:42:24 +08:00
# include "history.h"
2012-02-19 15:26:39 +08:00
# include "highlight.h"
2012-03-01 09:55:50 +08:00
# include "iothread.h"
# include "postfork.h"
# include "signal.h"
2007-09-24 04:59:18 +08:00
/**
The number of tests to run
*/
2012-02-16 16:24:27 +08:00
//#define ESCAPE_TEST_COUNT 1000000
# define ESCAPE_TEST_COUNT 10000
2007-09-24 04:59:18 +08:00
/**
The average length of strings to unescape
*/
# define ESCAPE_TEST_LENGTH 100
/**
The higest character number of character to try and escape
*/
# define ESCAPE_TEST_CHAR 4000
2005-10-24 23:26:25 +08:00
/**
Number of laps to run performance testing loop
*/
2005-09-20 21:26:39 +08:00
# define LAPS 50
2006-06-20 08:50:10 +08:00
/**
The result of one of the test passes
*/
# define NUM_ANS L"-7 99999999 1234567 deadbeef DEADBEEFDEADBEEF"
2005-10-24 23:26:25 +08:00
/**
Number of encountered errors
*/
2005-09-20 21:26:39 +08:00
static int err_count = 0 ;
2005-10-24 23:26:25 +08:00
/**
Print formatted output
*/
2011-12-27 11:18:46 +08:00
static void say ( const wchar_t * blah , . . . )
2005-09-20 21:26:39 +08:00
{
va_list va ;
va_start ( va , blah ) ;
vwprintf ( blah , va ) ;
2011-12-27 11:18:46 +08:00
va_end ( va ) ;
2005-09-20 21:26:39 +08:00
wprintf ( L " \n " ) ;
}
2005-10-24 23:26:25 +08:00
/**
Print formatted error string
*/
2011-12-27 11:18:46 +08:00
static void err ( const wchar_t * blah , . . . )
2005-09-20 21:26:39 +08:00
{
va_list va ;
va_start ( va , blah ) ;
err_count + + ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
wprintf ( L " Error: " ) ;
vwprintf ( blah , va ) ;
2011-12-27 11:18:46 +08:00
va_end ( va ) ;
2005-09-20 21:26:39 +08:00
wprintf ( L " \n " ) ;
}
2007-09-24 04:59:18 +08:00
/**
Test the escaping / unescaping code by escaping / unescaping random
strings and verifying that the original string comes back .
*/
static void test_escape ( )
{
int i ;
2012-02-23 04:00:02 +08:00
wcstring sb ;
2011-12-27 11:18:46 +08:00
2007-09-24 04:59:18 +08:00
say ( L " Testing escaping and unescaping " ) ;
2011-12-27 11:18:46 +08:00
2007-09-24 04:59:18 +08:00
for ( i = 0 ; i < ESCAPE_TEST_COUNT ; i + + )
{
2012-02-23 04:00:02 +08:00
const wchar_t * o , * e , * u ;
2011-12-27 11:18:46 +08:00
2012-02-23 04:00:02 +08:00
sb . clear ( ) ;
2007-09-24 04:59:18 +08:00
while ( rand ( ) % ESCAPE_TEST_LENGTH )
{
2012-02-23 04:00:02 +08:00
sb . push_back ( ( rand ( ) % ESCAPE_TEST_CHAR ) + 1 ) ;
2007-09-24 04:59:18 +08:00
}
2012-02-23 04:00:02 +08:00
o = ( const wchar_t * ) sb . c_str ( ) ;
2007-09-24 04:59:18 +08:00
e = escape ( o , 1 ) ;
u = unescape ( e , 0 ) ;
if ( ! o | | ! e | | ! u )
{
err ( L " Escaping cycle of string %ls produced null pointer on %ls " , o , e ? L " unescaping " : L " escaping " ) ;
2011-12-27 11:18:46 +08:00
2007-09-24 04:59:18 +08:00
}
2011-12-27 11:18:46 +08:00
2007-09-24 04:59:18 +08:00
if ( wcscmp ( o , u ) )
{
err ( L " Escaping cycle of string %ls produced different string %ls " , o , u ) ;
2011-12-27 11:18:46 +08:00
2007-09-24 04:59:18 +08:00
}
2012-02-23 04:00:02 +08:00
free ( ( void * ) e ) ;
free ( ( void * ) u ) ;
2011-12-27 11:18:46 +08:00
2012-03-01 03:27:14 +08:00
}
}
2007-09-24 04:59:18 +08:00
2012-03-01 03:27:14 +08:00
static void test_format ( void ) {
say ( L " Testing formatting functions " ) ;
struct { unsigned long long val ; const char * expected ; } tests [ ] = {
{ 0 , " empty " } ,
{ 1 , " 1B " } ,
{ 2 , " 2B " } ,
{ 1024 , " 1kB " } ,
{ 1870 , " 1.8kB " } ,
{ 4322911 , " 4.1MB " }
} ;
size_t i ;
for ( i = 0 ; i < sizeof tests / sizeof * tests ; i + + ) {
char buff [ 128 ] ;
format_size_safe ( buff , tests [ i ] . val ) ;
assert ( ! strcmp ( buff , tests [ i ] . expected ) ) ;
}
for ( int j = - 129 ; j < = 129 ; j + + ) {
char buff1 [ 128 ] , buff2 [ 128 ] ;
2012-03-04 07:20:30 +08:00
format_long_safe ( buff1 , j ) ;
2012-03-01 03:27:14 +08:00
sprintf ( buff2 , " %d " , j ) ;
assert ( ! strcmp ( buff1 , buff2 ) ) ;
2012-03-04 07:20:30 +08:00
}
long q = LONG_MIN ;
char buff1 [ 128 ] , buff2 [ 128 ] ;
format_long_safe ( buff1 , q ) ;
sprintf ( buff2 , " %ld " , q ) ;
assert ( ! strcmp ( buff1 , buff2 ) ) ;
2007-09-24 04:59:18 +08:00
}
/**
Test wide / narrow conversion by creating random strings and
verifying that the original string comes back thorugh double
conversion .
*/
static void test_convert ( )
{
2011-12-27 11:18:46 +08:00
/* char o[] =
2007-09-24 04:59:18 +08:00
{
- 17 , - 128 , - 121 , - 68 , 0
}
;
2011-12-27 11:18:46 +08:00
2007-09-24 04:59:18 +08:00
wchar_t * w = str2wcs ( o ) ;
char * n = wcs2str ( w ) ;
int i ;
2011-12-27 11:18:46 +08:00
2007-09-24 04:59:18 +08:00
for ( i = 0 ; o [ i ] ; i + + )
{
bitprint ( o [ i ] ) ; ;
//wprintf(L"%d ", o[i]);
}
wprintf ( L " \n " ) ;
for ( i = 0 ; w [ i ] ; i + + )
{
wbitprint ( w [ i ] ) ; ;
//wprintf(L"%d ", w[i]);
}
wprintf ( L " \n " ) ;
for ( i = 0 ; n [ i ] ; i + + )
{
bitprint ( n [ i ] ) ; ;
//wprintf(L"%d ", n[i]);
}
wprintf ( L " \n " ) ;
return ;
*/
int i ;
2012-03-04 14:48:21 +08:00
std : : vector < char > sb ;
2011-12-27 11:18:46 +08:00
2007-09-24 04:59:18 +08:00
say ( L " Testing wide/narrow string conversion " ) ;
2011-12-27 11:18:46 +08:00
2007-09-24 04:59:18 +08:00
for ( i = 0 ; i < ESCAPE_TEST_COUNT ; i + + )
{
wchar_t * w ;
2012-03-04 14:48:21 +08:00
const char * o , * n ;
2011-12-27 11:18:46 +08:00
2007-09-24 04:59:18 +08:00
char c ;
2011-12-27 11:18:46 +08:00
2012-03-04 14:48:21 +08:00
sb . clear ( ) ;
2011-12-27 11:18:46 +08:00
2007-09-24 04:59:18 +08:00
while ( rand ( ) % ESCAPE_TEST_LENGTH )
{
c = rand ( ) ;
2012-03-04 14:48:21 +08:00
sb . push_back ( c ) ;
2007-09-24 04:59:18 +08:00
}
c = 0 ;
2012-03-04 14:48:21 +08:00
sb . push_back ( c ) ;
2011-12-27 11:18:46 +08:00
2012-03-04 14:48:21 +08:00
o = & sb . at ( 0 ) ;
2007-09-24 04:59:18 +08:00
w = str2wcs ( o ) ;
n = wcs2str ( w ) ;
2011-12-27 11:18:46 +08:00
2007-09-24 04:59:18 +08:00
if ( ! o | | ! w | | ! n )
{
2011-12-27 11:18:46 +08:00
err ( L " Line %d - Conversion cycle of string %s produced null pointer on %s " , __LINE__ , o , w ? L " str2wcs " : L " wcs2str " ) ;
2007-09-24 04:59:18 +08:00
}
2011-12-27 11:18:46 +08:00
2007-09-24 04:59:18 +08:00
if ( strcmp ( o , n ) )
{
2011-12-27 11:18:46 +08:00
err ( L " Line %d - %d: Conversion cycle of string %s produced different string %s " , __LINE__ , i , o , n ) ;
2007-09-24 04:59:18 +08:00
}
free ( w ) ;
2012-03-04 14:48:21 +08:00
free ( ( void * ) n ) ;
2011-12-27 11:18:46 +08:00
2007-09-24 04:59:18 +08:00
}
}
2005-10-24 23:26:25 +08:00
/**
Test the tokenizer
*/
2005-09-20 21:26:39 +08:00
static void test_tok ( )
{
tokenizer t ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
say ( L " Testing tokenizer " ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
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 " ) ;
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
say ( L " Testing use of broken tokenizer " ) ;
if ( ! tok_has_next ( & t ) )
{
err ( L " tok_has_next() should return 1 once on broken tokenizer " ) ;
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
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 ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
{
2011-12-27 11:18:46 +08:00
const wchar_t * str = L " string <redirection 2>&1 'nested \" quoted \" '(string containing subshells ){and,brackets}$as[$well (as variable arrays)] " ;
const int types [ ] =
2005-09-20 21:26:39 +08:00
{
TOK_STRING , TOK_REDIRECT_IN , TOK_STRING , TOK_REDIRECT_FD , TOK_STRING , TOK_STRING , TOK_END
}
;
2011-12-27 11:18:46 +08:00
size_t i ;
2005-09-20 21:26:39 +08:00
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: " ) ;
2011-12-27 11:18:46 +08:00
wprintf ( L " Token number %d of string \n '%ls' \n , expected token type %ls, got token '%ls' of type %ls \n " ,
2005-09-20 21:26:39 +08:00
i + 1 ,
str ,
tok_get_desc ( types [ i ] ) ,
tok_last ( & t ) ,
tok_get_desc ( tok_last_type ( & t ) ) ) ;
}
}
}
2012-03-01 09:55:50 +08:00
}
2005-09-20 21:26:39 +08:00
2012-03-01 09:55:50 +08:00
static int test_fork_helper ( void * unused ) {
size_t i ;
for ( i = 0 ; i < 100000 ; i + + ) {
delete [ ] ( new char [ 4 * 1024 * 1024 ] ) ;
}
return 0 ;
}
static void test_fork ( void ) {
return ; // Test is disabled until I can force it to fail
say ( L " Testing fork " ) ;
size_t i , max = 100 ;
for ( i = 0 ; i < 100 ; i + + ) {
printf ( " %lu / %lu \n " , i + 1 , max ) ;
/* Do something horrible to try to trigger an error */
# define THREAD_COUNT 8
# define FORK_COUNT 200
# define FORK_LOOP_COUNT 16
signal_block ( ) ;
for ( size_t i = 0 ; i < THREAD_COUNT ; i + + ) {
iothread_perform < void > ( test_fork_helper , NULL , NULL ) ;
}
for ( size_t q = 0 ; q < FORK_LOOP_COUNT ; q + + ) {
2012-03-07 16:54:01 +08:00
pid_t pids [ FORK_COUNT ] ;
2012-03-01 09:55:50 +08:00
for ( size_t i = 0 ; i < FORK_COUNT ; i + + ) {
pid_t pid = execute_fork ( false ) ;
if ( pid > 0 ) {
/* Parent */
pids [ i ] = pid ;
} else if ( pid = = 0 ) {
/* Child */
new char [ 4 * 1024 * 1024 ] ;
exit_without_destructors ( 0 ) ;
} else {
perror ( " fork " ) ;
}
}
for ( size_t i = 0 ; i < FORK_COUNT ; i + + ) {
int status = 0 ;
if ( pids [ i ] ! = waitpid ( pids [ i ] , & status , 0 ) ) {
perror ( " waitpid " ) ;
assert ( 0 ) ;
}
assert ( WIFEXITED ( status ) & & 0 = = WEXITSTATUS ( status ) ) ;
}
}
iothread_drain_all ( ) ;
signal_unblock ( ) ;
}
# undef FORK_COUNT
2005-09-20 21:26:39 +08:00
}
2005-10-24 23:26:25 +08:00
/**
Test the parser
*/
2005-09-20 21:26:39 +08:00
static void test_parser ( )
{
say ( L " Testing parser " ) ;
2011-12-27 11:18:46 +08:00
2012-05-08 03:55:13 +08:00
parser_t parser ( PARSER_TYPE_GENERAL , true ) ;
2012-01-28 03:43:45 +08:00
2005-09-20 21:26:39 +08:00
say ( L " Testing null input to parser " ) ;
2012-01-28 03:43:45 +08:00
if ( ! parser . test ( 0 , 0 , 0 , 0 ) )
2005-09-20 21:26:39 +08:00
{
2012-01-28 03:43:45 +08:00
err ( L " Null input to parser.test undetected " ) ;
2005-09-20 21:26:39 +08:00
}
say ( L " Testing block nesting " ) ;
2012-01-28 03:43:45 +08:00
if ( ! parser . test ( L " if; end " , 0 , 0 , 0 ) )
2005-09-20 21:26:39 +08:00
{
2011-12-27 11:18:46 +08:00
err ( L " Incomplete if statement undetected " ) ;
2005-09-20 21:26:39 +08:00
}
2012-01-28 03:43:45 +08:00
if ( ! parser . test ( L " if test; echo " , 0 , 0 , 0 ) )
2005-09-20 21:26:39 +08:00
{
2011-12-27 11:18:46 +08:00
err ( L " Missing end undetected " ) ;
2005-09-20 21:26:39 +08:00
}
2012-01-28 03:43:45 +08:00
if ( ! parser . test ( L " if test; end; end " , 0 , 0 , 0 ) )
2005-09-20 21:26:39 +08:00
{
2011-12-27 11:18:46 +08:00
err ( L " Unbalanced end undetected " ) ;
2005-09-20 21:26:39 +08:00
}
say ( L " Testing detection of invalid use of builtin commands " ) ;
2012-01-28 03:43:45 +08:00
if ( ! parser . test ( L " case foo " , 0 , 0 , 0 ) )
2005-09-20 21:26:39 +08:00
{
2011-12-27 11:18:46 +08:00
err ( L " 'case' command outside of block context undetected " ) ;
2005-09-20 21:26:39 +08:00
}
2012-01-28 03:43:45 +08:00
if ( ! parser . test ( L " switch ggg; if true; case foo;end;end " , 0 , 0 , 0 ) )
2005-09-20 21:26:39 +08:00
{
2011-12-27 11:18:46 +08:00
err ( L " 'case' command outside of switch block context undetected " ) ;
2005-09-20 21:26:39 +08:00
}
2012-01-28 03:43:45 +08:00
if ( ! parser . test ( L " else " , 0 , 0 , 0 ) )
2005-09-20 21:26:39 +08:00
{
2011-12-27 11:18:46 +08:00
err ( L " 'else' command outside of conditional block context undetected " ) ;
2005-09-20 21:26:39 +08:00
}
2012-01-28 03:43:45 +08:00
if ( ! parser . test ( L " break " , 0 , 0 , 0 ) )
2005-09-20 21:26:39 +08:00
{
2011-12-27 11:18:46 +08:00
err ( L " 'break' command outside of loop block context undetected " ) ;
2005-09-20 21:26:39 +08:00
}
2012-01-28 03:43:45 +08:00
if ( ! parser . test ( L " exec ls|less " , 0 , 0 , 0 ) | | ! parser . test ( L " echo|return " , 0 , 0 , 0 ) )
2005-09-20 21:26:39 +08:00
{
2011-12-27 11:18:46 +08:00
err ( L " Invalid pipe command undetected " ) ;
}
2005-09-20 21:26:39 +08:00
say ( L " Testing basic evaluation " ) ;
2012-01-28 03:43:45 +08:00
#if 0
/* This fails now since the parser takes a wcstring&, and NULL converts to wchar_t * converts to wcstring which crashes (thanks C++) */
if ( ! parser . eval ( 0 , 0 , TOP ) )
2005-09-20 21:26:39 +08:00
{
err ( L " Null input when evaluating undetected " ) ;
2012-01-28 03:43:45 +08:00
}
# endif
if ( ! parser . eval ( L " ls " , 0 , WHILE ) )
2005-09-20 21:26:39 +08:00
{
err ( L " Invalid block mode when evaluating undetected " ) ;
2012-01-28 03:43:45 +08:00
}
}
2012-02-06 14:48:43 +08:00
class lru_node_test_t : public lru_node_t {
2012-01-28 03:43:45 +08:00
public :
2012-02-06 14:48:43 +08:00
lru_node_test_t ( const wcstring & tmp ) : lru_node_t ( tmp ) { }
} ;
class test_lru_t : public lru_cache_t < lru_node_test_t > {
public :
test_lru_t ( ) : lru_cache_t < lru_node_test_t > ( 16 ) { }
2012-01-28 03:43:45 +08:00
2012-02-06 14:48:43 +08:00
std : : vector < lru_node_test_t * > evicted_nodes ;
2012-01-28 03:43:45 +08:00
2012-02-06 14:48:43 +08:00
virtual void node_was_evicted ( lru_node_test_t * node ) {
2012-01-28 03:43:45 +08:00
assert ( find ( evicted_nodes . begin ( ) , evicted_nodes . end ( ) , node ) = = evicted_nodes . end ( ) ) ;
evicted_nodes . push_back ( node ) ;
}
} ;
static void test_lru ( void ) {
say ( L " Testing LRU cache " ) ;
test_lru_t cache ;
2012-02-06 14:48:43 +08:00
std : : vector < lru_node_test_t * > expected_evicted ;
2012-01-28 03:43:45 +08:00
size_t total_nodes = 20 ;
for ( size_t i = 0 ; i < total_nodes ; i + + ) {
assert ( cache . size ( ) = = std : : min ( i , ( size_t ) 16 ) ) ;
2012-03-07 16:54:01 +08:00
lru_node_test_t * node = new lru_node_test_t ( to_string ( i ) ) ;
2012-01-28 03:43:45 +08:00
if ( i < 4 ) expected_evicted . push_back ( node ) ;
// Adding the node the first time should work, and subsequent times should fail
assert ( cache . add_node ( node ) ) ;
assert ( ! cache . add_node ( node ) ) ;
}
assert ( cache . evicted_nodes = = expected_evicted ) ;
cache . evict_all_nodes ( ) ;
assert ( cache . evicted_nodes . size ( ) = = total_nodes ) ;
while ( ! cache . evicted_nodes . empty ( ) ) {
lru_node_t * node = cache . evicted_nodes . back ( ) ;
cache . evicted_nodes . pop_back ( ) ;
delete node ;
}
2005-09-20 21:26:39 +08:00
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
/**
2005-12-07 23:57:17 +08:00
Perform parameter expansion and test if the output equals the zero - terminated parameter list supplied .
2005-09-20 21:26:39 +08:00
\ 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 , . . . )
{
2012-02-06 08:42:24 +08:00
std : : vector < completion_t > output ;
2005-09-20 21:26:39 +08:00
va_list va ;
2012-02-06 08:42:24 +08:00
size_t i = 0 ;
2005-09-20 21:26:39 +08:00
int res = 1 ;
wchar_t * arg ;
2011-12-27 11:18:46 +08:00
2012-02-06 08:42:24 +08:00
if ( expand_string ( in , output , flags ) )
2006-10-22 06:59:00 +08:00
{
2011-12-27 11:18:46 +08:00
2006-10-22 06:59:00 +08:00
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
va_start ( va , flags ) ;
2011-12-27 11:18:46 +08:00
while ( ( arg = va_arg ( va , wchar_t * ) ) ! = 0 )
2005-09-20 21:26:39 +08:00
{
2012-02-06 08:42:24 +08:00
if ( output . size ( ) = = i )
2005-09-20 21:26:39 +08:00
{
res = 0 ;
break ;
}
2011-12-27 11:18:46 +08:00
2012-02-06 08:42:24 +08:00
if ( output . at ( i ) . completion ! = arg )
2005-09-20 21:26:39 +08:00
{
res = 0 ;
break ;
}
2011-12-27 11:18:46 +08:00
i + + ;
2005-09-20 21:26:39 +08:00
}
va_end ( va ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
return res ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
}
2005-10-24 23:26:25 +08:00
/**
2005-12-07 23:57:17 +08:00
Test globbing and other parameter expansion
2005-10-24 23:26:25 +08:00
*/
2005-09-20 21:26:39 +08:00
static void test_expand ( )
{
2005-12-07 23:57:17 +08:00
say ( L " Testing parameter expansion " ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
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 ) )
{
2005-12-07 23:57:17 +08:00
err ( L " Bracket expansion is broken " ) ;
2005-09-20 21:26:39 +08:00
}
if ( ! expand_test ( L " a* " , EXPAND_SKIP_WILDCARDS , L " a* " , 0 ) )
{
2005-12-07 23:57:17 +08:00
err ( L " Cannot skip wildcard expansion " ) ;
2005-09-20 21:26:39 +08:00
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
}
2012-03-07 16:54:01 +08:00
/** Test path functions */
2007-05-11 03:11:28 +08:00
static void test_path ( )
{
say ( L " Testing path functions " ) ;
2012-02-08 13:23:12 +08:00
wcstring path = L " //foo//////bar/ " ;
wcstring canon = path ;
path_make_canonical ( canon ) ;
2012-02-13 10:05:59 +08:00
if ( canon ! = L " /foo/bar " )
2007-05-11 03:11:28 +08:00
{
err ( L " Bug in canonical PATH code " ) ;
2012-02-08 13:23:12 +08:00
}
2007-05-11 03:11:28 +08:00
}
2012-03-07 16:54:01 +08:00
/** Test the 'test' builtin */
int builtin_test ( parser_t & parser , wchar_t * * argv ) ;
static bool run_test_test ( int expected , wcstring_list_t & lst ) {
2012-05-08 03:55:13 +08:00
parser_t parser ( PARSER_TYPE_GENERAL , true ) ;
2012-03-07 16:54:01 +08:00
size_t i , count = lst . size ( ) ;
wchar_t * * argv = new wchar_t * [ count + 2 ] ;
argv [ 0 ] = ( wchar_t * ) L " test " ;
for ( i = 0 ; i < count ; i + + ) {
argv [ i + 1 ] = ( wchar_t * ) lst . at ( i ) . c_str ( ) ;
}
argv [ i + 1 ] = NULL ;
int result = builtin_test ( parser , argv ) ;
delete [ ] argv ;
return expected = = result ;
}
static bool run_test_test ( int expected , const wcstring & str ) {
using namespace std ;
wcstring_list_t lst ;
wistringstream iss ( str ) ;
copy ( istream_iterator < wcstring , wchar_t , std : : char_traits < wchar_t > > ( iss ) ,
istream_iterator < wstring , wchar_t , std : : char_traits < wchar_t > > ( ) ,
back_inserter < vector < wcstring > > ( lst ) ) ;
return run_test_test ( expected , lst ) ;
}
static void test_test ( ) {
say ( L " Testing test builtin " ) ;
assert ( run_test_test ( 0 , L " 5 -ne 6 " ) ) ;
assert ( run_test_test ( 0 , L " 5 -eq 5 " ) ) ;
assert ( run_test_test ( 0 , L " 0 -eq 0 " ) ) ;
assert ( run_test_test ( 0 , L " -1 -eq -1 " ) ) ;
assert ( run_test_test ( 0 , L " 1 -ne -1 " ) ) ;
assert ( run_test_test ( 1 , L " -1 -ne -1 " ) ) ;
assert ( run_test_test ( 0 , L " abc != def " ) ) ;
assert ( run_test_test ( 1 , L " abc = def " ) ) ;
assert ( run_test_test ( 0 , L " 5 -le 10 " ) ) ;
assert ( run_test_test ( 0 , L " 10 -le 10 " ) ) ;
assert ( run_test_test ( 1 , L " 20 -le 10 " ) ) ;
assert ( run_test_test ( 0 , L " -1 -le 0 " ) ) ;
assert ( run_test_test ( 1 , L " 0 -le -1 " ) ) ;
assert ( run_test_test ( 0 , L " 15 -ge 10 " ) ) ;
assert ( run_test_test ( 0 , L " 15 -ge 10 " ) ) ;
assert ( run_test_test ( 1 , L " ! 15 -ge 10 " ) ) ;
assert ( run_test_test ( 0 , L " ! ! 15 -ge 10 " ) ) ;
assert ( run_test_test ( 0 , L " 0 -ne 1 -a 0 -eq 0 " ) ) ;
assert ( run_test_test ( 0 , L " 0 -ne 1 -a -n 5 " ) ) ;
assert ( run_test_test ( 0 , L " -n 5 -a 10 -gt 5 " ) ) ;
assert ( run_test_test ( 0 , L " -n 3 -a -n 5 " ) ) ;
/* test precedence:
' 0 = = 0 | | 0 = = 1 & & 0 = = 2 '
should be evaluated as :
' 0 = = 0 | | ( 0 = = 1 & & 0 = = 2 ) '
and therefore true . If it were
' ( 0 = = 0 | | 0 = = 1 ) & & 0 = = 2 '
it would be false . */
assert ( run_test_test ( 0 , L " 0 = 0 -o 0 = 1 -a 0 = 2 " ) ) ;
assert ( run_test_test ( 0 , L " -n 5 -o 0 = 1 -a 0 = 2 " ) ) ;
2012-03-16 11:40:31 +08:00
assert ( run_test_test ( 1 , L " ( 0 = 0 -o 0 = 1 ) -a 0 = 2 " ) ) ;
assert ( run_test_test ( 0 , L " 0 = 0 -o ( 0 = 1 -a 0 = 2 ) " ) ) ;
2012-03-07 16:54:01 +08:00
/* A few lame tests for permissions; these need to be a lot more complete. */
assert ( run_test_test ( 0 , L " -e /bin/ls " ) ) ;
assert ( run_test_test ( 1 , L " -e /bin/ls_not_a_path " ) ) ;
assert ( run_test_test ( 0 , L " -x /bin/ls " ) ) ;
assert ( run_test_test ( 1 , L " -x /bin/ls_not_a_path " ) ) ;
assert ( run_test_test ( 0 , L " -d /bin/ " ) ) ;
assert ( run_test_test ( 1 , L " -d /bin/ls " ) ) ;
2012-03-07 17:13:24 +08:00
/* This failed at once point */
assert ( run_test_test ( 1 , L " -d /bin -a 5 -eq 3 " ) ) ;
assert ( run_test_test ( 0 , L " -d /bin -o 5 -eq 3 " ) ) ;
assert ( run_test_test ( 0 , L " -d /bin -a ! 5 -eq 3 " ) ) ;
2012-03-07 16:54:01 +08:00
}
2012-02-13 10:05:59 +08:00
/** Testing colors */
static void test_colors ( )
{
say ( L " Testing colors " ) ;
assert ( rgb_color_t ( L " #FF00A0 " ) . is_rgb ( ) ) ;
assert ( rgb_color_t ( L " FF00A0 " ) . is_rgb ( ) ) ;
assert ( rgb_color_t ( L " #F30 " ) . is_rgb ( ) ) ;
assert ( rgb_color_t ( L " F30 " ) . is_rgb ( ) ) ;
assert ( rgb_color_t ( L " f30 " ) . is_rgb ( ) ) ;
assert ( rgb_color_t ( L " #FF30a5 " ) . is_rgb ( ) ) ;
assert ( rgb_color_t ( L " 3f30 " ) . is_none ( ) ) ;
assert ( rgb_color_t ( L " ##f30 " ) . is_none ( ) ) ;
assert ( rgb_color_t ( L " magenta " ) . is_named ( ) ) ;
assert ( rgb_color_t ( L " MaGeNTa " ) . is_named ( ) ) ;
assert ( rgb_color_t ( L " mooganta " ) . is_none ( ) ) ;
}
2007-05-11 03:11:28 +08:00
2012-02-19 15:26:39 +08:00
/* Testing autosuggestion */
static void test_autosuggest ( ) {
2012-05-08 03:55:13 +08:00
bool autosuggest_special_validate_from_history ( const wcstring & str , const wcstring & working_directory , bool * outSuggestionOK ) ;
2012-02-19 15:26:39 +08:00
}
2007-05-11 03:11:28 +08:00
2005-10-24 23:26:25 +08:00
/**
Test speed of completion calculations
*/
2005-09-20 21:26:39 +08:00
void perf_complete ( )
{
wchar_t c ;
2012-02-06 08:42:24 +08:00
std : : vector < completion_t > out ;
2005-09-20 21:26:39 +08:00
long long t1 , t2 ;
int matches = 0 ;
double t ;
wchar_t str [ 3 ] =
{
2011-12-27 11:18:46 +08:00
0 , 0 , 0
2005-09-20 21:26:39 +08:00
}
;
int i ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
say ( L " Testing completion performance " ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
reader_push ( L " " ) ;
say ( L " Here we go " ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
t1 = get_time ( ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
for ( c = L ' a ' ; c < = L ' z ' ; c + + )
{
str [ 0 ] = c ;
reader_set_buffer ( str , 0 ) ;
2011-12-27 11:18:46 +08:00
2012-02-29 07:11:46 +08:00
complete ( str , out , COMPLETE_DEFAULT , NULL ) ;
2011-12-27 11:18:46 +08:00
2012-02-06 08:42:24 +08:00
matches + = out . size ( ) ;
out . clear ( ) ;
2005-09-20 21:26:39 +08:00
}
t2 = get_time ( ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
t = ( double ) ( t2 - t1 ) / ( 1000000 * 26 ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
say ( L " One letter command completion took %f seconds per completion, %f microseconds/match " , t , ( double ) ( t2 - t1 ) / matches ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
matches = 0 ;
t1 = get_time ( ) ;
for ( i = 0 ; i < LAPS ; i + + )
{
str [ 0 ] = ' a ' + ( rand ( ) % 26 ) ;
str [ 1 ] = ' a ' + ( rand ( ) % 26 ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
reader_set_buffer ( str , 0 ) ;
2011-12-27 11:18:46 +08:00
2012-02-29 07:11:46 +08:00
complete ( str , out , COMPLETE_DEFAULT , NULL ) ;
2011-12-27 11:18:46 +08:00
2012-02-06 08:42:24 +08:00
matches + = out . size ( ) ;
out . clear ( ) ;
2005-09-20 21:26:39 +08:00
}
t2 = get_time ( ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
t = ( double ) ( t2 - t1 ) / ( 1000000 * LAPS ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
say ( L " Two letter command completion took %f seconds per completion, %f microseconds/match " , t , ( double ) ( t2 - t1 ) / matches ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
reader_pop ( ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
}
2012-02-06 08:42:24 +08:00
static void test_history_matches ( history_search_t & search , size_t matches ) {
size_t i ;
for ( i = 0 ; i < matches ; i + + ) {
assert ( search . go_backwards ( ) ) ;
2012-02-16 16:24:27 +08:00
wcstring item = search . current_string ( ) ;
2012-02-06 08:42:24 +08:00
}
assert ( ! search . go_backwards ( ) ) ;
for ( i = 1 ; i < matches ; i + + ) {
assert ( search . go_forwards ( ) ) ;
}
assert ( ! search . go_forwards ( ) ) ;
}
2012-04-17 11:26:50 +08:00
static bool history_contains ( history_t * history , const wcstring & txt ) {
bool result = false ;
size_t i ;
for ( i = 1 ; ; i + + ) {
history_item_t item = history - > item_at_index ( i ) ;
if ( item . empty ( ) )
break ;
if ( item . str ( ) = = txt ) {
result = true ;
break ;
}
}
return result ;
}
2012-02-16 16:24:27 +08:00
class history_tests_t {
public :
static void test_history ( void ) ;
2012-04-17 11:26:50 +08:00
static void test_history_merge ( void ) ;
2012-02-16 16:24:27 +08:00
} ;
static wcstring random_string ( void ) {
wcstring result ;
size_t max = 1 + rand ( ) % 32 ;
while ( max - - ) {
wchar_t c = 1 + rand ( ) % ESCAPE_TEST_CHAR ;
result . push_back ( c ) ;
}
return result ;
}
void history_tests_t : : test_history ( void ) {
2012-02-06 08:42:24 +08:00
say ( L " Testing history " ) ;
history_t & history = history_t : : history_with_name ( L " test_history " ) ;
2012-02-16 16:24:27 +08:00
history . clear ( ) ;
2012-02-06 08:42:24 +08:00
history . add ( L " Gamma " ) ;
2012-02-06 14:48:43 +08:00
history . add ( L " Beta " ) ;
history . add ( L " Alpha " ) ;
2012-02-06 08:42:24 +08:00
/* All three items match "a" */
history_search_t search1 ( history , L " a " ) ;
test_history_matches ( search1 , 3 ) ;
2012-02-16 16:24:27 +08:00
assert ( search1 . current_string ( ) = = L " Alpha " ) ;
2012-02-06 08:42:24 +08:00
/* One item matches "et" */
history_search_t search2 ( history , L " et " ) ;
test_history_matches ( search2 , 1 ) ;
2012-02-16 16:24:27 +08:00
assert ( search2 . current_string ( ) = = L " Beta " ) ;
/* Test history escaping and unescaping, yaml, etc. */
std : : vector < history_item_t > before , after ;
history . clear ( ) ;
size_t i , max = 100 ;
for ( i = 1 ; i < = max ; i + + ) {
/* Generate a value */
2012-03-07 16:54:01 +08:00
wcstring value = wcstring ( L " test item " ) + to_string ( i ) ;
2012-02-24 02:29:00 +08:00
/* Maybe add some backslashes */
if ( i % 3 = = 0 )
value . append ( L " (slashies \\ \\ \\ slashies) " ) ;
2012-02-16 16:24:27 +08:00
/* Generate some paths */
path_list_t paths ;
size_t count = rand ( ) % 6 ;
while ( count - - ) {
paths . push_front ( random_string ( ) ) ;
}
/* Record this item */
history_item_t item ( value , time ( NULL ) , paths ) ;
before . push_back ( item ) ;
history . add ( item ) ;
}
history . save ( ) ;
/* Read items back in reverse order and ensure they're the same */
for ( i = 100 ; i > = 1 ; i - - ) {
history_item_t item = history . item_at_index ( i ) ;
assert ( ! item . empty ( ) ) ;
after . push_back ( item ) ;
}
assert ( before . size ( ) = = after . size ( ) ) ;
for ( size_t i = 0 ; i < before . size ( ) ; i + + ) {
const history_item_t & bef = before . at ( i ) , & aft = after . at ( i ) ;
assert ( bef . contents = = aft . contents ) ;
assert ( bef . creation_timestamp = = aft . creation_timestamp ) ;
assert ( bef . required_paths = = aft . required_paths ) ;
}
2012-04-17 11:26:50 +08:00
/* Clean up after our tests */
history . clear ( ) ;
}
// wait until the next second
static void time_barrier ( void ) {
time_t start = time ( NULL ) ;
do {
usleep ( 1000 ) ;
} while ( time ( NULL ) = = start ) ;
}
void history_tests_t : : test_history_merge ( void ) {
// In a single fish process, only one history is allowed to exist with the given name
// But it's common to have multiple history instances with the same name active in different processes,
// e.g. when you have multiple shells open.
// We try to get that right and merge all their history together. Test that case.
say ( L " Testing history merge " ) ;
const size_t count = 3 ;
const wcstring name = L " merge_test " ;
history_t * hists [ count ] = { new history_t ( name ) , new history_t ( name ) , new history_t ( name ) } ;
wcstring texts [ count ] = { L " History 1 " , L " History 2 " , L " History 3 " } ;
/* Make sure history is clear */
for ( size_t i = 0 ; i < count ; i + + ) {
hists [ i ] - > clear ( ) ;
}
/* Make sure we don't add an item in the same second as we created the history */
time_barrier ( ) ;
/* Add a different item to each */
for ( size_t i = 0 ; i < count ; i + + ) {
hists [ i ] - > add ( texts [ i ] ) ;
}
/* Save them */
for ( size_t i = 0 ; i < count ; i + + ) {
hists [ i ] - > save ( ) ;
}
/* Make sure each history contains what it ought to, but they have not leaked into each other */
for ( size_t i = 0 ; i < count ; i + + ) {
for ( size_t j = 0 ; j < count ; j + + ) {
bool does_contain = history_contains ( hists [ i ] , texts [ j ] ) ;
bool should_contain = ( i = = j ) ;
assert ( should_contain = = does_contain ) ;
}
}
/* Make a new history. It should contain everything. The time_barrier() is so that the timestamp is newer, since we only pick up items whose timestamp is before the birth stamp. */
time_barrier ( ) ;
history_t * everything = new history_t ( name ) ;
for ( size_t i = 0 ; i < count ; i + + ) {
assert ( history_contains ( everything , texts [ i ] ) ) ;
}
/* Clean up */
for ( size_t i = 0 ; i < 3 ; i + + ) {
delete hists [ i ] ;
}
everything - > clear ( ) ;
delete everything ; //not as scary as it looks
2012-02-06 08:42:24 +08:00
}
2007-05-11 03:11:28 +08:00
2005-10-24 23:26:25 +08:00
/**
2011-12-27 11:18:46 +08:00
Main test
2005-10-24 23:26:25 +08:00
*/
2005-09-20 21:26:39 +08:00
int main ( int argc , char * * argv )
{
2007-09-24 04:59:18 +08:00
setlocale ( LC_ALL , " " ) ;
srand ( time ( 0 ) ) ;
2005-09-20 21:26:39 +08:00
2005-10-12 18:44:37 +08:00
program_name = L " (ignore) " ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
say ( L " Testing low-level functionality " ) ;
2005-10-12 18:44:37 +08:00
say ( L " Lines beginning with '(ignore):' are not errors, they are warning messages \n generated by the fish parser library when given broken input, and can be \n ignored. All actual errors begin with 'Error:'. " ) ;
2012-01-06 05:58:48 +08:00
set_main_thread ( ) ;
2012-03-07 16:54:01 +08:00
setup_fork_guards ( ) ;
2011-12-27 11:18:46 +08:00
proc_init ( ) ;
event_init ( ) ;
2005-09-20 21:26:39 +08:00
function_init ( ) ;
builtin_init ( ) ;
reader_init ( ) ;
2005-10-03 21:09:37 +08:00
env_init ( ) ;
2012-03-07 16:54:01 +08:00
2012-03-01 03:27:14 +08:00
test_format ( ) ;
2007-09-24 04:59:18 +08:00
test_escape ( ) ;
test_convert ( ) ;
2005-09-20 21:26:39 +08:00
test_tok ( ) ;
2012-03-01 09:55:50 +08:00
test_fork ( ) ;
2005-09-20 21:26:39 +08:00
test_parser ( ) ;
2012-01-28 03:43:45 +08:00
test_lru ( ) ;
2005-09-20 21:26:39 +08:00
test_expand ( ) ;
2012-03-07 16:54:01 +08:00
test_test ( ) ;
2007-05-11 03:11:28 +08:00
test_path ( ) ;
2012-02-13 10:05:59 +08:00
test_colors ( ) ;
2012-02-19 15:26:39 +08:00
test_autosuggest ( ) ;
2012-02-16 16:24:27 +08:00
history_tests_t : : test_history ( ) ;
2012-04-17 11:26:50 +08:00
history_tests_t : : test_history_merge ( ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
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();
2011-12-27 11:18:46 +08:00
2005-10-03 21:09:37 +08:00
env_destroy ( ) ;
2011-12-27 11:18:46 +08:00
reader_destroy ( ) ;
2005-09-20 21:26:39 +08:00
builtin_destroy ( ) ;
wutil_destroy ( ) ;
2005-10-06 06:37:08 +08:00
event_destroy ( ) ;
2005-10-14 19:40:33 +08:00
proc_destroy ( ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
}