/******************************************************************** * test-qofobject.c: GLib g_test test suite for qofobject.c. * * Copyright 2011 Muslim Chochlov * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License, or (at your option) any later version. * * * * 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, contact: * * * * Free Software Foundation Voice: +1-617-542-5942 * * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * * Boston, MA 02110-1301, USA gnu@gnu.org * ********************************************************************/ #ifdef __cplusplus extern "C" { #endif #include #include #include #include #ifdef __cplusplus } #endif #include "../qof.h" #include "../qofobject-p.h" static const gchar *suitename = "/qof/qofobject"; void test_suite_qofobject ( void ); typedef struct { QofObject *qofobject; } Fixture; typedef enum { MOCK_OBJECT_BOOK_BEGIN = 1, MOCK_OBJECT_BOOK_END, MOCK_OBJECT_DIRTY, MOCK_OBJECT_MARK_CLEAN, EMPTY } MockFields; static void mock_object_book_begin( QofBook *book ); static gboolean mock_object_dirty( const QofCollection *col ); static void mock_object_mark_clean( QofCollection *col ); static QofObject* new_object( QofIdType e_type, const char *type_label, MockFields field) { QofObject *object = NULL; object = g_new0( QofObject, 1 ); g_assert( object ); object->interface_version = QOF_OBJECT_VERSION; object->e_type = e_type; object->type_label = type_label; switch ( field ) { case MOCK_OBJECT_BOOK_BEGIN: object->book_begin = mock_object_book_begin; break; case MOCK_OBJECT_BOOK_END: object->book_end = mock_object_book_begin; break; case MOCK_OBJECT_DIRTY: object->is_dirty = mock_object_dirty; break; case MOCK_OBJECT_MARK_CLEAN: object->mark_clean = mock_object_mark_clean; case EMPTY: break; } return object; } #ifdef __cplusplus extern "C" { #endif extern gboolean get_object_is_initialized( void ); extern GList* get_object_modules( void ); extern GList* get_book_list( void ); #ifdef __cplusplus } #endif static void setup( Fixture *fixture, gconstpointer pData ) { qof_object_initialize(); fixture->qofobject = new_object( "my type object", "object desc", EMPTY ); } static void teardown( Fixture *fixture, gconstpointer pData ) { g_free( fixture->qofobject ); qof_object_shutdown(); } /* * Safely generates objects and registers them * * Input: min_objects - minimum number of objects to be generated (should be between 0 and 5) * mock_filed - function in qofobject to be mocked * Output: number of generated objects */ static gint32 generate_and_register_objects( guint min_objects, MockFields mock_field ) { gint32 list_length = g_test_rand_int_range( min_objects, 5 ); const char *types[5] = {"type1", "type2", "type3", "type4", "type5"}; int i; g_assert_cmpint( min_objects, >= , 0 ); g_assert_cmpint( min_objects, < , 5 ); for (i = 0; i < list_length; i++ ) { QofObject *object = new_object( types[i], "desc", mock_field ); g_assert( object ); g_assert( qof_object_register( object ) ); g_assert_cmpint( g_list_length( get_object_modules() ), == , (i + 1) ); } g_assert_cmpint( list_length, == , g_list_length( get_object_modules() ) ); return list_length; } /* * TESTS */ static struct { GList *books; guint call_count; } book_begin_struct; static void mock_book_begin( QofBook *book ) { g_assert( book ); g_assert( book == book_begin_struct.books->data ); book_begin_struct.books = book_begin_struct.books->next; book_begin_struct.call_count++; } static void test_qof_object_register( Fixture *fixture, gconstpointer pData ) { GList *books = NULL; gint32 list_length = g_test_rand_int_range( 0, 5 ); int i; QofObject *simple_object = NULL; for (i = 0; i < list_length; i++ ) { QofBook *book = qof_book_new(); g_assert( book ); books = g_list_prepend ( books, book ); g_assert_cmpint( g_list_length( books ), == , (i + 1) ); } g_assert_cmpint( list_length, == , g_list_length( books ) ); g_test_message( "Test null check" ); g_assert( qof_object_register( NULL ) == FALSE ); g_test_message( "Test new object register with book_begin specified" ); fixture->qofobject->book_begin = mock_book_begin; book_begin_struct.books = books; book_begin_struct.call_count = 0; g_assert( qof_object_register( fixture->qofobject ) == TRUE ); g_assert( qof_object_lookup( "my type object" ) == fixture->qofobject ); g_assert_cmpint( book_begin_struct.call_count, == , list_length ); g_test_message( "Test registering the same object one more time" ); book_begin_struct.call_count = 0; g_assert( qof_object_register( fixture->qofobject ) == FALSE ); g_assert( qof_object_lookup( "my type object" ) == fixture->qofobject ); g_assert_cmpint( book_begin_struct.call_count, == , 0 ); g_test_message( "Test new object register without book_begin specified" ); simple_object = new_object( "my type simple", "simple desc", EMPTY ); g_assert( qof_object_register( simple_object ) == TRUE ); g_assert( qof_object_lookup( "my type simple" ) == simple_object ); g_assert_cmpint( book_begin_struct.call_count, == , 0 ); g_test_message( "Test register simple object one more time" ); g_assert( qof_object_register( simple_object ) == FALSE ); g_assert( qof_object_lookup( "my type simple" ) == simple_object ); g_test_message( "Test book begin is called only one time when object is registered" ); simple_object->book_begin = mock_book_begin; book_begin_struct.books = books; book_begin_struct.call_count = 0; g_assert( qof_object_register( simple_object ) == FALSE ); g_assert_cmpint( book_begin_struct.call_count, == , 0 ); g_list_foreach( books, (GFunc) qof_book_destroy, NULL ); g_list_free( books ); g_free( simple_object ); } static void test_qof_object_lookup( Fixture *fixture, gconstpointer pData ) { g_test_message( "Test null check" ); g_assert( qof_object_lookup( NULL ) == NULL ); g_test_message( "Test existing object lookup" ); g_assert( qof_object_register( fixture->qofobject ) == TRUE ); g_assert( qof_object_lookup( "my type object" ) == fixture->qofobject ); g_test_message( "Test non existing object lookup" ); g_assert( qof_object_lookup( "anytype" ) == NULL ); } static void test_qof_object_get_type_label( Fixture *fixture, gconstpointer pData ) { g_assert( qof_object_get_type_label( NULL ) == NULL ); g_test_message( "Test with non existing object" ); g_assert( qof_object_get_type_label( "anytype" ) == NULL ); g_test_message( "Test with existing registered object" ); g_assert( qof_object_register( fixture->qofobject ) == TRUE ); g_assert_cmpstr( qof_object_get_type_label( "my type object" ), == , "object desc" ); } static struct { gpointer param; } printable_struct; static const char * mock_printable( gpointer instance ) { g_assert( instance ); g_assert( instance == printable_struct.param ); return "printable was called"; } static void test_qof_object_printable( Fixture *fixture, gconstpointer pData ) { gint param; g_test_message( "Test null checks" ); g_assert( qof_object_printable( NULL, (gpointer)¶m ) == NULL ); g_assert( qof_object_printable( "test", NULL ) == NULL ); g_test_message( "Test with non registered object" ); g_assert( qof_object_printable( "test", (gpointer)¶m ) == NULL ); g_test_message( "Test with registered object and printable not set" ); g_assert( qof_object_register( fixture->qofobject ) == TRUE ); g_assert( qof_object_printable( "my type object", (gpointer)¶m ) == NULL ); g_test_message( "Test with registered object and printable set" ); fixture->qofobject->printable = mock_printable; printable_struct.param = (gpointer)¶m; g_assert_cmpstr( qof_object_printable( "my type object", (gpointer)¶m ), == , "printable was called" ); } static struct { QofBook *book; guint call_count; } object_book_begin_struct; static void mock_object_book_begin( QofBook *book ) { g_assert( book ); g_assert( book == object_book_begin_struct.book ); object_book_begin_struct.call_count++; } static void test_qof_object_book_begin( Fixture *fixture, gconstpointer pData ) { QofBook *book = NULL, *book2 = NULL; gint32 list_length; g_test_message( "Test book begin with no objects" ); g_assert_cmpint( 0, == , g_list_length( get_book_list() ) ); object_book_begin_struct.call_count = 0; book = g_object_new(QOF_TYPE_BOOK, NULL); g_assert( book ); qof_object_book_begin( book ); g_assert_cmpint( 1, == , g_list_length( get_book_list() ) ); g_assert_cmpint( g_list_index( get_book_list(), (gconstpointer) book), != , -1 ); g_assert_cmpint( object_book_begin_struct.call_count, == , 0 ); qof_book_destroy( book ); list_length = generate_and_register_objects( 1, MOCK_OBJECT_BOOK_BEGIN ); g_test_message( "Test book begin with random objects registered and book begin set up" ); g_assert_cmpint( 0, == , g_list_length( get_book_list() ) ); book2 = g_object_new(QOF_TYPE_BOOK, NULL); g_assert( book2 ); object_book_begin_struct.book = book2; qof_object_book_begin( book2 ); g_assert_cmpint( 1, == , g_list_length( get_book_list() ) ); g_assert_cmpint( g_list_index( get_book_list(), (gconstpointer) book2 ), != , -1 ); g_assert_cmpint( object_book_begin_struct.call_count, == , list_length ); qof_book_destroy( book2 ); } static void test_qof_object_book_end( Fixture *fixture, gconstpointer pData ) { QofBook *book = NULL, *book2 = NULL; gint32 list_length; g_test_message( "Test book with no objects" ); book = qof_book_new(); g_assert( book ); object_book_begin_struct.call_count = 0; g_assert_cmpint( 1, == , g_list_length( get_book_list() ) ); g_assert_cmpint( g_list_index( get_book_list(), (gconstpointer) book), != , -1 ); qof_book_destroy( book ); /* calls object_book_end */ g_assert_cmpint( object_book_begin_struct.call_count, == , 0 ); g_assert_cmpint( 0, == , g_list_length( get_book_list() ) ); list_length = generate_and_register_objects( 1, MOCK_OBJECT_BOOK_END ); g_test_message( "Test book end with random objects registered and book end set up" ); book2 = qof_book_new(); g_assert( book2 ); object_book_begin_struct.book = book2; g_assert_cmpint( 1, == , g_list_length( get_book_list() ) ); g_assert_cmpint( g_list_index( get_book_list(), (gconstpointer) book2 ), != , -1 ); qof_book_destroy( book2 ); /* calls object_book_end */ g_assert_cmpint( object_book_begin_struct.call_count, == , list_length ); g_assert_cmpint( 0, == , g_list_length( get_book_list() ) ); } static struct { GList *objects; guint call_count; gboolean result; } object_dirty_struct; static gboolean mock_object_dirty( const QofCollection *col ) { QofObject *obj = NULL; g_assert( col ); obj = object_dirty_struct.objects->data; object_dirty_struct.objects = object_dirty_struct.objects->next; g_assert( obj ); g_assert_cmpstr( qof_collection_get_type( col ), == , obj->e_type ); object_dirty_struct.call_count++; return object_dirty_struct.result; } static void test_qof_object_is_dirty( Fixture *fixture, gconstpointer pData ) { QofBook *book = NULL; gint32 list_length; g_test_message( "Test null check returns false" ); g_assert( qof_object_is_dirty( NULL ) == FALSE ); g_test_message( "Test with no objects" ); book = qof_book_new(); g_assert( book ); object_dirty_struct.call_count = 0; g_assert( qof_object_is_dirty( book ) == FALSE ); g_assert_cmpint( object_dirty_struct.call_count, == , 0 ); list_length = generate_and_register_objects( 1, MOCK_OBJECT_DIRTY ); g_test_message( "Test with registered objects and suppose all collections are clean" ); object_dirty_struct.objects = get_object_modules(); object_dirty_struct.result = FALSE; g_assert( qof_object_is_dirty( book ) == FALSE ); g_assert_cmpint( object_dirty_struct.call_count, == , list_length ); g_test_message( "Test with registered objects and suppose first collection is dirty" ); object_dirty_struct.objects = get_object_modules(); object_dirty_struct.result = TRUE; object_dirty_struct.call_count = 0; g_assert( qof_object_is_dirty( book ) == TRUE ); g_assert_cmpint( object_dirty_struct.call_count, == , 1 ); /* should break on first */ qof_book_destroy( book ); } static struct { GList *objects; guint call_count; } object_mark_clean_struct; static void mock_object_mark_clean( QofCollection *col ) { QofObject *obj = NULL; g_assert( col ); obj = object_mark_clean_struct.objects->data; object_mark_clean_struct.objects = object_mark_clean_struct.objects->next; g_assert( obj ); g_assert_cmpstr( qof_collection_get_type( col ), == , obj->e_type ); object_mark_clean_struct.call_count++; } static void test_qof_object_mark_clean( Fixture *fixture, gconstpointer pData ) { QofBook *book = NULL; gint32 list_length; g_test_message( "Test with no objects" ); book = qof_book_new(); g_assert( book ); object_mark_clean_struct.call_count = 0; g_assert_cmpint( g_list_length( get_object_modules() ), == , 0 ); qof_object_mark_clean( book ); g_assert_cmpint( object_mark_clean_struct.call_count, == , 0 ); list_length = generate_and_register_objects( 1, MOCK_OBJECT_MARK_CLEAN ); g_test_message( "Test with registered objects and mark clean set up" ); object_mark_clean_struct.objects = get_object_modules(); qof_object_mark_clean( book ); g_assert_cmpint( object_mark_clean_struct.call_count, == , list_length ); qof_book_destroy( book ); } static struct { QofBook *book; QofInstance *inst; gboolean is_called; } object_create_struct; static gpointer mock_object_create( QofBook *book ) { QofInstance *inst = NULL; inst = g_object_new(QOF_TYPE_INSTANCE, NULL); g_assert( inst ); g_assert( QOF_IS_INSTANCE( inst ) ); g_assert( book ); g_assert( book == object_create_struct.book ); object_create_struct.is_called = TRUE; object_create_struct.inst = inst; return inst; } static void test_qof_object_new_instance( Fixture *fixture, gconstpointer pData ) { QofBook *book = NULL; QofInstance *inst = NULL; book = qof_book_new(); g_assert( book ); g_test_message( "Test null check" ); g_assert( qof_object_new_instance( NULL, book ) == NULL ); g_test_message( "Test non existing object type" ); g_assert( qof_object_new_instance( "non existing type", book ) == NULL ); g_test_message( "Test with registered object type and create not set" ); g_assert( qof_object_register( fixture->qofobject ) ); g_assert( qof_object_new_instance( fixture->qofobject->e_type, book ) == NULL ); g_test_message( "Test with registered object type and create set" ); object_create_struct.is_called = FALSE; object_create_struct.book = book; object_create_struct.inst = NULL; fixture->qofobject->create = mock_object_create; inst = qof_object_new_instance( fixture->qofobject->e_type, book ); g_assert( inst ); g_assert( object_create_struct.is_called == TRUE ); g_assert( object_create_struct.inst == inst ); g_object_unref( inst ); qof_book_destroy( book ); } static void mock_object_foreach( const QofCollection *col, QofInstanceForeachCB cb, gpointer data) { } static void test_qof_object_compliance( Fixture *fixture, gconstpointer pData ) { g_assert( qof_object_register( fixture->qofobject ) ); g_test_message( "Test when neither create nor foreach set" ); g_assert( qof_object_compliance( fixture->qofobject->e_type, FALSE ) == FALSE ); g_assert( qof_object_compliance( fixture->qofobject->e_type, TRUE ) == FALSE ); g_test_message( "Test when only create set" ); fixture->qofobject->create = mock_object_create; g_assert( qof_object_compliance( fixture->qofobject->e_type, FALSE ) == FALSE ); g_assert( qof_object_compliance( fixture->qofobject->e_type, TRUE ) == FALSE ); g_test_message( "Test when only foreach set" ); fixture->qofobject->create = NULL; fixture->qofobject->foreach = mock_object_foreach; g_assert( qof_object_compliance( fixture->qofobject->e_type, FALSE ) == FALSE ); g_assert( qof_object_compliance( fixture->qofobject->e_type, TRUE ) == FALSE ); g_test_message( "Test when both set" ); fixture->qofobject->create = mock_object_create; fixture->qofobject->foreach = mock_object_foreach; g_assert( qof_object_compliance( fixture->qofobject->e_type, FALSE ) == TRUE ); g_assert( qof_object_compliance( fixture->qofobject->e_type, TRUE ) == TRUE ); } static struct { GList *objects; gpointer user_data; guint call_count; } foreach_type_cb_struct; static void mock_foreach_type_cb( QofObject *object, gpointer user_data ) { g_assert( object ); g_assert( user_data ); g_assert( object == foreach_type_cb_struct.objects->data ); g_assert( user_data == foreach_type_cb_struct.user_data ); foreach_type_cb_struct.objects = foreach_type_cb_struct.objects->next; foreach_type_cb_struct.call_count++; } static void test_qof_object_foreach_type( Fixture *fixture, gconstpointer pData ) { gint user_data; gint32 list_length; g_test_message( "Test with no objects" ); foreach_type_cb_struct.call_count = 0; g_assert_cmpint( g_list_length( get_object_modules() ), == , 0 ); qof_object_foreach_type( mock_foreach_type_cb, ( gpointer ) &user_data ); g_assert_cmpint( foreach_type_cb_struct.call_count, == , 0 ); list_length = generate_and_register_objects( 1, EMPTY ); g_test_message( "Test foreach cb" ); foreach_type_cb_struct.objects = get_object_modules(); foreach_type_cb_struct.user_data = ( gpointer ) &user_data; foreach_type_cb_struct.call_count = 0; qof_object_foreach_type( mock_foreach_type_cb, ( gpointer ) &user_data ); g_assert_cmpint( foreach_type_cb_struct.call_count, == , list_length ); } static struct { gpointer user_data; QofInstanceForeachCB cb; QofCollection *col; gboolean is_called; } foreach_cb_struct; static void mock_instance_foreach_cb( QofInstance *inst, gpointer user_data ) { } static void mock_foreach( const QofCollection *col, QofInstanceForeachCB cb, gpointer user_data ) { g_assert( col ); g_assert( cb ); g_assert( user_data ); g_assert( col == foreach_cb_struct.col ); g_assert( user_data == foreach_cb_struct.user_data ); g_assert( cb == foreach_cb_struct.cb ); foreach_cb_struct.is_called = TRUE; } static void test_qof_object_foreach( Fixture *fixture, gconstpointer pData ) { gint user_data; QofBook *book = NULL; QofCollection *col = NULL; /* setup */ book = qof_book_new(); g_assert( book ); g_assert_cmpint( g_list_length( get_object_modules() ), == , 0 ); qof_object_register( fixture->qofobject ); g_assert_cmpint( g_list_length( get_object_modules() ), == , 1 ); col = qof_book_get_collection( book, fixture->qofobject->e_type ); /* make col already exist */ g_assert( col ); g_test_message( "Test foreach and data" ); foreach_cb_struct.user_data = ( gpointer ) &user_data; foreach_cb_struct.is_called = FALSE; foreach_cb_struct.col = col; foreach_cb_struct.cb = mock_instance_foreach_cb; fixture->qofobject->foreach = mock_foreach; qof_object_foreach( fixture->qofobject->e_type, book, mock_instance_foreach_cb, ( gpointer ) &user_data ); g_assert( foreach_cb_struct.is_called == TRUE ); qof_book_destroy( book ); } static struct { GList *instances; gpointer user_data; guint call_count; } foreach_for_sorted_struct; static void mock_foreach_for_sorted( const QofCollection *col, QofInstanceForeachCB cb, gpointer user_data ) { GList *iter; g_assert( col ); g_assert( cb ); g_assert( user_data ); for (iter = foreach_for_sorted_struct.instances; iter; iter = iter->next) { cb( iter->data, user_data ); } } static void mock_instance_foreach_cb_for_sorted( QofInstance *inst, gpointer user_data ) { g_assert( inst ); g_assert( user_data ); g_assert_cmpint( g_list_index( foreach_for_sorted_struct.instances, (gconstpointer) inst ), != , -1 ); g_assert( user_data == foreach_for_sorted_struct.user_data ); foreach_for_sorted_struct.call_count++; } static void test_qof_object_foreach_sorted( Fixture *fixture, gconstpointer pData ) { int i; gint32 list_length = g_test_rand_int_range( 0, 5 ); gint user_data; QofBook *book = NULL; QofCollection *col = NULL; foreach_for_sorted_struct.instances = NULL; /* setup */ book = qof_book_new(); g_assert( book ); g_assert_cmpint( g_list_length( get_object_modules() ), == , 0 ); qof_object_register( fixture->qofobject ); g_assert_cmpint( g_list_length( get_object_modules() ), == , 1 ); fixture->qofobject->foreach = mock_foreach_for_sorted; /* init instances */ col = qof_book_get_collection( book, fixture->qofobject->e_type ); for (i = 0; i < list_length; i++ ) { QofInstance * inst = g_object_new( QOF_TYPE_INSTANCE, NULL ); g_assert( QOF_IS_INSTANCE( inst ) ); foreach_for_sorted_struct.instances = g_list_append( foreach_for_sorted_struct.instances, inst ); qof_collection_insert_entity( col, inst ); } g_assert_cmpint( list_length, == , g_list_length( foreach_for_sorted_struct.instances ) ); foreach_for_sorted_struct.call_count = 0; foreach_for_sorted_struct.user_data = &user_data; qof_object_foreach_sorted( fixture->qofobject->e_type, book, mock_instance_foreach_cb_for_sorted, ( gpointer ) &user_data ); g_assert_cmpint( list_length, == , foreach_for_sorted_struct.call_count ); qof_book_destroy( book ); g_list_free( foreach_for_sorted_struct.instances ); } void test_suite_qofobject (void) { GNC_TEST_ADD( suitename, "qof object register", Fixture, NULL, setup, test_qof_object_register, teardown ); GNC_TEST_ADD( suitename, "qof object lookup", Fixture, NULL, setup, test_qof_object_lookup, teardown ); GNC_TEST_ADD( suitename, "qof object get type label", Fixture, NULL, setup, test_qof_object_get_type_label, teardown ); GNC_TEST_ADD( suitename, "qof object printable", Fixture, NULL, setup, test_qof_object_printable, teardown ); GNC_TEST_ADD( suitename, "qof object book begin", Fixture, NULL, setup, test_qof_object_book_begin, teardown ); GNC_TEST_ADD( suitename, "qof object book end", Fixture, NULL, setup, test_qof_object_book_end, teardown ); GNC_TEST_ADD( suitename, "qof object is dirty", Fixture, NULL, setup, test_qof_object_is_dirty, teardown ); GNC_TEST_ADD( suitename, "qof object mark clean", Fixture, NULL, setup, test_qof_object_mark_clean, teardown ); GNC_TEST_ADD( suitename, "qof object new instance", Fixture, NULL, setup, test_qof_object_new_instance, teardown ); GNC_TEST_ADD( suitename, "qof object compliance", Fixture, NULL, setup, test_qof_object_compliance, teardown ); GNC_TEST_ADD( suitename, "qof object foreach type", Fixture, NULL, setup, test_qof_object_foreach_type, teardown ); GNC_TEST_ADD( suitename, "qof object foreach", Fixture, NULL, setup, test_qof_object_foreach, teardown ); GNC_TEST_ADD( suitename, "qof object foreach sorted", Fixture, NULL, setup, test_qof_object_foreach_sorted, teardown ); }