| /* $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_queue.c | |
| */ | |
| #include "apr_time.h" /* some apr must be included first */ | |
| #include "etch_queue.h" | |
| #include <tchar.h> | |
| #include <stdio.h> | |
| #include <conio.h> | |
| #include "cunit.h" | |
| #include "basic.h" | |
| #include "automated.h" | |
| #include "etchthread.h" | |
| #include "etch_global.h" | |
| #include "etch_threadpool_apr.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; | |
| } | |
| /* - - - - - - - - - - - - - - | |
| * unit test support | |
| * - - - - - - - - - - - - - - | |
| */ | |
| #define NUMBER_CONSUMERS 2 | |
| #define CONSUMER_ACTIVITY 4 | |
| #define NUMBER_PRODUCERS 3 | |
| #define PRODUCER_ACTIVITY 5 | |
| #define ERROR_BAD_THREADDATA 1 | |
| int g_error, g_producercount, g_consumercount, g_unique_id, g_pro, g_con; | |
| unsigned next_test_id() | |
| { | |
| apr_atomic_inc32 ((volatile apr_uint32_t*) &g_unique_id); | |
| return g_unique_id; | |
| } | |
| unsigned next_producer() | |
| { | |
| apr_atomic_inc32 ((volatile apr_uint32_t*) &g_pro); | |
| return g_pro; | |
| } | |
| unsigned next_consumer() | |
| { | |
| apr_atomic_inc32 ((volatile apr_uint32_t*) &g_con); | |
| return g_con; | |
| } | |
| /** | |
| * push_some_objects() | |
| * push specified number of new objects onto the queue | |
| */ | |
| int push_some_objects(etch_queue* q, const int howmany) | |
| { | |
| int count = 0, result = 0; | |
| while(count < howmany && result == 0) | |
| result = etchqueue_put_withwait(q, ETCH_NOWAIT, new_int32(count++)); | |
| return result; | |
| } | |
| /** | |
| * pop_some_objects() | |
| * pop specified number of objects off the queue and destroy each object retrieved | |
| */ | |
| int pop_some_objects(etch_queue* q, const int howmany) | |
| { | |
| int count = 0, result = 0; | |
| objmask* thisobj = NULL; | |
| while(count++ < howmany && result == 0) | |
| { | |
| result = etchqueue_get_withwait(q, ETCH_NOWAIT, &thisobj); | |
| if (thisobj) | |
| thisobj->destroy(thisobj); | |
| thisobj = NULL; | |
| } | |
| return result; | |
| } | |
| /** | |
| * pop_all_objects() | |
| * pop all objects off the queue and destroy each object retrieved | |
| */ | |
| int pop_all_objects(etch_queue* q) | |
| { | |
| int popcount = 0, result = 0; | |
| const int queuecount = etchqueue_size(q); | |
| objmask* thisobj = NULL; | |
| while(popcount++ < queuecount && result == 0) | |
| { | |
| result = etchqueue_get_withwait(q, ETCH_NOWAIT, &thisobj); | |
| if (thisobj) | |
| thisobj->destroy(thisobj); | |
| thisobj = NULL; | |
| } | |
| return result; | |
| } | |
| /** | |
| * mytest_threaddata() | |
| * thread data for our test threads. we could pass the queue directly, but then | |
| * we'd have to configure the threadpool to not destroy a thread's data. | |
| */ | |
| typedef struct mytest_threaddata { etch_queue* queue; } mytest_threaddata; | |
| /** | |
| * new_testdata() | |
| * constructor for test thread data | |
| */ | |
| mytest_threaddata* new_testdata(etch_queue* q) | |
| { | |
| mytest_threaddata* td = etch_malloc(sizeof(mytest_threaddata), 0); | |
| td->queue = q; | |
| return td; | |
| } | |
| /** | |
| * consumer_threadproc() | |
| * consumer thread procedure. derived from APR queue test. | |
| */ | |
| void consumer_threadproc(void *data) | |
| { | |
| etch_threadparams* params = (etch_threadparams*) data; | |
| etch_queue* queue = (etch_queue*) params->data; | |
| int result = 0; | |
| const int thisthread = next_consumer(); | |
| etch_int32* content = NULL; | |
| int64 sleeprate = 1000000/CONSUMER_ACTIVITY; | |
| if (!is_etch_queue(queue)) /* can't test assert outside main thread */ | |
| { | |
| printf("consumer_threadproc data is not etch_queue\n"); | |
| g_error = ERROR_BAD_THREADDATA; | |
| return; | |
| } | |
| #if IS_DEBUG_CONSOLE | |
| printf("start consumer thread %d\n", thisthread); fflush(stdout); | |
| #endif | |
| etch_sleep((rand() % 4) * 1000); /* sleep random seconds */ | |
| while(-1 != result) /* until shutdown ... */ | |
| { | |
| g_consumercount++; | |
| switch(result = etchqueue_get(queue, &content)) | |
| { | |
| case -1: | |
| break; /* shutdown or error */ | |
| case ETCH_QUEUE_OPERATION_CANCELED: | |
| continue; /* signaled */ | |
| case ETCH_QUEUE_OPERATION_TIMEOUT: | |
| default: /* timed out (try again) or success */ | |
| if (content) | |
| { | |
| #if IS_DEBUG_CONSOLE | |
| printf("consumer thread %d popped content %d\n", thisthread, content->value); fflush(stdout); | |
| #endif | |
| content->destroy(content); | |
| content = NULL; | |
| } | |
| apr_sleep(sleeprate); /* sleep this long to achieve our rate */ | |
| } | |
| } | |
| #if IS_DEBUG_CONSOLE | |
| printf("consumer thread %d exit\n", thisthread); fflush(stdout); | |
| #endif | |
| } | |
| /** | |
| * producer_threadproc() | |
| * producer thread procedure. derived from APR queue test. | |
| */ | |
| void producer_threadproc(void *data) | |
| { | |
| etch_threadparams* params = (etch_threadparams*) data; | |
| etch_queue* queue = (etch_queue*) params->data; | |
| int result = 0, is_timeout = 0; | |
| etch_int32* newcontent = NULL; | |
| const int thisthread = next_producer(); | |
| int64 sleeprate = 1000000/PRODUCER_ACTIVITY; | |
| if (!is_etch_queue(queue)) /* can't test assert outside main thread */ | |
| { printf("producer_threadproc data is not etch_queue\n"); | |
| g_error = ERROR_BAD_THREADDATA; | |
| return; | |
| } | |
| #if IS_DEBUG_CONSOLE | |
| printf("start producer thread %d\n", thisthread); fflush(stdout); | |
| #endif | |
| etch_sleep((rand() % 4) * 1000); /* sleep random seconds */ | |
| while(-1 != result) /* until shutdown ... */ | |
| { | |
| g_producercount++; | |
| if (is_timeout) | |
| { | |
| #ifdef IS_DEBUG_CONSOLE | |
| printf("producer thread %d re-pushing content %d\n", thisthread, newcontent->value); fflush(stdout); | |
| #endif | |
| is_timeout = FALSE; | |
| } | |
| else | |
| { newcontent = new_int32(next_test_id()); | |
| #if IS_DEBUG_CONSOLE | |
| printf("producer thread %d pushing content %d\n", thisthread, newcontent->value); fflush(stdout); | |
| #endif | |
| } | |
| switch(result = etchqueue_put(queue, newcontent)) | |
| { | |
| case -1: /* queue closed or error */ | |
| newcontent->destroy(newcontent); | |
| newcontent = NULL; | |
| break; | |
| case ETCH_QUEUE_OPERATION_CANCELED: | |
| newcontent->destroy(newcontent); | |
| newcontent = NULL; | |
| continue; /* signaled */ | |
| case ETCH_QUEUE_OPERATION_TIMEOUT: | |
| is_timeout = TRUE; /* fall thru ... */ | |
| default: /* timeout or success */ | |
| apr_sleep(sleeprate); | |
| } | |
| } | |
| #if IS_DEBUG_CONSOLE | |
| printf("producer thread %d exit\n", thisthread); fflush(stdout); | |
| #endif | |
| } | |
| /* - - - - - - - - - - - - - - | |
| * unit tests | |
| * - - - - - - - - - - - - - - | |
| */ | |
| /** | |
| * test_constructor | |
| */ | |
| void test_constructor(void) | |
| { | |
| etch_queue* q = new_queue(ETCH_DEFSIZE); | |
| CU_ASSERT_PTR_NOT_NULL_FATAL(q); | |
| q->destroy(q); | |
| 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_putget() | |
| */ | |
| void test_putget(void) | |
| { | |
| int result = 0, qdepth = 0; | |
| etch_int32* thiscontent = NULL; | |
| etch_int32* thiscontent1 = new_int32(1); | |
| etch_int32* thiscontent2 = new_int32(2); | |
| etch_queue* q = new_queue(ETCH_DEFSIZE); | |
| result = etchqueue_put(q, thiscontent1); | |
| CU_ASSERT_EQUAL(result,0); | |
| qdepth = etchqueue_size(q); | |
| CU_ASSERT_EQUAL(qdepth,1); | |
| result = etchqueue_put(q, thiscontent2); | |
| CU_ASSERT_EQUAL(result,0); | |
| qdepth = etchqueue_size(q); | |
| CU_ASSERT_EQUAL(qdepth,2); | |
| result = etchqueue_get(q, &thiscontent); | |
| CU_ASSERT_EQUAL(result,0); | |
| result = is_etch_int32(thiscontent); | |
| CU_ASSERT_EQUAL(result, TRUE); | |
| CU_ASSERT_EQUAL(thiscontent->value, thiscontent1->value); | |
| qdepth = etchqueue_size(q); | |
| CU_ASSERT_EQUAL(qdepth,1); | |
| result = etchqueue_get(q, &thiscontent); | |
| CU_ASSERT_EQUAL(result,0); | |
| result = is_etch_int32(thiscontent); | |
| CU_ASSERT_EQUAL(result, TRUE); | |
| CU_ASSERT_EQUAL(thiscontent->value, thiscontent2->value); | |
| qdepth = etchqueue_size(q); | |
| CU_ASSERT_EQUAL(qdepth,0); | |
| q->destroy(q); | |
| thiscontent1->destroy(thiscontent1); | |
| thiscontent2->destroy(thiscontent2); | |
| 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_bulkputget() | |
| */ | |
| void test_bulkputget(void) | |
| { | |
| int result = 0, qdepth = 0; | |
| const int HOWMANY = 3; | |
| etch_queue* q = new_queue(HOWMANY); | |
| result = push_some_objects(q, HOWMANY); | |
| CU_ASSERT_EQUAL(result,0); | |
| qdepth = etchqueue_size(q); | |
| CU_ASSERT_EQUAL(qdepth, HOWMANY); | |
| result = pop_some_objects(q, HOWMANY); | |
| CU_ASSERT_EQUAL(result,0); | |
| qdepth = etchqueue_size(q); | |
| CU_ASSERT_EQUAL(qdepth, 0); | |
| q->destroy(q); | |
| 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_destroy_nonempty_queue() | |
| * destroy a nonempty queue and ensure content memory is freed as expected | |
| */ | |
| void test_destroy_nonempty_queue(void) | |
| { | |
| int result = 0, qdepth = 0; | |
| const int HOWMANY = 3; | |
| etch_queue* q = new_queue(HOWMANY); | |
| result = push_some_objects(q, HOWMANY); | |
| q->destroy(q); | |
| 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_queue_overflow() | |
| */ | |
| void test_queue_overflow(void) | |
| { | |
| int result = 0, qdepth = 0; | |
| const int MAXDEPTH = 2; | |
| etch_int32* newcontent = NULL; | |
| etch_queue* q = new_queue(MAXDEPTH); | |
| result = etchqueue_put_withwait(q, ETCH_NOWAIT, newcontent = new_int32(0)); | |
| CU_ASSERT_EQUAL(result, 0); | |
| result = etchqueue_put_withwait(q, ETCH_NOWAIT, newcontent = new_int32(0)); | |
| CU_ASSERT_EQUAL(result, 0); | |
| result = etchqueue_try_put(q, newcontent = new_int32(0)); | |
| CU_ASSERT_EQUAL(result, -1); | |
| result = etchqueue_put_withwait(q, ETCH_NOWAIT, newcontent); | |
| CU_ASSERT_NOT_EQUAL_FATAL(result, 0); | |
| newcontent->destroy(newcontent); | |
| qdepth = etchqueue_size(q); | |
| CU_ASSERT_EQUAL(qdepth, MAXDEPTH); | |
| q->destroy(q); | |
| 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_queue_underflow() | |
| */ | |
| void test_queue_underflow(void) | |
| { | |
| int result = 0, qdepth = 0; | |
| const int MAXDEPTH = 2; | |
| etch_int32* content = NULL; | |
| etch_queue* q = new_queue(MAXDEPTH); | |
| result = etchqueue_put_withwait(q, ETCH_NOWAIT, content = new_int32(0)); | |
| CU_ASSERT_EQUAL(result, 0); | |
| result = etchqueue_get_withwait(q, ETCH_NOWAIT, &content); | |
| CU_ASSERT_EQUAL(result, 0); | |
| content->destroy(content); | |
| content = NULL; | |
| result = etchqueue_try_get(q, &content); | |
| CU_ASSERT_EQUAL(result, -1); | |
| result = etchqueue_get_withwait(q, ETCH_NOWAIT, &content); | |
| CU_ASSERT_NOT_EQUAL_FATAL(result, 0); | |
| qdepth = etchqueue_size(q); | |
| CU_ASSERT_EQUAL(qdepth, 0); | |
| q->destroy(q); | |
| 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_iterator() | |
| */ | |
| void test_iterator(void) | |
| { | |
| etch_iterator iterator; | |
| int iterated = 0; | |
| const int HOWMANY = 3; | |
| etch_queue* q = new_queue(ETCH_DEFSIZE); | |
| push_some_objects(q, HOWMANY); | |
| set_iterator(&iterator, q, &q->iterable); | |
| while(iterator.has_next(&iterator)) | |
| { | |
| etch_int32* content = (etch_int32*) iterator.current_value; | |
| CU_ASSERT_EQUAL_FATAL(is_etch_int32(content), TRUE); | |
| CU_ASSERT_EQUAL(content->value, iterated++); | |
| iterator.next(&iterator); | |
| } | |
| CU_ASSERT_EQUAL(iterated, HOWMANY); | |
| q->destroy(q); | |
| 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_blocking() | |
| * derived from apr test_queue. | |
| * launch some number of threads which blocking write etch objects to a queue, | |
| * and some number of threads which blocking read from the same queue. let the | |
| * threads do their thing for a while, and close the queue. verify that all | |
| * memory is accounted for. | |
| */ | |
| void test_blocking(void) | |
| { | |
| #define NUMTHREADS NUMBER_CONSUMERS + NUMBER_PRODUCERS | |
| int result = 0, i = 0; | |
| etch_queue* myqueue = NULL; | |
| etch_threadpool* threadpool = new_threadpool(ETCH_THREADPOOLTYPE_FREE, ETCH_DEFSIZE); | |
| threadpool->is_free_data = FALSE; /* thread data will be our queue */ | |
| myqueue = new_queue(ETCH_DEFSIZE); /* create the single queue */ | |
| srand((unsigned int)apr_time_now()); | |
| g_error = g_producercount = g_consumercount = g_unique_id = g_pro = g_con = 0; | |
| for(i=0; i < NUMBER_CONSUMERS; i++) /* launch all consumer threads */ | |
| { void* newthread = threadpool->run(threadpool, consumer_threadproc, myqueue); | |
| CU_ASSERT_PTR_NOT_NULL(newthread); | |
| } | |
| for(i=0; i < NUMBER_PRODUCERS; i++) /* launch all producer threads */ | |
| { void* newthread = threadpool->run(threadpool, producer_threadproc, myqueue); | |
| CU_ASSERT_PTR_NOT_NULL(newthread); | |
| } | |
| etch_sleep(3000); /* pause long enough to let the threads do some work */ | |
| result = etchqueue_close(myqueue, TRUE); /* mark queue closed and notify all waiters */ | |
| /* pause to observe waiter threads unblocking | |
| etch_sleep(3000); | |
| */ | |
| #ifdef TEST_BLOCKING_PRECLEAR | |
| while(1) | |
| { /* when the queue is closed it clears the queue similarly to this. | |
| * this code is here to verify clearing the queue prior to closing it. | |
| */ | |
| etch_int32* queuecontent = NULL; | |
| result = etchqueue_get_withwait(myqueue, ETCHQUEUE_CLEARING_CLOSED_QUEUE, &queuecontent); | |
| CU_ASSERT_EQUAL(result, -1); | |
| if (-1 == result) break; | |
| queuecontent->destroy(queuecontent); | |
| } | |
| #endif | |
| /* note that we should always destroy (or otherwise join) the queue | |
| * accessor threads, before destroying the queue, as we do here */ | |
| threadpool->destroy(threadpool); | |
| myqueue->destroy(myqueue); | |
| 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 */ | |
| } | |
| /* - - - - - - - - - - - - - - - - - - | |
| * APR queue test with APR threadpool | |
| * - - - - - - - - - - - - - - - - - - | |
| */ | |
| etch_apr_queue_t* g_queue; /* global queue for APR queue test */ | |
| /** | |
| * aprqtest_consumer_thread() | |
| */ | |
| static void *APR_THREAD_FUNC aprqtest_consumer_thread(apr_thread_t *thd, void *data) | |
| { | |
| long sleeprate; | |
| apr_status_t rv; | |
| void *v; | |
| sleeprate = 1000000/CONSUMER_ACTIVITY; | |
| apr_sleep((rand() % 4) * 1000000); /* sleep random seconds */ | |
| while (1) | |
| { | |
| rv = etch_apr_queue_pop(g_queue, 0, &v); | |
| if (rv == APR_EINTR) | |
| continue; | |
| if (rv == APR_EOF) | |
| break; | |
| apr_sleep(sleeprate); /* sleep this long to acheive our rate */ | |
| } | |
| return NULL; | |
| } | |
| /** | |
| * aprqtest_producer_thread() | |
| */ | |
| static void * APR_THREAD_FUNC aprqtest_producer_thread(apr_thread_t *thd, void *data) | |
| { | |
| long sleeprate; | |
| apr_status_t rv; | |
| sleeprate = 1000000/PRODUCER_ACTIVITY; | |
| apr_sleep((rand() % 4) * 1000000); /* sleep random seconds */ | |
| while (1) | |
| { | |
| rv = etch_apr_queue_push(g_queue, 0, NULL); | |
| if (rv == APR_EINTR) | |
| continue; | |
| if (rv == APR_EOF) | |
| break; | |
| apr_sleep(sleeprate); /* sleep this long to acheive our rate */ | |
| } | |
| return NULL; | |
| } | |
| /** | |
| * test_queue_producer_consumer() | |
| * this is the APR queue test modified to use etch_apr_queue calls. | |
| * it uses the APR threadpool | |
| */ | |
| void test_queue_producer_consumer(void) | |
| { | |
| unsigned int i; | |
| apr_status_t rv; | |
| apr_thread_pool_t *threadpool; | |
| const int QUEUE_SIZE = 32, ZEROPRIORITY = 0; | |
| void* NULLPARAM = NULL, *NULLOWNER = NULL; | |
| srand((unsigned int)apr_time_now()); | |
| g_error = g_producercount = g_consumercount = g_unique_id = g_pro = g_con = 0; | |
| rv = etch_apr_queue_create(&g_queue, QUEUE_SIZE, etch_apr_mempool); | |
| CU_ASSERT_EQUAL(rv,0); | |
| rv = etch_apr_thread_pool_create(&threadpool, 0, NUMBER_CONSUMERS + NUMBER_PRODUCERS, etch_apr_mempool); | |
| CU_ASSERT_EQUAL(rv,0); | |
| for (i = 0; i < NUMBER_CONSUMERS; i++) | |
| { | |
| rv = etch_apr_thread_pool_push(threadpool, aprqtest_consumer_thread, NULLPARAM, ZEROPRIORITY, NULLOWNER); | |
| CU_ASSERT_EQUAL(rv,0); | |
| } | |
| for (i = 0; i < NUMBER_PRODUCERS; i++) | |
| { | |
| rv = etch_apr_thread_pool_push(threadpool, aprqtest_producer_thread, NULLPARAM, ZEROPRIORITY, NULLOWNER); | |
| CU_ASSERT_EQUAL(rv,0); | |
| } | |
| etch_sleep(5000); /* let threads work for a bit */ | |
| rv = etch_apr_queue_term(g_queue); | |
| CU_ASSERT_EQUAL(rv,0); | |
| /* pause to observe waiter threads unblocking | |
| etch_sleep(3000); | |
| */ | |
| rv = etch_apr_thread_pool_destroy(threadpool); | |
| CU_ASSERT_EQUAL(rv,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 0; | |
| pSuite = CU_add_suite("queue test suite", init_suite, clean_suite); | |
| CU_set_output_filename("../test_queue"); | |
| etch_watch_id = 0; | |
| CU_add_test(pSuite, "test constructor", test_constructor); | |
| CU_add_test(pSuite, "test put and get", test_putget); | |
| CU_add_test(pSuite, "test bulk put and get", test_bulkputget); | |
| CU_add_test(pSuite, "test destroy nonempty", test_destroy_nonempty_queue); | |
| CU_add_test(pSuite, "test overflow", test_queue_overflow); | |
| CU_add_test(pSuite, "test underflow", test_queue_underflow); | |
| CU_add_test(pSuite, "test iterator", test_iterator); | |
| CU_add_test(pSuite, "test blocking", test_blocking); | |
| CU_add_test(pSuite, "test producer-consumer (APR)", test_queue_producer_consumer); | |
| 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(); printf("\n"); } | |
| CU_cleanup_registry(); | |
| return CU_get_error(); | |
| } |