| /* $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_cache.c | |
| * test the runtime object cache | |
| * we can swap out cache back ends and this test should work the same regardless | |
| */ | |
| #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 "etch_arraylist.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; | |
| } | |
| /** | |
| * This subtest instantiates various etch objects which cache some part of | |
| * themselves, and destroys the objects. At each step the test verifies that | |
| * the cache contains the expected number of entries, e.g. if I create | |
| * multiple hashtables I should only have cached one hashtable vtable. | |
| */ | |
| void test_multiple_items(void) | |
| { | |
| int cache_start_count = 0, cache_current_count; | |
| int result1 = 0, result2 = 0, result3 = 0; | |
| etch_hashtable* myhashtab1 = NULL; | |
| etch_hashtable* myhashtab2 = NULL; | |
| etch_hashtable* myhashtab3 = NULL; | |
| etch_hashitem hashbucket; | |
| etch_hashitem* myentry = &hashbucket; | |
| wchar_t* wstr1 = L"abracadabra"; | |
| wchar_t* wstr2 = L"gilgamesh"; | |
| wchar_t* wstr3 = L"antidisestablishmentarianism"; | |
| const size_t numElements1 = wcslen(wstr1); | |
| const size_t numElements2 = wcslen(wstr2); | |
| const size_t numElements3 = wcslen(wstr3); | |
| const size_t numBytes1 = sizeof(wchar_t) * numElements1; | |
| const size_t numBytes2 = sizeof(wchar_t) * numElements2; | |
| const size_t numBytes3 = sizeof(wchar_t) * numElements3; | |
| size_t actlen1 = 0, actlen2 = 0, actlen3 = 0; | |
| wchar_t *key1 = NULL, *key2 = NULL, *key3 = NULL; | |
| key1 = malloc(numBytes1 + 2); | |
| key2 = malloc(numBytes2 + 2); | |
| key3 = malloc(numBytes3 + 2); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(key1); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(key2); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(key3); | |
| /* create one hashtable first, so in case we are tracking memory, we ensure that | |
| * the hashtable code module paths will already be cached. */ | |
| myhashtab1 = new_hashtable(16); | |
| cache_start_count = cache_count(); | |
| result1 = wcscpy_s(key1, numElements1+1, wstr1); /* wcscpy_s param 2 must be */ | |
| result2 = wcscpy_s(key2, numElements2+1, wstr2); /* number of characters + 1 */ | |
| result3 = wcscpy_s(key3, numElements3+1, wstr3); | |
| actlen1 = wcslen(key1); actlen2 = wcslen(key2); actlen3 = wcslen(key3); | |
| myhashtab2 = new_hashtable(16); | |
| myhashtab3 = new_hashtable(16); | |
| /* we should not have cached any more hashtable vtables */ | |
| cache_current_count = cache_count(); | |
| CU_ASSERT_EQUAL(cache_current_count,cache_start_count); | |
| myhashtab1->vtab->insert(myhashtab1->realtable, key1, (int)numBytes1, NULL,0,0,0); | |
| myhashtab2->vtab->insert(myhashtab2->realtable, key2, (int)numBytes2, NULL,0,0,0); | |
| myhashtab2->vtab->insert(myhashtab3->realtable, key3, (int)numBytes3, NULL,0,0,0); | |
| /* TODO instantiate some other object here which uses the cache */ | |
| destroy_hashtable(myhashtab1, TRUE, TRUE); | |
| destroy_hashtable(myhashtab2, TRUE, TRUE); | |
| destroy_hashtable(myhashtab3, TRUE, TRUE); | |
| /* note that key1 and key2 are now dangling pointers since we asked the | |
| * hashtable to free keys and values memory | |
| */ | |
| g_bytes_allocated = etch_showmem(0,0); /* verify all memory freed */ | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); /* start fresh for next test */ | |
| } | |
| /** | |
| * test_intkeys() | |
| * tests caching using integer keys as we might do for etchobjects such as vtables | |
| */ | |
| void test_intkeys(void) | |
| { | |
| int i, startsize, size; | |
| const int STARTKEY = 0, ENDKEY = 512, KEYCOUNT = ENDKEY - STARTKEY; | |
| char* teststring = "it works!"; | |
| char* item = etch_malloc(sizeof(teststring),0); | |
| memcpy(item, teststring, sizeof(teststring)); | |
| startsize = cache_count(); | |
| for(i = STARTKEY; i < ENDKEY; i++) | |
| cache_add(i, item); | |
| size = cache_count(); | |
| CU_ASSERT_EQUAL(size, KEYCOUNT + startsize); | |
| for(i = STARTKEY; i < ENDKEY; i++) | |
| CU_ASSERT_PTR_NOT_NULL(cache_find(i, 0)); | |
| for(i = STARTKEY; i < ENDKEY; i++) | |
| CU_ASSERT_PTR_NOT_NULL(cache_del(i)); | |
| size = cache_count(); | |
| CU_ASSERT_EQUAL(size, startsize); | |
| etch_free(item); | |
| g_bytes_allocated = etch_showmem(0,0); /* verify all memory freed */ | |
| CU_ASSERT_EQUAL(g_bytes_allocated, 0); | |
| memtable_clear(); /* start fresh for next test */ | |
| } | |
| /** | |
| * test_intkeys() | |
| * tests caching using string keys with no values, as we might do for source | |
| * file paths in the debug allocator | |
| */ | |
| void test_pathkeys(void) | |
| { | |
| char* path1 = "..\\..\\foo\\bar\\file1.dat"; | |
| char* path2 = "..\\..\\foo\\bar\\file2.dat"; | |
| char* path3 = "c:\\the\\quick\\brown\\fox\\jumped\\over\\the\\lazy\\dog\\file3.dat"; | |
| unsigned hash1 = 0, hash2 = 0, hash3 = 0; | |
| char* namefound = NULL; | |
| etch_hashitem hashbucket; | |
| etch_hashitem* thisitem = &hashbucket; | |
| int result = 0; | |
| int len1 = (int)strlen(path1), len2 = (int)strlen(path2), len3 = (int)strlen(path3); | |
| hash1 = cache_insertx (path1, NULL, FALSE); | |
| hash2 = cache_insertx (path2, NULL, FALSE); | |
| hash3 = cache_insertx (path3, NULL, FALSE); | |
| memset(thisitem, 0, sizeof(etch_hashitem)); | |
| cache_findx(path1, &thisitem); | |
| CU_ASSERT_PTR_NOT_NULL(thisitem->key); | |
| CU_ASSERT_EQUAL(hash1, thisitem->hash); | |
| result = strncmp(path1, thisitem->key, len1); | |
| CU_ASSERT_EQUAL(result,0); | |
| memset(thisitem, 0, sizeof(etch_hashitem)); | |
| cache_findx(path2, &thisitem); | |
| CU_ASSERT_PTR_NOT_NULL(thisitem->key); | |
| CU_ASSERT_EQUAL(hash2, thisitem->hash); | |
| result = strncmp(path2, thisitem->key, len2); | |
| CU_ASSERT_EQUAL(result,0); | |
| memset(thisitem, 0, sizeof(etch_hashitem)); | |
| cache_findx(path3, &thisitem); | |
| CU_ASSERT_PTR_NOT_NULL(thisitem->key); | |
| CU_ASSERT_EQUAL(hash3, thisitem->hash); | |
| result = strncmp(path3, thisitem->key, len3); | |
| CU_ASSERT_EQUAL(result,0); | |
| memset(thisitem, 0, sizeof(etch_hashitem)); | |
| cache_find_by_hash(hash1, &thisitem); | |
| CU_ASSERT_PTR_NOT_NULL(thisitem->key); | |
| result = strncmp(path1, thisitem->key, len1); | |
| CU_ASSERT_EQUAL(result,0); | |
| memset(thisitem, 0, sizeof(etch_hashitem)); | |
| cache_find_by_hash(hash2, &thisitem); | |
| CU_ASSERT_PTR_NOT_NULL(thisitem->key); | |
| result = strncmp(path2, thisitem->key, len2); | |
| CU_ASSERT_EQUAL(result,0); | |
| memset(thisitem, 0, sizeof(etch_hashitem)); | |
| cache_find_by_hash(hash3, &thisitem); | |
| CU_ASSERT_PTR_NOT_NULL(thisitem->key); | |
| result = strncmp(path3, thisitem->key, len3); | |
| CU_ASSERT_EQUAL(result,0); | |
| } | |
| /** | |
| * main | |
| */ | |
| int _tmain(int argc, _TCHAR* argv[]) | |
| { | |
| char c=0; | |
| CU_pSuite pSuite = NULL; | |
| g_is_automated_test = argc > 1 && 0 != wcscmp(argv[1], L"-a"); | |
| if (CUE_SUCCESS != CU_initialize_registry()) return CU_get_error(); | |
| CU_set_output_filename("../test_cache"); | |
| pSuite = CU_add_suite("suite_cache", init_suite, clean_suite); | |
| CU_add_test(pSuite, "test path strings as keys", test_pathkeys); | |
| CU_add_test(pSuite, "multiple of same object test", test_multiple_items); | |
| CU_add_test(pSuite, "integer cache key test", test_intkeys); | |
| 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(); | |
| } | |