@@ -2,6 +2,7 @@
* @file test_interpreter.c
* Unit tests for pure string-handling functions in src/interpreter.c:
* is_number, is_abbrev, delete_doubledollar, any_one_arg, one_word
* and for the alias expansion path perform_complex_alias() (via perform_alias()).
*/
# include "unity.h"
@@ -10,6 +11,7 @@
# include "sysdep.h"
# include "structs.h"
# include "utils.h"
# include "comm.h"
# include "interpreter.h"
extern FILE * logfile ;
@@ -17,6 +19,28 @@ extern FILE *logfile;
void setUp ( void ) { logfile = stderr ; }
void tearDown ( void ) { logfile = NULL ; }
/* =========================================================
* write_to_q — real implementation for alias tests.
* Overrides the __attribute__((weak)) stub in test_stubs.c so that
* the queue is actually populated and we can inspect its contents.
* ========================================================= */
void write_to_q ( const char * txt , struct txt_q * queue , int aliased )
{
struct txt_block * newt ;
CREATE ( newt , struct txt_block , 1 ) ;
newt - > text = strdup ( txt ) ;
newt - > aliased = aliased ;
if ( ! queue - > head ) {
newt - > next = NULL ;
queue - > head = queue - > tail = newt ;
} else {
queue - > tail - > next = newt ;
queue - > tail = newt ;
newt - > next = NULL ;
}
}
/* =========================================================
* is_number
* ========================================================= */
@@ -207,6 +231,219 @@ void test_one_word_leading_spaces_skipped(void)
TEST_ASSERT_EQUAL_STRING ( " hello " , first ) ;
}
/* =========================================================
* Helpers for perform_complex_alias tests
* ========================================================= */
/* Release every txt_block in a queue and reset head/tail to NULL. */
static void free_txt_q ( struct txt_q * q )
{
struct txt_block * b = q - > head , * n ;
while ( b ) { n = b - > next ; free ( b - > text ) ; free ( b ) ; b = n ; }
q - > head = q - > tail = NULL ;
}
/* Build a heap-allocated alias_data with type ALIAS_COMPLEX. */
static struct alias_data * make_test_alias ( const char * name , const char * repl )
{
struct alias_data * a ;
CREATE ( a , struct alias_data , 1 ) ;
a - > alias = strdup ( name ) ;
a - > replacement = strdup ( repl ) ;
a - > type = ALIAS_COMPLEX ;
a - > next = NULL ;
return a ;
}
/* Free a single alias_data allocated by make_test_alias(). */
static void destroy_test_alias ( struct alias_data * a )
{
free ( a - > alias ) ;
free ( a - > replacement ) ;
free ( a ) ;
}
/* Zero-initialise all three objects and wire them together so that
* IS_NPC() returns false and GET_ALIASES() returns a. */
static void alias_env_init ( struct descriptor_data * d ,
struct char_data * ch ,
struct player_special_data * psd ,
struct alias_data * a )
{
memset ( d , 0 , sizeof ( * d ) ) ;
memset ( ch , 0 , sizeof ( * ch ) ) ;
memset ( psd , 0 , sizeof ( * psd ) ) ;
ch - > player_specials = psd ;
psd - > aliases = a ;
d - > character = ch ;
}
/* =========================================================
* perform_complex_alias — tested via the public perform_alias()
* ========================================================= */
/* Literal replacement with no variable tokens. */
void test_pca_literal ( void )
{
struct alias_data * a = make_test_alias ( " lit " , " hello world " ) ;
struct descriptor_data d ;
struct char_data ch ;
struct player_special_data psd ;
char orig [ ] = " lit " ;
alias_env_init ( & d , & ch , & psd , a ) ;
perform_alias ( & d , orig , sizeof ( orig ) ) ;
TEST_ASSERT_NOT_NULL ( d . input . head ) ;
TEST_ASSERT_EQUAL_STRING ( " hello world " , d . input . head - > text ) ;
TEST_ASSERT_NULL ( d . input . head - > next ) ;
free_txt_q ( & d . input ) ;
destroy_test_alias ( a ) ;
}
/* $* glob expands to the full argument string after the alias trigger. */
void test_pca_glob_expansion ( void )
{
struct alias_data * a = make_test_alias ( " say " , " say $* " ) ;
struct descriptor_data d ;
struct char_data ch ;
struct player_special_data psd ;
char orig [ ] = " say hello there " ;
alias_env_init ( & d , & ch , & psd , a ) ;
perform_alias ( & d , orig , sizeof ( orig ) ) ;
TEST_ASSERT_NOT_NULL ( d . input . head ) ;
TEST_ASSERT_EQUAL_STRING ( " say hello there " , d . input . head - > text ) ;
free_txt_q ( & d . input ) ;
destroy_test_alias ( a ) ;
}
/* $1 expands to the first whitespace-delimited token of the arguments. */
void test_pca_token1_expansion ( void )
{
struct alias_data * a = make_test_alias ( " s1 " , " say $1 " ) ;
struct descriptor_data d ;
struct char_data ch ;
struct player_special_data psd ;
char orig [ ] = " s1 hello world " ;
alias_env_init ( & d , & ch , & psd , a ) ;
perform_alias ( & d , orig , sizeof ( orig ) ) ;
TEST_ASSERT_NOT_NULL ( d . input . head ) ;
TEST_ASSERT_EQUAL_STRING ( " say hello " , d . input . head - > text ) ;
free_txt_q ( & d . input ) ;
destroy_test_alias ( a ) ;
}
/* Semicolon separator (;) produces two independent queue entries. */
void test_pca_separator ( void )
{
struct alias_data * a = make_test_alias ( " seq " , " go north;go east " ) ;
struct descriptor_data d ;
struct char_data ch ;
struct player_special_data psd ;
char orig [ ] = " seq " ;
alias_env_init ( & d , & ch , & psd , a ) ;
perform_alias ( & d , orig , sizeof ( orig ) ) ;
TEST_ASSERT_NOT_NULL ( d . input . head ) ;
TEST_ASSERT_EQUAL_STRING ( " go north " , d . input . head - > text ) ;
TEST_ASSERT_NOT_NULL ( d . input . head - > next ) ;
TEST_ASSERT_EQUAL_STRING ( " go east " , d . input . head - > next - > text ) ;
free_txt_q ( & d . input ) ;
destroy_test_alias ( a ) ;
}
/* $$ in the replacement is preserved as $$ in the output (act-safety doubling). */
void test_pca_dollar_dollar ( void )
{
struct alias_data * a = make_test_alias ( " dol " , " cost $$5 " ) ;
struct descriptor_data d ;
struct char_data ch ;
struct player_special_data psd ;
char orig [ ] = " dol " ;
alias_env_init ( & d , & ch , & psd , a ) ;
perform_alias ( & d , orig , sizeof ( orig ) ) ;
TEST_ASSERT_NOT_NULL ( d . input . head ) ;
TEST_ASSERT_EQUAL_STRING ( " cost $$5 " , d . input . head - > text ) ;
free_txt_q ( & d . input ) ;
destroy_test_alias ( a ) ;
}
/* Overflow via $*: 255 "$*" tokens × 50-char argument exceeds
* MAX_RAW_INPUT_LENGTH. The queue must be empty after the call. */
void test_pca_overflow_glob ( void )
{
/* 255 × "$*" = 510 bytes + NUL */
char repl [ 511 ] ;
char orig [ 64 ] ;
struct alias_data * a ;
struct descriptor_data d ;
struct char_data ch ;
struct player_special_data psd ;
int i ;
for ( i = 0 ; i < 255 ; i + + ) { repl [ i * 2 ] = ' $ ' ; repl [ i * 2 + 1 ] = ' * ' ; }
repl [ 510 ] = ' \0 ' ;
/* "al " + 50 'x' chars */
memcpy ( orig , " al " , 3 ) ;
memset ( orig + 3 , ' x ' , 50 ) ;
orig [ 53 ] = ' \0 ' ;
a = make_test_alias ( " al " , repl ) ;
alias_env_init ( & d , & ch , & psd , a ) ;
perform_alias ( & d , orig , sizeof ( orig ) ) ;
/* No partial results must leak into the queue on overflow. */
TEST_ASSERT_NULL ( d . input . head ) ;
free_txt_q ( & d . input ) ;
destroy_test_alias ( a ) ;
}
/* Overflow via $1: 255 "$1" tokens × 50-char first token exceeds
* MAX_RAW_INPUT_LENGTH. The queue must be empty after the call. */
void test_pca_overflow_token ( void )
{
/* 255 × "$1" = 510 bytes + NUL */
char repl [ 511 ] ;
char orig [ 64 ] ;
struct alias_data * a ;
struct descriptor_data d ;
struct char_data ch ;
struct player_special_data psd ;
int i ;
for ( i = 0 ; i < 255 ; i + + ) { repl [ i * 2 ] = ' $ ' ; repl [ i * 2 + 1 ] = ' 1 ' ; }
repl [ 510 ] = ' \0 ' ;
/* "al " + 50 'y' chars */
memcpy ( orig , " al " , 3 ) ;
memset ( orig + 3 , ' y ' , 50 ) ;
orig [ 53 ] = ' \0 ' ;
a = make_test_alias ( " al " , repl ) ;
alias_env_init ( & d , & ch , & psd , a ) ;
perform_alias ( & d , orig , sizeof ( orig ) ) ;
/* No partial results must leak into the queue on overflow. */
TEST_ASSERT_NULL ( d . input . head ) ;
free_txt_q ( & d . input ) ;
destroy_test_alias ( a ) ;
}
/* =========================================================
* main
* ========================================================= */
@@ -252,5 +489,14 @@ int main(void)
RUN_TEST ( test_one_word_empty_quoted ) ;
RUN_TEST ( test_one_word_leading_spaces_skipped ) ;
/* perform_complex_alias */
RUN_TEST ( test_pca_literal ) ;
RUN_TEST ( test_pca_glob_expansion ) ;
RUN_TEST ( test_pca_token1_expansion ) ;
RUN_TEST ( test_pca_separator ) ;
RUN_TEST ( test_pca_dollar_dollar ) ;
RUN_TEST ( test_pca_overflow_glob ) ;
RUN_TEST ( test_pca_overflow_token ) ;
return UNITY_END ( ) ;
}