From ae998c22b2ce4f1023a6c9c2e925324e2c86c6a1 Mon Sep 17 00:00:00 2001 From: Eric Herman Date: Wed, 18 Dec 2024 13:50:03 +0100 Subject: [PATCH] MDEV-35683: add basic unit test for DYNAMIC_ARRAY This demonstrates and tests the basic usage of DYNAMIC_ARRAY. Additional tests could be added to demonstrate and test the remaining functions and capabilities. Signed-off-by: Eric Herman --- unittest/mysys/CMakeLists.txt | 2 +- unittest/mysys/dynamic_array-t.c | 246 +++++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 unittest/mysys/dynamic_array-t.c diff --git a/unittest/mysys/CMakeLists.txt b/unittest/mysys/CMakeLists.txt index 1f5681b3cc8..b0cdae738b9 100644 --- a/unittest/mysys/CMakeLists.txt +++ b/unittest/mysys/CMakeLists.txt @@ -14,7 +14,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA MY_ADD_TESTS(bitmap base64 my_atomic my_rdtsc lf my_malloc my_getopt dynstring - byte_order my_tzinfo + byte_order my_tzinfo dynamic_array queues stacktrace stack_allocation crc32 LINK_LIBRARIES mysys) MY_ADD_TESTS(my_vsnprintf LINK_LIBRARIES strings mysys) MY_ADD_TESTS(aes LINK_LIBRARIES mysys mysys_ssl) diff --git a/unittest/mysys/dynamic_array-t.c b/unittest/mysys/dynamic_array-t.c new file mode 100644 index 00000000000..bd538760f51 --- /dev/null +++ b/unittest/mysys/dynamic_array-t.c @@ -0,0 +1,246 @@ +/* Copyright (c) 2024 Eric Herman and MariaDB Foundation. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include +#include +#include + +struct thing { + unsigned id; + char name[40]; +}; + +#define THING_STACK_BUF_SIZE 3 +#define ROUND_TRIP_NO_GROW_SIZE (THING_STACK_BUF_SIZE - 1) +#define ROUND_TRIP_NEED_GROW_SIZE ((2 * THING_STACK_BUF_SIZE) + 1) + +int dyn_array_round_trip(size_t grow_to_size) +{ + struct thing things_buf[THING_STACK_BUF_SIZE]; + + DYNAMIC_ARRAY things_array; + PSI_memory_key psi_key= PSI_NOT_INSTRUMENTED; + size_t element_size= sizeof(struct thing); + void *init_buffer= (void *)&things_buf; + size_t init_alloc= THING_STACK_BUF_SIZE; + size_t alloc_increment= 2; + myf my_flags= MYF(MY_WME); + my_bool err= FALSE; + + ok(1, "THING_STACK_BUF_SIZE: %d, grow_to_size: %zu", THING_STACK_BUF_SIZE, + grow_to_size); + + err= my_init_dynamic_array2(psi_key, &things_array, element_size, init_buffer, + init_alloc, alloc_increment, my_flags); + + ok(!err, "my_init_dynamic_array2"); + if (err) { + return EXIT_FAILURE; + } + + for (size_t i= 0; i < grow_to_size; ++i) { + struct thing tmp_thing; + + tmp_thing.id= (unsigned)i; + snprintf(tmp_thing.name, sizeof(tmp_thing.name), "thing %zu", i); + + err= insert_dynamic(&things_array, &tmp_thing); + ok(!err, "insert_dynamic for %zu", i); + if (err) { + delete_dynamic(&things_array); + return EXIT_FAILURE; + } + } + ok(grow_to_size == things_array.elements, "size expect: %zu, actual: %zu", + grow_to_size, things_array.elements); + + for (size_t i= 0; i < things_array.elements; ++i) { + struct thing retrieved, expected; + + expected.id= (unsigned)i; + snprintf(expected.name, sizeof(expected.name), "thing %zu", i); + + memset(&retrieved, 0x00, sizeof(struct thing)); + get_dynamic(&things_array, &retrieved, i); + + ok(retrieved.id == expected.id, "%zu: retrieved id: %u, expected id: %u", + i, retrieved.id, expected.id); + ok(strncmp(retrieved.name, expected.name, sizeof(expected.name)) == 0, + "%zu: retrieved name: '%s', expected name: '%s'", + i, retrieved.name, expected.name); + } + + delete_dynamic(&things_array); + + return EXIT_SUCCESS; +} +static const int plan_round_trip_no_grow= 3 + (3 * ROUND_TRIP_NO_GROW_SIZE); +static const int plan_round_trip_grow= 3 + (3 * ROUND_TRIP_NEED_GROW_SIZE); + +int dyn_array_push_pop(void) +{ + DYNAMIC_ARRAY things_array; + PSI_memory_key psi_key= PSI_NOT_INSTRUMENTED; + size_t element_size= sizeof(struct thing); + size_t init_alloc= THING_STACK_BUF_SIZE; + size_t alloc_increment= 2; + myf my_flags= MYF(MY_WME); + my_bool err= FALSE; + struct thing tmp_thing, *popped; + + err= my_init_dynamic_array(psi_key, &things_array, element_size, init_alloc, + alloc_increment, my_flags); + + ok(!err, "my_init_dynamic_array"); + if (err) { + return EXIT_FAILURE; + } + + tmp_thing.id= 0; + snprintf(tmp_thing.name, sizeof(tmp_thing.name), "thing 0"); + err= push_dynamic(&things_array, &tmp_thing); + ok(!err, "push_dynamic for 0"); + if (err) { + delete_dynamic(&things_array); + return EXIT_FAILURE; + } + + tmp_thing.id= 1; + snprintf(tmp_thing.name, sizeof(tmp_thing.name), "thing 1"); + err= push_dynamic(&things_array, &tmp_thing); + ok(!err, "push_dynamic for 1"); + if (err) { + delete_dynamic(&things_array); + return EXIT_FAILURE; + } + + ok(2 == things_array.elements, "size expect: %d, actual: %zu", + 2, things_array.elements); + + popped= (struct thing *)pop_dynamic(&things_array); + ok(popped != NULL, "pop_dynamic 1"); + if (!popped) { + delete_dynamic(&things_array); + return EXIT_FAILURE; + } + ok(popped->id == 1, "pop expect 1, popped->id: %u", popped->id); + ok(1 == things_array.elements, "size expect: %d, actual: %zu", + 1, things_array.elements); + + popped= (struct thing *)pop_dynamic(&things_array); + ok(popped != NULL, "pop_dynamic 0"); + if (!popped) { + delete_dynamic(&things_array); + return EXIT_FAILURE; + } + ok(popped->id == 0, "pop expect 0, popped->id: %u", popped->id); + ok(0 == things_array.elements, "size expect: %d, actual: %zu", + 0, things_array.elements); + + + popped= (struct thing *)pop_dynamic(&things_array); + ok(!popped, "pop %p from empty array", popped); + + delete_dynamic(&things_array); + + return EXIT_SUCCESS; +} +static const int plan_push_pop= 11; + +struct string_thing { + unsigned id; + char *str; + size_t str_size; +}; + +static void free_string_thing(void *p) +{ + struct string_thing *thing= (struct string_thing *)p; + my_free(thing->str); +} + +#define NUM_DELETE_WITH_CALLBACK 3 + +int dyn_array_delete_with_callback(void) +{ + DYNAMIC_ARRAY things_array; + PSI_memory_key psi_key= PSI_NOT_INSTRUMENTED; + size_t element_size= sizeof(struct string_thing); + size_t init_alloc= (NUM_DELETE_WITH_CALLBACK - 1); + size_t alloc_increment= 2; + myf my_flags= MYF(MY_WME); + my_bool err= FALSE; + + err= my_init_dynamic_array(psi_key, &things_array, element_size, init_alloc, + alloc_increment, my_flags); + + ok(!err, "my_init_dynamic_array"); + if (err) { + return EXIT_FAILURE; + } + + for (size_t i= 0; i < NUM_DELETE_WITH_CALLBACK; ++i) { + struct string_thing thing= { (unsigned)i, NULL, 40 }; + thing.str= my_malloc(psi_key, thing.str_size, my_flags); + ok(thing.str != NULL, "%zu thing.str= malloc(%zu))", i, thing.str_size); + if (!thing.str) { + delete_dynamic_with_callback(&things_array, free_string_thing); + return EXIT_FAILURE; + } + snprintf(thing.str, thing.str_size, "thing %zu", i); + + err= insert_dynamic(&things_array, &thing); + ok(!err, "insert_dynamic for %zu", i); + if (err) { + delete_dynamic_with_callback(&things_array, free_string_thing); + return EXIT_FAILURE; + } + } + ok(3 == things_array.elements, "size expect: %d, actual: %zu", + 3, things_array.elements); + + delete_dynamic_with_callback(&things_array, free_string_thing); + + ok(0 == things_array.elements, "size expect: %d, actual: %zu", + 0, things_array.elements); + + return EXIT_SUCCESS; +} +static const int plan_delete_with_callback= 3 + (2 * NUM_DELETE_WITH_CALLBACK); + +int main(void) +{ + int err= 0; + + plan(plan_round_trip_no_grow + 1 + + plan_round_trip_grow + 1 + + plan_push_pop + 1 + + plan_delete_with_callback + 1 + ); + + err= dyn_array_round_trip(ROUND_TRIP_NO_GROW_SIZE); + ok(!err, "dyn_array_round_trip (no need to grow)"); + + err= dyn_array_round_trip(ROUND_TRIP_NEED_GROW_SIZE); + ok(!err, "dyn_array_round_trip (need to grow)"); + + err= dyn_array_push_pop(); + ok(!err, "dyn_array_push_pop"); + + err= dyn_array_delete_with_callback(); + ok(!err, "dyn_array_delete_with_callback"); + + return exit_status(); +}