diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 11 | ||||
-rw-r--r-- | src/channel.c | 83 | ||||
-rw-r--r-- | src/channel.h | 20 | ||||
-rw-r--r-- | src/channel.test.c | 6 | ||||
-rw-r--r-- | src/mossrose.c | 132 | ||||
-rw-r--r-- | src/mutex.c | 58 | ||||
-rw-r--r-- | src/mutex.h | 28 | ||||
-rw-r--r-- | src/sound.c | 44 | ||||
-rw-r--r-- | src/sound.h | 9 | ||||
-rw-r--r-- | src/sound.test.c | 31 | ||||
-rw-r--r-- | src/test/lily-test.c | 365 | ||||
-rw-r--r-- | src/test/lily-test.h | 263 | ||||
-rw-r--r-- | src/test/mossrose-test.c | 8 | ||||
-rw-r--r-- | src/test/mossrose-test.h | 13 |
14 files changed, 755 insertions, 316 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 069d00a..cca9610 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,15 @@ project(mossrose) target_sources(mossrose PUBLIC ${CMAKE_CURRENT_LIST_DIR}/mossrose.c - ${CMAKE_CURRENT_LIST_DIR}/mutex.c ${CMAKE_CURRENT_LIST_DIR}/channel.c ) + + +if (MOSSROSE_BUILD_TESTS) + target_sources(test PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/test/lily-test.c + ${CMAKE_CURRENT_LIST_DIR}/test/mossrose-test.c + ${CMAKE_CURRENT_LIST_DIR}/channel.test.c + ${CMAKE_CURRENT_LIST_DIR}/sound.test.c + ) +endif() diff --git a/src/channel.c b/src/channel.c index 169b245..fd6741b 100644 --- a/src/channel.c +++ b/src/channel.c @@ -1,85 +1,8 @@ #include <stdlib.h> #include <string.h> -#include "mutex.h" +#include <plibsys.h> #include "channel.h" -void mossrose_channel_init(struct mossrose_channel_t *chan) -{ - mossrose_mutex_init(&(chan->mutex)); - chan->left = NULL; - chan->right = NULL; - chan->n_samples = 0; - chan->pos = 0; -} - - -int mossrose_channel_set(struct mossrose_channel_t *chan, float *left, float *right, size_t len, int force) -{ - int result = 0; - mossrose_mutex_lock(&(chan->mutex)); - if (chan->n_samples != 0 && !force) { - /* channel is still playing! */ - result = 1; goto unlock; - } - - chan->n_samples = len; - chan->pos = 0; - - /* left channel */ - if (chan->left != NULL) free(chan->left); - if (left == NULL) - chan->left = NULL; - else { - chan->left = malloc(len * sizeof(float)); - if (chan->left == NULL) { result = 2; goto unlock; } - memcpy(chan->left, left, len * sizeof(float)); - } - - /* right channel */ - if (chan->right != NULL) free(chan->right); - if (right == NULL) - chan->right = NULL; - else { - chan->right = malloc(len * sizeof(float)); - if (chan->right == NULL) { result = 3; goto unlock; } - memcpy(chan->right, right, len * sizeof(float)); - } - - unlock: - mossrose_mutex_unlock(&(chan->mutex)); - return result; -} - - -void mossrose_channel_reset(struct mossrose_channel_t *chan) -{ - chan->n_samples = 0; - chan->pos = 0; -} - - -int mossrose_channel_advance(float *left, float *right, struct mossrose_channel_t *chan) -{ - if (chan->pos >= chan->n_samples) return 1; - if (chan->left != NULL) - *left = chan->left[chan->pos]; - else - *left = 0; - if (chan->right != NULL) - *right = chan->right[chan->pos]; - else - *right = 0; - chan->pos += 1; - return 0; -} - - -void mossrose_channel_destroy(struct mossrose_channel_t *chan) -{ - mossrose_mutex_destroy(&(chan->mutex)); - if (chan->left != NULL) free(chan->left); - if (chan->right != NULL) free(chan->right); -} - - +struct channel_t { +}; diff --git a/src/channel.h b/src/channel.h index 23c11d6..6266a10 100644 --- a/src/channel.h +++ b/src/channel.h @@ -2,25 +2,7 @@ #define MOSSROSE_CHANNEL_H #include <stddef.h> -#include "mutex.h" +#include <plibsys.h> -struct mossrose_channel_t { - mossrose_mutex_t mutex; - float *left; - float *right; - size_t n_samples; - size_t pos; -}; - - -void mossrose_channel_init(struct mossrose_channel_t *chan); - -int mossrose_channel_set(struct mossrose_channel_t *chan, float *left, float *right, size_t len, int force); - -void mossrose_channel_reset(struct mossrose_channel_t *chan); - -int mossrose_channel_advance(float *left, float *right, struct mossrose_channel_t *chan); - -void mossrose_channel_destroy(struct mossrose_channel_t *chan); #endif diff --git a/src/channel.test.c b/src/channel.test.c new file mode 100644 index 0000000..96b7364 --- /dev/null +++ b/src/channel.test.c @@ -0,0 +1,6 @@ +#include "test/mossrose-test.h" + + +void suite_channel() +{ +} diff --git a/src/mossrose.c b/src/mossrose.c index 4f31711..40f4393 100644 --- a/src/mossrose.c +++ b/src/mossrose.c @@ -1,149 +1,21 @@ #include <stdio.h> #include <stdlib.h> +#include <plibsys.h> #include <portaudio.h> #include <mossrose.h> -#include "mutex.h" #include "channel.h" -/* ~~~~~~~~~~~~~~~~ type definitions ~~~~~~~~~~~~~~~~ */ - -/* audio output */ -struct audio_output_t { - float l; - float r; -}; - - - -/* ~~~~~~~~~~~~~~~~ globals ~~~~~~~~~~~~~~~~ */ - -struct mossrose_globals_t { - PaStream *stream; - struct mossrose_channel_t *channels; - int n_channels; -} mossrose_global; - - -struct audio_output_t build_sample() -{ - struct audio_output_t out; - out.l = 0; out.r = 0; - - /* loop variables */ - struct mossrose_channel_t *chan; - float chan_l, chan_r; - - for (int i=0; i<mossrose_global.n_channels; i++) { - chan = mossrose_global.channels + i; - if (mossrose_mutex_trylock(&(chan->mutex)) != 0) { - /* can't lock the mutex, this channel is being modified */ - printf("can't lock channel %d\n", i); - continue; - } - - if (chan->n_samples == 0) { - /* channel is not currently in use, skip */ - } - else { - if (mossrose_channel_advance(&chan_l, &chan_r, chan) != 0) - /* channel is done playing, reset */ - mossrose_channel_reset(chan); - else { - out.l += chan_l; - out.r += chan_r; - } - } - - mossrose_mutex_unlock(&(chan->mutex)); - } - - return out; -} - - -static int callback( - const void *input, - void *output, - unsigned long frame_count, - const PaStreamCallbackTimeInfo *time_info, - void *userdata) -{ - struct audio_output_t *out = output; - struct audio_output_t sample; - - for (int i=0; i<frame_count; i++) { - sample = build_sample(); - out[i].l = sample.l; - out[i].r = sample.r; - } - return 0; -} - - int mossrose_init(double sample_rate, int n_channels) { - /* initialize channels */ - mossrose_global.n_channels = n_channels; - mossrose_global.channels = malloc(n_channels * sizeof(struct mossrose_channel_t)); - if (mossrose_global.channels == NULL) { - fprintf(stderr, "failed to allocate memory for %d channels", n_channels); - return 1; - } - for (int i=0; i<n_channels; i++) { - mossrose_channel_init(mossrose_global.channels + i); - } - - PaError err; - - /* initialize portaudio */ - err = Pa_Initialize(); - if (err != paNoError) { - fprintf(stderr, "failed to initialize PortAudio!\n"); - return 1; - } - - /* open stream */ - err = Pa_OpenDefaultStream(&(mossrose_global.stream), 0, 2, paFloat32, sample_rate, 0, callback, NULL); - if (err != paNoError) { - fprintf(stderr, "failed to open audio stream!\n"); - return 1; - } - - /* start stream */ - err = Pa_StartStream(mossrose_global.stream); - if (err != paNoError) { - fprintf(stderr, "failed to start audio stream!\n"); - return 1; - } - - return 0; }; -int mossrose_play(float *left, float *right, size_t len, int channel) +int mossrose_play(struct mossrose_sound_t *sound, int channel) { - if (channel > 0) { - if (mossrose_channel_set(mossrose_global.channels+channel, left, right, len, 1) == 0) - return channel; - else - return -1; - } - else { - struct mossrose_channel_t *chan; - for (int i=0; i<mossrose_global.n_channels; i++) { - chan = mossrose_global.channels + i; - if (mossrose_channel_set(chan, left, right, len, 0) == 0) return i; - } - return -1; - } } int mossrose_terminate(double sample_rate, int n_channels) { - Pa_AbortStream(mossrose_global.stream); - Pa_CloseStream(mossrose_global.stream); - Pa_Terminate(); - return 0; } diff --git a/src/mutex.c b/src/mutex.c deleted file mode 100644 index 4a51fa8..0000000 --- a/src/mutex.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "mutex.h" - -#ifdef WIN32 -#include <windows.h> -void mossrose_mutex_init(mossrose_mutex_t *mutex) -{ - *mutex = CreateMutex(NULL, false, NULL); -} - -void mossrose_mutex_lock(mossrose_mutex_t *mutex) -{ - WaitForSingleObject(*mutex, INFINITE); -} - -int mossrose_mutex_trylock(mossrose_mutex_t *mutex) -{ - int result = WaitForSingleObject(*mutex, 0); - return result != WAIT_OBJECT_0; -} - -void mossrose_mutex_unlock(mossrose_mutex_t *mutex) -{ - ReleaseMutex(*mutex); -} - -void mossrose_mutex_destroy(mossrose_mutex_t *mutex) -{ - ReleaseMutex(*mutex); -} - - -#else -#include <pthread.h> -void mossrose_mutex_init(mossrose_mutex_t *mutex) -{ - pthread_mutex_init(mutex, NULL); -} - -void mossrose_mutex_lock(mossrose_mutex_t *mutex) -{ - pthread_mutex_lock(mutex); -} - -int mossrose_mutex_trylock(mossrose_mutex_t *mutex) -{ - return pthread_mutex_trylock(mutex); -} - -void mossrose_mutex_unlock(mossrose_mutex_t *mutex) -{ - pthread_mutex_unlock(mutex); -} - -void mossrose_mutex_destroy(mossrose_mutex_t *mutex) -{ - pthread_mutex_destroy(mutex); -} -#endif diff --git a/src/mutex.h b/src/mutex.h deleted file mode 100644 index 84d1f63..0000000 --- a/src/mutex.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef MOSSROSE_MUTEX_H -#define MOSSROSE_MUTEX_H - - -#ifdef WIN32 -#include <windows.h> -typedef HANDLE mossrose_mutex_t; -#else -#include <pthread.h> -typedef pthread_mutex_t mossrose_mutex_t; -#endif - -/* initialize a mutex */ -void mossrose_mutex_init(mossrose_mutex_t *mutex); - -/* lock a mutex, hanging until locked */ -void mossrose_mutex_lock(mossrose_mutex_t *mutex); - -/* attempt to lock a mutex. returns 0 on success and 1 otherwise */ -int mossrose_mutex_trylock(mossrose_mutex_t *mutex); - -/* unlock a mutex */ -void mossrose_mutex_unlock(mossrose_mutex_t *mutex); - -/* destroy a mutex */ -void mossrose_mutex_destroy(mossrose_mutex_t *mutex); - -#endif diff --git a/src/sound.c b/src/sound.c new file mode 100644 index 0000000..36c1e5b --- /dev/null +++ b/src/sound.c @@ -0,0 +1,44 @@ +#include <stdlib.h> +#include <string.h> +#include <mossrose.h> +#include "sound.h" + + +int sound_copy(struct mossrose_sound_t *dest, struct mossrose_sound_t *src) +{ + sound_free_audio(dest); + dest->len = src->len; + + /* left channel */ + if (src->left != NULL) { + dest->left = malloc(src->len * sizeof(float)); + if (dest->left == NULL) return 1; + memcpy(dest->left, src->left, src->len * sizeof(float)); + } + else + dest->left = NULL; + + /* right channel */ + if (src->right != NULL) { + dest->right = malloc(src->len * sizeof(float)); + if (dest->right == NULL) return 1; + memcpy(dest->right, src->right, src->len * sizeof(float)); + } + else + dest->right = NULL; + + return 0; +} + + +void sound_free_audio(struct mossrose_sound_t *sound) +{ + if (sound->left == NULL) { + free(sound->left); + sound->left = NULL; + } + if (sound->right == NULL) { + free(sound->right); + sound->right = NULL; + } +} diff --git a/src/sound.h b/src/sound.h new file mode 100644 index 0000000..282441b --- /dev/null +++ b/src/sound.h @@ -0,0 +1,9 @@ +#ifndef MOSSROSE_SOUND_H +#define MOSSROSE_SOUND_H + +#include <mossrose.h> + +int sound_copy(struct mossrose_sound_t *dest, struct mossrose_sound_t *src); +void sound_free_audio(struct mossrose_sound_t *sound); + +#endif diff --git a/src/sound.test.c b/src/sound.test.c new file mode 100644 index 0000000..60dcdd1 --- /dev/null +++ b/src/sound.test.c @@ -0,0 +1,31 @@ +#include "test/mossrose-test.h" + +#include "sound.c" + + +void test_sound_copy() +{ + struct mossrose_sound_t src, dest; + float left[] = { 0.1, 0.2, 0.3 }; + float right[] = { 0.0, 0.5, 1.0 }; + src.left = left; + src.right = right; + src.len = 3; + + dest.left = NULL; + dest.right = NULL; + + sound_copy(&dest, &src); + + lily_assert_int_equal(dest.len, 3); + lily_assert_not_null(dest.left); + lily_assert_memory_equal(dest.left, left, 3*sizeof(float)); + lily_assert_not_null(dest.right); + lily_assert_memory_equal(dest.right, right, 3*sizeof(float)); +} + + +void suite_sound() +{ + lily_run_test(test_sound_copy); +} diff --git a/src/test/lily-test.c b/src/test/lily-test.c new file mode 100644 index 0000000..2f43a28 --- /dev/null +++ b/src/test/lily-test.c @@ -0,0 +1,365 @@ +/**************************************************************** + * + * ======== lily-test ======== + * + * a midsize C unit testing library + * copyright (c) 2022 kate swanson (sanine) + * + * This is anti-capitalist software, released for free use by individuals and + * organizations that do not operate by capitalist principles. + * + * Permission is hereby granted, free of charge, to any person or + * organization (the "User") obtaining a copy of this software and associated + * documentation files (the "Software"), to use, copy, modify, merge, + * distribute, and/or sell copies of the Software, subject to the following + * conditions: + * + * 1. The above copyright notice and this permission notice shall be included + * in all copies or modified versions of the Software. + * + * 2. The User is one of the following: + * a. An individual person, laboring for themselves + * b. A non-profit organization + * c. An educational institution + * d. An organization that seeks shared profit for all of its members, and + * allows non-members to set the cost of their labor + * + * 3. If the User is an organization with owners, then all owners are workers + * and all workers are owners with equal equity and/or equal vote. + * + * 4. If the User is an organization, then the User is not law enforcement or + * military, or working for or under either. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT EXPRESS OR IMPLIED WARRANTY OF + * ANY KIND, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://anticapitalist.software/ + * https://sanine.net + * + ****************************************************************/ + + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "lily-test.h" + +struct lily_globals_t _lily_globals = { {0}, 0, NULL, "" }; + +/* run an individual test */ +void _lily_run_test(const char *name, lily_test_t test) +{ + printf(" %s: ", name); + + _lily_globals.error_msg = NULL; + _lily_globals.error_location = ""; + int val = setjmp(_lily_globals.env); + + if (val) { + /* test failed */ + printf("FAIL - %s (%s)\n", + _lily_globals.error_msg, + _lily_globals.error_location); + free(_lily_globals.error_msg); /* error message was allocated! */ + return; + } + + test(); + + /* test succeeded */ + printf("OK\n"); +} + +/* run a suite */ +void _lily_run_suite(const char *name, lily_test_t suite) +{ + printf("=== %s ===\n", name); + suite(); + printf("\n"); +} + + +/* ======== ASSERTIONS ======== */ + +static void _lily_assert_msg(bool statement, const char *location, + const char *format_string, va_list args) +{ + if (statement) { + va_end(args); + return; // no error, return + } + + _lily_globals.error_location = location; + + va_list args_len; + va_copy(args_len, args); + size_t length = vsnprintf(NULL, 0, format_string, args_len); + va_end(args_len); + + if (_lily_globals.error_msg_len < length+1 || + _lily_globals.error_msg == NULL) { + if (_lily_globals.error_msg != NULL) + free(_lily_globals.error_msg); + + char *msg = malloc((length+2) * sizeof(char)); + + if (msg == NULL) { + fprintf(stderr, "WARNING: failed to allocate memory for failed test message!\n"); + _lily_globals.error_msg = NULL; + va_end(args); + longjmp(_lily_globals.env, 1); + return; + } + else { + _lily_globals.error_msg = msg; + _lily_globals.error_msg_len = length+1; + } + } + + vsnprintf(_lily_globals.error_msg, length+1, format_string, args); + + va_end(args); + longjmp(_lily_globals.env, 1); +} + + +void lily_assert_msg(bool statement, const char *location, + const char *format_string, ...) +{ + va_list args; + va_start(args, format_string); + // _lily_assert_msg may long jump, so it takes calls va_end(args) for you + _lily_assert_msg(statement, location, format_string, args); +} + + +void _lily_assert_true(const char *statement, bool value, const char *location) +{ + lily_assert_msg(value, location, + "%s is not true", + statement); +} + + +void _lily_assert_false(const char *statement, bool value, const char *location) +{ + lily_assert_msg(!value, location, + "%s is not false", + statement); +} + + +void _lily_assert_not_null(const char *name, void *ptr, const char *location) +{ + lily_assert_msg(ptr != NULL, location, + "%s is NULL", + name); +} + + +void _lily_assert_null(const char *name, void *ptr, const char *location) +{ + lily_assert_msg(ptr == NULL, location, + "%s (%p) is not NULL", + name, ptr); +} + + +void _lily_assert_ptr_equal(const char *name_a, const char *name_b, + void *a, void *b, const char *location) +{ + lily_assert_msg(a == b, location, + "%s (%p) is not equal to %s (%p)", + name_a, a, name_b, b); +} + + +void _lily_assert_ptr_not_equal(const char *name_a, const char *name_b, + void *a, void *b, const char *location) +{ + lily_assert_msg(a != b, location, + "%s (%p) is equal to %s", + name_a, a, name_b); +} + + +void _lily_assert_int_equal(const char *name_a, const char *name_b, + intmax_t a, intmax_t b, const char *location) +{ + lily_assert_msg(a == b, location, + "%s (%d) is not equal to %s (%d)", + name_a, a, name_b, b); +} + + +void _lily_assert_int_not_equal(const char *name_a, const char *name_b, + intmax_t a, intmax_t b, const char *location) +{ + lily_assert_msg(a != b, location, + "%s (%d) is equal to %s", + name_a, a, name_b); +} + + +void _lily_assert_float_equal(const char *name_a, const char *name_b, + double a, double b, double epsilon, const char *location) +{ + lily_assert_msg(abs(a - b) <= epsilon, + "%s (%f) is not equal to %s (%f) (epsilon: %f)", + name_a, a, name_b, b, epsilon); +} + + +void _lily_assert_float_not_equal(const char *name_a, const char *name_b, + double a, double b, double epsilon, const char *location) +{ + lily_assert_msg(abs(a - b) > epsilon, + "%s (%f) is equal to %s (%f) (epsilon: %f)", + name_a, a, name_b, b, epsilon); +} + + +void _lily_assert_string_equal(const char *name_a, const char *name_b, + char *a, char *b, const char *location) +{ + lily_assert_msg(strcmp(a, b) == 0, + "%s ('%s') is not equal to %s ('%s')", + name_a, a, name_b, b); +} + + +void _lily_assert_string_not_equal(const char *name_a, const char *name_b, + char *a, char *b, const char *location) +{ + lily_assert_msg(strcmp(a, b) != 0, + "%s ('%s') is equal to %s", + name_a, a, name_b); +} + + +void _lily_assert_memory_equal(const char *name_a, const char *name_b, + void *a, void *b, size_t size, const char *location) +{ + lily_assert_msg(memcmp(a, b, size) == 0, + "%s and %s contain different data", name_a, name_b); +} + + +void _lily_assert_memory_not_equal(const char *name_a, const char *name_b, + void *a, void *b, size_t size, const char *location) +{ + lily_assert_msg(memcmp(a, b, size) == 0, + "%s contains the same data s %s", name_a, name_b); +} + + +/* ======== MOCKS ======== */ + +lily_queue_t* lily_queue_create() +{ + const size_t size = 256 * sizeof(uint8_t); + + lily_queue_t *q = malloc(sizeof(lily_queue_t)); + q->buf_size = size; + q->buf = malloc(size); + q->front = q->buf; + q->back = q->front; + + return q; +} + + +void lily_queue_destroy(lily_queue_t *q) +{ + free(q->buf); + free(q); +} + + +void _lily_enqueue(lily_queue_t *q, size_t size, uint8_t *data) +{ + size_t used = q->back - q->buf; + size_t remaining = q->buf_size - used; + + if (remaining < size) { + /* re-allocate bigger buffer */ + size_t size_new = 2 * q->buf_size; + uint8_t *buf_new = realloc(q->buf, size_new); + if (buf_new == NULL) { + fprintf(stderr, "failed to allocated %lu bytes for queue %p!\n", + size_new, q); + return; + } + q->buf = buf_new; + } + + memcpy(q->back, data, size); + q->back += size; +} + + +void _lily_dequeue(lily_queue_t *q, size_t size, uint8_t *ptr) +{ + size_t dist = q->back - q->front; + if (dist < size) { + fprintf(stderr, "attempted to read %lu bytes out of queue %p, " + "which has %lu bytes presently queued\n", size, q, dist); + return; + } + + memcpy(ptr, q->front, size); + q->front += size; +} + + +lily_mock_t * lily_mock_create() +{ + lily_mock_t *m = malloc(sizeof(lily_mock_t)); + m->n_calls = 0; + m->arguments = lily_queue_create(); + m->values = lily_queue_create(); + return m; +} + +void lily_mock_destroy(lily_mock_t *m) +{ + lily_queue_destroy(m->arguments); + lily_queue_destroy(m->values); + free(m); +} + +void lily_mock_use(lily_mock_t **m) +{ + if (*m != NULL) lily_mock_destroy(*m); + *m = lily_mock_create(); +} + +void _lily_mock_call(lily_mock_t *m, struct lily_mock_arg_t *args, size_t n_args) +{ + m->n_calls += 1; + + for (int i=0; i<n_args; i++) { + _lily_enqueue(m->arguments, args[i].size, args[i].var); + } +} + +void _lily_get_call(lily_mock_t *m, + struct lily_mock_arg_t *args, + size_t n_args, + unsigned int call_num) +{ + size_t stride = 0; + for (int i=0; i<n_args; i++) { + stride += args[i].size; + } + + m->arguments->front = m->arguments->buf + (call_num * stride); + for (int i=0; i<n_args; i++) { + _lily_dequeue(m->arguments, args[i].size, args[i].var); + } +} diff --git a/src/test/lily-test.h b/src/test/lily-test.h new file mode 100644 index 0000000..b5f380c --- /dev/null +++ b/src/test/lily-test.h @@ -0,0 +1,263 @@ +/**************************************************************** + * + * ======== lily-test ======== + * + * a midsize C unit testing library + * copyright (c) 2022 kate swanson (sanine) + * + * This is anti-capitalist software, released for free use by individuals and + * organizations that do not operate by capitalist principles. + * + * Permission is hereby granted, free of charge, to any person or + * organization (the "User") obtaining a copy of this software and associated + * documentation files (the "Software"), to use, copy, modify, merge, + * distribute, and/or sell copies of the Software, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included + * in all copies or modified versions of the Software. + * + * 2. The User is one of the following: + * a. An individual person, laboring for themselves + * b. A non-profit organization + * c. An educational institution + * d. An organization that seeks shared profit for all of its members, and + * allows non-members to set the cost of their labor + * + * 3. If the User is an organization with owners, then all owners are workers + * and all workers are owners with equal equity and/or equal vote. + * + * 4. If the User is an organization, then the User is not law enforcement or + * military, or working for or under either. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT EXPRESS OR IMPLIED WARRANTY OF + * ANY KIND, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://anticapitalist.software/ + * https://sanine.net + * + ****************************************************************/ + + +#ifndef LILY_TEST_H +#define LILY_TEST_H + +#define LILY_VERSION_MAJOR 1 +#define LILY_VERSION_MINOR 0 +#define LILY_VERSION_PATCH 0 + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <setjmp.h> + +#define STR_IMP(x) #x +#define STR(x) STR_IMP(x) +/* define SOURCE_PATH_SIZE to strip away the + leading parts of the full compilation path */ +#ifndef SOURCE_PATH_SIZE +#define LILY_LOCATION (__FILE__ ":" STR(__LINE__)) +#else +#define LILY_LOCATION ((__FILE__ ":" STR(__LINE__)) + SOURCE_PATH_SIZE) +#endif + +/** a few nasty globals that make everything clean for the end user */ +struct lily_globals_t { + jmp_buf env; + size_t error_msg_len; + char *error_msg; + const char *error_location; +}; +extern struct lily_globals_t _lily_globals; + +typedef void (*lily_test_t)(void); + +/** run a single test */ +#define lily_run_test(test) _lily_run_test(#test, test) +void _lily_run_test(const char *name, lily_test_t test); + +/** run a suite */ +#define lily_run_suite(suite) _lily_run_suite(#suite, suite) +void _lily_run_suite(const char *name, lily_test_t suite); + + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * assertions + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +/** basic assertion function, mostly used by the other assertions */ +void lily_assert_msg(bool statement, const char *location, + const char *format_string, ...); + +#define lily_assert_true(statement) _lily_assert_true(#statement, statement, LILY_LOCATION) +void _lily_assert_true(const char *statement, bool value, const char *location); + + +#define lily_assert_false(statement) _lily_assert_false(#statement, statement, LILY_LOCATION) +void _lily_assert_false(const char *statement, bool value, const char *location); + + +#define lily_assert_not_null(ptr) _lily_assert_not_null(#ptr, ptr, LILY_LOCATION) +void _lily_assert_not_null(const char *name, void *ptr, const char *location); + + +#define lily_assert_null(ptr) _lily_assert_null(#ptr, ptr, LILY_LOCATION) +void _lily_assert_null(const char *name, void *ptr, const char *location); + + +#define lily_assert_ptr_equal(a, b) _lily_assert_ptr_equal(#a, #b, a, b, LILY_LOCATION) +void _lily_assert_ptr_equal(const char *name_a, const char *name_b, + void *a, void *b, const char *location); + + +#define lily_assert_ptr_not_equal(a, b) _lily_assert_ptr_not_equal(#a, #b, a, b, LILY_LOCATION) +void _lily_assert_ptr_not_equal(const char *name_a, const char *name_b, + void *a, void *b, const char *location); + + +#define lily_assert_int_equal(a, b) _lily_assert_int_equal(#a, #b, a, b, LILY_LOCATION) +void _lily_assert_int_equal(const char *name_a, const char *name_b, + intmax_t a, intmax_t b, const char *location); + + +#define lily_assert_int_not_equal(a, b) _lily_assert_int_not_equal(#a, #b, a, b, LILY_LOCATION) +void _lily_assert_int_not_equal(const char *name_a, const char *name_b, + intmax_t a, intmax_t b, const char *location); + + +#define lily_assert_float_equal(a, b, epsilon) \ + _lily_assert_float_equal(#a, #b, a, b, epsilon, LILY_LOCATION) +void _lily_assert_float_equal(const char *name_a, const char *name_b, + double a, double b, double epsilon, const char *location); + + +#define lily_assert_float_not_equal(a, b, epsilon) \ + _lily_assert_float_not_equal(#a, #b, a, b, epsilon, LILY_LOCATION) +void _lily_assert_float_not_equal(const char *name_a, const char *name_b, + double a, double b, double epsilon, const char *location); + + +#define lily_assert_string_equal(a, b) _lily_assert_string_equal(#a, #b, a, b, LILY_LOCATION) +void _lily_assert_string_equal(const char *name_a, const char *name_b, + char *a, char *b, const char *location); + + +#define lily_assert_string_not_equal(a, b) _lily_assert_string_not_equal(#a, #b, a, b, LILY_LOCATION) +void _lily_assert_string_not_equal(const char *name_a, const char *name_b, + char *a, char *b, const char *location); + + +#define lily_assert_memory_equal(a, b, size) \ + _lily_assert_memory_equal(#a, #b, a, b, size, LILY_LOCATION) +void _lily_assert_memory_equal(const char *name_a, const char *name_b, + void *a, void *b, size_t size, const char *location); + + +#define lily_assert_memory_not_equal(a, b, size) \ + _lily_assert_memory_not_equal(#a, #b, a, b, size, LILY_LOCATION) +void _lily_assert_memory_not_equal(const char *name_a, const char *name_b, + void *a, void *b, size_t size, const char *location); + + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * mocks + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + +/** queue structure capable of containing arbitrary data */ +typedef struct lily_queue_t { + size_t buf_size; + uint8_t *buf; + uint8_t *front, *back; +} lily_queue_t; + + +/** create a queue */ +lily_queue_t* lily_queue_create(); + +/** destroy a queue */ +void lily_queue_destroy(lily_queue_t *q); + + +/** enqueue a value + * + * q - the queue to append to + * type - the type of the value to append + * value - the value to append + */ +#define lily_enqueue(q, type, value) \ + do { \ + type _var = value; \ + _lily_enqueue(q, sizeof(type), (uint8_t*)(&_var)); \ + } while(0) +void _lily_enqueue(lily_queue_t *q, size_t size, uint8_t *data); + + +/** pop a value from the queue + * + * q - the queue to pop from + * type - the type of the value to pop + * ptr - the location to store the popped value + */ +#define lily_dequeue(q, type, ptr) \ + _lily_dequeue(q, sizeof(type), (uint8_t*) ptr) +void _lily_dequeue(lily_queue_t *q, size_t size, uint8_t *ptr); + + +struct lily_mock_arg_t { + size_t size; + void *var; +}; + +#define LILY_NARGS(args) (sizeof(args)/sizeof(struct lily_mock_arg_t)) + +/** structure to store data for mock functions */ +typedef struct lily_mock_t { + unsigned int n_calls; + lily_queue_t *arguments; + lily_queue_t *values; +} lily_mock_t; + +/** setup mock function storage */ +lily_mock_t * lily_mock_create(); +/** tear down mock function storage */ +void lily_mock_destroy(lily_mock_t *m); +/** automatically re-create mock function storage */ +void lily_mock_use(lily_mock_t **m); + +/** store a call to a mock function */ +#define lily_mock_store_call(m, args) \ + _lily_mock_call(m, args, LILY_NARGS(args)) +/* lily_mock_call is deprecated!! do not use it in new programs */ +#define lily_mock_call(m, args) \ + _lily_mock_call(m, args, LILY_NARGS(args)) +void _lily_mock_call(lily_mock_t *m, struct lily_mock_arg_t *args, size_t n_args); + +/** retrieve a call to a mock function */ +#define lily_mock_get_call(m, args, call_num) \ + _lily_get_call(m, args, LILY_NARGS(args), call_num) +/* lily_get_call is deprecated!! do not use it in new programs */ +#define lily_get_call(m, args, call_num) \ + _lily_get_call(m, args, LILY_NARGS(args), call_num) +void _lily_get_call(lily_mock_t *m, + struct lily_mock_arg_t *args, + size_t n_args, + unsigned int call_num); + +/** store a value in a mock structure */ +#define lily_store_value(m, type, value) \ + lily_enqueue(m->values, type, value) +/** retrieve a value from a mock structure */ +#define lily_get_value(m, type, ptr) \ + lily_dequeue(m->values, type, ptr) + +#endif diff --git a/src/test/mossrose-test.c b/src/test/mossrose-test.c new file mode 100644 index 0000000..88ce5ff --- /dev/null +++ b/src/test/mossrose-test.c @@ -0,0 +1,8 @@ +#include "mossrose-test.h" + + +int main() +{ + RUN_TESTS() + return 0; +} diff --git a/src/test/mossrose-test.h b/src/test/mossrose-test.h new file mode 100644 index 0000000..201a51b --- /dev/null +++ b/src/test/mossrose-test.h @@ -0,0 +1,13 @@ +#ifndef MOSSROSE_TEST_H +#define MOSSROSE_TEST_H + +#include "lily-test.h" + +void suite_channel(); +void suite_sound(); + +#define RUN_TESTS() \ + lily_run_suite(suite_channel); \ + lily_run_suite(suite_sound); \ + +#endif |