// :vi sw=4 ts=4 noet:
/*
==================================================================================
- Copyright (c) 2020 Nokia
- Copyright (c) 2020 AT&T Intellectual Property.
+ Copyright (c) 2020-2021 Nokia
+ Copyright (c) 2020-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.
#include <netdb.h>
#include <errno.h>
#include <string.h>
-#include <errno.h>
#include <pthread.h>
#include <ctype.h>
-
-#include <netdb.h> // these four needed for si address tests
-#include <stdio.h>
-#include <ctype.h>
-#include <netinet/in.h>
-
-
-
#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <strings.h>
-#include <errno.h>
-#include <string.h>
#include <stdint.h>
-#include <ctype.h>
#include <sys/epoll.h>
-#include <pthread.h>
#include <semaphore.h>
+
+#include <netdb.h> // these four needed for si address tests
+#include <stdio.h>
+#include <ctype.h>
+#include <netinet/in.h>
+
#define DEBUG 1
// specific test tools in this directory
#include "test_support.c" // things like fail_if()
#include "test_transport_em.c" // system/transport emulation (open, close, connect, etc)
-/*
-#include "rmr.h" // things the users see
-#include "rmr_symtab.h"
-#include "rmr_agnostic.h" // transport agnostic header
-*/
#include <rmr_logging.h>
#include <logging.c>
+
+// ------------- dummy functions to force edge cases when we can ---------------------------------------
+
+#define SYSTEM_UNDER_TEST 1 // for conditional code
+
+/*
+ These are global so they can be reset for individual tests.
+*/
+static int good_mallocs = 0; // number of initial good malocs before failurs
+static int bad_mallocs = 1; // number of failed mallocs (consecutive)
+
+static void* test_malloc( size_t n ) {
+
+ fprintf( stderr, ">>>> test malloc: %d %d\n", good_mallocs, bad_mallocs );
+ if( good_mallocs ) {
+ good_mallocs--;
+ return malloc( n );
+ }
+
+ if( bad_mallocs ) {
+ bad_mallocs--;
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ return malloc( n );
+}
+
+// -----------------------------------------------------------------------------------------------------
+
#include <si95/siaddress.c>
//#include <si95/sialloc.c>
#include <si95/sibldpoll.c>
#include <si95/silisten.c>
#include <si95/sinew.c>
#include <si95/sinewses.c>
-//#include <si95/sipoll.c>
+#include <si95/sipoll.c>
//#include <si95/sircv.c>
-//#include <si95/sisend.c>
+#include <si95/sisend.c>
#include <si95/sisendt.c>
#include <si95/sishutdown.c>
#include <si95/siterm.c>
#include <si95/sitrash.c>
-//#include <si95/siwait.c>
+#define malloc test_malloc
+#include <si95/siwait.c>
+#undef malloc
// ---------------------------------------------------------------------
ptr = SInew( GI_BLK );
errors += fail_if_nil( ptr, "memory: sinew returned nil when given giblk request" );
SItrash( GI_BLK, ptr ); // GI block cannot be trashed, ensure this (valgind will complain about a leak)
+ free( ptr ); // we can free GI block only in tests
fprintf( stderr, "<INFO> memory module finished with %d errors\n", errors );
return errors;
SItp_stats( si_ctx ); // drive for coverage only
SItp_stats( NULL );
- SIconnect( si_ctx, "localhost:43086" ); // ensure context has a tp block to free on shutdown
+ SIconnect( si_ctx, "127.0.0.1:43086" ); // ensure context has a tp block to free on shutdown
SIshutdown( NULL );
SIabort( si_ctx );
+ // cleaning up the remaining global resources
+ struct ginfo_blk *gptr = (struct ginfo_blk*)si_ctx;
+ SItrash( TP_BLK, gptr->tplist );
+ free( gptr->tp_map );
+ free( gptr->rbuf );
+ free( gptr->cbtab );
+ free( si_ctx );
fprintf( stderr, "<INFO> cleanup module finished with %d errors\n", errors );
return errors;
static int addr() {
int errors = 0;
int l;
- struct sockaddr* addr;
- char buf1[4096];
- char buf2[4096];
- char* dest;
+ char buf1[4096]; // space to build buffers for xlation
+ char* hr_addr; // human readable address returned
+ void* net_addr; // a network address block of some type
- addr = (struct sockaddr *) malloc( sizeof( struct sockaddr ) );
/*
+ struct sockaddr* addr;
+ addr = (struct sockaddr *) malloc( sizeof( struct sockaddr ) );
+
l = SIgenaddr( " [ff02::4]:4567", PF_INET6, IPPROTO_TCP, SOCK_STREAM, &addr );
SIgenaddr( " [ff02::4]:4567", PF_INET6, IPPROTO_TCP, SOCK_STREAM, &addr );
*/
- dest = NULL;
+ l = SIaddress( NULL, NULL, 0 );
+ errors += fail_if_true( l != 0, "SIaddress given two null pointers didn't return 0 len" );
+ l = SIaddress( buf1, NULL, 0 );
+ errors += fail_if_true( l != 0, "SIaddress given null dest pointer didn't return 0 len" );
+ l = SIaddress( NULL, (void *) &buf1, 0 );
+ errors += fail_if_true( l != 0, "SIaddress given null src pointer didn't return 0 len" );
+
+ net_addr = NULL;
snprintf( buf1, sizeof( buf1 ), " [ff02::5:4001" ); // invalid address, drive leading space eater too
- l = SIaddress( buf1, (void **) &dest, AC_TOADDR6 );
+ l = SIaddress( buf1, (void **) &net_addr, AC_TOADDR6 );
errors += fail_if_true( l > 0, "to addr6 with bad addr convdersion returned valid len" );
+ free( net_addr );
snprintf( buf1, sizeof( buf1 ), "[ff02::5]:4002" ); // v6 might not be supported so failure is OK here; driving for coverage
- l=SIaddress( buf1, (void **) &dest, AC_TOADDR6 );
+ l = SIaddress( buf1, &net_addr, AC_TOADDR6 );
+ if( l > 0 ) {
+ l = SIaddress( net_addr, (void *) &hr_addr, AC_TODOT ); // convert the address back to hr string
+ errors += fail_if_true( l < 1, "v6 to dot conversion failed" );
+ errors += fail_if_nil( hr_addr, "v6 to dot conversion yields a nil pointer" );
+ free( net_addr );
+ free( hr_addr );
+ }
snprintf( buf1, sizeof( buf1 ), "localhost:43086" );
- l = SIaddress( buf1, (void **) &dest, AC_TOADDR );
- errors += fail_if_true( l < 1, "to addr convdersion failed" );
+ l = SIaddress( buf1, (void **) &net_addr, AC_TOADDR );
+ errors += fail_if_true( l < 1, "v4 to addr conversion failed" );
- snprintf( buf1, sizeof( buf1 ), "localhost:4004" );
- l = SIaddress( buf1, (void **) &dest, AC_TODOT );
+ l = SIaddress( net_addr, (void *) &hr_addr, AC_TODOT ); // convert the address back to hr string
errors += fail_if_true( l < 1, "to dot convdersion failed" );
+ errors += fail_if_nil( hr_addr, "v4 to dot conversion yields a nil pointer" );
+ free( net_addr );
+ free( hr_addr );
fprintf( stderr, "<INFO> addr module finished with %d errors\n", errors );
return errors;
}
+/*
+ Prep related tests. These mostly drive cases that aren't driven by "normal"
+ connect, send, receive tests (e.g. UDP branches).
+*/
+static int prep() {
+ int errors = 0;
+ void* thing; // the thing that should be returned
+
+ thing = SIlisten_prep( UDP_DEVICE, "localhost:1234", AF_INET );
+ errors += fail_if_nil( thing, "listen prep udp returned nil block" );
+ SItrash( TP_BLK, thing );
+
+ thing = SIlisten_prep( UDP_DEVICE, "localhost:1234", 84306 ); // this should fail
+ errors += fail_not_nil( thing, "listen prep udp returned valid block ptr for bogus family" );
+
+ thing = SIconn_prep( si_ctx, UDP_DEVICE, "localhost:1234", 84306 ); // again, expect to fail; bogus family
+ errors += fail_not_nil( thing, "conn prep udp returned valid block ptr for bogus family" );
+
+ return errors;
+}
+
+/*
+ Polling/waiting tests. These are difficult at best because of the blocking
+ nature of things, not to mention needing to have real ports open etc.
+*/
+static int poll() {
+ int errors = 0;
+ int status;
+ struct ginfo_blk* dummy;
+
+
+ dummy = SIinitialise( 0 ); // get one to fiddle to drive edge cases
+ dummy->flags |= GIF_SHUTDOWN; // shutdown edge condition
+ SIpoll( dummy, 1 );
+
+ free( dummy->tp_map );
+ free( dummy->rbuf );
+ free( dummy->cbtab );
+
+ memset( dummy, 0, sizeof( *dummy ) ); // force bad cookie check code to drive
+ SIpoll( dummy, 1 );
+
+ free (dummy );
+
+ status = SIpoll( si_ctx, 1 );
+ errors += fail_if_true( status != 0, "poll failed" );
+
+ return errors;
+}
+
/*
Connection oriented tests.
errors += fail_if_true( state >= 0, "forced dup connect did not return error" );
tpem_set_addr_dup_state( 0 ); // back to normal
- tpem_set_conn_state( 1 );
+ tpem_set_conn_state( -1 );
state = SIconnect( si_ctx, "localhost:4567" ); // driver regular connect
errors += fail_if_true( state >= 0, "connect to low port successful when failure expected" );
+ tpem_set_conn_state( 3 );
tpem_set_sock_state( 1 ); // make scoket calls fail
state = SIconnect( si_ctx, "localhost:4567" ); // driver regular connect
errors++;
} else {
errors += fail_if_true( buf[0] == 0, "get name returned buf with emtpy string" );
+ free( buf );
}
+ buf = SIgetname( -1 ); // invalid fd
+ errors += fail_not_nil( buf, "get name returned buf with non-emtpy string when given bad fd" );
+
fprintf( stderr, "<INFO> conn module finished with %d errors\n", errors );
return errors;
}
status = SInewsession( si_ctx, tpptr );
errors += fail_if_true( status < 0, "newsession did failed when accept was good" );
+ free( tpptr );
+
fprintf( stderr, "<INFO> new_sess module finished with %d errors\n", errors );
return errors;
}
}
+/*
+ Wait testing. This is tricky because we don't have any sessions and thus it's difficult
+ to drive much of SIwait().
+*/
+static int wait_tests() {
+ int errors = 0;
+ struct ginfo_blk* dummy;
+
+
+ dummy = SIinitialise( 0 ); // get one to fiddle to drive edge cases
+ SIwait( dummy ); // malloc should "fail"
+
+ dummy->flags |= GIF_SHUTDOWN;
+ SIwait( dummy );
+
+ free( dummy->tp_map );
+ free( dummy->rbuf );
+ free( dummy->cbtab );
+
+ memset( dummy, 0, sizeof( *dummy ) ); // force bad cookie check code to drive
+ SIwait( dummy );
+
+ free( dummy );
+
+
+ SIwait( si_ctx ); // should drive once through the loop
+
+ return errors;
+}
+
// ----------------------------------------------------------------------------------------
/*
errors += init();
errors += memory();
errors += addr();
+ errors += prep();
errors += conn();
errors += misc();
errors += new_sess(); // should leave a "connected" session at fd == 6
errors += send_tests();
+ errors += poll();
+ errors += wait_tests();
+
errors += cleanup();
- fprintf( stderr, "<INFO> si95 tests finished (%d)\n", errors );
+ test_summary( errors, "SI95 tests" );
if( errors == 0 ) {
fprintf( stderr, "<PASS> all tests were OK\n\n" );
} else {