X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=blobdiff_plain;f=test%2Fsymtab_test.c;h=9beae8d58e320ebf3d2303786a66cc8e41563a3b;hb=11838bcf76f3614384459cb56e2ce80dea788cef;hp=b755e9c255921e0f66c6916a26cfbd12a3b2008e;hpb=412d53dfa2f9b5b56a448797d0dfec3b0f11f666;p=ric-plt%2Flib%2Frmr.git diff --git a/test/symtab_test.c b/test/symtab_test.c index b755e9c..9beae8d 100644 --- a/test/symtab_test.c +++ b/test/symtab_test.c @@ -1,7 +1,7 @@ /* ================================================================================== - Copyright (c) 2019 Nokia - Copyright (c) 2018-2019 AT&T Intellectual Property. + Copyright (c) 2019-2021 Nokia + Copyright (c) 2018-2021 AT&T Intellectual Property. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -24,56 +24,167 @@ the symbol table portion of RMr. Run with: ksh unit_test.ksh symtab_test.c Date: 1 April 2019 - Author: E. Scott Daniels + Author: E. Scott Daniels */ +#include + #define NO_DUMMY_RMR 1 // no dummy rmr functions; we don't pull in rmr.h or agnostic.h +#define NO_EMULATION +#define NO_PRIVATE_HEADERS +#include +#include +#include "test_support.c" #include "rmr_symtab.h" -#include "symtab.c" -#include "test_support.c" +#include "symtab.c" // module under test + +int terrors = 0; // thread errors int state = GOOD; // overall pass/fail state 0==fail int counter; // global counter for for-each tests -static void fetch( void* st, char* key, int class, int expected ) { +static int fetch( void* st, char* key, int class, int expected ) { char* val; + int error = 0; val = rmr_sym_get( st, key, class ); if( val ) { fprintf( stderr, "[%s] get returns key=%s val=%s\n", !expected ? "FAIL" : "OK", key, val ); if( !expected ) { state = BAD; + error = 1; } } else { fprintf( stderr, "[%s] string key fetch return nil\n", expected ? "FAIL" : "OK" ); if( expected ) { state = BAD; + error = 1; } } + + return error; } -static void nfetch( void* st, int key, int expected ) { +static int nfetch( void* st, int key, int expected ) { char* val; + int error = 0; val = rmr_sym_pull( st, key ); if( val ) { fprintf( stderr, "[%s] get returns key=%d val=%s\n", !expected ? "FAIL" : "OK", key, val ); if( !expected ) { state = BAD; + error = 1; } } else { fprintf( stderr, "[%s] get return nil for key=%d\n", expected ? "FAIL" : "OK", key ); if( expected ) { state = BAD; + error = 1; + } + } + + return error; +} + +// ----------------- thread based tests ------------------------------------------------------------------- +#define NUM_KEYS 512 // number of unique keys +#define NUM_ATTEMPTS 1000000 + +/* + This is started in a thread and will attempt 10,000 reads on the symtable + in an attempt to ensure that there are no concurrent read/write issues. +*/ +static void* reader( void* st ) { + char key[1024]; + int i; + int ncount = 0; // number not found + int fcount = 0; // number found + + for( i = 0; i < NUM_ATTEMPTS; i++ ) { + snprintf( key, sizeof( key ), "key_%d", i % NUM_KEYS ); + if( rmr_sym_get( st, key, 1 ) == NULL ) { + ncount++; + } else { + fcount++; + } + } + + fprintf( stderr, " reader finished: n=%d f=%d\n", ncount, fcount ); // there is no right answer + return NULL; +} + +/* + This is started in a thread and will attempt 10,000 writes on the symtable + in an attempt to ensure that there are no concurrent read/write issues. Keys are + written as key_n where n is an integer between 0 and 999 inclusive. +*/ +static void* writer( void* st ) { + char key[1024]; + int i; + int ncount = 0; // number first inserts + int rcount = 0; // number replacements + char* value = NULL; + int num_keys = 256; + + fprintf( stderr, " writer now turning\n" ); + for( i = 0; i < NUM_ATTEMPTS; i++ ) { + value++; + snprintf( key, sizeof( key ), "key_%d", i % NUM_KEYS ); + rmr_sym_del( st, key, 1 ); + if( rmr_sym_put( st, key, 1, value ) ) { + ncount++; + } else { + rcount++; } } + + if( ncount != NUM_ATTEMPTS ) { + fprintf( stderr, " writer finished: n=%d r=%d\n", ncount, rcount ); // there is no right answer + terrors++; + } else { + fprintf( stderr, " writer finished: n=%d r=%d\n", ncount, rcount ); // there is no right answer + } + + return NULL; +} + +/* + Drive a concurrent read/write test to ensure no race issues. +*/ +static int thread_test( ) { + pthread_t tids[10]; + int n2start = 3; + int i; + void* st; + + st = rmr_sym_alloc( 128 ); // should force collisions + + fprintf( stderr, " starting writer\n" ); + pthread_create( &tids[0], NULL, writer, st ); + + for( i = 1; i <= n2start; i++ ) { + fprintf( stderr, " starting reader %d\n", i ); + pthread_create( &tids[i], NULL, reader, st ); + } + + fprintf( stderr, " thread controller is waiting\n" ); + for( i = 0; i <= n2start; i++ ) { + pthread_join( tids[i], NULL ); // status is unimportant, just hold until all are done + fprintf( stderr, " thread %d has reported complete\n", i ); + } + + + rmr_sym_stats( st, 1 ); + return terrors; } +// --------------------------------------------------------------------------------------------------------- /* Driven by foreach class -- just incr the counter. @@ -91,25 +202,26 @@ int main( ) { int class = 1; int s; void* p; + int errors = 0; st = rmr_sym_alloc( 10 ); // alloc with small value to force adjustment inside - fail_if_nil( st, "symtab pointer" ); + errors += fail_if_nil( st, "symtab pointer" ); s = rmr_sym_put( st, foo, class, bar ); // add entry with string key; returns 1 if it was inserted - fail_if_false( s, "insert foo existed" ); + errors += fail_if_false( s, "insert foo existed" ); s = rmr_sym_put( st, foo, class+1, bar ); // add to table with a different class - fail_if_false( s, "insert foo existed" ); + errors += fail_if_false( s, "insert foo existed" ); s = rmr_sym_put( st, foo, class, bar ); // inserted above, should return not inserted (0) - fail_if_true( s, "insert foo existed" ); + errors += fail_if_true( s, "insert foo existed" ); - fetch( st, foo, class, 1 ); - fetch( st, goo, class, 0 ); // fetch non existant + errors += fetch( st, foo, class, 1 ); + errors += fetch( st, goo, class, 0 ); // fetch non existant rmr_sym_stats( st, 4 ); // early stats at verbose level 4 so chatter is minimised rmr_sym_dump( st ); - for( i = 2000; i < 3000; i++ ) { // bunch of dummy things to force chains in the table + for( i = 2000; i < 3000; i++ ) { // bunch of dummy things to force chains in the table rmr_sym_map( st, i, foo ); // add entry with unsigned integer key } rmr_sym_stats( st, 0 ); // just the small facts to verify the 1000 we stuffed in @@ -117,17 +229,16 @@ int main( ) { rmr_sym_ndel( st, 12001 ); // delete numeric key not there s = rmr_sym_map( st, 1234, foo ); // add known entries with unsigned integer key - fail_if_false( s, "numeric add of key 1234 should not have existed" ); + errors += fail_if_false( s, "numeric add of key 1234 should not have existed" ); s = rmr_sym_map( st, 2345, bar ); fail_if_true( s, "numeric add of key 2345 should have existed" ); counter = 0; rmr_sym_foreach_class( st, 0, each_counter, NULL ); - fail_if_false( counter, "expected counter after foreach to be non-zero" ); - - nfetch( st, 1234, 1 ); - nfetch( st, 2345, 1 ); + errors += fail_if_false( counter, "expected counter after foreach to be non-zero" ); + errors += nfetch( st, 1234, 1 ); + errors += nfetch( st, 2345, 1 ); rmr_sym_del( st, foo, 0 ); @@ -136,6 +247,16 @@ int main( ) { rmr_sym_free( NULL ); // ensure it doesn't barf when given a nil pointer rmr_sym_free( st ); - return state; + errors += thread_test(); // test as best we can for race issues + + test_summary( errors, "symtab tests" ); + if( state + errors == 0 ) { + fprintf( stderr, " all symtab tests were OK\n\n" ); + } else { + fprintf( stderr, " %d errors in symtab code\n\n", errors ); + } + + + return !!(state + errors); }