| /* $Id$ | |
| * | |
| * Licensed to the Apache Software Foundation (ASF) under one or more | |
| * contributor license agreements. See the NOTICE file distributed with | |
| * this work for additional information regarding copyright ownership. | |
| * The ASF licenses this file to you under the Apache License, Version | |
| * 2.0 (the "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| /* | |
| * test_etchobject.c.c -- test etch object inheritance and primitives | |
| */ | |
| #include "apr_time.h" /* some apr must be included first */ | |
| #include "etchthread.h" | |
| #include <tchar.h> | |
| #include <stdio.h> | |
| #include <conio.h> | |
| #include "cunit.h" | |
| #include "basic.h" | |
| #include "automated.h" | |
| #include "etch_global.h" | |
| #include "etchobj.h" | |
| int apr_setup(void); | |
| int apr_teardown(void); | |
| int this_setup(); | |
| int this_teardown(); | |
| apr_pool_t* g_apr_mempool; | |
| const char* pooltag = "etchpool"; | |
| /* - - - - - - - - - - - - - - | |
| * unit test infrastructure | |
| * - - - - - - - - - - - - - - | |
| */ | |
| int init_suite(void) | |
| { | |
| apr_setup(); | |
| etch_runtime_init(TRUE); | |
| return this_setup(); | |
| } | |
| int clean_suite(void) | |
| { | |
| this_teardown(); | |
| etch_runtime_cleanup(0,0); /* free memtable and cache etc */ | |
| apr_teardown(); | |
| return 0; | |
| } | |
| int g_is_automated_test, g_bytes_allocated; | |
| #define IS_DEBUG_CONSOLE FALSE | |
| /* | |
| * apr_setup() | |
| * establish apache portable runtime environment | |
| */ | |
| int apr_setup(void) | |
| { | |
| int result = apr_initialize(); | |
| if (result == 0) | |
| { result = etch_apr_init(); | |
| g_apr_mempool = etch_apr_mempool; | |
| } | |
| if (g_apr_mempool) | |
| apr_pool_tag(g_apr_mempool, pooltag); | |
| else result = -1; | |
| return result; | |
| } | |
| /* | |
| * apr_teardown() | |
| * free apache portable runtime environment | |
| */ | |
| int apr_teardown(void) | |
| { | |
| if (g_apr_mempool) | |
| apr_pool_destroy(g_apr_mempool); | |
| g_apr_mempool = NULL; | |
| apr_terminate(); | |
| return 0; | |
| } | |
| int this_setup() | |
| { | |
| etch_apr_mempool = g_apr_mempool; | |
| return 0; | |
| } | |
| int this_teardown() | |
| { | |
| return 0; | |
| } | |
| /** | |
| * class_a: base class | |
| */ | |
| typedef struct class_a | |
| { | |
| unsigned int hashkey; | |
| unsigned short obj_type; | |
| unsigned short class_id; | |
| struct objmask* vtab; | |
| int (*destroy)(void*); | |
| void*(*clone) (void*); | |
| obj_gethashkey get_hashkey; | |
| struct objmask* parent; | |
| etchresult* result; | |
| unsigned int refcount; | |
| unsigned int length; | |
| unsigned char is_null; | |
| unsigned char is_copy; | |
| unsigned char is_static; | |
| unsigned char reserved; | |
| etch_string* a_string; | |
| } class_a; | |
| /** | |
| * class_a destructor | |
| */ | |
| int destroy_class_a(class_a* thisp) | |
| { | |
| if (thisp->a_string) | |
| thisp->a_string->destroy(thisp->a_string); | |
| destroy_object((objmask*) thisp); | |
| return 0; | |
| } | |
| /** | |
| * class_a copy consttructor | |
| */ | |
| class_a* clone_class_a(class_a* origobj) | |
| { | |
| class_a* newobj = (class_a*) new_object(sizeof(class_a), 413, 0); | |
| memcpy(newobj, origobj, sizeof(objmask)); | |
| newobj->a_string = new_string(origobj->a_string->v.valw, ETCH_ENCODING_UTF16); | |
| return newobj; | |
| } | |
| /** | |
| * class_a constructor | |
| */ | |
| class_a* new_class_a(const wchar_t* strval) | |
| { | |
| class_a* newobj = (class_a*) new_object(sizeof(class_a), 403, 0); | |
| newobj->destroy = destroy_class_a; | |
| newobj->clone = clone_class_a; | |
| newobj->a_string = new_string(strval, ETCH_ENCODING_UTF16); | |
| return newobj; | |
| } | |
| /** | |
| * class_b: inherits from class_a | |
| */ | |
| typedef struct class_b | |
| { | |
| unsigned int hashkey; | |
| unsigned short obj_type; | |
| unsigned short class_id; | |
| struct objmask* vtab; | |
| int (*destroy)(void*); | |
| void*(*clone) (void*); | |
| obj_gethashkey get_hashkey; | |
| class_a* parent; | |
| etchresult* result; | |
| unsigned int refcount; | |
| unsigned int length; | |
| unsigned char is_null; | |
| unsigned char is_copy; | |
| unsigned char is_static; | |
| unsigned char reserved; | |
| char* data; | |
| int datasize; | |
| } class_b; | |
| /** | |
| * class_b destructor | |
| */ | |
| int destroy_class_b(class_b* thisp) | |
| { | |
| etch_free(thisp->data); | |
| destroy_object((objmask*) thisp); | |
| return 0; | |
| } | |
| /** | |
| * class_b copy consttructor | |
| */ | |
| class_b* clone_class_b(class_b* origobj) | |
| { | |
| class_b* newobj = (class_b*) new_object(sizeof(class_b), 415, 0); | |
| memcpy(newobj, origobj, sizeof(objmask)); | |
| newobj->parent = origobj->parent? | |
| origobj->parent->clone(origobj->parent): | |
| new_class_a(NULL); | |
| if (origobj->data) | |
| { | |
| newobj->data = etch_malloc(origobj->datasize, 412); | |
| newobj->datasize = origobj->datasize; | |
| memcpy(newobj->data, origobj->data, origobj->datasize); | |
| } | |
| return newobj; | |
| } | |
| /** | |
| * class_b constructor | |
| */ | |
| class_b* new_class_b(class_a* parent, const int datalen) | |
| { | |
| class_b* newobj = (class_b*) new_object(sizeof(class_b), 404, 0); | |
| newobj->parent = parent? parent: new_class_a(NULL); | |
| newobj->destroy = destroy_class_b; | |
| newobj->clone = clone_class_b; | |
| newobj->data = etch_malloc(datalen, 402); | |
| memset(newobj->data, 'x', datalen); | |
| newobj->datasize = datalen; | |
| return newobj; | |
| } | |
| /** | |
| * class_c: inherits from class_b | |
| */ | |
| typedef struct class_c | |
| { | |
| unsigned int hashkey; | |
| unsigned short obj_type; | |
| unsigned short class_id; | |
| struct objmask* vtab; | |
| int (*destroy)(void*); | |
| void*(*clone) (void*); | |
| obj_gethashkey get_hashkey; | |
| class_b* parent; | |
| etchresult* result; | |
| unsigned int refcount; | |
| unsigned int length; | |
| unsigned char is_null; | |
| unsigned char is_copy; | |
| unsigned char is_static; | |
| unsigned char reserved; | |
| int* intarray; | |
| int numitems; | |
| } class_c; | |
| /** | |
| * class_c destructor | |
| */ | |
| int destroy_class_c(class_c* thisp) | |
| { | |
| etch_free(thisp->intarray); | |
| destroy_object((objmask*) thisp); | |
| return 0; | |
| } | |
| /** | |
| * class_c copy consttructor | |
| */ | |
| class_c* clone_class_c(class_c* origobj) | |
| { | |
| class_c* newobj = (class_c*) new_object(sizeof(class_c), 410, 0); | |
| memcpy(newobj, origobj, sizeof(objmask)); | |
| if (origobj->parent) | |
| newobj->parent = origobj->parent->clone(origobj->parent); | |
| else newobj->parent = new_class_b(NULL, 0); | |
| if (origobj->intarray) | |
| { | |
| newobj->intarray = etch_malloc(origobj->numitems * sizeof(int), 411); | |
| newobj->numitems = origobj->numitems; | |
| memcpy(newobj->intarray, origobj->intarray, origobj->numitems * sizeof(int)); | |
| } | |
| return newobj; | |
| } | |
| /** | |
| * class_c constructor | |
| */ | |
| class_c* new_class_c(class_b* parent) | |
| { | |
| int i = 0; | |
| class_c* newobj = (class_c*) new_object(sizeof(class_b), 400, 0); | |
| newobj->parent = parent? parent: new_class_b(NULL, 0); | |
| newobj->destroy = destroy_class_c; | |
| newobj->clone = clone_class_c; | |
| newobj->numitems = 4; | |
| newobj->intarray = etch_malloc(4 * sizeof(int), 401); | |
| for(; i < 4; i++) newobj->intarray[i] = i; | |
| return newobj; | |
| } | |
| /** | |
| * test_inheritance() | |
| * test that in scenario c inherits from b inherits from a, | |
| * destroying c destroys b destroys a. | |
| */ | |
| void test_inheritance(void) | |
| { | |
| class_a* class_toplevel = NULL; | |
| class_b* class_midlevel = NULL; | |
| class_c* class_lowlevel = NULL; | |
| class_toplevel = new_class_a(L"it works!"); | |
| class_midlevel = new_class_b(class_toplevel, 128); | |
| class_lowlevel = new_class_c(class_midlevel); | |
| /* any object destructor should recursively destroy() its superclasses */ | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(class_lowlevel->parent); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(class_midlevel->parent); | |
| CU_ASSERT_PTR_NULL_FATAL(class_toplevel->parent); | |
| class_lowlevel->destroy(class_lowlevel); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); /* verify all memory freed */ | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); /* start fresh for next test */ | |
| } | |
| /** | |
| * test_clone() | |
| * test validity of cloned objects, also test construction of inherited object | |
| * when the parent(s) implementation(s) is/are not specified. | |
| * clones must be disposable, i.e. they must own all their memory | |
| * including that of their superclasses, such that if c inherits from b | |
| * inherits from a, and I clone c giving c', and I then destroy c, then | |
| * I can subsequently destroy c' via normal channels. | |
| */ | |
| void test_clone(void) | |
| { | |
| class_c* class_lowlevel = NULL; | |
| class_b* class_midlevel = NULL; | |
| class_a* class_toplevel = NULL; | |
| class_c* clone_lowlevel = NULL; | |
| class_b* clone_midlevel = NULL; | |
| class_a* clone_toplevel = NULL; | |
| /* construct class c and all its superclasses */ | |
| class_lowlevel = new_class_c(NULL); | |
| /* verify that superclasses were created */ | |
| class_midlevel = (class_b*) class_lowlevel->parent; | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(class_midlevel); | |
| class_toplevel = (class_a*) class_midlevel->parent; | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(class_toplevel); | |
| CU_ASSERT_PTR_NULL_FATAL(class_toplevel->parent); | |
| /* clone class c and all its superclasses */ | |
| clone_lowlevel = class_lowlevel->clone(class_lowlevel); | |
| /* verify that superclasses were created */ | |
| clone_midlevel = (class_b*) clone_lowlevel->parent; | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(clone_midlevel); | |
| clone_toplevel = (class_a*) clone_midlevel->parent; | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(clone_toplevel); | |
| CU_ASSERT_PTR_NULL_FATAL(clone_toplevel->parent); | |
| /* verify that superclass and data clones are not references to old memory */ | |
| CU_ASSERT_NOT_EQUAL_FATAL(class_midlevel, clone_midlevel); | |
| CU_ASSERT_NOT_EQUAL_FATAL(class_toplevel, clone_toplevel); | |
| if (class_midlevel->data != 0 && clone_midlevel->data != 0) | |
| CU_ASSERT_NOT_EQUAL_FATAL(class_midlevel->data, clone_midlevel->data); | |
| if (class_toplevel->a_string != 0 && clone_toplevel->a_string != 0) | |
| CU_ASSERT_NOT_EQUAL_FATAL(class_toplevel->a_string, clone_toplevel->a_string); | |
| /* we don't need to do this for the test, but here we illustrate | |
| * the etch C way of accessing superclass data from a subclass, | |
| * which is to traverse the parent chain to the class you want, | |
| * and cast the parent* to that class, if it is not so cast already. | |
| */ | |
| clone_midlevel->datasize = 1024; | |
| clone_midlevel->data = etch_malloc(clone_midlevel->datasize, 419); | |
| memset(clone_midlevel->data, '-', clone_midlevel->datasize); | |
| /* destroy class c with all its superclasses and associated instance data */ | |
| class_lowlevel->destroy(class_lowlevel); | |
| /* destroy class c clone and all its superclasses | |
| * if the clone of class_c had not properly cloned all its superclasses and | |
| * data, this would crash on attempt to free dangling pointer. | |
| */ | |
| clone_lowlevel->destroy(clone_lowlevel); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); /* verify all memory freed */ | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); /* start fresh for next test */ | |
| } | |
| void test_primitive_byte(void) | |
| { | |
| signed char v = 255; | |
| etch_byte* newobj = new_byte(v); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(newobj); | |
| CU_ASSERT_EQUAL(v, newobj->value); | |
| newobj->destroy(newobj); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| void test_primitive_bool(void) | |
| { | |
| etch_boolean* newobj = new_boolean(100); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(newobj); | |
| CU_ASSERT_EQUAL(newobj->value, TRUE); | |
| newobj->destroy(newobj); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| void test_primitive_int8(void) | |
| { | |
| signed char v = -1; | |
| etch_int8* newobj = new_int8(v); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(newobj); | |
| CU_ASSERT_EQUAL(newobj->value,v); | |
| newobj->destroy(newobj); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| void test_primitive_int16(void) | |
| { | |
| short v = -1; | |
| etch_int16* newobj = new_int16(v); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(newobj); | |
| CU_ASSERT_EQUAL(newobj->value,v); | |
| newobj->destroy(newobj); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| void test_primitive_int32(void) | |
| { | |
| int v = 1 << 31; | |
| etch_int32* newobj = new_int32(v); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(newobj); | |
| CU_ASSERT_EQUAL(newobj->value,v); | |
| newobj->destroy(newobj); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| void test_primitive_int64(void) | |
| { | |
| int64 v = ((int64)(1)) << 63; | |
| etch_int64* newobj = new_int64(v); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(newobj); | |
| CU_ASSERT_EQUAL(newobj->value,v); | |
| newobj->destroy(newobj); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| void test_primitive_float(void) | |
| { | |
| float v = (float)(3.14159); | |
| etch_float* newobj = new_float(v); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(newobj); | |
| CU_ASSERT_EQUAL(newobj->value,v); | |
| newobj->destroy(newobj); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| void test_primitive_double(void) | |
| { | |
| double v = (1 << 31) + 3.14159; | |
| etch_double* newobj = new_double(v); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(newobj); | |
| CU_ASSERT_EQUAL(newobj->value,v); | |
| newobj->destroy(newobj); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| void test_primitive_string(void) | |
| { | |
| wchar_t* v = L"it works!"; | |
| etch_string* newobj = new_string(v, ETCH_ENCODING_UTF16); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(newobj); | |
| CU_ASSERT_EQUAL(wcscmp(v, newobj->v.valw), 0); | |
| newobj->destroy(newobj); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| /** | |
| * test_nativearray_ctordtor() | |
| * test that we can create and destroy an etch_nativearray with all memory accounted for | |
| */ | |
| void test_nativearray_ctordtor(void) | |
| { | |
| etch_nativearray* bytearray_1x4 = new_nativearray(CLASSID_ARRAY_BYTE, sizeof(byte), 1, 4, 0, 0); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(bytearray_1x4); | |
| bytearray_1x4->destroy(bytearray_1x4); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| /** | |
| * test_nativearray_1x1x4() | |
| * test that we can populate and access a 1-dimensional array of byte | |
| */ | |
| void test_nativearray_1x1x4(void) | |
| { | |
| int i = 0, result = 0; | |
| char x[4] = {'a','b','c','d'}, thisx = 0; | |
| const int numdimensions = 1, itemcount = 4; | |
| etch_nativearray* a = new_nativearray | |
| (CLASSID_ARRAY_BYTE, sizeof(byte), numdimensions, itemcount, 0, 0); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(a); | |
| for(i = 0; i < itemcount; i++) /* populate array */ | |
| { | |
| result = a->put1(a, &x[i], i); /* insert value of i to ith slot */ | |
| CU_ASSERT_EQUAL(result, 0); | |
| } | |
| for(i = 0; i < itemcount; i++) /* read values out of array */ | |
| { | |
| result = a->get1(a, &thisx, i); /* get value of ith slot into thisx */ | |
| CU_ASSERT_EQUAL(result, 0); | |
| CU_ASSERT_EQUAL(x[i], thisx); | |
| } | |
| a->destroy(a); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| /** | |
| * test_nativearray_1x4x4() | |
| * test that we can populate and access a 1-dimensional array of int | |
| */ | |
| void test_nativearray_1x4x4(void) | |
| { | |
| int i = 0, j = 0, result = 0; | |
| const int numdimensions = 1, itemcount = 4; | |
| etch_nativearray* a = new_nativearray | |
| (CLASSID_ARRAY_INT32, sizeof(int), numdimensions, itemcount, 0, 0); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(a); | |
| for(i = 0; i < itemcount; i++) /* populate array */ | |
| { | |
| result = a->put1(a, &i, i); /* insert value of i to ith slot */ | |
| CU_ASSERT_EQUAL(result, 0); | |
| } | |
| for(i = 0; i < itemcount; i++) /* read values out of array */ | |
| { | |
| result = a->get1(a, &j, i); /* get value of ith slot into j */ | |
| CU_ASSERT_EQUAL(result, 0); | |
| CU_ASSERT_EQUAL(i, j); | |
| } | |
| a->destroy(a); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| /** | |
| * test_nativearray_1xstruct() | |
| * test that we can populate and access a 1-dimensional array of struct | |
| */ | |
| void test_nativearray_1xstruct(void) | |
| { | |
| int i = 0, result = 0; | |
| const int numdimensions = 1, itemcount = 4; | |
| struct x { int n; char c; }; | |
| struct x xgot = {-1,'?'}; | |
| struct x xs[4] = { {0,'a'}, {1,'b'}, {2,'c'}, {3,'d'}, }; | |
| etch_nativearray* a = new_nativearray | |
| (CLASSID_ARRAY_STRUCT, sizeof(struct x), numdimensions, itemcount, 0, 0); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(a); | |
| for(i = 0; i < itemcount; i++) /* populate array */ | |
| { | |
| result = a->put1(a, &xs[i], i); /* insert xs[i] to ith slot */ | |
| CU_ASSERT_EQUAL(result, 0); | |
| } | |
| for(i = 0; i < itemcount; i++) /* read values out of array */ | |
| { | |
| result = a->get1(a, &xgot, i); /* get value of ith slot into xgot */ | |
| CU_ASSERT_EQUAL(result, 0); | |
| result = memcmp(&xgot, &xs[i], sizeof(struct x)); | |
| CU_ASSERT_EQUAL(result, 0); | |
| } | |
| a->destroy(a); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| /** | |
| * test_nativearray_2x1x4() | |
| * test that we can populate and access a 2-dimensional array of byte | |
| */ | |
| void test_nativearray_2x1x4(void) | |
| { | |
| int i = 0, j = 0, result = 0; | |
| char x[2][4] = { {'a','b','c','d'}, {'e','f','g','h'}, }, thisx = 0; | |
| const int numdimensions = 2, dim0count = 4, dim1count = 2; | |
| /* note when creating arrays, the dimensions are specified in reverse, | |
| * low-order dimension (dim0) first, e.g., for x[2][3][4], | |
| * dim0count is 4, dim1count is 3, dim2count is 2. | |
| */ | |
| etch_nativearray* a = new_nativearray | |
| (CLASSID_ARRAY_BYTE, sizeof(byte), numdimensions, dim0count, dim1count, 0); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(a); | |
| for(i = 0; i < dim1count; i++) /* populate array */ | |
| { | |
| for(j = 0; j < dim0count; j++) | |
| { /* insert x[i][j] to array[i][j] */ | |
| result = a->put2(a, &x[i][j], i, j); | |
| CU_ASSERT_EQUAL(result, 0); | |
| } | |
| } | |
| for(i = 0; i < dim1count; i++) /* read array */ | |
| { | |
| for(j = 0; j < dim0count; j++) /* read values out of array */ | |
| { /* get array[i][j] into thisx */ | |
| result = a->get2(a, &thisx, i, j); | |
| CU_ASSERT_EQUAL(result, 0); | |
| CU_ASSERT_EQUAL(x[i][j], thisx); | |
| } | |
| } | |
| a->destroy(a); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| /** | |
| * test_nativearray_3xstruct() | |
| * test that we can populate and access a 3-dimensional array of struct | |
| */ | |
| void test_nativearray_3xstruct(void) | |
| { | |
| int i = 0, j = 0, k = 0, result = 0; | |
| struct x { int n; char c; }; | |
| struct x xgot = {-1,'?'}; | |
| struct x *xthis = 0; | |
| struct x xs[2][3][4] = | |
| { | |
| { | |
| { | |
| {0,'a'}, {1,'b'}, {2,'c'}, {3,'d'}, | |
| }, | |
| { | |
| {0,'e'}, {1,'f'}, {2,'g'}, {3,'h'}, | |
| }, | |
| { | |
| {0,'i'}, {1,'j'}, {2,'k'}, {3,'l'}, | |
| }, | |
| }, | |
| { | |
| { | |
| {0,'m'}, {1,'n'}, {2,'o'}, {3,'p'}, | |
| }, | |
| { | |
| {0,'q'}, {1,'r'}, {2,'s'}, {3,'t'}, | |
| }, | |
| { | |
| {0,'u'}, {1,'v'}, {2,'w'}, {3,'x'}, | |
| }, | |
| }, | |
| }; | |
| const int numdimensions = 3, dim0count = 4, dim1count = 3, dim2count = 2; | |
| etch_nativearray* a = new_nativearray (CLASSID_ARRAY_STRUCT, sizeof(struct x), | |
| numdimensions, dim0count, dim1count, dim2count); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(a); | |
| for(i = 0; i < dim2count; i++) /* write array */ | |
| { | |
| for(j = 0; j < dim1count; j++) | |
| { | |
| for(k = 0; k < dim0count; k++) | |
| { | |
| result = a->put3(a, &xs[i][j][k], i, j, k); | |
| CU_ASSERT_EQUAL(result, 0); | |
| } | |
| } | |
| } | |
| for(i = 0; i < dim2count; i++) /* read array */ | |
| { | |
| for(j = 0; j < dim1count; j++) | |
| { | |
| for(k = 0; k < dim0count; k++) | |
| { | |
| result = a->get3(a, &xgot, i, j, k); | |
| CU_ASSERT_EQUAL(result, 0); | |
| xthis = &xs[i][j][k]; | |
| result = memcmp(&xgot, xthis, sizeof(struct x)); | |
| CU_ASSERT_EQUAL(result, 0); | |
| } | |
| } | |
| } | |
| a->destroy(a); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| /** | |
| * test_arrayfrom_2x1x4() | |
| * test that we can access a static 2-dimensional array of byte. | |
| * also validates that our array subscripting calculations match those of | |
| * the C compiler, since we map and access an array mapped by the compiler. | |
| * also validates that the etch_nativearray will not attempt to destroy | |
| * the byte vector of an array created in this manner. | |
| */ | |
| void test_arrayfrom_2x1x4(void) | |
| { | |
| int i = 0, j = 0, result = 0; | |
| char x[2][4] = { {'a','b','c','d'}, {'e','f','g','h'}, }, thisx = 0; | |
| const int numdimensions = 2, dim0count = 4, dim1count = 2; | |
| etch_nativearray* a = new_nativearray_from(&x, CLASSID_ARRAY_BYTE, | |
| sizeof(byte), numdimensions, dim0count, dim1count, 0); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(a); | |
| for(i = 0; i < dim1count; i++) /* read array */ | |
| { | |
| for(j = 0; j < dim0count; j++) /* read values out of array */ | |
| { /* get array[i][j] into thisx */ | |
| result = a->get2(a, &thisx, i, j); | |
| CU_ASSERT_EQUAL(result, 0); | |
| CU_ASSERT_EQUAL(x[i][j], thisx); | |
| } | |
| } | |
| a->destroy(a); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| /** | |
| * test_subarray() | |
| * test that we can create a one-dimensional subarray from a 2-dimensional | |
| * array of byte, and that all memory is accounted for. | |
| */ | |
| void test_subarray(void) | |
| { | |
| int i = 0, j = 0, result = 0; | |
| char x[2][4] = { {'a','b','c','d'}, {'e','f','g','h'}, }, thisx = 0; | |
| const int numdimensions = 2, dim0count = 4, dim1count = 2; | |
| etch_nativearray *a0 = NULL, *a1 = NULL, *a2 = NULL; | |
| etch_nativearray* a = new_nativearray_from(&x, CLASSID_ARRAY_BYTE, | |
| sizeof(byte), numdimensions, dim0count, dim1count, 0); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(a); | |
| a->content_obj_type = ETCHTYPEB_BYTE; | |
| a->content_class_id = CLASSID_NONE; /* unwrapped content */ | |
| a0 = new_subarray(a, 0); /* test index 0 */ | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(a0); | |
| CU_ASSERT_EQUAL(a0->is_content_owned, FALSE); | |
| CU_ASSERT_EQUAL(a0->numdims, a->numdims-1); | |
| CU_ASSERT_EQUAL(a0->bytecount, a->bytecount/2); | |
| CU_ASSERT_EQUAL(a0->itemsize, a->itemsize); | |
| CU_ASSERT_EQUAL(a0->content_obj_type, a->content_obj_type); | |
| CU_ASSERT_EQUAL(a0->content_class_id, a->content_class_id); | |
| /* recall that dimension and dimsize are stored low-order | |
| * dimension first, so dimension[0] and dimsize[0] are | |
| * the same for byte x[2][4] as for x[4] */ | |
| CU_ASSERT_EQUAL(a0->dimension[0], a->dimension[0]); | |
| CU_ASSERT_EQUAL(a0->dimsize[0], a->dimsize[0]); | |
| for(i = 0; i < dim0count; i++) | |
| { | |
| result = a0->get1(a0, &thisx, i); | |
| CU_ASSERT_EQUAL(result, 0); | |
| CU_ASSERT_EQUAL(x[0][i], thisx); | |
| } | |
| a1 = new_subarray(a, 1); /* test index 1 */ | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(a1); | |
| CU_ASSERT_EQUAL(a1->is_content_owned, FALSE); | |
| CU_ASSERT_EQUAL(a1->numdims, a->numdims-1); | |
| CU_ASSERT_EQUAL(a1->bytecount, a->bytecount/2); | |
| CU_ASSERT_EQUAL(a1->itemsize, a->itemsize); | |
| CU_ASSERT_EQUAL(a1->content_obj_type, a->content_obj_type); | |
| CU_ASSERT_EQUAL(a1->content_class_id, a->content_class_id); | |
| /* recall that dimension and dimsize are stored low-order | |
| * dimension first, so dimension[0] and dimsize[0] are | |
| * the same for byte x[2][4] as for x[4] */ | |
| CU_ASSERT_EQUAL(a1->dimension[0], a->dimension[0]); | |
| CU_ASSERT_EQUAL(a1->dimsize[0], a->dimsize[0]); | |
| for(i = 0; i < dim0count; i++) | |
| { | |
| result = a1->get1(a1, &thisx, i); | |
| CU_ASSERT_EQUAL(result, 0); | |
| CU_ASSERT_EQUAL(x[1][i], thisx); | |
| } | |
| a2 = new_subarray(a, 2); /* test nonexistent index 2 */ | |
| CU_ASSERT_PTR_NULL(a2); | |
| a1->destroy(a1); | |
| a0->destroy(a0); | |
| a->destroy(a); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| /* | |
| * test_if_busted_subarray() | |
| * this test is here to debug something that was either broken in subarray, | |
| * or that was not broken, but rather the validator test using it was broken. | |
| * verdict: subarray itemsize was broken, is fixed. | |
| */ | |
| void test_if_busted_subarray(void) | |
| { | |
| short x[2][3][4] = | |
| { { { 1,1,1,1, }, { 1,1,1,1, }, { 1,-1,32767, -32768, }, }, | |
| { { 1,1,1,1, }, { 1,1,1,1, }, { 1,-1,0xfffe,0xffff, }, }, | |
| }; | |
| const int numdimensions = 3, dim0count = 4, dim1count = 3, dim2count = 2; | |
| int i=0, j=0, k=0; | |
| etch_nativearray* a1 = new_nativearray_from(&x, CLASSID_ARRAY_INT16, | |
| sizeof(short), numdimensions, dim0count, dim1count, dim2count); | |
| a1->content_obj_type = ETCHTYPEB_INT16; | |
| a1->content_class_id = CLASSID_NONE; /* unwrapped */ | |
| for(i = 0; i < dim2count; i++) | |
| { | |
| etch_nativearray* a2 = (etch_nativearray*) etch_nativearray_get_element(a1, i); | |
| CU_ASSERT_EQUAL_FATAL(is_etch_nativearray(a2), TRUE); | |
| CU_ASSERT_EQUAL_FATAL(a2->bytecount, a1->bytecount/a1->dimension[2]); | |
| CU_ASSERT_EQUAL_FATAL(a2->numdims, a1->numdims-1); | |
| CU_ASSERT_EQUAL_FATAL(a2->dimsize[0], a1->dimsize[0]); | |
| CU_ASSERT_EQUAL_FATAL(a2->dimsize[1], a1->dimsize[1]); | |
| CU_ASSERT_EQUAL_FATAL(a2->dimension[0],a1->dimension[0]); | |
| CU_ASSERT_EQUAL_FATAL(a2->dimension[1],a1->dimension[1]); | |
| CU_ASSERT_EQUAL_FATAL(a2->dimension[1],dim1count); | |
| for(j = 0; j < dim1count; j++) | |
| { | |
| etch_nativearray* a3 = (etch_nativearray*) etch_nativearray_get_element(a2, j); | |
| CU_ASSERT_EQUAL_FATAL(is_etch_nativearray(a3), TRUE); | |
| CU_ASSERT_EQUAL_FATAL(a3->bytecount, a2->bytecount/a2->dimension[1]); | |
| CU_ASSERT_EQUAL_FATAL(a3->numdims, a2->numdims-1); | |
| CU_ASSERT_EQUAL_FATAL(a3->dimsize[0], a2->dimsize[0]); | |
| CU_ASSERT_EQUAL_FATAL(a3->dimension[0],a2->dimension[0]); | |
| CU_ASSERT_EQUAL_FATAL(a3->dimension[0],dim0count); | |
| for(k = 0; k < dim0count; k++) | |
| { | |
| short n_expected = 0, n_actual = 0; | |
| etch_int16* shortobj = (etch_int16*) etch_nativearray_get_element(a3, k); | |
| CU_ASSERT_EQUAL_FATAL(is_etch_int16(shortobj), TRUE); | |
| n_expected = x[i][j][k]; | |
| n_actual = shortobj->value; | |
| CU_ASSERT_EQUAL_FATAL(n_expected, n_actual); | |
| shortobj->destroy(shortobj); | |
| } | |
| a3->destroy(a3); | |
| } | |
| a2->destroy(a2); | |
| } | |
| a1->destroy(a1); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); /* start fresh for next test */ | |
| } | |
| /** | |
| * test_get_element() | |
| * test that we can get element[i] of a native array, and that element | |
| * is another native array, or a wrapped etch object the same type as | |
| * the array's content_obj_type and possibly content_class_id. | |
| */ | |
| void test_get_element(void) | |
| { | |
| int i = 0, j = 0, result = 0; | |
| char x[2][4] = { {'a','b','c','d'}, {'e','f','g','h'}, }, thisx = 0; | |
| const int numdimensions = 2, dim0count = 4, dim1count = 2; | |
| etch_nativearray *a1 = NULL; | |
| objmask* returnobj = NULL; | |
| etch_byte* retbyteobj = NULL; | |
| etch_nativearray* a = new_nativearray_from(&x, CLASSID_ARRAY_BYTE, | |
| sizeof(byte), numdimensions, dim0count, dim1count, 0); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(a); | |
| a->content_obj_type = ETCHTYPEB_BYTE; | |
| a->content_class_id = CLASSID_NONE; /* unwrapped content */ | |
| /* get element[1] from the array, expecting a byte[4] native array */ | |
| returnobj = etch_nativearray_get_element(a, 1); | |
| CU_ASSERT_EQUAL_FATAL(is_etch_nativearray(returnobj), TRUE); | |
| a1 = (etch_nativearray*) returnobj; | |
| CU_ASSERT_EQUAL(a1->is_content_owned, FALSE); | |
| CU_ASSERT_EQUAL(a1->numdims, a->numdims-1); | |
| CU_ASSERT_EQUAL(a1->bytecount, a->bytecount/2); | |
| CU_ASSERT_EQUAL(a1->itemsize, a->itemsize); | |
| CU_ASSERT_EQUAL(a1->content_obj_type, a->content_obj_type); | |
| CU_ASSERT_EQUAL(a1->content_class_id, a->content_class_id); | |
| CU_ASSERT_EQUAL(a1->dimension[0], a->dimension[0]); | |
| CU_ASSERT_EQUAL(a1->dimsize[0], a->dimsize[0]); | |
| for(i = 0; i < dim0count; i++) | |
| { | |
| /* get element[i] from the subarray, expecting an etch_byte object */ | |
| returnobj = etch_nativearray_get_element(a1, i); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(returnobj); | |
| CU_ASSERT_EQUAL_FATAL(returnobj->class_id, CLASSID_PRIMITIVE_BYTE); | |
| retbyteobj = (etch_byte*) returnobj; /* verify that wrapped byte */ | |
| thisx = retbyteobj->value; /* matches original array */ | |
| CU_ASSERT_EQUAL(x[1][i], thisx); | |
| retbyteobj->destroy(retbyteobj); | |
| } | |
| a1->destroy(a1); | |
| a->destroy(a); | |
| g_bytes_allocated = etch_showmem(0, IS_DEBUG_CONSOLE); | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); | |
| } | |
| /** | |
| * main | |
| */ | |
| int _tmain(int argc, _TCHAR* argv[]) | |
| { | |
| char c=0; | |
| CU_pSuite ps = NULL; | |
| g_is_automated_test = argc > 1 && 0 != wcscmp(argv[1], L"-a"); | |
| if (CUE_SUCCESS != CU_initialize_registry()) return 0; | |
| CU_set_output_filename("../test_etchobject"); | |
| ps = CU_add_suite("etchobject test suite", init_suite, clean_suite); | |
| etch_watch_id = 0; | |
| CU_add_test(ps, "test primitive byte", test_primitive_byte); | |
| CU_add_test(ps, "test primitive boolean", test_primitive_bool); | |
| CU_add_test(ps, "test primitive int8", test_primitive_int8); | |
| CU_add_test(ps, "test primitive int16", test_primitive_int16); | |
| CU_add_test(ps, "test primitive int32", test_primitive_int32); | |
| CU_add_test(ps, "test primitive int64", test_primitive_int64); | |
| CU_add_test(ps, "test primitive float", test_primitive_float); | |
| CU_add_test(ps, "test primitive double", test_primitive_double); | |
| CU_add_test(ps, "test primitive string", test_primitive_string); | |
| CU_add_test(ps, "test tri-level inheritance", test_inheritance); | |
| CU_add_test(ps, "test clone and auto-construct superclass", test_clone); | |
| CU_add_test(ps, "test native array ctor", test_nativearray_ctordtor); | |
| CU_add_test(ps, "test 1-dim byte array", test_nativearray_1x1x4); | |
| CU_add_test(ps, "test 1-dim int array", test_nativearray_1x4x4); | |
| CU_add_test(ps, "test 1-dim struct array", test_nativearray_1xstruct); | |
| CU_add_test(ps, "test 2-dim byte array", test_nativearray_2x1x4); | |
| CU_add_test(ps, "test 3-dim struct array", test_nativearray_3xstruct); | |
| CU_add_test(ps, "test static 2-dim byte array", test_arrayfrom_2x1x4); | |
| CU_add_test(ps, "test subarray", test_subarray); | |
| CU_add_test(ps, "test get array element", test_get_element); | |
| CU_add_test(ps, "test 3-dim int16 subarray ", test_if_busted_subarray); | |
| if (g_is_automated_test) | |
| CU_automated_run_tests(); | |
| else | |
| { CU_basic_set_mode(CU_BRM_VERBOSE); | |
| CU_basic_run_tests(); | |
| } | |
| if (!g_is_automated_test) { printf("any key ..."); while(!c) c = _getch(); wprintf(L"\n"); } | |
| CU_cleanup_registry(); | |
| return CU_get_error(); | |
| } | |