Development mbed library for MAX32630FTHR
Dependents: blinky_max32630fthr
Diff: events/equeue/tests/tests.c
- Revision:
- 0:5c4d7b2438d3
diff -r 000000000000 -r 5c4d7b2438d3 events/equeue/tests/tests.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/events/equeue/tests/tests.c Fri Nov 11 20:59:50 2016 +0000 @@ -0,0 +1,681 @@ +/* + * Testing framework for the events library + * + * Copyright (c) 2016 Christopher Haster + * + * Licensed 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. + */ +#include "equeue.h" +#include <unistd.h> +#include <stdio.h> +#include <setjmp.h> +#include <stdint.h> +#include <stdlib.h> +#include <pthread.h> + + +// Testing setup +static jmp_buf test_buf; +static int test_line; +static int test_failure; + +#define test_assert(test) ({ \ + if (!(test)) { \ + test_line = __LINE__; \ + longjmp(test_buf, 1); \ + } \ +}) + +#define test_run(func, ...) ({ \ + printf("%s: ...", #func); \ + fflush(stdout); \ + \ + if (!setjmp(test_buf)) { \ + func(__VA_ARGS__); \ + printf("\r%s: \e[32mpassed\e[0m\n", #func); \ + } else { \ + printf("\r%s: \e[31mfailed\e[0m at line %d\n", #func, test_line); \ + test_failure = true; \ + } \ +}) + + +// Test functions +void pass_func(void *eh) { +} + +void simple_func(void *p) { + (*(int *)p)++; +} + +void sloth_func(void *p) { + usleep(10000); + (*(int *)p)++; +} + +struct indirect { + int *touched; + uint8_t buffer[7]; +}; + +void indirect_func(void *p) { + struct indirect *i = (struct indirect*)p; + (*i->touched)++; +} + +struct timing { + unsigned tick; + unsigned delay; +}; + +void timing_func(void *p) { + struct timing *timing = (struct timing*)p; + unsigned tick = equeue_tick(); + + unsigned t1 = timing->delay; + unsigned t2 = tick - timing->tick; + test_assert(t1 > t2 - 10 && t1 < t2 + 10); + + timing->tick = tick; +} + +struct fragment { + equeue_t *q; + size_t size; + struct timing timing; +}; + +void fragment_func(void *p) { + struct fragment *fragment = (struct fragment*)p; + timing_func(&fragment->timing); + + struct fragment *nfragment = equeue_alloc(fragment->q, fragment->size); + test_assert(nfragment); + + *nfragment = *fragment; + equeue_event_delay(nfragment, fragment->timing.delay); + + int id = equeue_post(nfragment->q, fragment_func, nfragment); + test_assert(id); +} + +struct cancel { + equeue_t *q; + int id; +}; + +void cancel_func(void *p) { + struct cancel *cancel = (struct cancel *)p; + equeue_cancel(cancel->q, cancel->id); +} + +struct nest { + equeue_t *q; + void (*cb)(void *); + void *data; +}; + +void nest_func(void *p) { + struct nest *nest = (struct nest *)p; + equeue_call(nest->q, nest->cb, nest->data); + + usleep(10000); +} + + +// Simple call tests +void simple_call_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + bool touched = false; + equeue_call(&q, simple_func, &touched); + equeue_dispatch(&q, 0); + test_assert(touched); + + equeue_destroy(&q); +} + +void simple_call_in_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + bool touched = false; + int id = equeue_call_in(&q, 10, simple_func, &touched); + test_assert(id); + + equeue_dispatch(&q, 15); + test_assert(touched); + + equeue_destroy(&q); +} + +void simple_call_every_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + bool touched = false; + int id = equeue_call_every(&q, 10, simple_func, &touched); + test_assert(id); + + equeue_dispatch(&q, 15); + test_assert(touched); + + equeue_destroy(&q); +} + +void simple_post_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + int touched = false; + struct indirect *i = equeue_alloc(&q, sizeof(struct indirect)); + test_assert(i); + + i->touched = &touched; + int id = equeue_post(&q, indirect_func, i); + test_assert(id); + + equeue_dispatch(&q, 0); + test_assert(*i->touched); + + equeue_destroy(&q); +} + +// Misc tests +void destructor_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + int touched; + struct indirect *e; + int ids[3]; + + touched = 0; + for (int i = 0; i < 3; i++) { + e = equeue_alloc(&q, sizeof(struct indirect)); + test_assert(e); + + e->touched = &touched; + equeue_event_dtor(e, indirect_func); + int id = equeue_post(&q, pass_func, e); + test_assert(id); + } + + equeue_dispatch(&q, 0); + test_assert(touched == 3); + + touched = 0; + for (int i = 0; i < 3; i++) { + e = equeue_alloc(&q, sizeof(struct indirect)); + test_assert(e); + + e->touched = &touched; + equeue_event_dtor(e, indirect_func); + ids[i] = equeue_post(&q, pass_func, e); + test_assert(ids[i]); + } + + for (int i = 0; i < 3; i++) { + equeue_cancel(&q, ids[i]); + } + + equeue_dispatch(&q, 0); + test_assert(touched == 3); + + touched = 0; + for (int i = 0; i < 3; i++) { + e = equeue_alloc(&q, sizeof(struct indirect)); + test_assert(e); + + e->touched = &touched; + equeue_event_dtor(e, indirect_func); + int id = equeue_post(&q, pass_func, e); + test_assert(id); + } + + equeue_destroy(&q); + test_assert(touched == 3); +} + +void allocation_failure_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + void *p = equeue_alloc(&q, 4096); + test_assert(!p); + + for (int i = 0; i < 100; i++) { + p = equeue_alloc(&q, 0); + } + test_assert(!p); + + equeue_destroy(&q); +} + +void cancel_test(int N) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + bool touched = false; + int *ids = malloc(N*sizeof(int)); + + for (int i = 0; i < N; i++) { + ids[i] = equeue_call(&q, simple_func, &touched); + } + + for (int i = N-1; i >= 0; i--) { + equeue_cancel(&q, ids[i]); + } + + free(ids); + + equeue_dispatch(&q, 0); + test_assert(!touched); + + equeue_destroy(&q); +} + +void cancel_inflight_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + bool touched = false; + + int id = equeue_call(&q, simple_func, &touched); + equeue_cancel(&q, id); + + equeue_dispatch(&q, 0); + test_assert(!touched); + + id = equeue_call(&q, simple_func, &touched); + equeue_cancel(&q, id); + + equeue_dispatch(&q, 0); + test_assert(!touched); + + struct cancel *cancel = equeue_alloc(&q, sizeof(struct cancel)); + test_assert(cancel); + cancel->q = &q; + cancel->id = 0; + + id = equeue_post(&q, cancel_func, cancel); + test_assert(id); + + cancel->id = equeue_call(&q, simple_func, &touched); + + equeue_dispatch(&q, 0); + test_assert(!touched); + + equeue_destroy(&q); +} + +void cancel_unnecessarily_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + int id = equeue_call(&q, pass_func, 0); + for (int i = 0; i < 5; i++) { + equeue_cancel(&q, id); + } + + id = equeue_call(&q, pass_func, 0); + equeue_dispatch(&q, 0); + for (int i = 0; i < 5; i++) { + equeue_cancel(&q, id); + } + + bool touched = false; + equeue_call(&q, simple_func, &touched); + for (int i = 0; i < 5; i++) { + equeue_cancel(&q, id); + } + + equeue_dispatch(&q, 0); + test_assert(touched); + + equeue_destroy(&q); +} + +void loop_protect_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + bool touched = false; + equeue_call_every(&q, 0, simple_func, &touched); + + equeue_dispatch(&q, 0); + test_assert(touched); + + touched = false; + equeue_call_every(&q, 1, simple_func, &touched); + + equeue_dispatch(&q, 0); + test_assert(touched); + + equeue_destroy(&q); +} + +void break_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + bool touched = false; + equeue_call_every(&q, 0, simple_func, &touched); + + equeue_break(&q); + equeue_dispatch(&q, -1); + test_assert(touched); + + equeue_destroy(&q); +} + +void period_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + int count = 0; + equeue_call_every(&q, 10, simple_func, &count); + + equeue_dispatch(&q, 55); + test_assert(count == 5); + + equeue_destroy(&q); +} + +void nested_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + int touched = 0; + struct nest *nest = equeue_alloc(&q, sizeof(struct nest)); + test_assert(nest); + nest->q = &q; + nest->cb = simple_func; + nest->data = &touched; + + int id = equeue_post(&q, nest_func, nest); + test_assert(id); + + equeue_dispatch(&q, 5); + test_assert(touched == 0); + + equeue_dispatch(&q, 5); + test_assert(touched == 1); + + touched = 0; + nest = equeue_alloc(&q, sizeof(struct nest)); + test_assert(nest); + nest->q = &q; + nest->cb = simple_func; + nest->data = &touched; + + id = equeue_post(&q, nest_func, nest); + test_assert(id); + + equeue_dispatch(&q, 20); + test_assert(touched == 1); + + equeue_destroy(&q); +} + +void sloth_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + int touched = 0; + int id = equeue_call(&q, sloth_func, &touched); + test_assert(id); + + id = equeue_call_in(&q, 5, simple_func, &touched); + test_assert(id); + + id = equeue_call_in(&q, 15, simple_func, &touched); + test_assert(id); + + equeue_dispatch(&q, 20); + test_assert(touched == 3); + + equeue_destroy(&q); +} + +void *multithread_thread(void *p) { + equeue_t *q = (equeue_t *)p; + equeue_dispatch(q, -1); + return 0; +} + +void multithread_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + int touched = 0; + equeue_call_every(&q, 1, simple_func, &touched); + + pthread_t thread; + err = pthread_create(&thread, 0, multithread_thread, &q); + test_assert(!err); + + usleep(10000); + equeue_break(&q); + err = pthread_join(thread, 0); + test_assert(!err); + + test_assert(touched); + + equeue_destroy(&q); +} + +void background_func(void *p, int ms) { + *(unsigned *)p = ms; +} + +void background_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + int id = equeue_call_in(&q, 20, pass_func, 0); + test_assert(id); + + unsigned ms; + equeue_background(&q, background_func, &ms); + test_assert(ms == 20); + + id = equeue_call_in(&q, 10, pass_func, 0); + test_assert(id); + test_assert(ms == 10); + + id = equeue_call(&q, pass_func, 0); + test_assert(id); + test_assert(ms == 0); + + equeue_dispatch(&q, 0); + test_assert(ms == 10); + + equeue_destroy(&q); + test_assert(ms == -1); +} + +void chain_test(void) { + equeue_t q1; + int err = equeue_create(&q1, 2048); + test_assert(!err); + + equeue_t q2; + err = equeue_create(&q2, 2048); + test_assert(!err); + + equeue_chain(&q2, &q1); + + int touched = 0; + + int id1 = equeue_call_in(&q1, 20, simple_func, &touched); + int id2 = equeue_call_in(&q2, 20, simple_func, &touched); + test_assert(id1 && id2); + + id1 = equeue_call(&q1, simple_func, &touched); + id2 = equeue_call(&q2, simple_func, &touched); + test_assert(id1 && id2); + + id1 = equeue_call_in(&q1, 5, simple_func, &touched); + id2 = equeue_call_in(&q2, 5, simple_func, &touched); + test_assert(id1 && id2); + + equeue_cancel(&q1, id1); + equeue_cancel(&q2, id2); + + id1 = equeue_call_in(&q1, 10, simple_func, &touched); + id2 = equeue_call_in(&q2, 10, simple_func, &touched); + test_assert(id1 && id2); + + equeue_dispatch(&q1, 30); + + test_assert(touched == 6); +} + +// Barrage tests +void simple_barrage_test(int N) { + equeue_t q; + int err = equeue_create(&q, N*(EQUEUE_EVENT_SIZE+sizeof(struct timing))); + test_assert(!err); + + for (int i = 0; i < N; i++) { + struct timing *timing = equeue_alloc(&q, sizeof(struct timing)); + test_assert(timing); + + timing->tick = equeue_tick(); + timing->delay = (i+1)*100; + equeue_event_delay(timing, timing->delay); + equeue_event_period(timing, timing->delay); + + int id = equeue_post(&q, timing_func, timing); + test_assert(id); + } + + equeue_dispatch(&q, N*100); + + equeue_destroy(&q); +} + +void fragmenting_barrage_test(int N) { + equeue_t q; + int err = equeue_create(&q, + 2*N*(EQUEUE_EVENT_SIZE+sizeof(struct fragment)+N*sizeof(int))); + test_assert(!err); + + for (int i = 0; i < N; i++) { + size_t size = sizeof(struct fragment) + i*sizeof(int); + struct fragment *fragment = equeue_alloc(&q, size); + test_assert(fragment); + + fragment->q = &q; + fragment->size = size; + fragment->timing.tick = equeue_tick(); + fragment->timing.delay = (i+1)*100; + equeue_event_delay(fragment, fragment->timing.delay); + + int id = equeue_post(&q, fragment_func, fragment); + test_assert(id); + } + + equeue_dispatch(&q, N*100); + + equeue_destroy(&q); +} + +struct ethread { + pthread_t thread; + equeue_t *q; + int ms; +}; + +static void *ethread_dispatch(void *p) { + struct ethread *t = (struct ethread*)p; + equeue_dispatch(t->q, t->ms); + return 0; +} + +void multithreaded_barrage_test(int N) { + equeue_t q; + int err = equeue_create(&q, N*(EQUEUE_EVENT_SIZE+sizeof(struct timing))); + test_assert(!err); + + struct ethread t; + t.q = &q; + t.ms = N*100; + err = pthread_create(&t.thread, 0, ethread_dispatch, &t); + test_assert(!err); + + for (int i = 0; i < N; i++) { + struct timing *timing = equeue_alloc(&q, sizeof(struct timing)); + test_assert(timing); + + timing->tick = equeue_tick(); + timing->delay = (i+1)*100; + equeue_event_delay(timing, timing->delay); + equeue_event_period(timing, timing->delay); + + int id = equeue_post(&q, timing_func, timing); + test_assert(id); + } + + err = pthread_join(t.thread, 0); + test_assert(!err); + + equeue_destroy(&q); +} + + +int main() { + printf("beginning tests...\n"); + + test_run(simple_call_test); + test_run(simple_call_in_test); + test_run(simple_call_every_test); + test_run(simple_post_test); + test_run(destructor_test); + test_run(allocation_failure_test); + test_run(cancel_test, 20); + test_run(cancel_inflight_test); + test_run(cancel_unnecessarily_test); + test_run(loop_protect_test); + test_run(break_test); + test_run(period_test); + test_run(nested_test); + test_run(sloth_test); + test_run(background_test); + test_run(chain_test); + test_run(multithread_test); + test_run(simple_barrage_test, 20); + test_run(fragmenting_barrage_test, 20); + test_run(multithreaded_barrage_test, 20); + + printf("done!\n"); + return test_failure; +}