/* * Unit tests for Trema messenger. * * Author: Toshio Koide * * Copyright (C) 2008-2012 NEC Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include <arpa/inet.h> #include <errno.h> #include <linux/limits.h> #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/un.h> #include <time.h> #include <unistd.h> #include "checks.h" #include "cmockery_trema.h" #include "doubly_linked_list.h" #include "hash_table.h" #include "event_handler.h" #include "messenger.h" #include "timer.h" #include "wrapper.h" #define static extern enum { MESSAGE_TYPE_NOTIFY, MESSAGE_TYPE_REQUEST, MESSAGE_TYPE_REPLY, }; typedef struct message_buffer { void *buffer; size_t data_length; size_t size; } message_buffer; typedef struct messenger_socket { int fd; } messenger_socket; typedef struct messenger_context { uint32_t transaction_id; int life_count; void *user_data; } messenger_context; typedef struct receive_queue_callback { void *function; uint8_t message_type; } receive_queue_callback; typedef struct receive_queue { char service_name[ MESSENGER_SERVICE_NAME_LENGTH ]; dlist_element *message_callbacks; int listen_socket; struct sockaddr_un listen_addr; dlist_element *client_sockets; message_buffer *buffer; } receive_queue; typedef struct send_queue { char service_name[ MESSENGER_SERVICE_NAME_LENGTH ]; int server_socket; int refused_count; struct timespec reconnect_at; struct sockaddr_un server_addr; message_buffer *buffer; } send_queue; static void send_dump_message( uint16_t dump_type, const char *service_name, const void *data, uint32_t data_len ); static bool run_once( void ); static void on_accept( int fd, receive_queue *queue ); static void on_recv( int fd, receive_queue *queue ); static void on_send( int fd, send_queue *queue ); static receive_queue *create_receive_queue( const char *service_name ); static void delete_all_receive_queues( void ); static void delete_receive_queue( void *service_name, void *queue, void *user_data ); static int pull_from_recv_queue( receive_queue *queue, uint8_t *message_type, uint16_t *tag, void *data, size_t *len, size_t maxlen ); static void set_recv_queue_fd_set( fd_set *read_set ); static void check_recv_queue_fd_isset( fd_set *read_set ); static void add_recv_queue_client_fd( receive_queue *queue, int fd ); static int del_recv_queue_client_fd( receive_queue *queue, int fd ); static void call_message_callbacks( receive_queue *rq, const uint8_t message_type, const uint16_t tag, void *data, size_t len ); static send_queue *create_send_queue( const char *service_name ); static int send_queue_connect( send_queue *queue ); static void delete_all_send_queues( void ); static void delete_send_queue( send_queue *sq ); static void number_of_send_queue( int *connected_count, int *sending_count, int *reconnecting_count, int *closed_count ); static bool push_message_to_send_queue( const char *service_name, const uint8_t message_type, const uint16_t tag, const void *data, size_t len ); static void set_send_queue_fd_set( fd_set *read_set, fd_set *write_set ); static void check_send_queue_fd_isset( fd_set *read_set, fd_set *write_set ); static message_buffer *create_message_buffer( size_t size ); static bool write_message_buffer( message_buffer *buf, const void *data, size_t len ); static void truncate_message_buffer( message_buffer *buf, size_t len ); static void free_message_buffer( message_buffer *buf ); static size_t message_buffer_remain_bytes( message_buffer *buf ); static void delete_timer_callbacks( void ); static messenger_context *insert_context( void *user_data ); static messenger_context *get_context( uint32_t transaction_id ); static void delete_context( messenger_context *context ); static void delete_context_db( void ); static void age_context_db( void * ); static const uint32_t messenger_buffer_length; static char socket_directory[ PATH_MAX ]; static bool running; static bool initialized; static bool finalized; static hash_table *receive_queues; static hash_table *send_queues; static hash_table *context_db; static dlist_element *timer_callbacks; static char *_dump_service_name; static char *_dump_app_name; static uint32_t last_transaction_id; #undef static #define SERVICE_NAME1 "test1" #define SERVICE_NAME2 "test2" #define MESSAGE1 "message1" #define MESSAGE2 "message2" #define MESSAGE_TYPE1 1234 #define DUMP_SERVICE_NAME "dump" #define DUMP_APP_NAME "appname" #define TAG1 1 #define TAG2 2 #define CONTEXT_DATA "context data" /******************************************************************************** * Mocks. ********************************************************************************/ void mock_execute_timer_events( int *next_timeout_usec ) { UNUSED( next_timeout_usec ); // Do nothing. } bool mock_add_periodic_event_callback( const time_t seconds, void ( *callback )( void *user_data ), void *user_data ) { UNUSED( seconds ); UNUSED( callback ); UNUSED( user_data ); return true; } static bool fail_mock_socket = false; int mock_socket( int domain, int type, int protocol ) { return fail_mock_socket ? -1 : socket( domain, type, protocol ); } static bool fail_mock_bind = false; int mock_bind( int sockfd, const struct sockaddr *addr, socklen_t addrlen ) { return fail_mock_bind ? -1 : bind( sockfd, addr, addrlen ); } static bool fail_mock_listen = false; int mock_listen( int sockfd, int backlog ) { return fail_mock_listen ? -1 :listen( sockfd, backlog ); } static bool fail_mock_close = false; int mock_close( int fd ) { return fail_mock_close ? -1 : close( fd ); } static bool fail_mock_connect = false; int mock_connect( int sockfd, const struct sockaddr *addr, socklen_t addrlen ) { return fail_mock_connect ? -1 : connect( sockfd, addr, addrlen ); } static bool fail_mock_select = false; int mock_select( int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout ) { return fail_mock_select ? -1 : select( nfds, readfds, writefds, exceptfds, timeout ); } static bool fail_mock_accept = false; int mock_accept( int sockfd, struct sockaddr *addr, socklen_t *addrlen ) { return fail_mock_accept ? -1 : accept( sockfd, addr, addrlen ); } static bool fail_mock_recv = false; ssize_t mock_recv( int sockfd, void *buf, size_t len, int flags ) { return fail_mock_recv ? -1 : recv( sockfd, buf, len, flags ); } static bool fail_mock_send = false; ssize_t mock_send( int sockfd, const void *buf, size_t len, int flags ) { return fail_mock_send ? -1 : send( sockfd, buf, len, flags ); } int mock_setsockopt( int s, int level, int optname, const void *optval, socklen_t optlen ) { UNUSED( s ); UNUSED( level ); UNUSED( optname ); UNUSED( optval ); UNUSED( optlen ); return 0; } int mock_clock_gettime( clockid_t clk_id, struct timespec *tp ) { UNUSED( clk_id ); UNUSED( tp ); return ( int ) mock(); } void mock_die( char *format, ... ) { UNUSED( format ); } void mock_error( char *format, ... ) { UNUSED( format ); } void mock_debug( char *format, ... ) { UNUSED( format ); } static bool check_warn = false; static void mock_warn_check( char *format, va_list args ) { char message[ 1000 ]; vsnprintf( message, 1000, format, args ); check_expected( message ); } void mock_warn( char *format, ... ) { UNUSED( format ); if ( check_warn ) { va_list arg; va_start( arg, format ); mock_warn_check( format, arg ); va_end( arg ); } } /******************************************************************************** * Callbacks. ********************************************************************************/ void message_requested_callback( const messenger_context_handle *handle, uint16_t tag, void *data, size_t len ) { assert_true( handle != NULL ); assert_int_equal( tag, TAG1 ); assert_string_equal( data, MESSAGE1 ); assert_int_equal( ( int ) len, strlen( MESSAGE1 ) + 1 ); assert_true( send_reply_message( handle, TAG2, MESSAGE2, strlen( MESSAGE2 ) + 1 ) ); } void message_replied_callback( uint16_t tag, void *data, size_t len, void *user_data ) { assert_int_equal( tag, TAG2 ); assert_string_equal( data, MESSAGE2 ); assert_int_equal( ( int ) len, strlen( MESSAGE2 ) + 1 ); assert_string_equal( user_data, CONTEXT_DATA ); xfree( user_data ); stop_event_handler(); assert_true( stop_messenger() ); } /******************************************************************************** * Helpers. ********************************************************************************/ static void reset_messenger() { initialized = false; finalized = false; execute_timer_events = mock_execute_timer_events; check_warn = false; } /******************************************************************************** * Init and finalize messenger tests. ********************************************************************************/ static void test_init_and_finalize() { assert_true( init_messenger( "/tmp" ) ); assert_true( finalize_messenger() ); } static void test_init_twice() { assert_true( init_messenger( "/tmp" ) ); assert_true( init_messenger( "/tmp" ) ); finalize_messenger(); } static void test_finalize_twice() { init_messenger( "/tmp" ); assert_true( finalize_messenger() ); assert_false( finalize_messenger() ); } static void test_finalize_without_init() { assert_false( finalize_messenger() ); } /******************************************************************************** * Message callback tests. ********************************************************************************/ static void callback_hello( uint16_t tag, void *data, size_t len ) { check_expected( tag ); check_expected( data ); check_expected( len ); stop_event_handler(); stop_messenger(); } static void test_send_then_message_received_callback_is_called() { init_messenger( "/tmp" ); const char service_name[] = "Say HELLO"; expect_value( callback_hello, tag, 43556 ); expect_string( callback_hello, data, "HELLO" ); expect_value( callback_hello, len, 6 ); add_message_received_callback( service_name, callback_hello ); send_message( service_name, 43556, "HELLO", strlen( "HELLO" ) + 1 ); start_messenger(); start_event_handler(); delete_message_received_callback( service_name, callback_hello ); delete_send_queue( lookup_hash_entry( send_queues, service_name ) ); finalize_messenger(); } static void callback_req_hello( const messenger_context_handle *handle, uint16_t tag, void *data, size_t len ) { UNUSED( handle ); check_expected( tag ); check_expected( data ); check_expected( len ); send_reply_message( handle, 65534, "OLLEH", strlen("OLLEH")+1 ); } static void callback_req_hello2( const messenger_context_handle *handle, uint16_t tag, void *data, size_t len ) { UNUSED( handle ); UNUSED( tag ); UNUSED( data ); UNUSED( len ); } static void callback_rep_hello_end( uint16_t tag, void *data, size_t len, void *user_data ) { check_expected( tag ); check_expected( data ); check_expected( len ); check_expected( user_data ); stop_event_handler(); stop_messenger(); } static void callback_rep_hello2( uint16_t tag, void *data, size_t len, void *user_data ) { UNUSED( tag ); UNUSED( data ); UNUSED( len ); UNUSED( user_data ); } static void test_send_then_message_requested_and_replied_callback_is_called() { init_messenger( "/tmp" ); const char service_name[] = "Say HELLO"; expect_value( callback_req_hello, tag, 43556 ); expect_string( callback_req_hello, data, "HELLO" ); expect_value( callback_req_hello, len, 6 ); add_message_requested_callback( service_name, callback_req_hello ); expect_value( callback_rep_hello_end, tag, 65534 ); expect_string( callback_rep_hello_end, data, "OLLEH" ); expect_value( callback_rep_hello_end, len, 6 ); expect_value( callback_rep_hello_end, user_data, NULL ); add_message_replied_callback( service_name, callback_rep_hello_end ); send_request_message( service_name, service_name, 43556, "HELLO", strlen( "HELLO" ) + 1, NULL ); start_messenger(); start_event_handler(); delete_message_replied_callback( service_name, callback_rep_hello_end ); delete_message_requested_callback( service_name, callback_req_hello ); delete_send_queue( lookup_hash_entry( send_queues, service_name ) ); finalize_messenger(); } static void test_double_add_message_requested_callback_prints_error_message() { init_messenger( "/tmp" ); const char service_name[] = "Some Service"; char expected_mes[ 1024+1 ] = {}; snprintf( expected_mes, 1024, "Multiple message_requested/replied handler is not supported. ( service_name = %s, message_type = %#x, callback = %p )", service_name, MESSAGE_TYPE_REQUEST, callback_req_hello2 ); check_warn = true; assert_true( add_message_requested_callback( service_name, callback_req_hello ) ); expect_string( mock_warn_check, message, expected_mes ); assert_true( add_message_requested_callback( service_name, callback_req_hello2 ) ); check_warn = false; finalize_messenger(); } static void test_double_add_message_replied_callback_prints_error_message() { init_messenger( "/tmp" ); const char service_name[] = "Some Service"; char expected_mes[ 1024+1 ] = {}; snprintf( expected_mes, 1024, "Multiple message_requested/replied handler is not supported. ( service_name = %s, message_type = %#x, callback = %p )", service_name, MESSAGE_TYPE_REPLY, callback_rep_hello2 ); check_warn = true; assert_true( add_message_replied_callback( service_name, callback_rep_hello_end ) ); expect_string( mock_warn_check, message, expected_mes ); assert_true( add_message_replied_callback( service_name, callback_rep_hello2 ) ); check_warn = false; finalize_messenger(); } static void test_add_1_message_requested_and_replied_callback_each_prints_no_error_message() { init_messenger( "/tmp" ); const char service_name[] = "Some Service"; check_warn = true; assert_true( add_message_requested_callback( service_name, callback_req_hello ) ); assert_true( add_message_replied_callback( service_name, callback_rep_hello_end ) ); check_warn = false; finalize_messenger(); } /******************************************************************************** * Run tests. ********************************************************************************/ int main() { const UnitTest tests[] = { // init and finalize messenger tests. unit_test_setup_teardown( test_init_and_finalize, reset_messenger, reset_messenger ), unit_test_setup_teardown( test_init_twice, reset_messenger, reset_messenger ), unit_test_setup_teardown( test_finalize_twice, reset_messenger, reset_messenger ), unit_test_setup_teardown( test_finalize_without_init, reset_messenger, reset_messenger ), // Message callback tests. unit_test_setup_teardown( test_send_then_message_received_callback_is_called, reset_messenger, reset_messenger ), // Message request callback tests. unit_test_setup_teardown( test_send_then_message_requested_and_replied_callback_is_called, reset_messenger, reset_messenger ), // Message request duplicate registrationcallback tests. unit_test_setup_teardown( test_double_add_message_requested_callback_prints_error_message, reset_messenger, reset_messenger ), unit_test_setup_teardown( test_double_add_message_replied_callback_prints_error_message, reset_messenger, reset_messenger ), unit_test_setup_teardown( test_add_1_message_requested_and_replied_callback_each_prints_no_error_message, reset_messenger, reset_messenger ), }; return run_tests( tests ); } /* * Local variables: * c-basic-offset: 2 * indent-tabs-mode: nil * End: */