From e4a85cdc611b87438401b13b7ead85203f4f8a8f Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Sat, 6 Apr 2024 19:13:38 +0200 Subject: [PATCH] Add tests for exception+array --- tests/Makefile | 2 +- tests/except-array.c | 102 +++++++++++++++++ tests/test-obj-except.h | 236 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 tests/except-array.c create mode 100644 tests/test-obj-except.h diff --git a/tests/Makefile b/tests/Makefile index 6ef673d5..f2cd2658 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -102,7 +102,7 @@ exe: depend @echo All executable generated check: depend - $(MAKE) `ls test-*.c|$(SED) 's/\.c/\.log/g'` `ls fail-*.c|$(SED) 's/\.c/\.log.err/g'` CC="$(CC)" CFLAGS="$(CFLAGS)" + $(MAKE) `ls test-*.c|$(SED) 's/\.c/\.log/g'` `ls fail-*.c|$(SED) 's/\.c/\.log.err/g'` `ls except-*.c|$(SED) 's/\.c/\.log/g'` CC="$(CC)" CFLAGS="$(CFLAGS)" @echo All tests passed check-stl: depend diff --git a/tests/except-array.c b/tests/except-array.c new file mode 100644 index 00000000..78933f22 --- /dev/null +++ b/tests/except-array.c @@ -0,0 +1,102 @@ +/* + * Test that the array module properly support exceptions + * + * Copyright (c) 2017-2024, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include "test-obj-except.h" +#include "m-array.h" +#include "m-string.h" + +M_TRY_DEF_ONCE() + +ARRAY_DEF(array_obj, test_obj_except__t, TEST_OBJ_EXCEPT_OPLIST) +#define M_OPL_array_obj_t() ARRAY_OPLIST(array_obj, TEST_OBJ_EXCEPT_OPLIST) + +static void test1(unsigned n) +{ + FILE *f = m_core_fopen ("a-earray.dat", "wt"); + assert(f != NULL); + M_TRY(test1) { + M_LET(obj, test_obj_except__t) + M_LET(array, tmp, array_obj_t) { + for(unsigned i = 0; i < n; i++) { + test_obj_except__set_ui(obj, i); + array_obj_push_back(array, obj); + } + array_obj_set(tmp, array); + M_LET( (tmp2, tmp), array_obj_t) { + array_obj_out_str(f, tmp2); + } + array_obj_push_back(tmp, obj); + array_obj_resize(tmp, 4); + array_obj_push_new(tmp); + array_obj_resize(tmp, 20); + array_obj_set_at(tmp, 10, obj); + array_obj_push_at(tmp, 10, obj); + test_obj_except__t *ref = array_obj_safe_get(tmp, 30); + assert(ref != NULL); + array_obj_splice(tmp, array); + M_LET(str, string_t) { + array_obj_get_str(str, tmp, false); + bool b = array_obj_parse_str(array, string_get_cstr(str), NULL); + assert(b); + } + } + } M_CATCH(test1, 0) { + // Nothing to do + } + fclose(f); + + f = m_core_fopen ("a-earray.dat", "rt"); + assert(f != NULL); + M_TRY(test1) { + M_LET(obj, test_obj_except__t) + M_LET(array, tmp, array_obj_t) { + bool b = array_obj_in_str(array, f); + (void)b; + } + } M_CATCH(test1, 0) { + // Nothing to do + } +} + +static void do_test1(void) +{ + // Run once to get the number of exceptions point existing in the test service + test_obj_except__trigger_exception = 0; + test1(10); + int count = -test_obj_except__trigger_exception; + assert(count > 0); + // Run once again the test service, and for each registered exception point, throw an exception + for(int i = 1; i <= count; i++) { + test_obj_except__trigger_exception = i; + test1(10); + // Check there is no memory leak + test_obj_except__final_check(); + } +} + +int main(void) +{ + do_test1(); + exit(0); +} diff --git a/tests/test-obj-except.h b/tests/test-obj-except.h new file mode 100644 index 00000000..b42b63b9 --- /dev/null +++ b/tests/test-obj-except.h @@ -0,0 +1,236 @@ +/* + * Define a test object that sometimes throw exception + * Use only for tests + * + * Copyright (c) 2017-2024, Patrick Pelissier + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef TEST_OBJ_EXCEPT_T +#define TEST_OBJ_EXCEPT_T + +#include +#include +#include + +// Overwrite malloc to throw exceptions sometimes +// Needs to be done before including M*LIB headers +static inline void test_obj_except_trigger(void); +#ifdef __cplusplus +# include +# define M_MEMORY_ALLOC(type) (test_obj_except_trigger(), (type*)std::malloc (sizeof (type))) +# define M_MEMORY_DEL(ptr) std::free(ptr) +# define M_MEMORY_REALLOC(type, ptr, n) \ + ((type*) (test_obj_except_trigger(), std::realloc ((ptr), (n)*sizeof (type)))) +# define M_MEMORY_FREE(ptr) std::free(ptr) +#else +# define M_MEMORY_ALLOC(type) (test_obj_except_trigger(), malloc (sizeof (type))) +# define M_MEMORY_DEL(ptr) free(ptr) +# define M_MEMORY_REALLOC(type, ptr, n) (test_obj_except_trigger(), realloc ((ptr), (n)*sizeof (type))) +# define M_MEMORY_FREE(ptr) free(ptr) +#endif + +// Include M*LIB headers +#include "m-core.h" +#include "m-try.h" +#include "m-string.h" + +M_BEGIN_PROTECTED_CODE + +/* This is a trivial encapsulation of an opaque structure that triggers exception. + Used for test purpose only.*/ +typedef struct test_obj_except__s { + unsigned int n; + unsigned int *ptr; +} test_obj_except__t[1]; + +int test_obj_except__init_counter = 0; +int test_obj_except__trigger_exception = 0; + +// Trigger an exception if the counter reaches 0 +static inline void test_obj_except_trigger(void) +{ + test_obj_except__trigger_exception --; + if (test_obj_except__trigger_exception == 0) { + M_THROW(M_ERROR_MEMORY); + } +} + +// All created testobj shall have been cleared at the end. +static inline void test_obj_except__final_check(void) +{ + assert(test_obj_except__init_counter == 0); +} + +static inline void test_obj_except__init(test_obj_except__t z) +{ + test_obj_except_trigger(); + z->n = 1; + z->ptr = (unsigned int*) calloc(1, sizeof(unsigned int)); + assert(z->ptr != NULL); + //printf("%p: init\n", z); + test_obj_except__init_counter++; +} + +static inline void test_obj_except__clear(test_obj_except__t z) +{ + //printf("%p: clear\n", z); + assert (z->ptr != NULL); + free(z->ptr); + z->ptr = NULL; + test_obj_except__init_counter--; + assert(test_obj_except__init_counter >= 0); +} + +static inline void test_obj_except__init_set(test_obj_except__t d, const test_obj_except__t s) +{ + test_obj_except_trigger(); + d->n = s->n; + d->ptr = (unsigned int *) calloc (s->n, sizeof(unsigned int)); + assert(d->ptr != NULL); + memcpy(d->ptr, s->ptr, sizeof(unsigned int) * s->n); + //printf("%p: init_set\n", d); + test_obj_except__init_counter++; +} + +static inline void test_obj_except__set(test_obj_except__t d, const test_obj_except__t s) +{ + if (d != s) { + test_obj_except_trigger(); + //printf("%p: set\n", d); + test_obj_except__clear(d); + // Don't trigger exception for init_set now + test_obj_except__trigger_exception++; + test_obj_except__init_set(d, s); + } +} + +static inline void test_obj_except__set_ui(test_obj_except__t d, unsigned int v) +{ + test_obj_except_trigger(); + d->n = 1; + d->ptr[0] = v; +} + +static inline void test_obj_except__init_set_ui(test_obj_except__t d, unsigned int v) +{ + test_obj_except_trigger(); + test_obj_except__init(d); + test_obj_except__set_ui(d, v); +} + +static inline void test_obj_except__init_set_str(test_obj_except__t d, const char v[]) +{ + test_obj_except_trigger(); + test_obj_except__init(d); + test_obj_except__set_ui(d, (unsigned int) atoi(v) ); +} + +static inline unsigned int test_obj_except__get_ui(const test_obj_except__t z) +{ + return z->ptr[0]; +} + +static inline void test_obj_except__add(test_obj_except__t d, const test_obj_except__t a, const test_obj_except__t b) +{ + test_obj_except_trigger(); + d->ptr[0] = a->ptr[0] + b->ptr[0]; +} + +static inline void test_obj_except__out_str(FILE *f, const test_obj_except__t z) +{ + test_obj_except_trigger(); + assert(z->n == 1); + fprintf(f, "%u", z->ptr[0]); +} + +static inline bool test_obj_except__in_str(test_obj_except__t z, FILE *f) +{ + test_obj_except_trigger(); + z->n = 1; + return m_core_fscanf(f, "%u", &z->ptr[0]) == 1; +} + +static inline bool test_obj_except__parse_str(test_obj_except__t z, const char str[], const char **endptr) +{ + test_obj_except_trigger(); + z->n = 1; + char *end; + z->ptr[0] = (unsigned int) strtol(str, &end, 0); + if (endptr) { *endptr = (const char*) end; } + return (uintptr_t) end != (uintptr_t) str; +} + +static inline bool test_obj_except__equal_p(const test_obj_except__t z1, const test_obj_except__t z2) +{ + if (z1->n != z2->n) return false; + return memcmp(z1->ptr, z2->ptr, z1->n*sizeof(unsigned int)) == 0; +} + +static inline int test_obj_except__cmp(const test_obj_except__t z1, const test_obj_except__t z2) +{ + int ret = 0; + if (z1->n != z2->n) { + ret = z1-> n < z2->n ? -1 : 1; + } else { + for(unsigned int i = 0; i < z1->n; i++) { + if (z1->ptr[i] != z2->ptr[i]) { + ret = z1->ptr[i] < z2->ptr[i] ? -1 : 1; + break; + } + } + } + return ret; +} + +static inline int test_obj_except__cmp_ui(const test_obj_except__t z1, unsigned int z2) +{ + if (z1->n != 1) return z1-> n < 1 ? -1 : 1; + return z1->ptr[0] < z2 ? -1 : z1->ptr[0] > z2; +} + +static inline void test_obj_except__str(string_t str, const test_obj_except__t z, bool append) +{ + if (append) string_cat_printf (str, "%u", z->ptr[0]); + else string_printf (str, "%u", z->ptr[0]); +} + +#define TEST_OBJ_EXCEPT_OPLIST \ + (INIT(test_obj_except__init), \ + INIT_SET(test_obj_except__init_set), \ + SET(test_obj_except__set), \ + CLEAR(test_obj_except__clear), \ + TYPE(test_obj_except__t), \ + ADD(test_obj_except__add), \ + OUT_STR(test_obj_except__out_str), \ + IN_STR(test_obj_except__in_str), \ + PARSE_STR(test_obj_except__parse_str), \ + GET_STR(test_obj_except__str), \ + EQUAL(test_obj_except__equal_p), \ + CMP(test_obj_except__cmp), \ + INIT_WITH(test_obj_except__init_set_ui), \ + EMPLACE_TYPE( LIST( (_ui, test_obj_except__init_set_ui, unsigned int), (_str, test_obj_except__init_set_str, const char *), ( /*empty*/, test_obj_except__init_set, test_obj_except__t) ) ) \ + ) + +#define M_OPL_test_obj_except__t() TEST_OBJ_EXCEPT_OPLIST + +M_END_PROTECTED_CODE + +#endif